changeset 4808:93bc9770f938 HEAD

Initial code for separation of mailbox accessing and directory layout handling. It's not yet possible to change the default layouts though.
author Timo Sirainen <tss@iki.fi>
date Thu, 16 Nov 2006 02:16:31 +0200
parents ef3e7ba494bc
children af1c6c3f674a
files configure.in src/deliver/Makefile.am src/imap/Makefile.am src/imap/cmd-list.c src/imap/cmd-subscribe.c src/imap/commands-util.c src/imap/main.c src/lib-storage/Makefile.am src/lib-storage/index/dbox/Makefile.am src/lib-storage/index/dbox/dbox-list.c src/lib-storage/index/dbox/dbox-storage.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/maildir/Makefile.am src/lib-storage/index/maildir/maildir-list.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/mbox/Makefile.am src/lib-storage/index/mbox/mbox-list.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/index/mbox/mbox-storage.h src/lib-storage/list/.cvsignore src/lib-storage/list/mailbox-list-fs-iter.c src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-fs.h src/lib-storage/list/mailbox-list-maildir-iter.c src/lib-storage/list/mailbox-list-maildir.c src/lib-storage/list/mailbox-list-maildir.h src/lib-storage/list/subscription-file.c src/lib-storage/list/subscription-file.h src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c src/lib-storage/mailbox-list.h src/lib-storage/mailbox-tree.h src/lib-storage/register/.cvsignore src/lib-storage/register/Makefile.am src/lib-storage/subscription-file/Makefile.am src/lib-storage/subscription-file/subscription-file.c src/lib-storage/subscription-file/subscription-file.h src/plugins/acl/Makefile.am src/plugins/acl/acl-plugin.c src/plugins/acl/acl-plugin.h src/plugins/acl/acl-storage.c src/plugins/convert/Makefile.am src/plugins/convert/convert-storage.c src/plugins/expire/Makefile.am src/plugins/quota/quota-maildir.c src/pop3/Makefile.am
diffstat 53 files changed, 3140 insertions(+), 2823 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Wed Nov 15 21:14:08 2006 +0200
+++ b/configure.in	Thu Nov 16 02:16:31 2006 +0200
@@ -1843,11 +1843,11 @@
 src/lib-otp/Makefile
 src/lib-settings/Makefile
 src/lib-storage/Makefile
+src/lib-storage/list/Makefile
 src/lib-storage/index/Makefile
 src/lib-storage/index/maildir/Makefile
 src/lib-storage/index/mbox/Makefile
 src/lib-storage/index/dbox/Makefile
-src/lib-storage/subscription-file/Makefile
 src/lib-storage/register/Makefile
 src/auth/Makefile
 src/deliver/Makefile
--- a/src/deliver/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/deliver/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -23,7 +23,7 @@
 	../lib-storage/register/libstorage-register.a \
 	$(STORAGE_LIBS) \
 	../lib-storage/libstorage.a \
-	../lib-storage/subscription-file/libstorage_subscription_file.a \
+	../lib-storage/list/libstorage_list.a \
 	../lib-imap/libimap.a \
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \
--- a/src/imap/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/imap/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -22,8 +22,8 @@
 libs = \
 	../lib-storage/register/libstorage-register.a \
 	$(STORAGE_LIBS) \
+	../lib-storage/list/libstorage_list.a \
 	../lib-storage/libstorage.a \
-	../lib-storage/subscription-file/libstorage_subscription_file.a \
 	../lib-imap/libimap.a \
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \
--- a/src/imap/cmd-list.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/imap/cmd-list.c	Thu Nov 16 02:16:31 2006 +0200
@@ -9,8 +9,8 @@
 #include "namespace.h"
 
 enum {
-	_MAILBOX_LIST_HIDE_CHILDREN	= 0x1000000,
-	_MAILBOX_LIST_LISTEXT		= 0x0800000
+	_MAILBOX_LIST_ITER_HIDE_CHILDREN	= 0x1000000,
+	_MAILBOX_LIST_ITER_LISTEXT		= 0x0800000
 };
 
 struct cmd_list_context {
@@ -19,7 +19,7 @@
 	enum mailbox_list_flags list_flags;
 
 	struct namespace *ns;
-	struct mailbox_list_context *list_ctx;
+	struct mailbox_list_iterate_context *list_iter;
 	struct imap_match_glob *glob;
 
 	unsigned int lsub:1;
@@ -28,24 +28,25 @@
 };
 
 static const char *
-mailbox_flags2str(enum mailbox_flags flags, enum mailbox_list_flags list_flags)
+mailbox_flags2str(enum mailbox_info_flags flags,
+		  enum mailbox_list_flags list_flags)
 {
 	const char *str;
 
 	if (flags & MAILBOX_PLACEHOLDER) {
 		i_assert((flags & ~MAILBOX_CHILDREN) == MAILBOX_PLACEHOLDER);
 
-		if ((list_flags & _MAILBOX_LIST_LISTEXT) == 0)
+		if ((list_flags & _MAILBOX_LIST_ITER_LISTEXT) == 0)
 			flags = MAILBOX_NOSELECT;
 		flags |= MAILBOX_CHILDREN;
 	}
 	if ((flags & MAILBOX_NONEXISTENT) != 0 &&
-	    (list_flags & _MAILBOX_LIST_LISTEXT) == 0) {
+	    (list_flags & _MAILBOX_LIST_ITER_LISTEXT) == 0) {
 		flags |= MAILBOX_NOSELECT;
 		flags &= ~MAILBOX_NONEXISTENT;
 	}
 
-	if ((list_flags & _MAILBOX_LIST_HIDE_CHILDREN) != 0)
+	if ((list_flags & _MAILBOX_LIST_ITER_HIDE_CHILDREN) != 0)
 		flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN);
 
 	str = t_strconcat(
@@ -78,9 +79,9 @@
 		atom = IMAP_ARG_STR(args);
 
 		if (strcasecmp(atom, "SUBSCRIBED") == 0)
-			*list_flags |= MAILBOX_LIST_SUBSCRIBED;
+			*list_flags |= MAILBOX_LIST_ITER_SUBSCRIBED;
 		else if (strcasecmp(atom, "CHILDREN") == 0)
-			*list_flags |= MAILBOX_LIST_CHILDREN;
+			*list_flags |= MAILBOX_LIST_ITER_CHILDREN;
 		else {
 			client_send_tagline(cmd, t_strconcat(
 				"BAD Invalid list option ", atom, NULL));
@@ -97,7 +98,7 @@
 	const char *str;
 
 	if (!ctx->inbox_found && ctx->ns->inbox && ctx->match_inbox &&
-	    (ctx->list_flags & MAILBOX_LIST_SUBSCRIBED) == 0) {
+	    (ctx->list_flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0) {
 		/* INBOX always exists */
 		str = t_strdup_printf("* LIST (\\Unmarked) \"%s\" \"INBOX\"",
 				      ctx->ns->sep_str);
@@ -108,12 +109,12 @@
 static int
 list_namespace_mailboxes(struct client *client, struct cmd_list_context *ctx)
 {
-	struct mailbox_list *list;
+	struct mailbox_info *info;
 	const char *name;
 	string_t *str, *name_str;
-	int ret;
+	int ret = 0;
 
-	if (ctx->list_ctx == NULL) {
+	if (ctx->list_iter == NULL) {
 		list_namespace_inbox(client, ctx);
 		return 1;
 	}
@@ -121,13 +122,13 @@
 	t_push();
 	str = t_str_new(256);
 	name_str = t_str_new(256);
-	while ((list = mail_storage_mailbox_list_next(ctx->list_ctx)) != NULL) {
+	while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) {
 		str_truncate(name_str, 0);
 		/* when listing INBOX from inbox=yes namespace, don't insert
 		   the namespace prefix */
-		if (strcasecmp(list->name, "INBOX") != 0 || !ctx->ns->inbox)
+		if (strcasecmp(info->name, "INBOX") != 0 || !ctx->ns->inbox)
 			str_append(name_str, ctx->ns->prefix);
-		str_append(name_str, list->name);
+		str_append(name_str, info->name);
 
 		if (ctx->ns->sep != ctx->ns->real_sep) {
                         char *p = str_c_modifiable(name_str);
@@ -156,7 +157,7 @@
 		str_truncate(str, 0);
 		str_printfa(str, "* %s (%s) \"%s\" ",
 			    ctx->lsub ? "LSUB" : "LIST",
-			    mailbox_flags2str(list->flags, ctx->list_flags),
+			    mailbox_flags2str(info->flags, ctx->list_flags),
 			    ctx->ns->sep_str);
 		imap_quote_append_string(str, name, FALSE);
 		if (client_send_line(client, str_c(str)) == 0) {
@@ -166,11 +167,15 @@
 		}
 	}
 
-        list_namespace_inbox(client, ctx);
-	t_pop();
+	if (mailbox_list_iter_deinit(&ctx->list_iter) < 0) {
+		mail_storage_set_list_error(ctx->ns->storage);
+		ret = -1;
+	}
 
-	ret = mail_storage_mailbox_list_deinit(&ctx->list_ctx);
-	ctx->list_ctx = NULL;
+	if (ret == 0)
+		list_namespace_inbox(client, ctx);
+
+	t_pop();
 	return ret < 0 ? -1 : 1;
 }
 
@@ -222,8 +227,8 @@
 	struct namespace *ns = ctx->ns;
 	const char *cur_ns_prefix, *cur_ref, *cur_mask;
 	enum imap_match_result match;
-	enum mailbox_list_flags list_flags;
 	enum imap_match_result inbox_match;
+	struct mailbox_list *list;
 	struct imap_match_glob *inbox_glob;
 	unsigned int count;
 	size_t len;
@@ -306,11 +311,11 @@
 
 		len = strlen(ns->prefix);
 		if (match == IMAP_MATCH_YES &&
-		    (ctx->list_flags & MAILBOX_LIST_SUBSCRIBED) == 0 &&
+		    (ctx->list_flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0 &&
 		    (!ctx->ns->inbox ||
 		     strncmp(ns->prefix, "INBOX", len-1) != 0)) {
 			/* The prefix itself matches */
-                        enum mailbox_flags flags;
+                        enum mailbox_info_flags flags;
 			string_t *str = t_str_new(128);
 
 			flags = MAILBOX_PLACEHOLDER;
@@ -380,12 +385,9 @@
 	cur_ref = namespace_fix_sep(ns, cur_ref);
 	cur_mask = namespace_fix_sep(ns, cur_mask);
 
-	list_flags = ctx->list_flags;
-	if ((*ns->prefix == '\0' || ns->inbox) && ctx->match_inbox)
-		list_flags |= MAILBOX_LIST_INBOX;
-	ctx->list_ctx = mail_storage_mailbox_list_init(ns->storage,
-						       cur_ref, cur_mask,
-						       list_flags);
+	list = mail_storage_get_list(ns->storage);
+	ctx->list_iter = mailbox_list_iter_init(list, cur_ref, cur_mask,
+						ctx->list_flags);
 }
 
 static bool cmd_list_continue(struct client_command_context *cmd)
@@ -395,7 +397,7 @@
 	int ret;
 
 	for (; ctx->ns != NULL; ctx->ns = ctx->ns->next) {
-		if (ctx->list_ctx == NULL)
+		if (ctx->list_iter == NULL)
 			list_namespace_init(cmd, ctx);
 
 		if ((ret = list_namespace_mailboxes(client, ctx)) < 0) {
@@ -427,21 +429,22 @@
 
 	if (lsub) {
 		/* LSUB - we don't care about flags */
-		list_flags = MAILBOX_LIST_SUBSCRIBED | MAILBOX_LIST_FAST_FLAGS |
-			_MAILBOX_LIST_HIDE_CHILDREN;
+		list_flags = MAILBOX_LIST_ITER_SUBSCRIBED |
+			MAILBOX_LIST_ITER_FAST_FLAGS |
+			_MAILBOX_LIST_ITER_HIDE_CHILDREN;
 	} else if (args[0].type != IMAP_ARG_LIST) {
 		/* LIST - allow children flags, but don't require them */
 		list_flags = 0;
 	} else {
-		list_flags = _MAILBOX_LIST_LISTEXT;
+		list_flags = _MAILBOX_LIST_ITER_LISTEXT;
 		if (!parse_list_flags(cmd, IMAP_ARG_LIST(&args[0])->args,
 				      &list_flags))
 			return TRUE;
 		args++;
 
 		/* don't show children flags unless explicitly specified */
-		if ((list_flags & MAILBOX_LIST_CHILDREN) == 0)
-			list_flags |= _MAILBOX_LIST_HIDE_CHILDREN;
+		if ((list_flags & MAILBOX_LIST_ITER_CHILDREN) == 0)
+			list_flags |= _MAILBOX_LIST_ITER_HIDE_CHILDREN;
 	}
 
 	ref = imap_arg_string(&args[0]);
--- a/src/imap/cmd-subscribe.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/imap/cmd-subscribe.c	Thu Nov 16 02:16:31 2006 +0200
@@ -6,6 +6,7 @@
 bool _cmd_subscribe_full(struct client_command_context *cmd, bool subscribe)
 {
         struct mail_storage *storage;
+	struct mailbox_list *list;
 	const char *mailbox, *verify_name;
 
 	/* <mailbox> */
@@ -32,7 +33,8 @@
 	if (!client_verify_mailbox_name(cmd, verify_name, subscribe, FALSE))
 		return TRUE;
 
-	if (mail_storage_set_subscribed(storage, mailbox, subscribe) < 0)
+	list = mail_storage_get_list(storage);
+	if (mailbox_list_set_subscribed(list, mailbox, subscribe) < 0)
 		client_send_storage_error(cmd, storage);
 	else {
 		client_send_tagline(cmd, subscribe ?
--- a/src/imap/commands-util.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/imap/commands-util.c	Thu Nov 16 02:16:31 2006 +0200
@@ -36,6 +36,7 @@
 				bool should_exist, bool should_not_exist)
 {
 	struct mail_storage *storage;
+	struct mailbox_list *list;
 	enum mailbox_name_status mailbox_status;
 	const char *p;
 	char sep;
@@ -65,7 +66,8 @@
 	}
 
 	/* check what our storage thinks of it */
-	if (mail_storage_get_mailbox_name_status(storage, mailbox,
+	list = mail_storage_get_list(storage);
+	if (mailbox_list_get_mailbox_name_status(list, mailbox,
 						 &mailbox_status) < 0) {
 		client_send_storage_error(cmd, storage);
 		return FALSE;
--- a/src/imap/main.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/imap/main.c	Thu Nov 16 02:16:31 2006 +0200
@@ -182,6 +182,7 @@
 	dict_driver_register(&dict_driver_client);
         mail_storage_init();
 	mail_storage_register_all();
+	mailbox_list_register_all();
 	clients_init();
 	commands_init();
 	imap_thread_init();
--- a/src/lib-storage/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -1,4 +1,4 @@
-SUBDIRS = index subscription-file register
+SUBDIRS = list index register
 
 noinst_LIBRARIES = libstorage.a
 
@@ -13,6 +13,7 @@
 	mail-copy.c \
 	mail-search.c \
 	mail-storage.c \
+	mailbox-list.c \
 	mailbox-tree.c
 
 noinst_HEADERS = \
@@ -20,4 +21,6 @@
 	mail-search.h \
 	mail-storage.h \
 	mail-storage-private.h \
+	mailbox-list.h \
+	mailbox-list-private.h \
 	mailbox-tree.h
--- a/src/lib-storage/index/dbox/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/dbox/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -11,7 +11,6 @@
 libstorage_dbox_a_SOURCES = \
 	dbox-file.c \
 	dbox-keywords.c \
-	dbox-list.c \
 	dbox-mail.c \
 	dbox-save.c \
 	dbox-sync.c \
--- a/src/lib-storage/index/dbox/dbox-list.c	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,415 +0,0 @@
-/* Copyright (C) 2002-2005 Timo Sirainen */
-
-#include "lib.h"
-#include "unlink-directory.h"
-#include "imap-match.h"
-#include "subscription-file/subscription-file.h"
-#include "dbox-storage.h"
-#include "home-expand.h"
-
-#include <dirent.h>
-#include <sys/stat.h>
-
-struct list_dir_context {
-	struct list_dir_context *prev;
-
-	DIR *dirp;
-	char *real_path, *virtual_path;
-};
-
-struct dbox_list_context {
-	struct mailbox_list_context mailbox_ctx;
-	struct index_storage *istorage;
-
-	struct imap_match_glob *glob;
-	struct subsfile_list_context *subsfile_ctx;
-
-	bool inbox_found;
-
-	struct mailbox_list *(*next)(struct dbox_list_context *ctx);
-
-	pool_t list_pool;
-	struct mailbox_list list;
-        struct list_dir_context *dir;
-};
-
-static struct mailbox_list *dbox_list_subs(struct dbox_list_context *ctx);
-static struct mailbox_list *dbox_list_path(struct dbox_list_context *ctx);
-static struct mailbox_list *dbox_list_next(struct dbox_list_context *ctx);
-
-static const char *mask_get_dir(const char *mask)
-{
-	const char *p, *last_dir;
-
-	last_dir = NULL;
-	for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
-		if (*p == '/')
-			last_dir = p;
-	}
-
-	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
-}
-
-static const char *
-dbox_get_path(struct index_storage *storage, const char *name)
-{
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) == 0 ||
-	    name == NULL || (*name != '/' && *name != '~' && *name != '\0'))
-		return t_strconcat(storage->dir, "/", name, NULL);
-	else
-		return home_expand(name);
-}
-
-static int list_opendir(struct mail_storage *storage,
-			const char *path, bool root, DIR **dirp)
-{
-	*dirp = opendir(*path == '\0' ? "/" : path);
-	if (*dirp != NULL)
-		return 1;
-
-	if (ENOTFOUND(errno)) {
-		/* root) user gave invalid hiearchy, ignore
-		   sub) probably just race condition with other client
-		   deleting the mailbox. */
-		return 0;
-	}
-
-	if (errno == EACCES) {
-		if (!root) {
-			/* subfolder, ignore */
-			return 0;
-		}
-		mail_storage_set_error(storage, "Access denied");
-		return -1;
-	}
-
-	mail_storage_set_critical(storage, "opendir(%s) failed: %m", path);
-	return -1;
-}
-
-struct mailbox_list_context *
-dbox_mailbox_list_init(struct mail_storage *storage,
-		       const char *ref, const char *mask,
-		       enum mailbox_list_flags flags)
-{
-	struct index_storage *istorage = (struct index_storage *)storage;
-	struct dbox_list_context *ctx;
-	const char *path, *virtual_path;
-	DIR *dirp;
-
-	ctx = i_new(struct dbox_list_context, 1);
-	ctx->mailbox_ctx.storage = storage;
-	ctx->istorage = istorage;
-	ctx->list_pool = pool_alloconly_create("dbox_list", 1024);
-        ctx->next = dbox_list_next;
-
-	mail_storage_clear_error(storage);
-
-	/* check that we're not trying to do any "../../" lists */
-	if (!dbox_is_valid_mask(storage, ref) ||
-	    !dbox_is_valid_mask(storage, mask)) {
-		mail_storage_set_error(storage, "Invalid mask");
-		return &ctx->mailbox_ctx;
-	}
-
-	if (*mask == '/' || *mask == '~') {
-		/* mask overrides reference */
-	} else if (*ref != '\0') {
-		/* merge reference and mask */
-		mask = t_strconcat(ref, mask, NULL);
-	}
-
-	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-		ctx->mailbox_ctx.storage = storage;
-		ctx->mailbox_ctx.flags = flags;
-		ctx->istorage = istorage;
-		ctx->next = dbox_list_subs;
-
-		path = t_strconcat(istorage->dir,
-				   "/"DBOX_SUBSCRIPTION_FILE_NAME, NULL);
-		ctx->subsfile_ctx =
-			subsfile_list_init(storage, path);
-		if (ctx->subsfile_ctx == NULL) {
-			ctx->next = dbox_list_next;
-			ctx->mailbox_ctx.failed = TRUE;
-			return &ctx->mailbox_ctx;
-		}
-		ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
-		return &ctx->mailbox_ctx;
-	}
-
-	/* if we're matching only subdirectories, don't bother scanning the
-	   parent directories */
-	virtual_path = mask_get_dir(mask);
-
-	path = dbox_get_path(istorage, virtual_path);
-	if (list_opendir(storage, path, TRUE, &dirp) < 0)
-		return &ctx->mailbox_ctx;
-	/* if user gave invalid directory, we just don't show any results. */
-
-	ctx->mailbox_ctx.flags = flags;
-	ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
-
-	if (virtual_path != NULL && dirp != NULL)
-		ctx->next = dbox_list_path;
-
-	if (dirp != NULL) {
-		ctx->dir = i_new(struct list_dir_context, 1);
-		ctx->dir->dirp = dirp;
-		ctx->dir->real_path = i_strdup(path);
-		ctx->dir->virtual_path = i_strdup(virtual_path);
-	}
-	return &ctx->mailbox_ctx;
-}
-
-static void list_dir_context_free(struct list_dir_context *dir)
-{
-	(void)closedir(dir->dirp);
-	i_free(dir->real_path);
-	i_free(dir->virtual_path);
-	i_free(dir);
-}
-
-int dbox_mailbox_list_deinit(struct mailbox_list_context *_ctx)
-{
-	struct dbox_list_context *ctx = (struct dbox_list_context *)_ctx;
-	int ret = ctx->mailbox_ctx.failed ? -1 : 0;
-
-	if (ctx->subsfile_ctx != NULL) {
-		if (subsfile_list_deinit(ctx->subsfile_ctx) < 0)
-			ret = -1;
-	}
-
-	while (ctx->dir != NULL) {
-		struct list_dir_context *dir = ctx->dir;
-
-		ctx->dir = dir->prev;
-                list_dir_context_free(dir);
-	}
-
-	if (ctx->list_pool != NULL)
-		pool_unref(ctx->list_pool);
-	if (ctx->glob != NULL)
-		imap_match_deinit(&ctx->glob);
-	i_free(ctx);
-
-	return ret;
-}
-
-struct mailbox_list *
-dbox_mailbox_list_next(struct mailbox_list_context *_ctx)
-{
-	struct dbox_list_context *ctx = (struct dbox_list_context *)_ctx;
-
-	return ctx->next(ctx);
-}
-
-static int list_file(struct dbox_list_context *ctx, const char *fname)
-{
-        struct list_dir_context *dir;
-	const char *list_path, *real_path, *path, *mail_path;
-	struct stat st;
-	DIR *dirp;
-	size_t len;
-	enum imap_match_result match, match2;
-	int ret, noselect;
-
-	/* skip all hidden files */
-	if (fname[0] == '.')
-		return 0;
-
-	/* skip all .lock files */
-	len = strlen(fname);
-	if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
-		return 0;
-
-	/* skip dbox-Mails/ dir */
-	if (strcmp(fname, DBOX_MAILDIR_NAME) == 0)
-		return 0;
-
-	/* check the mask */
-	if (ctx->dir->virtual_path == NULL)
-		list_path = fname;
-	else {
-		list_path = t_strconcat(ctx->dir->virtual_path,
-					"/", fname, NULL);
-	}
-
-	if ((match = imap_match(ctx->glob, list_path)) < 0)
-		return 0;
-
-	/* first as an optimization check if it contains dbox-Mails/ directory.
-	   that means it's a directory and it contains mails. */
-	real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
-	mail_path = t_strconcat(real_path, "/"DBOX_MAILDIR_NAME, NULL);
-
-	if (stat(mail_path, &st) == 0)
-		noselect = FALSE;
-	else {
-		/* non-selectable, but may contain subdirs */
-		noselect = TRUE;
-		if (stat(real_path, &st) < 0) {
-			if (ENOTFOUND(errno))
-				return 0; /* just lost it */
-
-			if (errno != EACCES && errno != ELOOP) {
-				mail_storage_set_critical(
-					ctx->mailbox_ctx.storage,
-					"stat(%s) failed: %m", real_path);
-				return -1;
-			}
-		}
-	}
-
-	if (!S_ISDIR(st.st_mode)) {
-		/* not a directory - we don't care about it */
-		return 0;
-	}
-
-	/* make sure we give only one correct INBOX */
-	if (strcasecmp(list_path, "INBOX") == 0 &&
-	    (ctx->mailbox_ctx.flags & MAILBOX_LIST_INBOX) != 0) {
-		if (ctx->inbox_found)
-			return 0;
-
-		ctx->inbox_found = TRUE;
-	}
-
-	/* scan inside the directory */
-	path = t_strconcat(list_path, "/", NULL);
-	match2 = imap_match(ctx->glob, path);
-
-	ctx->list.flags = noselect ? MAILBOX_NOSELECT : 0;
-	if (match > 0)
-		ctx->list.name = p_strdup(ctx->list_pool, list_path);
-	else if (match2 > 0)
-		ctx->list.name = p_strdup(ctx->list_pool, path);
-	else
-		ctx->list.name = NULL;
-
-	ret = match2 < 0 ? 0 :
-		list_opendir(ctx->mailbox_ctx.storage,
-			     real_path, FALSE, &dirp);
-	if (ret > 0) {
-		dir = i_new(struct list_dir_context, 1);
-		dir->dirp = dirp;
-		dir->real_path = i_strdup(real_path);
-		dir->virtual_path = i_strdup(list_path);
-
-		dir->prev = ctx->dir;
-		ctx->dir = dir;
-	} else if (ret < 0)
-		return -1;
-	return match > 0 || match2 > 0;
-}
-
-static struct mailbox_list *dbox_list_subs(struct dbox_list_context *ctx)
-{
-	struct stat st;
-	const char *name, *path, *p;
-	enum imap_match_result match = IMAP_MATCH_NO;
-
-	while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
-		match = imap_match(ctx->glob, name);
-		if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
-			break;
-	}
-
-	if (name == NULL)
-		return NULL;
-
-	ctx->list.flags = 0;
-	ctx->list.name = name;
-
-	if (match == IMAP_MATCH_PARENT) {
-		/* placeholder */
-		ctx->list.flags = MAILBOX_PLACEHOLDER;
-		while ((p = strrchr(name, '/')) != NULL) {
-			name = t_strdup_until(name, p);
-			if (imap_match(ctx->glob, name) > 0) {
-				p_clear(ctx->list_pool);
-				ctx->list.name = p_strdup(ctx->list_pool, name);
-				return &ctx->list;
-			}
-		}
-		i_unreached();
-	}
-
-	if ((ctx->mailbox_ctx.flags & MAILBOX_LIST_FAST_FLAGS) != 0)
-		return &ctx->list;
-
-	t_push();
-	path = dbox_get_path(ctx->istorage, ctx->list.name);
-	if (stat(path, &st) == 0) {
-		if (S_ISDIR(st.st_mode))
-			ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-		else {
-			ctx->list.flags = MAILBOX_NOINFERIORS;
-		}
-	} else {
-		ctx->list.flags = MAILBOX_NONEXISTENT;
-	}
-	t_pop();
-	return &ctx->list;
-}
-
-static struct mailbox_list *dbox_list_path(struct dbox_list_context *ctx)
-{
-	ctx->next = dbox_list_next;
-
-	ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-	ctx->list.name =
-		p_strconcat(ctx->list_pool, ctx->dir->virtual_path, "/", NULL);
-
-	if (imap_match(ctx->glob, ctx->list.name) > 0)
-		return &ctx->list;
-	else
-		return ctx->next(ctx);
-}
-
-static struct mailbox_list *dbox_list_inbox(struct dbox_list_context *ctx)
-{
-	ctx->list.flags = MAILBOX_UNMARKED | MAILBOX_NOCHILDREN;
-	ctx->list.name = "INBOX";
-	return &ctx->list;
-}
-
-static struct mailbox_list *dbox_list_next(struct dbox_list_context *ctx)
-{
-	struct list_dir_context *dir;
-	struct dirent *d;
-	int ret;
-
-	p_clear(ctx->list_pool);
-
-	while (ctx->dir != NULL) {
-		/* NOTE: list_file() may change ctx->dir */
-		while ((d = readdir(ctx->dir->dirp)) != NULL) {
-			t_push();
-			ret = list_file(ctx, d->d_name);
-			t_pop();
-
-			if (ret > 0)
-				return &ctx->list;
-			if (ret < 0) {
-				ctx->mailbox_ctx.failed = TRUE;
-				return NULL;
-			}
-		}
-
-		dir = ctx->dir;
-		ctx->dir = dir->prev;
-		list_dir_context_free(dir);
-	}
-
-	if (!ctx->inbox_found &&
-	    (ctx->mailbox_ctx.flags & MAILBOX_LIST_INBOX) != 0 &&
-	    ctx->glob != NULL && imap_match(ctx->glob, "INBOX")  > 0) {
-		/* show inbox */
-		ctx->inbox_found = TRUE;
-		return dbox_list_inbox(ctx);
-	}
-
-	/* finished */
-	return NULL;
-}
--- a/src/lib-storage/index/dbox/dbox-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -1,12 +1,10 @@
 /* Copyright (C) 2005 Timo Sirainen */
 
 #include "lib.h"
-#include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
-#include "subscription-file/subscription-file.h"
+#include "index-mail.h"
 #include "mail-copy.h"
-#include "index-mail.h"
 #include "dbox-uidlist.h"
 #include "dbox-sync.h"
 #include "dbox-file.h"
@@ -19,17 +17,11 @@
 
 #define CREATE_MODE 0770 /* umask() should limit it more */
 
-/* Don't allow creating too long mailbox names. They could start causing
-   problems when they reach the limit. */
-#define DBOX_MAX_MAILBOX_NAME_LENGTH (PATH_MAX/2)
-
 extern struct mail_storage dbox_storage;
 extern struct mailbox dbox_mailbox;
 
-static bool dbox_handle_errors(struct index_storage *istorage)
+static bool dbox_handle_errors(struct mail_storage *storage)
 {
-	struct mail_storage *storage = &istorage->storage;
-
 	if (ENOACCESS(errno))
 		mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION);
 	else if (ENOSPACE(errno))
@@ -41,25 +33,22 @@
 	return TRUE;
 }
 
-static struct mail_storage *
-dbox_create(const char *data, const char *user,
-	    enum mail_storage_flags flags,
-	    enum mail_storage_lock_method lock_method)
+static int
+dbox_get_list_settings(struct mailbox_list_settings *list_set,
+		       const char *data, enum mail_storage_flags flags)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
-	struct dbox_storage *storage;
-	struct index_storage *istorage;
-	const char *root_dir, *index_dir, *p;
+	const char *p;
 	size_t len;
-	pool_t pool;
 
-	root_dir = index_dir = NULL;
+	memset(list_set, 0, sizeof(*list_set));
+	list_set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
 
 	if (data == NULL || *data == '\0') {
 		/* we won't do any guessing for this format. */
 		if (debug)
 			i_info("dbox: mailbox location not given");
-		return NULL;
+		return -1;
 	}
 
 	/* <root dir> [:INDEX=<dir>] */
@@ -67,51 +56,69 @@
 		i_info("dbox: data=%s", data);
 	p = strchr(data, ':');
 	if (p == NULL)
-		root_dir = data;
+		list_set->root_dir = data;
 	else {
-		root_dir = t_strdup_until(data, p);
+		list_set->root_dir = t_strdup_until(data, p);
 
 		do {
 			p++;
 			if (strncmp(p, "INDEX=", 6) == 0)
-				index_dir = t_strcut(p+6, ':');
+				list_set->index_dir = t_strcut(p+6, ':');
 			p = strchr(p, ':');
 		} while (p != NULL);
 	}
 
 	/* strip trailing '/' */
-	len = strlen(root_dir);
-	if (root_dir[len-1] == '/')
-		root_dir = t_strndup(root_dir, len-1);
+	len = strlen(list_set->root_dir);
+	if (list_set->root_dir[len-1] == '/')
+		list_set->root_dir = t_strndup(list_set->root_dir, len-1);
 
-	if (index_dir == NULL)
-		index_dir = root_dir;
-	else if (strcmp(index_dir, "MEMORY") == 0)
-		index_dir = NULL;
+	if (list_set->index_dir != NULL &&
+	    strcmp(list_set->index_dir, "MEMORY") == 0)
+		list_set->index_dir = "";
+	return 0;
+}
 
-	if (debug) {
-		i_info("dbox: root=%s, index=%s",
-		       root_dir, index_dir == NULL ? "" : index_dir);
-	}
+static struct mail_storage *
+dbox_create(const char *data, const char *user,
+	    enum mail_storage_flags flags,
+	    enum mail_storage_lock_method lock_method)
+{
+	struct dbox_storage *storage;
+	struct index_storage *istorage;
+	struct mailbox_list_settings list_set;
+	struct mailbox_list *list;
+	const char *error;
+	pool_t pool;
 
-        root_dir = home_expand(root_dir);
-	if (mkdir_parents(root_dir, CREATE_MODE) < 0 && errno != EEXIST) {
-		i_error("mkdir_parents(%s) failed: %m", root_dir);
+	if (dbox_get_list_settings(&list_set, data, flags) < 0)
+		return NULL;
+
+	if (mkdir_parents(list_set.root_dir, CREATE_MODE) < 0 &&
+	    errno != EEXIST) {
+		i_error("mkdir_parents(%s) failed: %m", list_set.root_dir);
 		return NULL;
 	}
 
 	pool = pool_alloconly_create("storage", 512);
 	storage = p_new(pool, struct dbox_storage, 1);
 
+	if (mailbox_list_init("fs", &list_set,
+			      mail_storage_get_list_flags(flags),
+			      mailbox_storage_list_is_mailbox, storage,
+			      &list, &error) < 0) {
+		i_error("dbox fs: %s", error);
+		pool_unref(pool);
+		return NULL;
+	}
+
 	istorage = INDEX_STORAGE(storage);
 	istorage->storage = dbox_storage;
 	istorage->storage.pool = pool;
 
-	istorage->dir = p_strdup(pool, root_dir);
-	istorage->index_dir = p_strdup(pool, home_expand(index_dir));
 	istorage->user = p_strdup(pool, user);
 	istorage->callbacks = p_new(pool, struct mail_storage_callbacks, 1);
-	index_storage_init(istorage, flags, lock_method);
+	index_storage_init(istorage, list, flags, lock_method);
 
 	return STORAGE(storage);
 }
@@ -147,124 +154,33 @@
 	return TRUE;
 }
 
-bool dbox_is_valid_mask(struct mail_storage *storage, const char *mask)
+static int create_dbox(struct mail_storage *storage, const char *path)
 {
-	const char *p;
-	bool newdir;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-		return TRUE;
-
-	/* make sure it's not absolute path */
-	if (*mask == '/' || *mask == '~')
-		return FALSE;
-
-	/* make sure the mailbox name doesn't contain any foolishness:
-	   "../" could give access outside the mailbox directory.
-	   "./" and "//" could fool ACL checks. */
-	newdir = TRUE;
-	for (p = mask; *p != '\0'; p++) {
-		if (newdir) {
-			if (p[0] == '/')
-				return FALSE; /* // */
-			if (p[0] == '.') {
-				if (p[1] == '/')
-					return FALSE; /* ./ */
-				if (p[1] == '.' && p[2] == '/')
-					return FALSE; /* ../ */
-			}
-			if (strncmp(p, DBOX_MAILDIR_NAME,
-				    sizeof(DBOX_MAILDIR_NAME)-1) == 0 &&
-			    (p[sizeof(DBOX_MAILDIR_NAME)-1] == '\0' ||
-			     p[sizeof(DBOX_MAILDIR_NAME)-1] == '/')) {
-				/* don't allow the dbox-Mails directory to be
-				   used as part of the mask */
-				return FALSE;
-			}
-		}
-		newdir = p[0] == '/';
-	}
-
-	if (mask[0] == '.' && (mask[1] == '\0' ||
-			       (mask[1] == '.' && mask[2] == '\0'))) {
-		/* "." and ".." aren't allowed. */
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static bool dbox_is_valid_create_name(struct mail_storage *storage,
-				      const char *name)
-{
-	size_t len;
-
-	len = strlen(name);
-	if (name[0] == '\0' || name[len-1] == '/' ||
-	    len > DBOX_MAX_MAILBOX_NAME_LENGTH)
-		return FALSE;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) == 0) {
-		if (mailbox_name_is_too_large(name, '/'))
-		    return FALSE;
-	}
-
-	return dbox_is_valid_mask(storage, name);
-}
-
-static bool dbox_is_valid_existing_name(struct mail_storage *storage,
-					const char *name)
-{
-	size_t len;
-
-	len = strlen(name);
-	if (name[0] == '\0' || name[len-1] == '/')
-		return FALSE;
-
-	return dbox_is_valid_mask(storage, name);
-}
-
-static const char *
-dbox_get_path(struct index_storage *storage, const char *name)
-{
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return home_expand(name);
-
-	return t_strconcat(storage->dir, "/", name, NULL);
-}
-
-static int create_dbox(struct index_storage *storage, const char *dir)
-{
-	const char *path;
-
-	path = t_strconcat(dir, "/", DBOX_MAILDIR_NAME, NULL);
 	if (mkdir_parents(path, CREATE_MODE) < 0 && errno != EEXIST) {
 		if (dbox_handle_errors(storage))
 			return -1;
 
-		mail_storage_set_critical(&storage->storage,
-					  "mkdir(%s) failed: %m", dir);
+		mail_storage_set_critical(storage, "mkdir(%s) failed: %m",
+					  path);
 		return -1;
 	}
 	return 0;
 }
 
-static int create_index_dir(struct index_storage *storage, const char *name)
+static int create_index_dir(struct mail_storage *storage, const char *name)
 {
-	const char *dir;
+	const char *root_dir, *index_dir;
 
-	if (storage->index_dir == NULL)
+	root_dir = mailbox_list_get_path(storage->list, name,
+					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	index_dir = mailbox_list_get_path(storage->list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (strcmp(index_dir, root_dir) == 0)
 		return 0;
 
-	if (strcmp(storage->index_dir, storage->dir) == 0)
-		return 0;
-
-	dir = t_strconcat(storage->index_dir, "/", name,
-			  "/"DBOX_MAILDIR_NAME, NULL);
-	if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
-		mail_storage_set_critical(&storage->storage,
-					  "mkdir(%s) failed: %m", dir);
+	if (mkdir_parents(index_dir, CREATE_MODE) < 0 && errno != EEXIST) {
+		mail_storage_set_critical(storage, "mkdir(%s) failed: %m",
+					  index_dir);
 		return -1;
 	}
 
@@ -278,54 +194,17 @@
 }
 
 static const char *
-dbox_get_index_dir(struct index_storage *storage, const char *name)
-{
-	const char *p;
-
-	if (storage->index_dir == NULL)
-		return NULL;
-
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~')) {
-		name = home_expand(name);
-		p = strrchr(name, '/');
-		return t_strconcat(t_strdup_until(name, p),
-				   "/"DBOX_MAILDIR_NAME"/", p+1, NULL);
-	}
-
-	return t_strconcat(storage->index_dir,
-			   "/", name, "/"DBOX_MAILDIR_NAME, NULL);
-}
-
-static const char *
-dbox_get_mailbox_path(struct mail_storage *_storage,
-		      const char *name, bool *is_file_r)
-{
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	*is_file_r = FALSE;
-	if (*name == '\0')
-		return istorage->dir;
-	return dbox_get_path(istorage, name);
-}
-
-static const char *
 dbox_get_mailbox_control_dir(struct mail_storage *_storage, const char *name)
 {
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	return dbox_get_path(istorage, name);
+	return mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_CONTROL);
 }
 
 static const char *
 dbox_get_mailbox_index_dir(struct mail_storage *_storage, const char *name)
 {
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	return dbox_get_index_dir(istorage, name);
+	return mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_INDEX);
 }
 
 static struct mailbox *
@@ -333,17 +212,20 @@
 	  enum mailbox_open_flags flags)
 {
 	struct index_storage *istorage = INDEX_STORAGE(storage);
+	struct mail_storage *_storage = STORAGE(storage);
 	struct dbox_mailbox *mbox;
 	struct mail_index *index;
 	const char *path, *index_dir, *value;
 	pool_t pool;
 
-	path = dbox_get_path(istorage, name);
-	index_dir = dbox_get_index_dir(istorage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	index_dir = mailbox_list_get_path(_storage->list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
 
-	if (create_dbox(istorage, path) < 0)
+	if (create_dbox(_storage, path) < 0)
 		return NULL;
-	if (create_index_dir(istorage, name) < 0)
+	if (create_index_dir(_storage, name) < 0)
 		return NULL;
 
 	index = index_storage_alloc(index_dir, path, DBOX_INDEX_PREFIX);
@@ -404,7 +286,6 @@
 		  struct istream *input, enum mailbox_open_flags flags)
 {
 	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
 	const char *path;
 	struct stat st;
 
@@ -419,12 +300,13 @@
 	if (strcmp(name, "INBOX") == 0)
 		return dbox_open(storage, "INBOX", flags);
 
-	if (!dbox_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return NULL;
 	}
 
-	path = dbox_get_path(istorage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (stat(path, &st) == 0) {
 		return dbox_open(storage, name, flags);
 	} else if (errno == ENOENT) {
@@ -442,34 +324,29 @@
 			       const char *name,
 			       bool directory __attr_unused__)
 {
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-	const char *path, *mail_path;
+	const char *path;
 	struct stat st;
 
 	mail_storage_clear_error(_storage);
 
-	if (!dbox_is_valid_create_name(_storage, name)) {
+	if (!mailbox_list_is_valid_create_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	path = dbox_get_path(istorage, name);
-	mail_path = t_strconcat(path, "/", DBOX_MAILDIR_NAME, NULL);
-
-	if (stat(mail_path, &st) == 0) {
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (stat(path, &st) == 0) {
 		mail_storage_set_error(_storage, "Mailbox already exists");
 		return -1;
 	}
 
-	return create_dbox(istorage, path);
+	return create_dbox(_storage, path);
 }
 
 static int dbox_mailbox_delete(struct mail_storage *_storage,
 			       const char *name)
 {
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
 	const char *path, *mail_path;
 	struct stat st;
 
@@ -480,16 +357,19 @@
 		return -1;
 	}
 
-	if (!dbox_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	path = dbox_get_path(istorage, name);
-	mail_path = t_strconcat(path, "/", DBOX_MAILDIR_NAME, NULL);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_DIR);
+	mail_path = mailbox_list_get_path(_storage->list, name,
+					  MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
 	if (stat(mail_path, &st) < 0 && ENOTFOUND(errno)) {
 		if (stat(path, &st) < 0) {
+			/* doesn't exist at all */
 			mail_storage_set_error(_storage,
 				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
 			return -1;
@@ -515,7 +395,7 @@
 	index_storage_destroy_unrefed();
 
 	if (unlink_directory(mail_path, TRUE) < 0) {
-		if (!dbox_handle_errors(istorage)) {
+		if (!dbox_handle_errors(_storage)) {
 			mail_storage_set_critical(_storage,
 				"unlink_directory() failed for %s: %m",
 				mail_path);
@@ -524,14 +404,15 @@
 	}
 	/* try also removing the root directory. it can fail if the deleted
 	   mailbox had submailboxes. do it as long as we can. */
-	while (rmdir(path) == 0) {
+	while (rmdir(path) == 0 || errno == ENOENT) {
 		const char *p = strrchr(name, '/');
 
 		if (p == NULL)
 			break;
 
 		name = t_strdup_until(name, p);
-		path = dbox_get_path(istorage, name);
+		path = mailbox_list_get_path(_storage->list, name,
+					     MAILBOX_LIST_PATH_TYPE_DIR);
 	}
 	return 0;
 }
@@ -539,27 +420,28 @@
 static int dbox_mailbox_rename(struct mail_storage *_storage,
 			       const char *oldname, const char *newname)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *oldpath, *newpath, *p;
 	struct stat st;
 
 	mail_storage_clear_error(_storage);
 
-	if (!dbox_is_valid_existing_name(_storage, oldname) ||
-	    !dbox_is_valid_create_name(_storage, newname)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) ||
+	    !mailbox_list_is_valid_create_name(_storage->list, newname)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	oldpath = dbox_get_path(storage, oldname);
-	newpath = dbox_get_path(storage, newname);
+	oldpath = mailbox_list_get_path(_storage->list, oldname,
+					MAILBOX_LIST_PATH_TYPE_DIR);
+	newpath = mailbox_list_get_path(_storage->list, newname,
+					MAILBOX_LIST_PATH_TYPE_DIR);
 
 	/* create the hierarchy */
 	p = strrchr(newpath, '/');
 	if (p != NULL) {
 		p = t_strdup_until(newpath, p);
 		if (mkdir_parents(p, CREATE_MODE) < 0) {
-			if (dbox_handle_errors(storage))
+			if (dbox_handle_errors(_storage))
 				return -1;
 
 			mail_storage_set_critical(_storage,
@@ -592,7 +474,7 @@
 		if (ENOTFOUND(errno)) {
 			mail_storage_set_error(_storage,
 				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-		} else if (!dbox_handle_errors(storage)) {
+		} else if (!dbox_handle_errors(_storage)) {
 			mail_storage_set_critical(_storage,
 				"rename(%s, %s) failed: %m", oldpath, newpath);
 		}
@@ -602,58 +484,6 @@
 	return 0;
 }
 
-static int dbox_set_subscribed(struct mail_storage *_storage,
-			       const char *name, bool set)
-{
-	struct dbox_storage *storage = (struct dbox_storage *)_storage;
-	const char *path;
-
-	path = t_strconcat(INDEX_STORAGE(storage)->dir,
-			   "/"DBOX_SUBSCRIPTION_FILE_NAME, NULL);
-
-	return subsfile_set_subscribed(_storage, path,
-				       INDEX_STORAGE(storage)->temp_prefix,
-				       name, set);
-}
-
-static int dbox_get_mailbox_name_status(struct mail_storage *_storage,
-					const char *name,
-					enum mailbox_name_status *status)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	struct stat st;
-	const char *path, *mail_path;
-
-	mail_storage_clear_error(_storage);
-
-	if (!dbox_is_valid_existing_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	path = dbox_get_path(storage, name);
-	mail_path = t_strconcat(path, "/", DBOX_MAILDIR_NAME, NULL);
-
-	if (strcmp(name, "INBOX") == 0 || stat(mail_path, &st) == 0) {
-		*status = MAILBOX_NAME_EXISTS;
-		return 0;
-	}
-
-	if (!dbox_is_valid_create_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	if (errno == ENOENT) {
-		*status = MAILBOX_NAME_VALID;
-		return 0;
-	} else {
-		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
-					  path);
-		return -1;
-	}
-}
-
 static int dbox_storage_close(struct mailbox *box)
 {
 	struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
@@ -686,6 +516,69 @@
 		t_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME, NULL));
 }
 
+static int dbox_is_mailbox(struct mail_storage *storage,
+			   const char *dir, const char *fname,
+			   enum mailbox_list_iter_flags iter_flags,
+			   enum mailbox_info_flags *flags,
+			   enum mailbox_list_file_type type)
+{
+	const char *path, *mail_path;
+	size_t len;
+	struct stat st;
+	int ret = 1;
+
+	if (strcmp(fname, DBOX_MAILDIR_NAME) == 0) {
+		*flags = MAILBOX_NOSELECT;
+		return 0;
+	}
+
+	/* skip all .lock files */
+	len = strlen(fname);
+	if (len > 5 && strcmp(fname+len-5, ".lock") == 0) {
+		*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+		return 0;
+	}
+
+	/* try to avoid stat() with these checks */
+	if (type != MAILBOX_LIST_FILE_TYPE_DIR &&
+	    type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
+	    type != MAILBOX_LIST_FILE_TYPE_UNKNOWN &&
+	    (iter_flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
+		/* it's a file */
+		*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+		return 0;
+	}
+
+	/* need to stat() then */
+	t_push();
+	path = t_strconcat(dir, "/", fname, NULL);
+	mail_path = t_strconcat(path, "/"DBOX_MAILDIR_NAME, NULL);
+
+	if (stat(mail_path, &st) == 0) {
+		if (!S_ISDIR(st.st_mode)) {
+			/* non-directory */
+			*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+			ret = 0;
+		}
+	} else {
+		/* non-selectable, but may contain subdirs */
+		*flags |= MAILBOX_NOSELECT;
+		if (stat(path, &st) < 0) {
+			if (ENOTFOUND(errno)) {
+				/* just lost it */
+				ret = 0;
+			} else if (errno != EACCES && errno != ELOOP) {
+				mail_storage_set_critical(storage,
+					"stat(%s) failed: %m", path);
+				ret = -1;
+			}
+		}
+	}
+	t_pop();
+
+	return ret;
+}
+
 static void dbox_class_init(void)
 {
 	dbox_transaction_class_init();
@@ -698,7 +591,7 @@
 
 struct mail_storage dbox_storage = {
 	MEMBER(name) DBOX_STORAGE_NAME,
-	MEMBER(hierarchy_sep) '/',
+	MEMBER(mailbox_is_file) FALSE,
 
 	{
 		dbox_class_init,
@@ -707,18 +600,13 @@
 		dbox_free,
 		dbox_autodetect,
 		index_storage_set_callbacks,
-		dbox_get_mailbox_path,
 		dbox_get_mailbox_control_dir,
 		dbox_get_mailbox_index_dir,
 		dbox_mailbox_open,
 		dbox_mailbox_create,
 		dbox_mailbox_delete,
 		dbox_mailbox_rename,
-		dbox_mailbox_list_init,
-		dbox_mailbox_list_next,
-		dbox_mailbox_list_deinit,
-		dbox_set_subscribed,
-		dbox_get_mailbox_name_status,
+		dbox_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/index/dbox/dbox-storage.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Thu Nov 16 02:16:31 2006 +0200
@@ -77,13 +77,6 @@
 
 extern struct mail_vfuncs dbox_mail_vfuncs;
 
-struct mailbox_list_context *
-dbox_mailbox_list_init(struct mail_storage *storage,
-		       const char *ref, const char *mask,
-		       enum mailbox_list_flags flags);
-int dbox_mailbox_list_deinit(struct mailbox_list_context *ctx);
-struct mailbox_list *dbox_mailbox_list_next(struct mailbox_list_context *ctx);
-
 void dbox_transaction_created(struct mail_index_transaction *t);
 void dbox_transaction_class_init(void);
 void dbox_transaction_class_deinit(void);
@@ -101,8 +94,6 @@
 void dbox_transaction_save_commit_post(struct dbox_save_context *ctx);
 void dbox_transaction_save_rollback(struct dbox_save_context *ctx);
 
-bool dbox_is_valid_mask(struct mail_storage *storage, const char *mask);
-
 int dbox_mail_lookup_offset(struct index_transaction_context *trans,
 			    uint32_t seq, uint32_t *file_seq_r,
 			    uoff_t *offset_r);
--- a/src/lib-storage/index/index-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/index-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -41,9 +41,11 @@
 static int index_storage_refcount = 0;
 
 void index_storage_init(struct index_storage *storage,
+			struct mailbox_list *list,
 			enum mail_storage_flags flags,
 			enum mail_storage_lock_method lock_method)
 {
+	storage->storage.list = list;
 	storage->storage.flags = flags;
 	storage->storage.lock_method = lock_method;
 
@@ -95,8 +97,11 @@
 	struct stat st, st2;
 	int destroy_count;
 
-	if (index_dir == NULL || stat(index_dir, &st) < 0)
+	if (*index_dir == '\0' || stat(index_dir, &st) < 0) {
+		if (*index_dir == '\0')
+			index_dir = NULL;
 		memset(&st, 0, sizeof(st));
+	}
 
 	/* compare index_dir inodes so we don't break even with symlinks.
 	   for in-memory indexes compare just mailbox paths */
--- a/src/lib-storage/index/index-storage.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/index-storage.h	Thu Nov 16 02:16:31 2006 +0200
@@ -24,11 +24,6 @@
 struct index_storage {
 	struct mail_storage storage;
 
-	const char *dir; /* root directory */
-	const char *index_dir;
-	const char *inbox_path; /* INBOX location */
-        const char *temp_prefix; /* prefix for temporary files */
-
 	const char *user; /* name of user accessing the storage */
 
 	struct mail_storage_callbacks *callbacks;
@@ -113,6 +108,7 @@
 void index_storage_destroy_unrefed(void);
 
 void index_storage_init(struct index_storage *storage,
+			struct mailbox_list *list,
 			enum mail_storage_flags flags,
 			enum mail_storage_lock_method lock_method);
 void index_storage_deinit(struct index_storage *storage);
--- a/src/lib-storage/index/maildir/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/maildir/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -11,7 +11,6 @@
 libstorage_maildir_a_SOURCES = \
 	maildir-copy.c \
 	maildir-keywords.c \
-	maildir-list.c \
 	maildir-mail.c \
 	maildir-save.c \
 	maildir-storage.c \
--- a/src/lib-storage/index/maildir/maildir-list.c	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,401 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "ioloop.h"
-#include "str.h"
-#include "home-expand.h"
-#include "unlink-directory.h"
-#include "imap-match.h"
-#include "subscription-file/subscription-file.h"
-#include "maildir-storage.h"
-#include "mailbox-tree.h"
-
-#include <stdlib.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
-#define MAILBOX_FLAG_MATCHED 0x40000000
-
-struct maildir_list_context {
-	struct mailbox_list_context mailbox_ctx;
-	pool_t pool;
-
-	const char *dir, *prefix;
-
-        struct mailbox_tree_context *tree_ctx;
-
-	string_t *node_path;
-	size_t parent_pos;
-	struct mailbox_node *root, *next_node;
-	struct mailbox_list list;
-};
-
-static void maildir_nodes_fix(struct mailbox_node *node, bool is_subs)
-{
-	while (node != NULL) {
-		if (node->children != NULL) {
-			node->flags |= MAILBOX_CHILDREN;
-			node->flags &= ~MAILBOX_NOCHILDREN;
-			maildir_nodes_fix(node->children, is_subs);
-		} else if ((node->flags & MAILBOX_PLACEHOLDER) != 0) {
-			if (!is_subs) {
-				node->flags &= ~MAILBOX_PLACEHOLDER;
-				node->flags |= MAILBOX_NOSELECT;
-			}
-		}
-		node = node->next;
-	}
-}
-
-static bool
-maildir_fill_readdir(struct maildir_list_context *ctx,
-		     struct imap_match_glob *glob, bool update_only)
-{
-	DIR *dirp;
-	struct dirent *d;
-	struct stat st;
-	const char *path, *p, *mailbox_c;
-	string_t *mailbox;
-	enum imap_match_result match;
-	struct mailbox_node *node;
-	bool stat_dirs, created, hide;
-
-	dirp = opendir(ctx->dir);
-	if (dirp == NULL) {
-		if (errno != ENOENT) {
-			mail_storage_set_critical(ctx->mailbox_ctx.storage,
-				"opendir(%s) failed: %m", ctx->dir);
-			return FALSE;
-		}
-		return TRUE;
-	}
-
-	stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL;
-
-	t_push();
-	mailbox = t_str_new(PATH_MAX);
-	while ((d = readdir(dirp)) != NULL) {
-		const char *fname = d->d_name;
-
-		if (fname[0] != MAILDIR_FS_SEP)
-			continue;
-
-		/* skip . and .. */
-		if (fname[0] == '.' &&
-		    (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0')))
-			continue;
-
-#ifdef HAVE_DIRENT_D_TYPE
-		/* check the type always since there's no extra cost */
-		if (d->d_type == DT_DIR)
-			;
-		else if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
-			continue;
-		else if (d->d_type == DT_LNK && !stat_dirs)
-			;
-		else
-#endif
-		/* Check files beginning with .nfs always because they may be
-		   temporary files created by the kernel */
-		if (stat_dirs || strncmp(fname, ".nfs", 4) == 0) {
-			t_push();
-			path = t_strdup_printf("%s/%s", ctx->dir, fname);
-			hide = stat(path, &st) < 0 || !S_ISDIR(st.st_mode);
-			t_pop();
-			if (hide)
-				continue;
-		}
-
-		if (fname[1] == MAILDIR_FS_SEP &&
-		    strcmp(fname+1, MAILDIR_UNLINK_DIRNAME) == 0) {
-			/* this directory is in the middle of being deleted,
-			   or the process trying to delete it had died.
-			   delete it ourself if it's been there longer than
-			   one hour. */
-			t_push();
-			path = t_strdup_printf("%s/%s", ctx->dir, fname);
-			if (stat(path, &st) == 0 &&
-			    st.st_mtime < ioloop_time - 3600)
-				(void)unlink_directory(path, TRUE);
-			t_pop();
-			continue;
-		}
-		fname++;
-
-		/* make sure the mask matches */
-		str_truncate(mailbox, 0);
-		str_append(mailbox, ctx->prefix);
-		str_append(mailbox, fname);
-                mailbox_c = str_c(mailbox);
-
-		match = imap_match(glob, mailbox_c);
-
-		if (match != IMAP_MATCH_YES &&
-		    match != IMAP_MATCH_PARENT)
-			continue;
-
-		if (match == IMAP_MATCH_PARENT) {
-			t_push();
-			while ((p = strrchr(mailbox_c,
-					    MAILDIR_FS_SEP)) != NULL) {
-				str_truncate(mailbox, (size_t) (p-mailbox_c));
-				mailbox_c = str_c(mailbox);
-				if (imap_match(glob, mailbox_c) > 0)
-					break;
-			}
-			i_assert(p != NULL);
-
-			created = FALSE;
-			node = update_only ?
-				mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
-				mailbox_tree_get(ctx->tree_ctx,
-						 mailbox_c, &created);
-			if (node != NULL) {
-				if (created)
-					node->flags = MAILBOX_PLACEHOLDER;
-
-				node->flags |= MAILBOX_CHILDREN |
-					MAILBOX_FLAG_MATCHED;
-				node->flags &= ~MAILBOX_NOCHILDREN;
-			}
-
-			t_pop();
-		} else {
-			created = FALSE;
-			node = update_only ?
-				mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
-				mailbox_tree_get(ctx->tree_ctx,
-						 mailbox_c, &created);
-
-			if (node != NULL) {
-				if (created)
-					node->flags = MAILBOX_NOCHILDREN;
-				node->flags &= ~(MAILBOX_PLACEHOLDER |
-						 MAILBOX_NONEXISTENT);
-				node->flags |= MAILBOX_FLAG_MATCHED;
-			}
-		}
-	}
-	t_pop();
-
-	if (closedir(dirp) < 0) {
-		mail_storage_set_critical(ctx->mailbox_ctx.storage,
-					  "readdir(%s) failed: %m", ctx->dir);
-		return FALSE;
-	}
-
-	if ((ctx->mailbox_ctx.flags &
-	     (MAILBOX_LIST_SUBSCRIBED |
-	      MAILBOX_LIST_INBOX)) == MAILBOX_LIST_INBOX) {
-		/* make sure INBOX is there */
-		node = mailbox_tree_get(ctx->tree_ctx, "INBOX", &created);
-		if (created)
-			node->flags = MAILBOX_NOCHILDREN;
-		else
-			node->flags &= ~MAILBOX_PLACEHOLDER;
-
-		switch (imap_match(glob, "INBOX")) {
-		case IMAP_MATCH_YES:
-		case IMAP_MATCH_PARENT:
-			node->flags |= MAILBOX_FLAG_MATCHED;
-			break;
-		default:
-			break;
-		}
-	}
-	maildir_nodes_fix(mailbox_tree_get(ctx->tree_ctx, NULL, NULL),
-			  (ctx->mailbox_ctx.flags &
-			   MAILBOX_LIST_SUBSCRIBED) != 0);
-	return TRUE;
-}
-
-static bool maildir_fill_subscribed(struct maildir_list_context *ctx,
-				    struct imap_match_glob *glob)
-{
-	struct maildir_storage *storage =
-		(struct maildir_storage *)ctx->mailbox_ctx.storage;
-	struct subsfile_list_context *subsfile_ctx;
-	const char *path, *name, *p;
-	struct mailbox_node *node;
-	bool created;
-
-	path = t_strconcat(storage->control_dir != NULL ?
-			   storage->control_dir : INDEX_STORAGE(storage)->dir,
-			   "/" SUBSCRIPTION_FILE_NAME, NULL);
-	subsfile_ctx = subsfile_list_init(ctx->mailbox_ctx.storage, path);
-	if (subsfile_ctx == NULL)
-		return FALSE;
-
-	while ((name = subsfile_list_next(subsfile_ctx)) != NULL) {
-		switch (imap_match(glob, name)) {
-		case IMAP_MATCH_YES:
-			node = mailbox_tree_get(ctx->tree_ctx, name, NULL);
-			node->flags = MAILBOX_FLAG_MATCHED;
-			if ((ctx->mailbox_ctx.flags &
-			     MAILBOX_LIST_FAST_FLAGS) == 0) {
-				node->flags |= MAILBOX_NONEXISTENT |
-					MAILBOX_NOCHILDREN;
-			}
-			break;
-		case IMAP_MATCH_PARENT:
-			/* placeholder */
-			while ((p = strrchr(name, MAILDIR_FS_SEP)) != NULL) {
-				name = t_strdup_until(name, p);
-				if (imap_match(glob, name) > 0)
-					break;
-			}
-			i_assert(p != NULL);
-
-			node = mailbox_tree_get(ctx->tree_ctx, name, &created);
-			if (created) node->flags = MAILBOX_PLACEHOLDER;
-			node->flags |= MAILBOX_FLAG_MATCHED | MAILBOX_CHILDREN;
-			node->flags &= ~MAILBOX_NOCHILDREN;
-			break;
-		default:
-			break;
-		}
-	}
-
-	return subsfile_list_deinit(subsfile_ctx) == 0;
-
-}
-
-struct mailbox_list_context *
-maildir_mailbox_list_init(struct mail_storage *storage,
-			  const char *ref, const char *mask,
-			  enum mailbox_list_flags flags)
-{
-	struct index_storage *istorage = (struct index_storage *)storage;
-        struct maildir_list_context *ctx;
-        struct imap_match_glob *glob;
-	const char *dir, *p;
-	pool_t pool;
-
-	mail_storage_clear_error(storage);
-
-	pool = pool_alloconly_create("maildir_list", 1024);
-	ctx = p_new(pool, struct maildir_list_context, 1);
-	ctx->mailbox_ctx.storage = storage;
-	ctx->mailbox_ctx.flags = flags;
-	ctx->pool = pool;
-	ctx->tree_ctx = mailbox_tree_init(MAILDIR_FS_SEP);
-
-	if (*ref != '\0') {
-		/* join reference + mask */
-		mask = t_strconcat(ref, mask, NULL);
-	}
-
-	glob = imap_match_init(pool, mask, TRUE, MAILDIR_FS_SEP);
-
-	ctx->dir = istorage->dir;
-	ctx->prefix = "";
-
-	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-		if (!maildir_fill_subscribed(ctx, glob)) {
-			ctx->mailbox_ctx.failed = TRUE;
-			return &ctx->mailbox_ctx;
-		}
-	} else if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-		   (p = strrchr(mask, '/')) != NULL) {
-		dir = t_strdup_until(mask, p);
-		ctx->prefix = p_strdup_until(pool, mask, p+1);
-
-		if (*mask != '/' && *mask != '~')
-			dir = t_strconcat(istorage->dir, "/", dir, NULL);
-		ctx->dir = p_strdup(pool, home_expand(dir));
-	}
-
-	if ((flags & MAILBOX_LIST_SUBSCRIBED) == 0 ||
-	    (ctx->mailbox_ctx.flags & MAILBOX_LIST_FAST_FLAGS) == 0) {
-		bool update_only = (flags & MAILBOX_LIST_SUBSCRIBED) != 0;
-		if (!maildir_fill_readdir(ctx, glob, update_only)) {
-			ctx->mailbox_ctx.failed = TRUE;
-			return &ctx->mailbox_ctx;
-		}
-	}
-
-	ctx->node_path = str_new(pool, 256);
-	ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL);
-	ctx->mailbox_ctx.storage = storage;
-	return &ctx->mailbox_ctx;
-}
-
-int maildir_mailbox_list_deinit(struct mailbox_list_context *_ctx)
-{
-	struct maildir_list_context *ctx = (struct maildir_list_context *)_ctx;
-	int ret = ctx->mailbox_ctx.failed ? -1 : 0;
-
-	mailbox_tree_deinit(ctx->tree_ctx);
-	pool_unref(ctx->pool);
-	return ret;
-}
-
-static struct mailbox_node *find_next(struct mailbox_node **node,
-				      string_t *path, char hierarchy_sep)
-{
-	struct mailbox_node *child;
-	size_t len;
-
-	while (*node != NULL) {
-		if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0)
-			return *node;
-
-		if ((*node)->children != NULL) {
-			len = str_len(path);
-			if (len != 0)
-				str_append_c(path, hierarchy_sep);
-			str_append(path, (*node)->name);
-
-			child = find_next(&(*node)->children, path,
-					  hierarchy_sep);
-			if (child != NULL)
-				return child;
-
-			str_truncate(path, len);
-		}
-
-		*node = (*node)->next;
-	}
-
-	return NULL;
-}
-
-struct mailbox_list *
-maildir_mailbox_list_next(struct mailbox_list_context *_ctx)
-{
-	struct maildir_list_context *ctx = (struct maildir_list_context *)_ctx;
-	struct mailbox_node *node;
-
-	for (node = ctx->next_node; node != NULL; node = node->next) {
-		if ((node->flags & MAILBOX_FLAG_MATCHED) != 0)
-			break;
-	}
-
-	if (node == NULL) {
-		if (ctx->root == NULL)
-			return NULL;
-
-		str_truncate(ctx->node_path, 0);
-		node = find_next(&ctx->root, ctx->node_path,
-				 ctx->mailbox_ctx.storage->hierarchy_sep);
-                ctx->parent_pos = str_len(ctx->node_path);
-
-		if (node == NULL)
-			return NULL;
-	}
-	ctx->next_node = node->next;
-
-	i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0);
-	node->flags &= ~MAILBOX_FLAG_MATCHED;
-
-	str_truncate(ctx->node_path, ctx->parent_pos);
-	if (ctx->parent_pos != 0) {
-		str_append_c(ctx->node_path,
-			     ctx->mailbox_ctx.storage->hierarchy_sep);
-	}
-	str_append(ctx->node_path, node->name);
-
-	ctx->list.name = str_c(ctx->node_path);
-	ctx->list.flags = node->flags;
-	return &ctx->list;
-}
--- a/src/lib-storage/index/maildir/maildir-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -1,12 +1,12 @@
 /* Copyright (C) 2002-2006 Timo Sirainen */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "array.h"
 #include "hostpid.h"
 #include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
-#include "subscription-file/subscription-file.h"
 #include "maildir-storage.h"
 #include "maildir-uidlist.h"
 #include "maildir-keywords.h"
@@ -19,10 +19,7 @@
 
 #define CREATE_MODE 0777 /* umask() should limit it more */
 
-/* Don't allow creating too long mailbox names. They could start causing
-   problems when they reach the limit. */
-#define MAILDIR_MAX_MAILBOX_NAME_LENGTH (PATH_MAX/2)
-
+#define MAILDIR_PLUSPLUS_DRIVER_NAME "maildir++"
 #define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
 
 struct rename_context {
@@ -36,27 +33,38 @@
 
 static const char *maildirs[] = { "cur", "new", "tmp", NULL  };
 
-static int verify_inbox(struct maildir_storage *storage);
+static int verify_inbox(struct mail_storage *storage);
+
+static const char *strip_tail_slash(const char *path)
+{
+	size_t len = strlen(path);
 
-static struct mail_storage *
-maildir_create(const char *data, const char *user,
-	       enum mail_storage_flags flags,
-	       enum mail_storage_lock_method lock_method)
+	if (len > 0 && path[len-1] == '/')
+		return t_strndup(path, len-1);
+	else
+		return path;
+}
+
+static const char *strip_tail_slash_and_cut(const char *path)
+{
+	return strip_tail_slash(t_strcut(path, ':'));
+}
+
+static int
+maildir_get_list_settings(struct mailbox_list_settings *list_set,
+			  const char *data, enum mail_storage_flags flags)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
-	struct maildir_storage *storage;
-	struct index_storage *istorage;
-	const char *root_dir, *inbox_dir, *index_dir, *control_dir;
 	const char *home, *path, *p;
-	size_t len;
-	pool_t pool;
 
-	inbox_dir = root_dir = index_dir = control_dir = NULL;
+	memset(list_set, 0, sizeof(*list_set));
+	list_set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME;
+	list_set->maildir_name = "";
 
 	if (data == NULL || *data == '\0') {
 		if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
 			i_error("maildir: root directory not given");
-			return NULL;
+			return -1;
 		}
 
 		/* we'll need to figure out the maildir location ourself.
@@ -68,7 +76,7 @@
 					i_info("maildir: root exists (%s)",
 					       path);
 				}
-				root_dir = path;
+				list_set->root_dir = path;
 			} else {
 				if (debug) {
 					i_info("maildir: access(%s, rwx): "
@@ -83,7 +91,7 @@
 		if (access("/cur", R_OK|W_OK|X_OK) == 0) {
 			if (debug)
 				i_info("maildir: /cur exists, assuming chroot");
-			root_dir = "/";
+			list_set->root_dir = "/";
 		}
 	} else {
 		/* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
@@ -91,68 +99,87 @@
 			i_info("maildir: data=%s", data);
 		p = strchr(data, ':');
 		if (p == NULL)
-			root_dir = data;
+			list_set->root_dir = data;
 		else {
-			root_dir = t_strdup_until(data, p);
+			list_set->root_dir = t_strdup_until(data, p);
 
 			do {
 				p++;
-				if (strncmp(p, "INBOX=", 6) == 0)
-					inbox_dir = t_strcut(p+6, ':');
-				else if (strncmp(p, "INDEX=", 6) == 0)
-					index_dir = t_strcut(p+6, ':');
-				else if (strncmp(p, "CONTROL=", 8) == 0)
-					control_dir = t_strcut(p+8, ':');
+				if (strncmp(p, "INBOX=", 6) == 0) {
+					list_set->inbox_path =
+						strip_tail_slash_and_cut(p+6);
+				} else if (strncmp(p, "INDEX=", 6) == 0) {
+					list_set->index_dir =
+						strip_tail_slash_and_cut(p+6);
+				} else if (strncmp(p, "CONTROL=", 8) == 0) {
+					list_set->control_dir =
+						strip_tail_slash_and_cut(p+8);
+				}
 				p = strchr(p, ':');
 			} while (p != NULL);
 		}
 	}
 
-	if (root_dir == NULL) {
+	if (list_set->root_dir == NULL) {
 		if (debug)
 			i_info("maildir: couldn't find root dir");
+		return -1;
+	}
+	list_set->root_dir = strip_tail_slash(list_set->root_dir);
+
+	if (list_set->index_dir != NULL &&
+	    strcmp(list_set->index_dir, "MEMORY") == 0)
+		list_set->index_dir = "";
+	return 0;
+}
+
+static struct mail_storage *
+maildir_create(const char *data, const char *user,
+	       enum mail_storage_flags flags,
+	       enum mail_storage_lock_method lock_method)
+{
+	struct maildir_storage *storage;
+	struct index_storage *istorage;
+	struct mailbox_list_settings list_set;
+	struct mailbox_list *list;
+	const char *error;
+	pool_t pool;
+
+	if (maildir_get_list_settings(&list_set, data, flags) < 0)
+		return NULL;
+
+	pool = pool_alloconly_create("storage", 512);
+	storage = p_new(pool, struct maildir_storage, 1);
+
+	if (mailbox_list_init("maildir++", &list_set,
+			      mail_storage_get_list_flags(flags),
+			      mailbox_storage_list_is_mailbox, storage,
+			      &list, &error) < 0) {
+		i_error("maildir++: %s", error);
+		pool_unref(pool);
 		return NULL;
 	}
 
-	/* strip trailing '/' */
-	len = strlen(root_dir);
-	if (root_dir[len-1] == '/')
-		root_dir = t_strndup(root_dir, len-1);
-
-	if (index_dir == NULL)
-		index_dir = root_dir;
-	else if (strcmp(index_dir, "MEMORY") == 0)
-		index_dir = NULL;
-
-	if (debug) {
-		i_info("maildir: root=%s, index=%s, control=%s, inbox=%s",
-		       root_dir, index_dir == NULL ? "" : index_dir,
-		       control_dir == NULL ? "" : control_dir,
-		       inbox_dir == NULL ? "" : inbox_dir);
-	}
-
-	pool = pool_alloconly_create("storage", 512);
-	storage = p_new(pool, struct maildir_storage, 1);
-	storage->control_dir = p_strdup(pool, home_expand(control_dir));
 	storage->copy_with_hardlinks =
 		getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
+	storage->stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL;
+
+	storage->temp_prefix = mailbox_list_get_temp_prefix(list);
+	if (list_set.control_dir == NULL) {
+		/* put the temp files into tmp/ directory preferrably */
+		storage->temp_prefix =
+			p_strconcat(pool, "tmp/", storage->temp_prefix, NULL);
+	}
 
 	istorage = INDEX_STORAGE(storage);
 	istorage->storage = maildir_storage;
 	istorage->storage.pool = pool;
 
-	/* the default ".temp.xxx" prefix would be treated as directory */
-	istorage->temp_prefix =
-		p_strconcat(pool, "temp.", my_hostname, ".", my_pid, ".", NULL);
-
-	istorage->dir = p_strdup(pool, home_expand(root_dir));
-	istorage->inbox_path = p_strdup(pool, home_expand(inbox_dir));
-	istorage->index_dir = p_strdup(pool, home_expand(index_dir));
 	istorage->user = p_strdup(pool, user);
 	istorage->callbacks = p_new(pool, struct mail_storage_callbacks, 1);
-	index_storage_init(istorage, flags, lock_method);
+	index_storage_init(istorage, list, flags, lock_method);
 
-	(void)verify_inbox(storage);
+	(void)verify_inbox(STORAGE(storage));
 	return STORAGE(storage);
 }
 
@@ -187,130 +214,28 @@
 	return TRUE;
 }
 
-static bool maildir_is_valid_create_name(struct mail_storage *storage,
-					 const char *name)
+static const char *
+maildir_get_unlink_dest(struct mail_storage *storage, const char *name)
 {
-	size_t len;
-
-	/* check that there are no adjacent hierarchy separators */
-	for (len = 0; name[len] != '\0'; len++) {
-		if (name[len] == MAILDIR_FS_SEP &&
-		    name[len+1] == MAILDIR_FS_SEP)
-			return FALSE;
-	}
-
-	if (len == 0 || len > MAILDIR_MAX_MAILBOX_NAME_LENGTH ||
-	    name[0] == MAILDIR_FS_SEP || name[len-1] == MAILDIR_FS_SEP)
-		return FALSE;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-		return TRUE;
-
-	if (*name == '~' || strchr(name, '/') != NULL)
-		return FALSE;
-
-	if (name[0] == '.' && (name[1] == '\0' ||
-			       (name[1] == '.' && name[2] == '\0'))) {
-		/* "." and ".." aren't allowed. */
-		return FALSE;
-	}
-
-	if (mailbox_name_is_too_large(name, '.'))
-		return FALSE;
-
-	return TRUE;
-}
-
-static bool maildir_is_valid_existing_name(struct mail_storage *storage,
-					   const char *name)
-{
-	if (name[0] == '\0' || name[strlen(name)-1] == '/')
-		return FALSE;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-		return TRUE;
+	const char *root_dir;
 
-	if (*name == '~' || strchr(name, '/') != NULL)
-		return FALSE;
-
-	if (name[0] == '.' && (name[1] == '\0' ||
-			       (name[1] == '.' && name[2] == '\0'))) {
-		/* "." and ".." aren't allowed. */
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static const char *maildir_get_absolute_path(const char *name)
-{
-	const char *p;
-
-	name = home_expand(name);
-
-	p = strrchr(name, '/');
-	if (p == NULL)
-		return name;
-	return t_strconcat(t_strdup_until(name, p+1),
-			   MAILDIR_FS_SEP_S, p+1, NULL);
-}
-
-const char *maildir_get_path(struct index_storage *storage, const char *name)
-{
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return maildir_get_absolute_path(name);
-
-	if (strcmp(name, "INBOX") == 0) {
-		return storage->inbox_path != NULL ?
-			storage->inbox_path : storage->dir;
-	}
-
-	return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
-}
-
-static const char *
-maildir_get_unlink_dest(struct index_storage *storage, const char *name)
-{
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
+	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
 	    (*name == '/' || *name == '~'))
 		return NULL;
 
-	return maildir_get_path(storage, MAILDIR_UNLINK_DIRNAME);
+	if (strcmp(mailbox_list_get_driver_name(storage->list),
+		   MAILDIR_PLUSPLUS_DRIVER_NAME) != 0) {
+		/* Not maildir++ driver. Don't use this trick. */
+		return NULL;
+	}
+
+	root_dir = mailbox_list_get_path(storage->list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	return t_strdup_printf("%s/%c"MAILDIR_UNLINK_DIRNAME, root_dir,
+			       mailbox_list_get_hierarchy_sep(storage->list));
 }
 
-static const char *maildir_get_index_path(struct index_storage *storage,
-					  const char *name)
-{
-	if (storage->index_dir == NULL)
-		return NULL;
-
-	if (strcmp(name, "INBOX") == 0 &&
-	    strcmp(storage->index_dir, storage->dir) == 0)
-		return storage->dir;
-
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return maildir_get_absolute_path(name);
-
-	return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
-}
-
-static const char *maildir_get_control_path(struct maildir_storage *storage,
-					    const char *name)
-{
-	if (storage->control_dir == NULL)
-		return maildir_get_path(INDEX_STORAGE(storage), name);
-
-	if ((STORAGE(storage)->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return maildir_get_absolute_path(name);
-
-	return t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
-			   name, NULL);
-}
-
-static int mkdir_verify(struct index_storage *storage,
+static int mkdir_verify(struct mail_storage *storage,
 			const char *dir, bool verify)
 {
 	struct stat st;
@@ -320,7 +245,7 @@
 			return 0;
 
 		if (errno != ENOENT) {
-			mail_storage_set_critical(&storage->storage,
+			mail_storage_set_critical(storage,
 						  "lstat(%s) failed: %m", dir);
 			return -1;
 		}
@@ -329,7 +254,7 @@
 	if (mkdir_parents(dir, CREATE_MODE) < 0 &&
 	    (errno != EEXIST || !verify)) {
 		if (errno != EEXIST && (!verify || errno != ENOENT)) {
-			mail_storage_set_critical(&storage->storage,
+			mail_storage_set_critical(storage,
 						  "mkdir(%s) failed: %m", dir);
 		}
 		return -1;
@@ -339,7 +264,7 @@
 }
 
 /* create or fix maildir, ignore if it already exists */
-static int create_maildir(struct index_storage *storage,
+static int create_maildir(struct mail_storage *storage,
 			  const char *dir, bool verify)
 {
 	const char **tmp, *path;
@@ -367,21 +292,25 @@
 	return 0;
 }
 
-static int create_index_dir(struct index_storage *storage, const char *name)
+static int create_index_dir(struct mail_storage *storage, const char *name)
 {
-	const char *dir;
+	const char *index_dir, *root_dir, *dir;
 
-	if (storage->index_dir == NULL)
+	index_dir = mailbox_list_get_path(storage->list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*index_dir == '\0')
 		return 0;
 
-	if (strcmp(storage->index_dir, storage->dir) == 0 ||
-	    (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL &&
-	     strcmp(storage->index_dir, storage->inbox_path) == 0))
+	root_dir = mailbox_list_get_path(storage->list, name,
+					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (strcmp(index_dir, root_dir) == 0)
 		return 0;
 
-	dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
+	dir = t_strdup_printf("%s/%c%s", index_dir,
+			      mailbox_list_get_hierarchy_sep(storage->list),
+			      name);
 	if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
-		mail_storage_set_critical(&storage->storage,
+		mail_storage_set_critical(storage,
 					  "mkdir(%s) failed: %m", dir);
 		return -1;
 	}
@@ -389,17 +318,22 @@
 	return 0;
 }
 
-static int create_control_dir(struct maildir_storage *storage, const char *name)
+static int create_control_dir(struct mail_storage *storage, const char *name)
 {
-	const char *dir;
+	const char *control_dir, *root_dir, *dir;
 
-	if (storage->control_dir == NULL)
+	control_dir = mailbox_list_get_path(storage->list, name,
+					    MAILBOX_LIST_PATH_TYPE_CONTROL);
+	root_dir = mailbox_list_get_path(storage->list, name,
+					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (strcmp(control_dir, root_dir) == 0)
 		return 0;
 
-	dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
-			  name, NULL);
+	dir = t_strdup_printf("%s/%c%s", control_dir,
+			      mailbox_list_get_hierarchy_sep(storage->list),
+			      name);
 	if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
-		mail_storage_set_critical(STORAGE(storage),
+		mail_storage_set_critical(storage,
 					  "mkdir(%s) failed: %m", dir);
 		return -1;
 	}
@@ -407,19 +341,16 @@
 	return 0;
 }
 
-static int verify_inbox(struct maildir_storage *storage)
+static int verify_inbox(struct mail_storage *storage)
 {
-	struct index_storage *istorage = INDEX_STORAGE(storage);
 	const char *path;
 
-	path = istorage->inbox_path != NULL ?
-		istorage->inbox_path : istorage->dir;
-
-	if (create_maildir(istorage, path, TRUE) < 0)
+	path = mailbox_list_get_path(storage->list, "INBOX",
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (create_maildir(storage, path, TRUE) < 0)
 		return -1;
 
-	/* make sure the index directories exist */
-	if (create_index_dir(istorage, "INBOX") < 0)
+	if (create_index_dir(storage, "INBOX") < 0)
 		return -1;
 	if (create_control_dir(storage, "INBOX") < 0)
 		return -1;
@@ -445,12 +376,15 @@
 	int shared;
 	pool_t pool;
 
-	path = maildir_get_path(istorage, name);
-	index_dir = maildir_get_index_path(istorage, name);
-	control_dir = maildir_get_control_path(storage, name);
+	path = mailbox_list_get_path(istorage->storage.list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	index_dir = mailbox_list_get_path(istorage->storage.list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
+	control_dir = mailbox_list_get_path(istorage->storage.list, name,
+					    MAILBOX_LIST_PATH_TYPE_CONTROL);
 
 	if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) != 0)
-		index_dir = NULL;
+		index_dir = "";
 
 	index = index_storage_alloc(index_dir, path,
 				    MAILDIR_INDEX_PREFIX);
@@ -502,33 +436,17 @@
 }
 
 static const char *
-maildir_get_mailbox_path(struct mail_storage *_storage,
-			 const char *name, bool *is_file_r)
+maildir_get_mailbox_control_dir(struct mail_storage *storage, const char *name)
 {
-	struct maildir_storage *storage = (struct maildir_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	*is_file_r = FALSE;
-	if (*name == '\0')
-		return istorage->dir;
-	return maildir_get_path(istorage, name);
+	return mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_CONTROL);
 }
 
 static const char *
-maildir_get_mailbox_control_dir(struct mail_storage *_storage, const char *name)
+maildir_get_mailbox_index_dir(struct mail_storage *storage, const char *name)
 {
-	struct maildir_storage *storage = (struct maildir_storage *)_storage;
-
-	return maildir_get_control_path(storage, name);
-}
-
-static const char *
-maildir_get_mailbox_index_dir(struct mail_storage *_storage, const char *name)
-{
-	struct maildir_storage *storage = (struct maildir_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	return maildir_get_index_path(istorage, name);
+	return mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_INDEX);
 }
 
 static struct mailbox *
@@ -536,7 +454,6 @@
 		     struct istream *input, enum mailbox_open_flags flags)
 {
 	struct maildir_storage *storage = (struct maildir_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
 	const char *path;
 	struct stat st;
 
@@ -549,25 +466,26 @@
 	}
 
 	if (strcmp(name, "INBOX") == 0) {
-		if (verify_inbox(storage) < 0)
+		if (verify_inbox(_storage) < 0)
 			return NULL;
 		return maildir_open(storage, "INBOX", flags);
 	}
 
-	if (!maildir_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return NULL;
 	}
 
-	path = maildir_get_path(istorage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (stat(path, &st) == 0) {
 		/* exists - make sure the required directories are also there */
-		if (create_maildir(istorage, path, TRUE) < 0 ||
-		    create_control_dir(storage, name) < 0)
+		if (create_maildir(_storage, path, TRUE) < 0 ||
+		    create_control_dir(_storage, name) < 0)
 			return NULL;
 
 		if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) == 0) {
-			if (create_index_dir(istorage, name) < 0)
+			if (create_index_dir(_storage, name) < 0)
 				return NULL;
 		}
 
@@ -583,7 +501,7 @@
 	}
 }
 
-static int maildir_create_shared(struct index_storage *storage,
+static int maildir_create_shared(struct mail_storage *storage,
 				 const char *dir, mode_t mode, gid_t gid)
 {
 	const char *path;
@@ -598,15 +516,14 @@
 	old_mask = umask(0777 ^ mode);
 	if (create_maildir(storage, dir, FALSE) < 0) {
 		if (errno == EEXIST) {
-			mail_storage_set_error(&storage->storage,
+			mail_storage_set_error(storage,
 					       "Mailbox already exists");
 		}
 		umask(old_mask);
 		return -1;
 	}
 	if (chown(dir, (uid_t)-1, gid) < 0) {
-		mail_storage_set_critical(&storage->storage,
-					  "chown(%s) failed: %m", dir);
+		mail_storage_set_critical(storage, "chown(%s) failed: %m", dir);
 	}
 
 	path = t_strconcat(dir, "/dovecot-shared", NULL);
@@ -614,13 +531,12 @@
 	umask(old_mask);
 
 	if (fd == -1) {
-		mail_storage_set_critical(&storage->storage,
-					  "open(%s) failed: %m", path);
+		mail_storage_set_critical(storage, "open(%s) failed: %m", path);
 		return -1;
 	}
 
 	if (fchown(fd, (uid_t)-1, gid) < 0) {
-		mail_storage_set_critical(&storage->storage,
+		mail_storage_set_critical(storage,
 					  "fchown(%s) failed: %m", path);
 	}
 	(void)close(fd);
@@ -631,29 +547,31 @@
 				  const char *name,
 				  bool directory __attr_unused__)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
 	struct stat st;
-	const char *path, *shared_path;
+	const char *path, *root_dir, *shared_path;
 	int fd;
 
 	mail_storage_clear_error(_storage);
 
-	if (!maildir_is_valid_create_name(_storage, name)) {
+	if (!mailbox_list_is_valid_create_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	path = maildir_get_path(storage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	root_dir = mailbox_list_get_path(_storage->list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
 	/* if dovecot-shared exists in the root dir, create the mailbox using
 	   its permissions and gid, and copy the dovecot-shared inside it. */
-	shared_path = t_strconcat(storage->dir, "/dovecot-shared", NULL);
+	shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
 	if (stat(shared_path, &st) == 0) {
-		return maildir_create_shared(storage, path,
+		return maildir_create_shared(_storage, path,
 					     st.st_mode & 0666, st.st_gid);
 	}
 
-	if (create_maildir(storage, path, FALSE) < 0) {
+	if (create_maildir(_storage, path, FALSE) < 0) {
 		if (errno == EEXIST) {
 			mail_storage_set_error(_storage,
 					       "Mailbox already exists");
@@ -675,7 +593,6 @@
 static int maildir_mailbox_delete(struct mail_storage *_storage,
 				  const char *name)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
 	struct stat st;
 	const char *src, *dest, *index_dir;
 	int count;
@@ -687,16 +604,14 @@
 		return -1;
 	}
 
-	if (!maildir_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	/* rename the .maildir into ..DOVECOT-TRASH which marks it as being
-	   deleted. delete indexes before the actual maildir. this way we
-	   never see partially deleted mailboxes. */
-	src = maildir_get_path(storage, name);
-	dest = maildir_get_unlink_dest(storage, name);
+	/* check if the mailbox actually exists */
+	src = mailbox_list_get_path(_storage->list, name,
+				    MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (stat(src, &st) != 0 && errno == ENOENT) {
 		mail_storage_set_error(_storage,
 			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
@@ -706,13 +621,16 @@
 	/* Make sure the indexes are closed before trying to delete the
 	   directory that contains them. It can still fail with some NFS
 	   implementations if indexes are opened by another session, but
-	   can't really help that. */
+	   that can't really be helped. */
 	index_storage_destroy_unrefed();
 
-	if (storage->index_dir != NULL && *name != '/' && *name != '~' &&
-	    strcmp(storage->index_dir, storage->dir) != 0) {
-		index_dir = t_strconcat(storage->index_dir,
-					"/"MAILDIR_FS_SEP_S, name, NULL);
+	/* if there's a separate index directory, delete it before the actual
+	   maildir. this way we never see partially deleted mailboxes. */
+	index_dir = mailbox_list_get_path(_storage->list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (strcmp(index_dir, src) != 0) {
+		i_assert(*name != '/' && *name != '~');
+
 		if (unlink_directory(index_dir, TRUE) < 0 &&
 		    errno != ENOTEMPTY) {
 			mail_storage_set_critical(_storage,
@@ -721,11 +639,16 @@
 		}
 	}
 
+	dest = maildir_get_unlink_dest(_storage, name);
 	if (dest == NULL) {
 		/* absolute maildir path, delete the directory directly
 		   without any renaming */
 		dest = src;
 	} else {
+		/* rename the .maildir into ..DOVECOT-TRASH which atomically
+		   marks it as being deleted. If we die before deleting the
+		   ..DOVECOT-TRASH directory, it gets deleted the next time
+		   mailbox listing sees it. */
 		count = 0;
 		while (rename(src, dest) < 0 && count < 2) {
 			/* EBUSY is given by some NFS implementations */
@@ -752,30 +675,26 @@
 			"unlink_directory(%s) failed: %m", dest);
 
 		/* it's already renamed to ..dir, which means it's deleted
-		   as far as client is concerned. Report success. */
+		   as far as the client is concerned. Report success. */
 	}
 
 	return 0;
 }
 
-static int rename_indexes(struct index_storage *storage,
+static int rename_indexes(struct mail_storage *storage,
 			  const char *oldname, const char *newname)
 {
 	const char *oldpath, *newpath;
 
-	if (storage->index_dir == NULL ||
-	    strcmp(storage->index_dir, storage->dir) == 0)
+	oldpath = mailbox_list_get_path(storage->list, oldname,
+					MAILBOX_LIST_PATH_TYPE_INDEX);
+	newpath = mailbox_list_get_path(storage->list, newname,
+					MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (strcmp(oldpath, newpath) == 0)
 		return 0;
 
-	/* Rename it's index. */
-	oldpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
-			      oldname, NULL);
-	newpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
-			      newname, NULL);
-
 	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
-		mail_storage_set_critical(&storage->storage,
-					  "rename(%s, %s) failed: %m",
+		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
 					  oldpath, newpath);
 		return -1;
 	}
@@ -783,13 +702,13 @@
 	return 0;
 }
 
-static int rename_subfolders(struct index_storage *storage,
+static int rename_subfolders(struct mail_storage *storage,
 			     const char *oldname, const char *newname)
 {
-	struct mailbox_list_context *ctx;
-        struct mailbox_list *list;
+	struct mailbox_list_iterate_context *iter;
+        struct mailbox_info *info;
 	ARRAY_DEFINE(names_arr, const char *);
-	const char *oldpath, *newpath, *old_listname, *new_listname;
+	const char *mask, *oldpath, *newpath, *old_listname, *new_listname;
 	const char *const *names;
 	unsigned int i, count;
 	size_t oldnamelen;
@@ -805,18 +724,20 @@
 	   other processes though. */
 	pool = pool_alloconly_create("Maildir subfolders list", 1024);
 	i_array_init(&names_arr, 64);
-	ctx = maildir_mailbox_list_init(&storage->storage, oldname,
-					MAILDIR_FS_SEP_S"*",
-					MAILBOX_LIST_FAST_FLAGS);
-	while ((list = maildir_mailbox_list_next(ctx)) != NULL) {
+
+	mask = t_strdup_printf("%s%c*", oldname,
+			       mailbox_list_get_hierarchy_sep(storage->list));
+	iter = mailbox_list_iter_init(storage->list, "", mask,
+				      MAILBOX_LIST_ITER_FAST_FLAGS);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
 		const char *name;
 
-		i_assert(oldnamelen <= strlen(list->name));
+		i_assert(oldnamelen <= strlen(info->name));
 
-		name = p_strdup(pool, list->name + oldnamelen);
+		name = p_strdup(pool, info->name + oldnamelen);
 		array_append(&names_arr, &name, 1);
 	}
-	if (maildir_mailbox_list_deinit(ctx) < 0) {
+	if (mailbox_list_iter_deinit(&iter) < 0) {
 		ret = -1;
 		names = NULL; count = 0;
 	} else {
@@ -828,8 +749,10 @@
 
 		old_listname = t_strconcat(oldname, names[i], NULL);
 		new_listname = t_strconcat(newname, names[i], NULL);
-		oldpath = maildir_get_path(storage, old_listname);
-		newpath = maildir_get_path(storage, new_listname);
+		oldpath = mailbox_list_get_path(storage->list, old_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		newpath = mailbox_list_get_path(storage->list, new_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
 		/* FIXME: it's possible to merge two folders if either one of
 		   them doesn't have existing root folder. We could check this
@@ -843,9 +766,8 @@
 		    errno == EEXIST || errno == ENOTEMPTY)
 			ret = 1;
 		else {
-			mail_storage_set_critical(&storage->storage,
-						  "rename(%s, %s) failed: %m",
-						  oldpath, newpath);
+			mail_storage_set_critical(storage,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
 			ret = -1;
 			t_pop();
 			break;
@@ -863,15 +785,14 @@
 static int maildir_mailbox_rename(struct mail_storage *_storage,
 				  const char *oldname, const char *newname)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *oldpath, *newpath;
 	int ret;
         bool found;
 
 	mail_storage_clear_error(_storage);
 
-	if (!maildir_is_valid_existing_name(_storage, oldname) ||
-	    !maildir_is_valid_create_name(_storage, newname)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) ||
+	    !mailbox_list_is_valid_create_name(_storage->list, newname)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
@@ -884,15 +805,17 @@
 
 	/* NOTE: it's possible to rename a nonexisting folder which has
 	   subfolders. In that case we should ignore the rename() error. */
-	oldpath = maildir_get_path(storage, oldname);
-	newpath = maildir_get_path(storage, newname);
+	oldpath = mailbox_list_get_path(_storage->list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(_storage->list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
 	ret = rename(oldpath, newpath);
 	if (ret == 0 || errno == ENOENT) {
-		(void)rename_indexes(storage, oldname, newname);
+		(void)rename_indexes(_storage, oldname, newname);
 
 		found = ret == 0;
-		ret = rename_subfolders(storage, oldname, newname);
+		ret = rename_subfolders(_storage, oldname, newname);
 		if (ret < 0)
 			return -1;
 		if (!found && ret == 0) {
@@ -915,57 +838,6 @@
 	}
 }
 
-static int maildir_set_subscribed(struct mail_storage *_storage,
-				  const char *name, bool set)
-{
-	struct maildir_storage *storage = (struct maildir_storage *)_storage;
-	const char *path;
-
-	path = t_strconcat(storage->control_dir != NULL ?
-			   storage->control_dir : INDEX_STORAGE(storage)->dir,
-			   "/" SUBSCRIPTION_FILE_NAME, NULL);
-
-	return subsfile_set_subscribed(_storage, path,
-				       INDEX_STORAGE(storage)->temp_prefix,
-				       name, set);
-}
-
-static int maildir_get_mailbox_name_status(struct mail_storage *_storage,
-					   const char *name,
-					   enum mailbox_name_status *status)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	struct stat st;
-	const char *path;
-
-	mail_storage_clear_error(_storage);
-
-	if (!maildir_is_valid_existing_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	path = maildir_get_path(storage, name);
-	if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
-		*status = MAILBOX_NAME_EXISTS;
-		return 0;
-	}
-
-	if (!maildir_is_valid_create_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	if (errno == ENOENT) {
-		*status = MAILBOX_NAME_VALID;
-		return 0;
-	} else {
-		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
-					  path);
-		return -1;
-	}
-}
-
 static int maildir_storage_close(struct mailbox *box)
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
@@ -1006,6 +878,75 @@
 		t_strconcat(mbox->path, "/cur", NULL));
 }
 
+static int
+maildir_storage_is_mailbox(struct mail_storage *_storage,
+			   const char *dir, const char *fname,
+			   enum mailbox_list_iter_flags iter_flags
+				__attr_unused__,
+			   enum mailbox_info_flags *flags,
+			   enum mailbox_list_file_type type)
+{
+	struct maildir_storage *storage = (struct maildir_storage *)_storage;
+	int ret;
+
+	if (fname[1] == mailbox_list_get_hierarchy_sep(_storage->list) &&
+	    strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0 &&
+	    strcmp(mailbox_list_get_driver_name(_storage->list),
+		   MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) {
+		const char *path;
+		struct stat st;
+
+		/* this directory is in the middle of being deleted,
+		   or the process trying to delete it had died.
+		   delete it ourself if it's been there longer than
+		   one hour. */
+		t_push();
+		path = t_strdup_printf("%s/%s", dir, fname);
+		if (stat(path, &st) == 0 &&
+		    st.st_mtime < ioloop_time - 3600)
+			(void)unlink_directory(path, TRUE);
+		t_pop();
+
+		*flags = MAILBOX_NONEXISTENT;
+		return 0;
+	}
+
+	switch (type) {
+	case MAILBOX_LIST_FILE_TYPE_DIR:
+		/* all directories are valid maildirs */
+		return 1;
+
+	case MAILBOX_LIST_FILE_TYPE_FILE:
+	case MAILBOX_LIST_FILE_TYPE_OTHER:
+		/* non-directories are not */
+		*flags = MAILBOX_NOSELECT;
+		return 0;
+
+	case MAILBOX_LIST_FILE_TYPE_UNKNOWN:
+	case MAILBOX_LIST_FILE_TYPE_SYMLINK:
+		/* need to check with stat() to be sure */
+		break;
+	}
+
+	/* Check files beginning with .nfs always because they may be
+	   temporary files created by the kernel */
+	if (storage->stat_dirs || strncmp(fname, ".nfs", 4) == 0) {
+		const char *path;
+		struct stat st;
+
+		t_push();
+		path = t_strdup_printf("%s/%s", dir, fname);
+		ret = (stat(path, &st) < 0 || !S_ISDIR(st.st_mode)) ? 0 : 1;
+		t_pop();
+
+		if (ret == 0)
+			*flags = MAILBOX_NONEXISTENT;
+	} else {
+		ret = 1;
+	}
+	return ret;
+}
+
 static void maildir_class_init(void)
 {
 	maildir_transaction_class_init();
@@ -1018,7 +959,7 @@
 
 struct mail_storage maildir_storage = {
 	MEMBER(name) MAILDIR_STORAGE_NAME,
-	MEMBER(hierarchy_sep) MAILDIR_FS_SEP,
+	MEMBER(mailbox_is_file) FALSE,
 
 	{
 		maildir_class_init,
@@ -1027,18 +968,13 @@
 		maildir_free,
 		maildir_autodetect,
 		index_storage_set_callbacks,
-		maildir_get_mailbox_path,
 		maildir_get_mailbox_control_dir,
 		maildir_get_mailbox_index_dir,
 		maildir_mailbox_open,
 		maildir_mailbox_create,
 		maildir_mailbox_delete,
 		maildir_mailbox_rename,
-		maildir_mailbox_list_init,
-		maildir_mailbox_list_next,
-		maildir_mailbox_list_deinit,
-		maildir_set_subscribed,
-		maildir_get_mailbox_name_status,
+		maildir_storage_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/index/maildir/maildir-storage.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Thu Nov 16 02:16:31 2006 +0200
@@ -1,14 +1,10 @@
 #ifndef __MAILDIR_STORAGE_H
 #define __MAILDIR_STORAGE_H
 
-/* Hierarchy separator in Maildir++ filenames - shouldn't be changed */
-#define MAILDIR_FS_SEP '.'
-#define MAILDIR_FS_SEP_S "."
-
 #define MAILDIR_STORAGE_NAME "maildir"
-#define SUBSCRIPTION_FILE_NAME "subscriptions"
+#define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions"
 #define MAILDIR_INDEX_PREFIX "dovecot.index"
-#define MAILDIR_UNLINK_DIRNAME MAILDIR_FS_SEP_S"DOVECOT-TRASHED"
+#define MAILDIR_UNLINK_DIRNAME "DOVECOT-TRASHED"
 
 /* "base,S=123:2," means:
    <base> [<extra sep> <extra data> [..]] <info sep> 2 <flags sep> */
@@ -52,9 +48,11 @@
 struct maildir_storage {
 	struct index_storage storage;
 
-	const char *control_dir;
+	const char *temp_prefix;
+
 	unsigned int copy_with_hardlinks:1;
 	unsigned int save_size_in_filename:1;
+	unsigned int stat_dirs:1;
 };
 
 struct maildir_mailbox {
@@ -93,14 +91,6 @@
 		       mode_t mode, const char **fname_r);
 bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r);
 
-struct mailbox_list_context *
-maildir_mailbox_list_init(struct mail_storage *storage,
-			  const char *ref, const char *mask,
-			  enum mailbox_list_flags flags);
-int maildir_mailbox_list_deinit(struct mailbox_list_context *ctx);
-struct mailbox_list *
-maildir_mailbox_list_next(struct mailbox_list_context *ctx);
-
 int maildir_sync_is_synced(struct maildir_mailbox *mbox);
 
 struct mailbox_sync_context *
@@ -146,8 +136,6 @@
 int maildir_transaction_copy_commit(struct maildir_copy_context *ctx);
 void maildir_transaction_copy_rollback(struct maildir_copy_context *ctx);
 
-const char *maildir_get_path(struct index_storage *storage, const char *name);
-
 int maildir_sync_last_commit(struct maildir_mailbox *mbox);
 
 int maildir_filename_get_flags(struct maildir_keywords_sync_ctx *ctx,
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Thu Nov 16 02:16:31 2006 +0200
@@ -174,8 +174,7 @@
 
 	uidlist->dotlock_settings.timeout = UIDLIST_LOCK_STALE_TIMEOUT + 2;
 	uidlist->dotlock_settings.stale_timeout = UIDLIST_LOCK_STALE_TIMEOUT;
-	uidlist->dotlock_settings.temp_prefix =
-		INDEX_STORAGE(mbox->storage)->temp_prefix;
+	uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix;
 
 	return uidlist;
 }
--- a/src/lib-storage/index/mbox/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/mbox/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -12,7 +12,6 @@
 	istream-raw-mbox.c \
 	mbox-file.c \
 	mbox-from.c \
-	mbox-list.c \
 	mbox-lock.c \
 	mbox-mail.c \
 	mbox-md5.c \
--- a/src/lib-storage/index/mbox/mbox-list.c	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,434 +0,0 @@
-/* Copyright (C) 2002-2003 Timo Sirainen */
-
-#include "lib.h"
-#include "unlink-directory.h"
-#include "imap-match.h"
-#include "subscription-file/subscription-file.h"
-#include "mbox-storage.h"
-#include "home-expand.h"
-
-#include <dirent.h>
-#include <sys/stat.h>
-
-/* Assume that if atime < mtime, there are new mails. If it's good enough for
-   UW-IMAP, it's good enough for us. */
-#define STAT_GET_MARKED(st) \
-	((st).st_size == 0 ? MAILBOX_UNMARKED : \
-	 (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED)
-
-struct list_dir_context {
-	struct list_dir_context *prev;
-
-	DIR *dirp;
-	char *real_path, *virtual_path;
-};
-
-struct mbox_list_context {
-	struct mailbox_list_context mailbox_ctx;
-	struct index_storage *istorage;
-
-	struct imap_match_glob *glob;
-	struct subsfile_list_context *subsfile_ctx;
-
-	bool inbox_found;
-
-	struct mailbox_list *(*next)(struct mbox_list_context *ctx);
-
-	pool_t list_pool;
-	struct mailbox_list list;
-        struct list_dir_context *dir;
-};
-
-static struct mailbox_list *mbox_list_subs(struct mbox_list_context *ctx);
-static struct mailbox_list *mbox_list_path(struct mbox_list_context *ctx);
-static struct mailbox_list *mbox_list_next(struct mbox_list_context *ctx);
-
-static const char *mask_get_dir(const char *mask)
-{
-	const char *p, *last_dir;
-
-	last_dir = NULL;
-	for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
-		if (*p == '/')
-			last_dir = p;
-	}
-
-	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
-}
-
-static const char *
-mbox_get_path(struct index_storage *storage, const char *name)
-{
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) == 0 ||
-	    name == NULL || (*name != '/' && *name != '~' && *name != '\0'))
-		return t_strconcat(storage->dir, "/", name, NULL);
-	else
-		return home_expand(name);
-}
-
-static int list_opendir(struct mail_storage *storage,
-			const char *path, bool root, DIR **dirp)
-{
-	*dirp = opendir(*path == '\0' ? "/" : path);
-	if (*dirp != NULL)
-		return 1;
-
-	if (ENOTFOUND(errno)) {
-		/* root) user gave invalid hiearchy, ignore
-		   sub) probably just race condition with other client
-		   deleting the mailbox. */
-		return 0;
-	}
-
-	if (errno == EACCES) {
-		if (!root) {
-			/* subfolder, ignore */
-			return 0;
-		}
-		mail_storage_set_error(storage, "Access denied");
-		return -1;
-	}
-
-	mail_storage_set_critical(storage, "opendir(%s) failed: %m", path);
-	return -1;
-}
-
-struct mailbox_list_context *
-mbox_mailbox_list_init(struct mail_storage *storage,
-		       const char *ref, const char *mask,
-		       enum mailbox_list_flags flags)
-{
-	struct index_storage *istorage = (struct index_storage *)storage;
-	struct mbox_list_context *ctx;
-	const char *path, *virtual_path;
-	DIR *dirp;
-
-	ctx = i_new(struct mbox_list_context, 1);
-	ctx->mailbox_ctx.storage = storage;
-	ctx->istorage = istorage;
-	ctx->list_pool = pool_alloconly_create("mbox_list", 1024);
-        ctx->next = mbox_list_next;
-
-	mail_storage_clear_error(storage);
-
-	/* check that we're not trying to do any "../../" lists */
-	if (!mbox_is_valid_mask(storage, ref) ||
-	    !mbox_is_valid_mask(storage, mask)) {
-		mail_storage_set_error(storage, "Invalid mask");
-		return &ctx->mailbox_ctx;
-	}
-
-	if (*mask == '/' || *mask == '~') {
-		/* mask overrides reference */
-	} else if (*ref != '\0') {
-		/* merge reference and mask */
-		mask = t_strconcat(ref, mask, NULL);
-	}
-
-	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-		ctx->mailbox_ctx.storage = storage;
-		ctx->mailbox_ctx.flags = flags;
-		ctx->istorage = istorage;
-		ctx->next = mbox_list_subs;
-
-		path = t_strconcat(istorage->dir,
-				   "/" SUBSCRIPTION_FILE_NAME, NULL);
-		ctx->subsfile_ctx =
-			subsfile_list_init(storage, path);
-		if (ctx->subsfile_ctx == NULL) {
-			ctx->next = mbox_list_next;
-			ctx->mailbox_ctx.failed = TRUE;
-			return &ctx->mailbox_ctx;
-		}
-		ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
-		return &ctx->mailbox_ctx;
-	}
-
-	/* if we're matching only subdirectories, don't bother scanning the
-	   parent directories */
-	virtual_path = mask_get_dir(mask);
-
-	path = mbox_get_path(istorage, virtual_path);
-	if (list_opendir(storage, path, TRUE, &dirp) < 0)
-		return &ctx->mailbox_ctx;
-	/* if user gave invalid directory, we just don't show any results. */
-
-	ctx->mailbox_ctx.flags = flags;
-	ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
-
-	if (virtual_path != NULL && dirp != NULL)
-		ctx->next = mbox_list_path;
-
-	if (dirp != NULL) {
-		ctx->dir = i_new(struct list_dir_context, 1);
-		ctx->dir->dirp = dirp;
-		ctx->dir->real_path = i_strdup(path);
-		ctx->dir->virtual_path = i_strdup(virtual_path);
-	}
-	return &ctx->mailbox_ctx;
-}
-
-static void list_dir_context_free(struct list_dir_context *dir)
-{
-	(void)closedir(dir->dirp);
-	i_free(dir->real_path);
-	i_free(dir->virtual_path);
-	i_free(dir);
-}
-
-int mbox_mailbox_list_deinit(struct mailbox_list_context *_ctx)
-{
-	struct mbox_list_context *ctx = (struct mbox_list_context *)_ctx;
-	int ret = ctx->mailbox_ctx.failed ? -1 : 0;
-
-	if (ctx->subsfile_ctx != NULL) {
-		if (subsfile_list_deinit(ctx->subsfile_ctx) < 0)
-			ret = -1;
-	}
-
-	while (ctx->dir != NULL) {
-		struct list_dir_context *dir = ctx->dir;
-
-		ctx->dir = dir->prev;
-                list_dir_context_free(dir);
-	}
-
-	if (ctx->list_pool != NULL)
-		pool_unref(ctx->list_pool);
-	if (ctx->glob != NULL)
-		imap_match_deinit(&ctx->glob);
-	i_free(ctx);
-
-	return ret;
-}
-
-struct mailbox_list *mbox_mailbox_list_next(struct mailbox_list_context *_ctx)
-{
-	struct mbox_list_context *ctx = (struct mbox_list_context *)_ctx;
-
-	return ctx->next(ctx);
-}
-
-static int list_file(struct mbox_list_context *ctx, const char *fname)
-{
-        struct list_dir_context *dir;
-	const char *list_path, *real_path, *path;
-	struct stat st;
-	DIR *dirp;
-	size_t len;
-	enum imap_match_result match, match2;
-	int ret;
-	bool noselect;
-
-	/* skip ., .., subscription file and .imap/ directory */
-	if (fname[0] == '.' &&
-	    (fname[1] == '\0' ||
-	     (fname[1] == '.' && fname[2] == '\0') ||
-	     strcmp(fname, SUBSCRIPTION_FILE_NAME) == 0 ||
-	     strcmp(fname, MBOX_INDEX_DIR_NAME) == 0))
-		return 0;
-
-	/* skip all .lock files */
-	len = strlen(fname);
-	if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
-		return 0;
-
-	/* check the mask */
-	if (ctx->dir->virtual_path == NULL)
-		list_path = fname;
-	else {
-		list_path = t_strconcat(ctx->dir->virtual_path,
-					"/", fname, NULL);
-	}
-
-	if ((match = imap_match(ctx->glob, list_path)) < 0)
-		return 0;
-
-	/* see if it's a directory */
-	real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
-	if (stat(real_path, &st) == 0)
-		noselect = FALSE;
-	else if (errno == EACCES || errno == ELOOP)
-		noselect = TRUE;
-	else {
-		if (ENOTFOUND(errno))
-			return 0;
-		mail_storage_set_critical(ctx->mailbox_ctx.storage,
-					  "stat(%s) failed: %m", real_path);
-		return -1;
-	}
-
-	/* make sure we give only one correct INBOX */
-	if (strcasecmp(list_path, "INBOX") == 0 &&
-	    (ctx->mailbox_ctx.flags & MAILBOX_LIST_INBOX) != 0) {
-		if (ctx->inbox_found ||
-		    strcmp(real_path, ctx->istorage->inbox_path) != 0)
-			return 0;
-
-		ctx->inbox_found = TRUE;
-	}
-
-	if (!noselect && S_ISDIR(st.st_mode)) {
-		/* subdirectory. scan inside it. */
-		path = t_strconcat(list_path, "/", NULL);
-		match2 = imap_match(ctx->glob, path);
-
-		ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-		if (match > 0)
-			ctx->list.name = p_strdup(ctx->list_pool, list_path);
-		else if (match2 > 0)
-			ctx->list.name = p_strdup(ctx->list_pool, path);
-		else
-			ctx->list.name = NULL;
-
-		ret = match2 < 0 ? 0 :
-			list_opendir(ctx->mailbox_ctx.storage,
-				     real_path, FALSE, &dirp);
-		if (ret > 0) {
-			dir = i_new(struct list_dir_context, 1);
-			dir->dirp = dirp;
-			dir->real_path = i_strdup(real_path);
-			dir->virtual_path = i_strdup(list_path);
-
-			dir->prev = ctx->dir;
-			ctx->dir = dir;
-		} else if (ret < 0)
-			return -1;
-		return match > 0 || match2 > 0;
-	} else if (match > 0) {
-		ctx->list.flags = noselect ? MAILBOX_NOSELECT :
-			(MAILBOX_NOINFERIORS | STAT_GET_MARKED(st));
-		ctx->list.name = p_strdup(ctx->list_pool, list_path);
-		return 1;
-	}
-
-	return 0;
-}
-
-static struct mailbox_list *mbox_list_subs(struct mbox_list_context *ctx)
-{
-	struct stat st;
-	const char *name, *path, *p;
-	enum imap_match_result match = IMAP_MATCH_NO;
-
-	while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
-		match = imap_match(ctx->glob, name);
-		if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
-			break;
-	}
-
-	if (name == NULL)
-		return NULL;
-
-	ctx->list.flags = 0;
-	ctx->list.name = name;
-
-	if (match == IMAP_MATCH_PARENT) {
-		/* placeholder */
-		ctx->list.flags = MAILBOX_PLACEHOLDER;
-		while ((p = strrchr(name, '/')) != NULL) {
-			name = t_strdup_until(name, p);
-			if (imap_match(ctx->glob, name) > 0) {
-				p_clear(ctx->list_pool);
-				ctx->list.name = p_strdup(ctx->list_pool, name);
-				return &ctx->list;
-			}
-		}
-		i_unreached();
-	}
-
-	if ((ctx->mailbox_ctx.flags & MAILBOX_LIST_FAST_FLAGS) != 0)
-		return &ctx->list;
-
-	t_push();
-	path = mbox_get_path(ctx->istorage, ctx->list.name);
-	if (stat(path, &st) == 0) {
-		if (S_ISDIR(st.st_mode))
-			ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-		else {
-			ctx->list.flags = MAILBOX_NOINFERIORS |
-				STAT_GET_MARKED(st);
-		}
-	} else {
-		ctx->list.flags = MAILBOX_NONEXISTENT;
-	}
-	t_pop();
-	return &ctx->list;
-}
-
-static struct mailbox_list *mbox_list_path(struct mbox_list_context *ctx)
-{
-	ctx->next = mbox_list_next;
-
-	ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
-	ctx->list.name =
-		p_strconcat(ctx->list_pool, ctx->dir->virtual_path, "/", NULL);
-
-	if (imap_match(ctx->glob, ctx->list.name) > 0)
-		return &ctx->list;
-	else
-		return ctx->next(ctx);
-}
-
-static struct mailbox_list *mbox_list_inbox(struct mbox_list_context *ctx)
-{
-	struct stat st;
-
-	ctx->list.flags = MAILBOX_NOINFERIORS;
-	ctx->list.name = "INBOX";
-
-	if (stat(ctx->istorage->inbox_path, &st) == 0)
-		ctx->list.flags |= STAT_GET_MARKED(st);
-	else if (errno == EACCES || errno == ELOOP)
-		ctx->list.flags = MAILBOX_NOSELECT;
-	else if (ENOTFOUND(errno))
-		ctx->list.flags |= MAILBOX_UNMARKED;
-	else {
-		mail_storage_set_critical(ctx->mailbox_ctx.storage,
-			"stat(%s) failed: %m", ctx->istorage->inbox_path);
-		ctx->mailbox_ctx.failed = TRUE;
-		return NULL;
-	}
-
-	return &ctx->list;
-}
-
-static struct mailbox_list *mbox_list_next(struct mbox_list_context *ctx)
-{
-	struct list_dir_context *dir;
-	struct dirent *d;
-	int ret;
-
-	p_clear(ctx->list_pool);
-
-	while (ctx->dir != NULL) {
-		/* NOTE: list_file() may change ctx->dir */
-		while ((d = readdir(ctx->dir->dirp)) != NULL) {
-			t_push();
-			ret = list_file(ctx, d->d_name);
-			t_pop();
-
-			if (ret > 0)
-				return &ctx->list;
-			if (ret < 0) {
-				ctx->mailbox_ctx.failed = TRUE;
-				return NULL;
-			}
-		}
-
-		dir = ctx->dir;
-		ctx->dir = dir->prev;
-		list_dir_context_free(dir);
-	}
-
-	if (!ctx->inbox_found &&
-	    (ctx->mailbox_ctx.flags & MAILBOX_LIST_INBOX) != 0 &&
-	    ctx->glob != NULL && imap_match(ctx->glob, "INBOX")  > 0) {
-		/* show inbox */
-		ctx->inbox_found = TRUE;
-		return mbox_list_inbox(ctx);
-	}
-
-	/* finished */
-	return NULL;
-}
--- a/src/lib-storage/index/mbox/mbox-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -3,10 +3,8 @@
 #include "lib.h"
 #include "buffer.h"
 #include "istream.h"
-#include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
-#include "subscription-file/subscription-file.h"
 #include "mbox-storage.h"
 #include "mbox-lock.h"
 #include "mbox-file.h"
@@ -22,9 +20,11 @@
 
 #define CREATE_MODE 0770 /* umask() should limit it more */
 
-/* Don't allow creating too long mailbox names. They could start causing
-   problems when they reach the limit. */
-#define MBOX_MAX_MAILBOX_NAME_LENGTH (PATH_MAX/2)
+/* Assume that if atime < mtime, there are new mails. If it's good enough for
+   UW-IMAP, it's good enough for us. */
+#define STAT_GET_MARKED(st) \
+	((st).st_size == 0 ? MAILBOX_UNMARKED : \
+	 (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED)
 
 /* NOTE: must be sorted for istream-header-filter. Note that it's not such
    a good idea to change this list, as the messages will then change from
@@ -251,31 +251,30 @@
 	return path;
 }
 
-static struct mail_storage *
-mbox_create(const char *data, const char *user, enum mail_storage_flags flags,
-	    enum mail_storage_lock_method lock_method)
+static int
+mbox_get_list_settings(struct mailbox_list_settings *list_set,
+		       const char *data, enum mail_storage_flags flags)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
-	struct mbox_storage *storage;
-	struct index_storage *istorage;
-	const char *root_dir, *inbox_file, *index_dir, *p;
+	const char *p;
 	struct stat st;
 	bool autodetect;
-	pool_t pool;
 
-	root_dir = inbox_file = index_dir = NULL;
+	memset(list_set, 0, sizeof(*list_set));
+	list_set->subscription_fname = MBOX_SUBSCRIPTION_FILE_NAME;
+	list_set->maildir_name = "";
 
 	autodetect = data == NULL || *data == '\0';
 	if (autodetect) {
 		if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
 			i_error("mbox: root mail directory not given");
-			return NULL;
+			return -1;
 		}
 
 		/* we'll need to figure out the mail location ourself.
 		   it's root dir if we've already chroot()ed, otherwise
 		   either $HOME/mail or $HOME/Mail */
-		root_dir = get_root_dir(flags);
+		list_set->root_dir = get_root_dir(flags);
 	} else {
 		/* <root mail directory> | <INBOX path>
 		   [:INBOX=<path>] [:INDEX=<dir>] */
@@ -286,74 +285,99 @@
 			/* if the data points to a file, treat it as an INBOX */
 			if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0 ||
 			    stat(data, &st) < 0 || S_ISDIR(st.st_mode))
-				root_dir = data;
+				list_set->root_dir = data;
 			else {
-				root_dir = get_root_dir(flags);
-				inbox_file = data;
+				list_set->root_dir = get_root_dir(flags);
+				list_set->inbox_path = data;
 			}
 		} else {
-			root_dir = t_strdup_until(data, p);
+			list_set->root_dir = t_strdup_until(data, p);
 			do {
 				p++;
-				if (strncmp(p, "INBOX=", 6) == 0)
-					inbox_file = t_strcut(p+6, ':');
-				else if (strncmp(p, "INDEX=", 6) == 0)
-					index_dir = t_strcut(p+6, ':');
+				if (strncmp(p, "INBOX=", 6) == 0) {
+					list_set->inbox_path =
+						t_strcut(p+6, ':');
+				} else if (strncmp(p, "INDEX=", 6) == 0) {
+					list_set->index_dir =
+						t_strcut(p+6, ':');
+				}
 				p = strchr(p, ':');
 			} while (p != NULL);
 		}
 	}
 
-	if (root_dir == NULL) {
-		root_dir = create_root_dir(debug);
-		if (root_dir == NULL)
-			return NULL;
+	if (list_set->root_dir == NULL) {
+		list_set->root_dir = create_root_dir(debug);
+		if (list_set->root_dir == NULL)
+			return -1;
 	} else {
 		/* strip trailing '/' */
-		size_t len = strlen(root_dir);
-		if (root_dir[len-1] == '/')
-			root_dir = t_strndup(root_dir, len-1);
+		size_t len = strlen(list_set->root_dir);
+
+		if (list_set->root_dir[len-1] == '/') {
+			list_set->root_dir =
+				t_strndup(list_set->root_dir, len-1);
+		}
 
 		/* make sure the directory exists */
-		if (*root_dir == '\0' ||
-		    lstat(root_dir, &st) == 0) {
+		if (*list_set->root_dir == '\0' ||
+		    lstat(list_set->root_dir, &st) == 0) {
 			/* yep, go ahead */
 		} else if (errno != ENOENT && errno != ENOTDIR) {
-			i_error("lstat(%s) failed: %m", root_dir);
-			return NULL;
-		} else if (mkdir_parents(root_dir, CREATE_MODE) < 0 &&
+			i_error("lstat(%s) failed: %m", list_set->root_dir);
+			return -1;
+		} else if (mkdir_parents(list_set->root_dir, CREATE_MODE) < 0 &&
 			   errno != EEXIST) {
-			i_error("mkdir_parents(%s) failed: %m", root_dir);
-			return NULL;
+			i_error("mkdir_parents(%s) failed: %m",
+				list_set->root_dir);
+			return -1;
 		}
 	}
 
-	if (inbox_file == NULL)
-		inbox_file = get_inbox_file(root_dir, !autodetect, debug);
+	if (list_set->inbox_path == NULL) {
+		list_set->inbox_path =
+			get_inbox_file(list_set->root_dir, !autodetect, debug);
+	}
+
+	if (list_set->index_dir != NULL &&
+	    strcmp(list_set->index_dir, "MEMORY") == 0)
+		list_set->index_dir = "";
+	return 0;
+}
 
-	if (index_dir == NULL)
-		index_dir = root_dir;
-	else if (strcmp(index_dir, "MEMORY") == 0)
-		index_dir = NULL;
+static struct mail_storage *
+mbox_create(const char *data, const char *user, enum mail_storage_flags flags,
+	    enum mail_storage_lock_method lock_method)
+{
+	struct mbox_storage *storage;
+	struct index_storage *istorage;
+	struct mailbox_list_settings list_set;
+	struct mailbox_list *list;
+	const char *error;
+	pool_t pool;
 
-	if (debug) {
-		i_info("mbox: root=%s, index=%s, inbox=%s",
-		       root_dir, index_dir == NULL ? "" : index_dir,
-		       inbox_file == NULL ? "" : inbox_file);
-	}
+	if (mbox_get_list_settings(&list_set, data, flags) < 0)
+		return NULL;
 
 	pool = pool_alloconly_create("storage", 512);
 	storage = p_new(pool, struct mbox_storage, 1);
+
+	if (mailbox_list_init("maildir++", &list_set,
+			      mail_storage_get_list_flags(flags),
+			      mailbox_storage_list_is_mailbox, storage,
+			      &list, &error) < 0) {
+		i_error("mbox fs: %s", error);
+		pool_unref(pool);
+		return NULL;
+	}
+
 	istorage = INDEX_STORAGE(storage);
 	istorage->storage = mbox_storage;
 	istorage->storage.pool = pool;
 
-	istorage->dir = p_strdup(pool, home_expand(root_dir));
-	istorage->inbox_path = p_strdup(pool, home_expand(inbox_file));
-	istorage->index_dir = p_strdup(pool, home_expand(index_dir));
 	istorage->user = p_strdup(pool, user);
 	istorage->callbacks = p_new(pool, struct mail_storage_callbacks, 1);
-	index_storage_init(istorage, flags, lock_method);
+	index_storage_init(istorage, list, flags, lock_method);
 	return &storage->storage.storage;
 }
 
@@ -365,102 +389,25 @@
 	pool_unref(storage->storage.pool);
 }
 
-bool mbox_is_valid_mask(struct mail_storage *storage, const char *mask)
+static const char *
+mbox_get_index_dir(struct mail_storage *storage, const char *name)
 {
-	const char *p;
-	bool newdir;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-		return TRUE;
-
-	/* make sure it's not absolute path */
-	if (*mask == '/' || *mask == '~')
-		return FALSE;
+	const char *dir, *p;
 
-	/* make sure the mailbox name doesn't contain any foolishness:
-	   "../" could give access outside the mailbox directory.
-	   "./" and "//" could fool ACL checks. */
-	newdir = TRUE;
-	for (p = mask; *p != '\0'; p++) {
-		if (newdir) {
-			if (p[0] == '/')
-				return FALSE; /* // */
-			if (p[0] == '.') {
-				if (p[1] == '/')
-					return FALSE; /* ./ */
-				if (p[1] == '.' && p[2] == '/')
-					return FALSE; /* ../ */
-			}
-		} 
-		newdir = p[0] == '/';
-	}
-	if (mask[0] == '.' && (mask[1] == '\0' ||
-			       (mask[1] == '.' && mask[2] == '\0'))) {
-		/* "." and ".." aren't allowed. */
-		return FALSE;
-	}
+	dir = mailbox_list_get_path(storage->list, name,
+				    MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*dir == '\0')
+		return NULL;
 
-	return TRUE;
+	p = strrchr(dir, '/');
+	if (p == NULL)
+		return NULL;
+
+	return t_strconcat(t_strdup_until(dir, p),
+			   "/"MBOX_INDEX_DIR_NAME"/", p+1, NULL);
 }
 
-static bool mbox_is_valid_create_name(struct mail_storage *storage,
-				      const char *name)
-{
-	size_t len;
-
-	len = strlen(name);
-	if (name[0] == '\0' || name[len-1] == '/' ||
-	    len > MBOX_MAX_MAILBOX_NAME_LENGTH)
-		return FALSE;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) == 0) {
-		if (mailbox_name_is_too_large(name, '/'))
-		    return FALSE;
-	}
-
-	return mbox_is_valid_mask(storage, name);
-}
-
-static bool mbox_is_valid_existing_name(struct mail_storage *storage,
-					const char *name)
-{
-	size_t len;
-
-	len = strlen(name);
-	if (name[0] == '\0' || name[len-1] == '/')
-		return FALSE;
-
-	return mbox_is_valid_mask(storage, name);
-}
-
-static const char *mbox_get_index_dir(struct index_storage *storage,
-				      const char *name)
-{
-	const char *p;
-
-	if (storage->index_dir == NULL)
-		return NULL;
-
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~')) {
-		name = home_expand(name);
-		p = strrchr(name, '/');
-		return t_strconcat(t_strdup_until(name, p),
-				   "/"MBOX_INDEX_DIR_NAME"/", p+1, NULL);
-	}
-
-	p = strrchr(name, '/');
-	if (p == NULL) {
-		return t_strconcat(storage->index_dir,
-				   "/"MBOX_INDEX_DIR_NAME"/", name, NULL);
-	} else {
-		return t_strconcat(storage->index_dir, "/",
-				   t_strdup_until(name, p),
-				   "/"MBOX_INDEX_DIR_NAME"/", p+1, NULL);
-	}
-}
-
-static int create_mbox_index_dirs(struct index_storage *storage,
+static int create_mbox_index_dirs(struct mail_storage *storage,
 				  const char *name)
 {
 	const char *index_dir;
@@ -470,7 +417,7 @@
 		return 0;
 
 	if (mkdir_parents(index_dir, CREATE_MODE) < 0) {
-		mail_storage_set_critical(&storage->storage,
+		mail_storage_set_critical(storage,
 			"mkdir_parents(%s) failed: %m", index_dir);
 		return -1;
 	}
@@ -478,33 +425,26 @@
 	return 0;
 }
 
-static int verify_inbox(struct index_storage *storage)
+static int verify_inbox(struct mail_storage *storage)
 {
+	const char *inbox_path;
 	int fd;
 
+	inbox_path = mailbox_list_get_path(storage->list, "INBOX",
+					   MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
 	/* make sure inbox file itself exists */
-	fd = open(storage->inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
+	fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
 	if (fd != -1)
 		(void)close(fd);
 	else if (errno != EEXIST) {
-		mail_storage_set_critical(&storage->storage,
-			"open(%s, O_CREAT) failed: %m", storage->inbox_path);
+		mail_storage_set_critical(storage,
+			"open(%s, O_CREAT) failed: %m", inbox_path);
 	}
 
 	return 0;
 }
 
-static const char *
-mbox_get_path(struct index_storage *storage, const char *name)
-{
-	if (strcmp(name, "INBOX") == 0)
-		return storage->inbox_path;
-	if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return home_expand(name);
-	return t_strconcat(storage->dir, "/", name, NULL);
-}
-
 static bool mbox_mail_is_recent(struct index_mailbox *ibox __attr_unused__,
 				uint32_t uid __attr_unused__)
 {
@@ -578,31 +518,21 @@
 mbox_open(struct mbox_storage *storage, const char *name,
 	  enum mailbox_open_flags flags)
 {
-	struct index_storage *istorage = INDEX_STORAGE(storage);
+	struct mail_storage *_storage = STORAGE(storage);
 	struct mbox_mailbox *mbox;
 	struct mail_index *index;
 	const char *path, *index_dir;
 
-	if (strcmp(name, "INBOX") == 0) {
-		/* name = "INBOX"
-		   path = "<inbox_file>/INBOX"
-		   index_dir = "/mail/.imap/INBOX" */
-		path = istorage->inbox_path;
-		index_dir = mbox_get_index_dir(istorage, "INBOX");
-	} else {
-		/* name = "foo/bar"
-		   path = "/mail/foo/bar"
-		   index_dir = "/mail/foo/.imap/bar" */
-		path = mbox_get_path(istorage, name);
-		index_dir = mbox_get_index_dir(istorage, name);
-	}
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	index_dir = mbox_get_index_dir(_storage, name);
 
 	if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) != 0)
-		index_dir = NULL;
+		index_dir = "";
 
-	if (index_dir != NULL) {
+	if (*index_dir != '\0') {
 		/* make sure the index directories exist */
-		if (create_mbox_index_dirs(istorage, name) < 0)
+		if (create_mbox_index_dirs(_storage, name) < 0)
 			return NULL;
 	}
 
@@ -638,21 +568,22 @@
 mbox_mailbox_open_stream(struct mbox_storage *storage, const char *name,
 			 struct istream *input, enum mailbox_open_flags flags)
 {
-	struct index_storage *istorage = INDEX_STORAGE(storage);
+	struct mail_storage *_storage = STORAGE(storage);
 	struct mail_index *index;
 	struct mbox_mailbox *mbox;
 	const char *path, *index_dir;
 
 	flags |= MAILBOX_OPEN_READONLY;
 
-	path = mbox_get_path(istorage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) != 0)
 		index_dir = NULL;
 	else {
-		index_dir = mbox_get_index_dir(istorage, name);
+		index_dir = mbox_get_index_dir(_storage, name);
 
 		/* make sure the required directories are also there */
-		if (create_mbox_index_dirs(istorage, name) < 0)
+		if (create_mbox_index_dirs(_storage, name) < 0)
 			return NULL;
 	}
 
@@ -670,31 +601,6 @@
 	return &mbox->ibox.box;
 }
 
-static const char *
-mbox_get_mailbox_path(struct mail_storage *_storage,
-		      const char *name, bool *is_file_r)
-{
-	struct mbox_storage *storage = (struct mbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	if (*name == '\0') {
-		*is_file_r = FALSE;
-		return istorage->dir;
-	}
-
-	*is_file_r = TRUE;
-	return mbox_get_path(istorage, name);
-}
-
-static const char *
-mbox_get_mailbox_index_dir(struct mail_storage *_storage, const char *name)
-{
-	struct mbox_storage *storage = (struct mbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-
-	return mbox_get_index_dir(istorage, name);
-}
-
 static struct mailbox *
 mbox_mailbox_open(struct mail_storage *_storage, const char *name,
 		  struct istream *input, enum mailbox_open_flags flags)
@@ -711,17 +617,18 @@
 
 	if (strcmp(name, "INBOX") == 0) {
 		/* make sure INBOX exists */
-		if (verify_inbox(istorage) < 0)
+		if (verify_inbox(_storage) < 0)
 			return NULL;
 		return mbox_open(storage, "INBOX", flags);
 	}
 
-	if (!mbox_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return NULL;
 	}
 
-	path = mbox_get_path(istorage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (stat(path, &st) == 0) {
 		if (S_ISDIR(st.st_mode)) {
 			mail_storage_set_error(_storage,
@@ -753,7 +660,7 @@
 
 	mail_storage_clear_error(_storage);
 
-	if (!mbox_is_valid_create_name(_storage, name)) {
+	if (!mailbox_list_is_valid_create_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
@@ -770,7 +677,8 @@
 	}
 
 	/* make sure it doesn't exist already */
-	path = mbox_get_path(storage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (stat(path, &st) == 0) {
 		mail_storage_set_error(_storage, "Mailbox already exists");
 		return -1;
@@ -836,12 +744,13 @@
 		return -1;
 	}
 
-	if (!mbox_is_valid_existing_name(_storage, name)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
 
-	path = mbox_get_path(storage, name);
+	path = mailbox_list_get_path(_storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	if (lstat(path, &st) < 0) {
 		if (ENOTFOUND(errno)) {
 			mail_storage_set_error(_storage,
@@ -854,12 +763,13 @@
 	}
 
 	if (S_ISDIR(st.st_mode)) {
-		/* deleting a folder, only allow it if it's empty.
-		   Delete .imap folder before to make sure it goes empty. */
-		index_dir = t_strconcat(storage->index_dir, "/", name,
-					"/"MBOX_INDEX_DIR_NAME, NULL);
+		/* deleting a directory. allow it only if it doesn't contain
+		   anything. Delete the ".imap" directory first in case there
+		   have been indexes. */
+		index_dir = mailbox_list_get_path(_storage->list, name,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
-		if (index_dir != NULL && rmdir(index_dir) < 0 &&
+		if (*index_dir != '\0' && rmdir(index_dir) < 0 &&
 		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
 			if (!mbox_handle_errors(storage)) {
 				mail_storage_set_critical(_storage,
@@ -885,7 +795,19 @@
 		return -1;
 	}
 
-	/* first unlink the mbox file */
+	/* delete the index directory first, so that if we crash we don't
+	   leave indexes for deleted mailboxes lying around */
+	index_dir = mbox_get_index_dir(_storage, name);
+	if (index_dir != NULL) {
+		index_storage_destroy_unrefed();
+
+		if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) {
+			mail_storage_set_critical(_storage,
+				"unlink_directory(%s) failed: %m", index_dir);
+			return -1;
+		}
+	}
+
 	if (unlink(path) < 0) {
 		if (ENOTFOUND(errno)) {
 			mail_storage_set_error(_storage,
@@ -897,20 +819,6 @@
 		return -1;
 	}
 
-	/* next delete the index directory */
-	index_dir = mbox_get_index_dir(storage, name);
-	if (index_dir != NULL) {
-		index_storage_destroy_unrefed();
-
-		if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) {
-			mail_storage_set_critical(_storage,
-				"unlink_directory(%s) failed: %m", index_dir);
-
-			/* mailbox itself is deleted, so return success
-			   anyway */
-		}
-	}
-
 	return 0;
 }
 
@@ -923,8 +831,8 @@
 
 	mail_storage_clear_error(_storage);
 
-	if (!mbox_is_valid_existing_name(_storage, oldname) ||
-	    !mbox_is_valid_create_name(_storage, newname)) {
+	if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) ||
+	    !mailbox_list_is_valid_create_name(_storage->list, newname)) {
 		mail_storage_set_error(_storage, "Invalid mailbox name");
 		return -1;
 	}
@@ -936,8 +844,10 @@
 		return -1;
 	}
 
-	oldpath = mbox_get_path(storage, oldname);
-	newpath = mbox_get_path(storage, newname);
+	oldpath = mailbox_list_get_path(_storage->list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(_storage->list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
 	/* create the hierarchy */
 	p = strrchr(newpath, '/');
@@ -985,8 +895,8 @@
 	}
 
 	/* we need to rename the index directory as well */
-	old_indexdir = mbox_get_index_dir(storage, oldname);
-	new_indexdir = mbox_get_index_dir(storage, newname);
+	old_indexdir = mbox_get_index_dir(_storage, oldname);
+	new_indexdir = mbox_get_index_dir(_storage, newname);
 	if (old_indexdir != NULL) {
 		if (rename(old_indexdir, new_indexdir) < 0 &&
 		    errno != ENOENT) {
@@ -999,56 +909,6 @@
 	return 0;
 }
 
-static int mbox_set_subscribed(struct mail_storage *_storage,
-			       const char *name, bool set)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *path;
-
-	path = t_strconcat(storage->dir, "/" SUBSCRIPTION_FILE_NAME, NULL);
-	return subsfile_set_subscribed(_storage, path, storage->temp_prefix,
-				       name, set);
-}
-
-static int mbox_get_mailbox_name_status(struct mail_storage *_storage,
-					const char *name,
-					enum mailbox_name_status *status)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	struct stat st;
-	const char *path;
-
-	mail_storage_clear_error(_storage);
-
-	if (!mbox_is_valid_existing_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	path = mbox_get_path(storage, name);
-	if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
-		*status = MAILBOX_NAME_EXISTS;
-		return 0;
-	}
-
-	if (!mbox_is_valid_create_name(_storage, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
-	if (ENOTFOUND(errno) || errno == EACCES) {
-		*status = MAILBOX_NAME_VALID;
-		return 0;
-	} else if (errno == ENOTDIR) {
-		*status = MAILBOX_NAME_NOINFERIORS;
-		return 0;
-	} else {
-		mail_storage_set_critical(_storage, "mailbox name status: "
-					  "stat(%s) failed: %m", path);
-		return -1;
-	}
-}
-
 static int mbox_storage_close(struct mailbox *box)
 {
 	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
@@ -1091,6 +951,70 @@
 		index_mailbox_check_add(&mbox->ibox, mbox->path);
 }
 
+static int mbox_is_mailbox(struct mail_storage *storage,
+			   const char *dir, const char *fname,
+			   enum mailbox_list_iter_flags iter_flags,
+			   enum mailbox_info_flags *flags,
+			   enum mailbox_list_file_type type)
+{
+	const char *path, *root_dir;
+	size_t len;
+	struct stat st;
+	int ret = 1;
+
+	if (strcmp(fname, MBOX_INDEX_DIR_NAME) == 0) {
+		*flags = MAILBOX_NOSELECT;
+		return 0;
+	}
+	if (strcmp(fname, MBOX_SUBSCRIPTION_FILE_NAME) == 0) {
+		root_dir = mailbox_list_get_path(storage->list, NULL,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		if (strcmp(root_dir, dir) == 0) {
+			*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+			return 0;
+		}
+	}
+
+	/* skip all .lock files */
+	len = strlen(fname);
+	if (len > 5 && strcmp(fname+len-5, ".lock") == 0) {
+		*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+		return 0;
+	}
+
+	/* try to avoid stat() with these checks */
+	if (type == MAILBOX_LIST_FILE_TYPE_DIR) {
+		*flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		return 1;
+	}
+	if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK &&
+	    type != MAILBOX_LIST_FILE_TYPE_UNKNOWN &&
+	    (iter_flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
+		*flags |= MAILBOX_NOINFERIORS;
+		return 1;
+	}
+
+	/* need to stat() then */
+	t_push();
+	path = t_strconcat(dir, "/", fname, NULL);
+	if (stat(path, &st) == 0) {
+		if (S_ISDIR(st.st_mode))
+			*flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		else
+			*flags |= MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+	} else if (errno == EACCES || errno == ELOOP)
+		*flags |= MAILBOX_NOSELECT;
+	else if (ENOTFOUND(errno))
+		ret = 0;
+	else {
+		mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
+		ret = -1;
+	}
+	t_pop();
+
+	return ret;
+}
+
 static void mbox_class_init(void)
 {
 	mbox_transaction_class_init();
@@ -1103,7 +1027,7 @@
 
 struct mail_storage mbox_storage = {
 	MEMBER(name) MBOX_STORAGE_NAME,
-	MEMBER(hierarchy_sep) '/',
+	MEMBER(mailbox_is_file) TRUE,
 
 	{
 		mbox_class_init,
@@ -1112,18 +1036,13 @@
 		mbox_free,
 		mbox_autodetect,
 		index_storage_set_callbacks,
-		mbox_get_mailbox_path,
-		mbox_get_mailbox_index_dir,
-		mbox_get_mailbox_index_dir,
+		mbox_get_index_dir,
+		mbox_get_index_dir,
 		mbox_mailbox_open,
 		mbox_mailbox_create,
 		mbox_mailbox_delete,
 		mbox_mailbox_rename,
-		mbox_mailbox_list_init,
-		mbox_mailbox_list_next,
-		mbox_mailbox_list_deinit,
-		mbox_set_subscribed,
-		mbox_get_mailbox_name_status,
+		mbox_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/index/mbox/mbox-storage.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Thu Nov 16 02:16:31 2006 +0200
@@ -7,7 +7,7 @@
 #define MBOX_MIN_CONTENT_LENGTH_SIZE 1024
 
 #define MBOX_STORAGE_NAME "mbox"
-#define SUBSCRIPTION_FILE_NAME ".subscriptions"
+#define MBOX_SUBSCRIPTION_FILE_NAME ".subscriptions"
 #define MBOX_INDEX_PREFIX "dovecot.index"
 #define MBOX_INDEX_DIR_NAME ".imap"
 
@@ -64,13 +64,6 @@
 
 int mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function);
 
-struct mailbox_list_context *
-mbox_mailbox_list_init(struct mail_storage *storage,
-		       const char *ref, const char *mask,
-		       enum mailbox_list_flags flags);
-int mbox_mailbox_list_deinit(struct mailbox_list_context *ctx);
-struct mailbox_list *mbox_mailbox_list_next(struct mailbox_list_context *ctx);
-
 void mbox_transaction_created(struct mail_index_transaction *t);
 void mbox_transaction_class_init(void);
 void mbox_transaction_class_deinit(void);
@@ -90,6 +83,4 @@
 int mbox_transaction_save_commit(struct mbox_save_context *ctx);
 void mbox_transaction_save_rollback(struct mbox_save_context *ctx);
 
-bool mbox_is_valid_mask(struct mail_storage *storage, const char *mask);
-
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/.cvsignore	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-fs-iter.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,417 @@
+/* Copyright (C) 2002-2006 Timo Sirainen */
+
+#include "lib.h"
+#include "home-expand.h"
+#include "unlink-directory.h"
+#include "imap-match.h"
+#include "subscription-file.h"
+#include "mailbox-list-fs.h"
+
+#include <dirent.h>
+
+struct list_dir_context {
+	struct list_dir_context *prev;
+
+	DIR *dirp;
+	char *real_path, *virtual_path;
+};
+
+struct fs_list_iterate_context {
+	struct mailbox_list_iterate_context ctx;
+
+	struct imap_match_glob *glob;
+	struct subsfile_list_context *subsfile_ctx;
+
+	bool inbox_found;
+
+	struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx);
+
+	pool_t info_pool;
+	struct mailbox_info info;
+        struct list_dir_context *dir;
+};
+
+static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx);
+static struct mailbox_info *fs_list_path(struct fs_list_iterate_context *ctx);
+static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx);
+
+static const char *mask_get_dir(const char *mask)
+{
+	const char *p, *last_dir;
+
+	last_dir = NULL;
+	for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
+		if (*p == '/')
+			last_dir = p;
+	}
+
+	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
+}
+
+static int list_opendir(struct mailbox_list *list,
+			const char *path, bool root, DIR **dirp)
+{
+	*dirp = opendir(*path == '\0' ? "/" : path);
+	if (*dirp != NULL)
+		return 1;
+
+	if (ENOTFOUND(errno)) {
+		/* root) user gave invalid hiearchy, ignore
+		   sub) probably just race condition with other client
+		   deleting the mailbox. */
+		return 0;
+	}
+
+	if (errno == EACCES) {
+		if (!root) {
+			/* subfolder, ignore */
+			return 0;
+		}
+		mailbox_list_set_error(list, "Access denied");
+		return -1;
+	}
+
+	mailbox_list_set_critical(list, "opendir(%s) failed: %m", path);
+	return -1;
+}
+
+struct mailbox_list_iterate_context *
+fs_list_iter_init(struct mailbox_list *_list,
+		  const char *ref, const char *mask,
+		  enum mailbox_list_iter_flags flags)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+	struct fs_list_iterate_context *ctx;
+	const char *path, *virtual_path;
+	DIR *dirp;
+
+	mailbox_list_clear_error(&list->list);
+
+	ctx = i_new(struct fs_list_iterate_context, 1);
+	ctx->ctx.list = _list;
+	ctx->ctx.flags = flags;
+	ctx->info_pool = pool_alloconly_create("fs list", 1024);
+        ctx->next = fs_list_next;
+
+	/* check that we're not trying to do any "../../" lists */
+	if (!mailbox_list_is_valid_mask(_list, ref) ||
+	    !mailbox_list_is_valid_mask(_list, mask)) {
+		mailbox_list_set_error(_list, "Invalid mask");
+		ctx->ctx.failed = TRUE;
+		return &ctx->ctx;
+	}
+
+	if (*mask == '/' || *mask == '~') {
+		/* mask overrides reference */
+	} else if (*ref != '\0') {
+		/* merge reference and mask */
+		mask = t_strconcat(ref, mask, NULL);
+	}
+
+	if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0) {
+		ctx->next = fs_list_subs;
+
+		path = t_strconcat(_list->set.control_dir != NULL ?
+				   _list->set.control_dir : _list->set.root_dir,
+				   "/", _list->set.subscription_fname, NULL);
+		ctx->subsfile_ctx = subsfile_list_init(_list, path);
+		if (ctx->subsfile_ctx == NULL) {
+			ctx->next = fs_list_next;
+			ctx->ctx.failed = TRUE;
+			return &ctx->ctx;
+		}
+		ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+		return &ctx->ctx;
+	}
+
+	/* if we're matching only subdirectories, don't bother scanning the
+	   parent directories */
+	virtual_path = mask_get_dir(mask);
+
+	path = mailbox_list_get_path(_list, virtual_path,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (list_opendir(_list, path, TRUE, &dirp) < 0)
+		return &ctx->ctx;
+	/* if user gave invalid directory, we just don't show any results. */
+
+	ctx->ctx.flags = flags;
+	ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+
+	if (virtual_path != NULL && dirp != NULL)
+		ctx->next = fs_list_path;
+
+	if (dirp != NULL) {
+		ctx->dir = i_new(struct list_dir_context, 1);
+		ctx->dir->dirp = dirp;
+		ctx->dir->real_path = i_strdup(path);
+		ctx->dir->virtual_path = i_strdup(virtual_path);
+	}
+	return &ctx->ctx;
+}
+
+static void list_dir_context_free(struct list_dir_context *dir)
+{
+	(void)closedir(dir->dirp);
+	i_free(dir->real_path);
+	i_free(dir->virtual_path);
+	i_free(dir);
+}
+
+int fs_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
+{
+	struct fs_list_iterate_context *ctx =
+		(struct fs_list_iterate_context *)_ctx;
+
+	int ret = ctx->ctx.failed ? -1 : 0;
+
+	if (ctx->subsfile_ctx != NULL) {
+		if (subsfile_list_deinit(ctx->subsfile_ctx) < 0)
+			ret = -1;
+	}
+
+	while (ctx->dir != NULL) {
+		struct list_dir_context *dir = ctx->dir;
+
+		ctx->dir = dir->prev;
+                list_dir_context_free(dir);
+	}
+
+	if (ctx->info_pool != NULL)
+		pool_unref(ctx->info_pool);
+	if (ctx->glob != NULL)
+		imap_match_deinit(&ctx->glob);
+	i_free(ctx);
+
+	return ret;
+}
+
+struct mailbox_info *
+fs_list_iter_next(struct mailbox_list_iterate_context *_ctx)
+{
+	struct fs_list_iterate_context *ctx =
+		(struct fs_list_iterate_context *)_ctx;
+
+	if (ctx->ctx.failed)
+		return NULL;
+
+	return ctx->next(ctx);
+}
+
+static int
+list_file(struct fs_list_iterate_context *ctx, const struct dirent *d)
+{
+	const char *fname = d->d_name;
+	struct list_dir_context *dir;
+	const char *list_path, *real_path, *path, *inbox_path;
+	DIR *dirp;
+	enum imap_match_result match, match2;
+	int ret;
+
+	/* skip . and .. */
+	if (fname[0] == '.' &&
+	    (fname[1] == '\0' ||
+	     (fname[1] == '.' && fname[2] == '\0')))
+		return 0;
+
+	/* check the mask */
+	if (ctx->dir->virtual_path == NULL)
+		list_path = fname;
+	else {
+		list_path = t_strconcat(ctx->dir->virtual_path,
+					"/", fname, NULL);
+	}
+
+	if ((match = imap_match(ctx->glob, list_path)) < 0)
+		return 0;
+
+	/* get the info.flags using callback */
+	ctx->info.flags = 0;
+	ret = ctx->ctx.list->callback(ctx->dir->real_path, fname,
+				      mailbox_list_get_file_type(d),
+				      ctx->ctx.flags, &ctx->info.flags,
+				      ctx->ctx.list->context);
+	if (ret <= 0)
+		return ret;
+
+	/* make sure we give only one correct INBOX */
+	real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
+	if (strcasecmp(list_path, "INBOX") == 0 &&
+	    (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0) {
+		inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		if (ctx->inbox_found || strcmp(real_path, inbox_path) != 0)
+			return 0;
+
+		ctx->inbox_found = TRUE;
+	}
+
+	if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) {
+		/* subdirectory. scan inside it. */
+		path = t_strconcat(list_path, "/", NULL);
+		match2 = imap_match(ctx->glob, path);
+
+		if (match > 0)
+			ctx->info.name = p_strdup(ctx->info_pool, list_path);
+		else if (match2 > 0)
+			ctx->info.name = p_strdup(ctx->info_pool, path);
+		else
+			ctx->info.name = NULL;
+
+		ret = match2 < 0 ? 0 :
+			list_opendir(ctx->ctx.list, real_path, FALSE, &dirp);
+		if (ret > 0) {
+			dir = i_new(struct list_dir_context, 1);
+			dir->dirp = dirp;
+			dir->real_path = i_strdup(real_path);
+			dir->virtual_path = i_strdup(list_path);
+
+			dir->prev = ctx->dir;
+			ctx->dir = dir;
+		} else if (ret < 0)
+			return -1;
+		return match > 0 || match2 > 0;
+	} else if (match > 0) {
+		ctx->info.name = p_strdup(ctx->info_pool, list_path);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+path_split(const char *path, const char **dir_r, const char **fname_r)
+{
+	const char *p;
+
+	p = strrchr(path, '/');
+	if (p == NULL) {
+		*dir_r = "";
+		*fname_r = path;
+	} else {
+		*dir_r = t_strdup_until(path, p);
+		*fname_r = p + 1;
+	}
+}
+
+static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx)
+{
+	const char *name, *path, *p, *dir, *fname;
+	enum imap_match_result match = IMAP_MATCH_NO;
+
+	while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
+		match = imap_match(ctx->glob, name);
+		if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
+			break;
+	}
+
+	if (name == NULL)
+		return NULL;
+
+	ctx->info.flags = 0;
+	ctx->info.name = name;
+
+	if (match == IMAP_MATCH_PARENT) {
+		/* placeholder */
+		ctx->info.flags = MAILBOX_PLACEHOLDER;
+		while ((p = strrchr(name, '/')) != NULL) {
+			name = t_strdup_until(name, p);
+			if (imap_match(ctx->glob, name) > 0) {
+				p_clear(ctx->info_pool);
+				ctx->info.name = p_strdup(ctx->info_pool, name);
+				return &ctx->info;
+			}
+		}
+		i_unreached();
+	}
+
+	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0)
+		return &ctx->info;
+
+	t_push();
+	path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	path_split(path, &dir, &fname);
+	if (ctx->ctx.list->callback(dir, fname,
+				    MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+				    ctx->ctx.flags, &ctx->info.flags,
+				    ctx->ctx.list->context) < 0)
+		ctx->ctx.failed = TRUE;
+	t_pop();
+	return &ctx->info;
+}
+
+static struct mailbox_info *fs_list_path(struct fs_list_iterate_context *ctx)
+{
+	ctx->next = fs_list_next;
+
+	ctx->info.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+	ctx->info.name =
+		p_strconcat(ctx->info_pool, ctx->dir->virtual_path, "/", NULL);
+
+	if (imap_match(ctx->glob, ctx->info.name) > 0)
+		return &ctx->info;
+	else
+		return ctx->next(ctx);
+}
+
+static struct mailbox_info *fs_list_inbox(struct fs_list_iterate_context *ctx)
+{
+	const char *inbox_path, *dir, *fname;
+
+	ctx->info.flags = 0;
+	ctx->info.name = "INBOX";
+
+	t_push();
+	inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
+					   MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	path_split(inbox_path, &dir, &fname);
+	if (ctx->ctx.list->callback(dir, fname,
+				    MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+				    ctx->ctx.flags, &ctx->info.flags,
+				    ctx->ctx.list->context) < 0)
+		ctx->ctx.failed = TRUE;
+	t_pop();
+
+	return &ctx->info;
+}
+
+static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx)
+{
+	struct list_dir_context *dir;
+	struct dirent *d;
+	int ret;
+
+	p_clear(ctx->info_pool);
+
+	while (ctx->dir != NULL) {
+		/* NOTE: list_file() may change ctx->dir */
+		while ((d = readdir(ctx->dir->dirp)) != NULL) {
+			t_push();
+			ret = list_file(ctx, d);
+			t_pop();
+
+			if (ret > 0)
+				return &ctx->info;
+			if (ret < 0) {
+				ctx->ctx.failed = TRUE;
+				return NULL;
+			}
+		}
+
+		dir = ctx->dir;
+		ctx->dir = dir->prev;
+		list_dir_context_free(dir);
+	}
+
+	if (!ctx->inbox_found &&
+	    (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
+	    ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) {
+		/* show inbox */
+		ctx->inbox_found = TRUE;
+		return fs_list_inbox(ctx);
+	}
+
+	/* finished */
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-fs.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,285 @@
+/* Copyright (C) 2006 Timo Sirainen */
+
+#include "lib.h"
+#include "hostpid.h"
+#include "home-expand.h"
+#include "subscription-file.h"
+#include "mailbox-list-fs.h"
+
+#include <sys/stat.h>
+
+extern struct mailbox_list fs_mailbox_list;
+
+static struct mailbox_list *fs_list_alloc(void)
+{
+	struct fs_mailbox_list *list;
+	pool_t pool;
+
+	pool = pool_alloconly_create("fs list", 512);
+
+	list = p_new(pool, struct fs_mailbox_list, 1);
+	list->list = fs_mailbox_list;
+	list->list.pool = pool;
+
+	list->temp_prefix = p_strconcat(pool, ".temp.", my_hostname, ".",
+					my_pid, ".", NULL);
+	return &list->list;
+}
+
+static void fs_list_deinit(struct mailbox_list *_list)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+
+	pool_unref(list->list.pool);
+}
+
+static bool fs_list_is_valid_common(const char *name, size_t *len_r)
+{
+	*len_r = strlen(name);
+
+	if (name[0] == '\0' || name[*len_r-1] == '/')
+		return FALSE;
+	return TRUE;
+}
+
+static bool
+fs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name)
+{
+	const char *p;
+	bool newdir;
+	size_t maildir_len;
+
+	/* make sure it's not absolute path */
+	if (*name == '/' || *name == '~')
+		return FALSE;
+
+	/* make sure the mailbox name doesn't contain any foolishness:
+	   "../" could give access outside the mailbox directory.
+	   "./" and "//" could fool ACL checks. */
+	newdir = TRUE;
+	maildir_len = strlen(list->set.maildir_name);
+	for (p = name; *p != '\0'; p++) {
+		if (newdir) {
+			if (p[0] == '/')
+				return FALSE; /* // */
+			if (p[0] == '.') {
+				if (p[1] == '/')
+					return FALSE; /* ./ */
+				if (p[1] == '.' && p[2] == '/')
+					return FALSE; /* ../ */
+			}
+			if (maildir_len > 0 &&
+			    strncmp(p, list->set.maildir_name,
+				    maildir_len) == 0 &&
+			    (p[maildir_len-1] == '\0' ||
+			     p[maildir_len-1] == '/')) {
+				/* don't allow maildir_name to be used as part
+				   of the mailbox name */
+				return FALSE;
+			}
+		} 
+		newdir = p[0] == '/';
+	}
+	if (name[0] == '.' && (name[1] == '\0' ||
+			       (name[1] == '.' && name[2] == '\0'))) {
+		/* "." and ".." aren't allowed. */
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static bool
+fs_is_valid_mask(struct mailbox_list *list, const char *mask)
+{
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+		return TRUE;
+
+	return fs_list_is_valid_common_nonfs(list, mask);
+}
+
+static bool
+fs_is_valid_existing_name(struct mailbox_list *list, const char *name)
+{
+	size_t len;
+
+	if (!fs_list_is_valid_common(name, &len))
+		return FALSE;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+		return TRUE;
+
+	return fs_list_is_valid_common_nonfs(list, name);
+}
+
+static bool
+fs_is_valid_create_name(struct mailbox_list *list, const char *name)
+{
+	size_t len;
+
+	if (!fs_list_is_valid_common(name, &len))
+		return FALSE;
+	if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH)
+		return FALSE;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+		return TRUE;
+
+	if (mailbox_list_name_is_too_large(name, '/'))
+		return FALSE;
+	return fs_list_is_valid_common_nonfs(list, name);
+}
+
+static const char *
+fs_list_get_path(struct mailbox_list *_list, const char *name,
+		 enum mailbox_list_path_type type)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+	const struct mailbox_list_settings *set = &_list->set;
+
+	mailbox_list_clear_error(&list->list);
+
+	if (name == NULL) {
+		/* return root directories */
+		switch (type) {
+		case MAILBOX_LIST_PATH_TYPE_DIR:
+		case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+			return set->root_dir;
+		case MAILBOX_LIST_PATH_TYPE_CONTROL:
+			return set->control_dir != NULL ?
+				set->control_dir : set->root_dir;
+		case MAILBOX_LIST_PATH_TYPE_INDEX:
+			return set->index_dir != NULL ?
+				set->index_dir : set->root_dir;
+		}
+		i_unreached();
+	}
+
+	i_assert(mailbox_list_is_valid_existing_name(_list, name));
+
+	if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+	    (*name == '/' || *name == '~'))
+		return name;
+
+	switch (type) {
+	case MAILBOX_LIST_PATH_TYPE_DIR:
+		if (*set->maildir_name != '\0')
+			return t_strdup_printf("%s/%s", set->root_dir, name);
+		break;
+	case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+		break;
+	case MAILBOX_LIST_PATH_TYPE_CONTROL:
+		if (set->control_dir != NULL)
+			return t_strdup_printf("%s/%s", set->control_dir,
+					       name);
+		break;
+	case MAILBOX_LIST_PATH_TYPE_INDEX:
+		if (set->index_dir != NULL) {
+			if (*set->index_dir == '\0')
+				return "";
+			return t_strdup_printf("%s/%s", set->index_dir, name);
+		}
+		break;
+	}
+
+	if (strcmp(name, "INBOX") == 0) {
+		return set->inbox_path != NULL ?
+			set->inbox_path : set->root_dir;
+	}
+
+	if (*set->maildir_name == '\0')
+		return t_strdup_printf("%s/%s", set->root_dir, name);
+	else {
+		return t_strdup_printf("%s/%s/%s", set->root_dir, name,
+				       set->maildir_name);
+	}
+}
+
+static int
+fs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name,
+				enum mailbox_name_status *status)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+	struct stat st;
+	const char *path;
+
+	mailbox_list_clear_error(&list->list);
+
+	if (!mailbox_list_is_valid_existing_name(_list, name)) {
+		*status = MAILBOX_NAME_INVALID;
+		return 0;
+	}
+
+	path = mailbox_list_get_path(_list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
+		*status = MAILBOX_NAME_EXISTS;
+		return 0;
+	}
+
+	if (!mailbox_list_is_valid_create_name(_list, name)) {
+		*status = MAILBOX_NAME_INVALID;
+		return 0;
+	}
+
+	if (ENOTFOUND(errno) || errno == EACCES) {
+		*status = MAILBOX_NAME_VALID;
+		return 0;
+	} else if (errno == ENOTDIR) {
+		*status = MAILBOX_NAME_NOINFERIORS;
+		return 0;
+	} else {
+		mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
+		return -1;
+	}
+}
+
+static const char *
+fs_list_get_temp_prefix(struct mailbox_list *_list)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+
+	return list->temp_prefix;
+}
+
+static int fs_list_set_subscribed(struct mailbox_list *_list,
+				  const char *name, bool set)
+{
+	struct fs_mailbox_list *list =
+		(struct fs_mailbox_list *)_list;
+	const char *path;
+
+	mailbox_list_clear_error(&list->list);
+
+	path = t_strconcat(_list->set.control_dir != NULL ?
+			   _list->set.control_dir : _list->set.root_dir,
+			   "/", _list->set.subscription_fname, NULL);
+	return subsfile_set_subscribed(_list, path, list->temp_prefix,
+				       name, set);
+}
+
+struct mailbox_list fs_mailbox_list = {
+	MEMBER(name) "fs",
+	MEMBER(hierarchy_sep) '/',
+	MEMBER(mailbox_name_max_length) PATH_MAX,
+
+	{
+		fs_list_alloc,
+		fs_list_deinit,
+		fs_is_valid_mask,
+		fs_is_valid_existing_name,
+		fs_is_valid_create_name,
+		fs_list_get_path,
+		fs_list_get_mailbox_name_status,
+		fs_list_get_temp_prefix,
+		fs_list_iter_init,
+		fs_list_iter_next,
+		fs_list_iter_deinit,
+		fs_list_set_subscribed
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-fs.h	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,24 @@
+#ifndef __MAILBOX_LIST_FS_H
+#define __MAILBOX_LIST_FS_H
+
+#include "mailbox-list-private.h"
+
+/* Don't allow creating too long mailbox names. They could start causing
+   problems when they reach the limit. */
+#define FS_MAX_CREATE_MAILBOX_NAME_LENGTH (PATH_MAX/2)
+
+struct fs_mailbox_list {
+	struct mailbox_list list;
+
+	const char *temp_prefix;
+};
+
+struct mailbox_list_iterate_context *
+fs_list_iter_init(struct mailbox_list *_list,
+		  const char *ref, const char *mask,
+		  enum mailbox_list_iter_flags flags);
+int fs_list_iter_deinit(struct mailbox_list_iterate_context *ctx);
+struct mailbox_info *
+fs_list_iter_next(struct mailbox_list_iterate_context *ctx);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-maildir-iter.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,371 @@
+/* Copyright (C) 2002-2006 Timo Sirainen */
+
+#include "lib.h"
+#include "str.h"
+#include "home-expand.h"
+#include "imap-match.h"
+#include "subscription-file.h"
+#include "mailbox-tree.h"
+#include "mailbox-list-maildir.h"
+
+#include <dirent.h>
+
+#define MAILBOX_FLAG_MATCHED 0x40000000
+
+struct maildir_list_iterate_context {
+	struct mailbox_list_iterate_context ctx;
+	pool_t pool;
+
+	const char *dir, *prefix;
+
+        struct mailbox_tree_context *tree_ctx;
+
+	string_t *node_path;
+	size_t parent_pos;
+	struct mailbox_node *root, *next_node;
+	struct mailbox_info info;
+};
+
+static void maildir_nodes_fix(struct mailbox_node *node, bool is_subs)
+{
+	while (node != NULL) {
+		if (node->children != NULL) {
+			node->flags |= MAILBOX_CHILDREN;
+			node->flags &= ~MAILBOX_NOCHILDREN;
+			maildir_nodes_fix(node->children, is_subs);
+		} else if ((node->flags & MAILBOX_PLACEHOLDER) != 0) {
+			if (!is_subs) {
+				node->flags &= ~MAILBOX_PLACEHOLDER;
+				node->flags |= MAILBOX_NOSELECT;
+			}
+		}
+		node = node->next;
+	}
+}
+
+static int
+maildir_fill_readdir(struct maildir_list_iterate_context *ctx,
+		     struct imap_match_glob *glob, bool update_only)
+{
+	DIR *dirp;
+	struct dirent *d;
+	const char *p, *mailbox_c;
+	string_t *mailbox;
+	enum mailbox_info_flags flags;
+	enum imap_match_result match;
+	struct mailbox_node *node;
+	bool created;
+	char hierarchy_sep;
+	int ret;
+
+	dirp = opendir(ctx->dir);
+	if (dirp == NULL) {
+		if (errno != ENOENT) {
+			mailbox_list_set_critical(ctx->ctx.list,
+				"opendir(%s) failed: %m", ctx->dir);
+			return -1;
+		}
+		return 0;
+	}
+
+	hierarchy_sep = ctx->ctx.list->hierarchy_sep;
+
+	t_push();
+	mailbox = t_str_new(PATH_MAX);
+	while ((d = readdir(dirp)) != NULL) {
+		const char *fname = d->d_name;
+
+		if (fname[0] != hierarchy_sep)
+			continue;
+
+		/* skip . and .. */
+		if (fname[0] == '.' &&
+		    (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0')))
+			continue;
+
+		/* make sure the mask matches */
+		str_truncate(mailbox, 0);
+		str_append(mailbox, ctx->prefix);
+		str_append(mailbox, fname + 1);
+                mailbox_c = str_c(mailbox);
+
+		match = imap_match(glob, mailbox_c);
+
+		if (match != IMAP_MATCH_YES &&
+		    match != IMAP_MATCH_PARENT)
+			continue;
+
+		/* check if this is an actual mailbox */
+		flags = 0;
+		ret = ctx->ctx.list->callback(ctx->dir, fname,
+					      mailbox_list_get_file_type(d),
+					      ctx->ctx.flags, &flags,
+					      ctx->ctx.list->context);
+		if (ret < 0) {
+			t_pop();
+			return -1;
+		}
+		if (ret == 0)
+			continue;
+
+		if (match == IMAP_MATCH_PARENT) {
+			t_push();
+			while ((p = strrchr(mailbox_c,
+					    hierarchy_sep)) != NULL) {
+				str_truncate(mailbox, (size_t) (p-mailbox_c));
+				mailbox_c = str_c(mailbox);
+				if (imap_match(glob, mailbox_c) > 0)
+					break;
+			}
+			i_assert(p != NULL);
+
+			created = FALSE;
+			node = update_only ?
+				mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
+				mailbox_tree_get(ctx->tree_ctx,
+						 mailbox_c, &created);
+			if (node != NULL) {
+				if (created)
+					node->flags = MAILBOX_PLACEHOLDER;
+
+				node->flags |= MAILBOX_CHILDREN |
+					MAILBOX_FLAG_MATCHED;
+				node->flags &= ~MAILBOX_NOCHILDREN;
+			}
+
+			t_pop();
+		} else {
+			created = FALSE;
+			node = update_only ?
+				mailbox_tree_update(ctx->tree_ctx, mailbox_c) :
+				mailbox_tree_get(ctx->tree_ctx,
+						 mailbox_c, &created);
+
+			if (node != NULL) {
+				if (created)
+					node->flags = MAILBOX_NOCHILDREN;
+				node->flags &= ~(MAILBOX_PLACEHOLDER |
+						 MAILBOX_NONEXISTENT);
+				node->flags |= MAILBOX_FLAG_MATCHED;
+			}
+		}
+	}
+	t_pop();
+
+	if (closedir(dirp) < 0) {
+		mailbox_list_set_critical(ctx->ctx.list,
+					  "readdir(%s) failed: %m", ctx->dir);
+		return -1;
+	}
+
+	if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
+	    (ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0) {
+		/* make sure INBOX is there */
+		node = mailbox_tree_get(ctx->tree_ctx, "INBOX", &created);
+		if (created)
+			node->flags = MAILBOX_NOCHILDREN;
+		else
+			node->flags &= ~MAILBOX_PLACEHOLDER;
+
+		switch (imap_match(glob, "INBOX")) {
+		case IMAP_MATCH_YES:
+		case IMAP_MATCH_PARENT:
+			node->flags |= MAILBOX_FLAG_MATCHED;
+			break;
+		default:
+			break;
+		}
+	}
+	maildir_nodes_fix(mailbox_tree_get(ctx->tree_ctx, NULL, NULL),
+			  (ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0);
+	return 0;
+}
+
+static int maildir_fill_subscribed(struct maildir_list_iterate_context *ctx,
+				   struct imap_match_glob *glob)
+{
+	struct subsfile_list_context *subsfile_ctx;
+	const char *path, *name, *p;
+	struct mailbox_node *node;
+	char hierarchy_sep;
+	bool created;
+
+	path = t_strconcat(ctx->ctx.list->set.control_dir != NULL ?
+			   ctx->ctx.list->set.control_dir :
+			   ctx->ctx.list->set.root_dir,
+			   "/", ctx->ctx.list->set.subscription_fname, NULL);
+	subsfile_ctx = subsfile_list_init(ctx->ctx.list, path);
+
+	hierarchy_sep = ctx->ctx.list->hierarchy_sep;
+	while ((name = subsfile_list_next(subsfile_ctx)) != NULL) {
+		switch (imap_match(glob, name)) {
+		case IMAP_MATCH_YES:
+			node = mailbox_tree_get(ctx->tree_ctx, name, NULL);
+			node->flags = MAILBOX_FLAG_MATCHED;
+			if ((ctx->ctx.flags &
+			     MAILBOX_LIST_ITER_FAST_FLAGS) == 0) {
+				node->flags |= MAILBOX_NONEXISTENT |
+					MAILBOX_NOCHILDREN;
+			}
+			break;
+		case IMAP_MATCH_PARENT:
+			/* placeholder */
+			while ((p = strrchr(name, hierarchy_sep)) != NULL) {
+				name = t_strdup_until(name, p);
+				if (imap_match(glob, name) > 0)
+					break;
+			}
+			i_assert(p != NULL);
+
+			node = mailbox_tree_get(ctx->tree_ctx, name, &created);
+			if (created) node->flags = MAILBOX_PLACEHOLDER;
+			node->flags |= MAILBOX_FLAG_MATCHED | MAILBOX_CHILDREN;
+			node->flags &= ~MAILBOX_NOCHILDREN;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return subsfile_list_deinit(subsfile_ctx);
+}
+
+struct mailbox_list_iterate_context *
+maildir_list_iter_init(struct mailbox_list *_list,
+		       const char *ref, const char *mask,
+		       enum mailbox_list_iter_flags flags)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+	struct maildir_list_iterate_context *ctx;
+        struct imap_match_glob *glob;
+	const char *dir, *p;
+	pool_t pool;
+
+	mailbox_list_clear_error(&list->list);
+
+	pool = pool_alloconly_create("maildir_list", 1024);
+	ctx = p_new(pool, struct maildir_list_iterate_context, 1);
+	ctx->ctx.list = _list;
+	ctx->ctx.flags = flags;
+	ctx->pool = pool;
+	ctx->tree_ctx = mailbox_tree_init(_list->hierarchy_sep);
+
+	if (*ref != '\0') {
+		/* join reference + mask */
+		mask = t_strconcat(ref, mask, NULL);
+	}
+
+	glob = imap_match_init(pool, mask, TRUE, _list->hierarchy_sep);
+
+	ctx->dir = _list->set.root_dir;
+	ctx->prefix = "";
+
+	if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0) {
+		if (maildir_fill_subscribed(ctx, glob) < 0) {
+			ctx->ctx.failed = TRUE;
+			return &ctx->ctx;
+		}
+	} else if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+		   (p = strrchr(mask, '/')) != NULL) {
+		dir = t_strdup_until(mask, p);
+		ctx->prefix = p_strdup_until(pool, mask, p+1);
+
+		if (*mask != '/' && *mask != '~')
+			dir = t_strconcat(_list->set.root_dir, "/", dir, NULL);
+		ctx->dir = p_strdup(pool, home_expand(dir));
+	}
+
+	if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0 ||
+	    (ctx->ctx.flags & MAILBOX_LIST_ITER_FAST_FLAGS) == 0) {
+		bool update_only = (flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0;
+		if (maildir_fill_readdir(ctx, glob, update_only) < 0) {
+			ctx->ctx.failed = TRUE;
+			return &ctx->ctx;
+		}
+	}
+
+	ctx->node_path = str_new(pool, 256);
+	ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL);
+	return &ctx->ctx;
+}
+
+int maildir_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
+{
+	struct maildir_list_iterate_context *ctx =
+		(struct maildir_list_iterate_context *)_ctx;
+	int ret = ctx->ctx.failed ? -1 : 0;
+
+	mailbox_tree_deinit(ctx->tree_ctx);
+	pool_unref(ctx->pool);
+	return ret;
+}
+
+static struct mailbox_node *find_next(struct mailbox_node **node,
+				      string_t *path, char hierarchy_sep)
+{
+	struct mailbox_node *child;
+	size_t len;
+
+	while (*node != NULL) {
+		if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0)
+			return *node;
+
+		if ((*node)->children != NULL) {
+			len = str_len(path);
+			if (len != 0)
+				str_append_c(path, hierarchy_sep);
+			str_append(path, (*node)->name);
+
+			child = find_next(&(*node)->children, path,
+					  hierarchy_sep);
+			if (child != NULL)
+				return child;
+
+			str_truncate(path, len);
+		}
+
+		*node = (*node)->next;
+	}
+
+	return NULL;
+}
+
+struct mailbox_info *
+maildir_list_iter_next(struct mailbox_list_iterate_context *_ctx)
+{
+	struct maildir_list_iterate_context *ctx =
+		(struct maildir_list_iterate_context *)_ctx;
+	struct mailbox_node *node;
+
+	for (node = ctx->next_node; node != NULL; node = node->next) {
+		if ((node->flags & MAILBOX_FLAG_MATCHED) != 0)
+			break;
+	}
+
+	if (node == NULL) {
+		if (ctx->root == NULL)
+			return NULL;
+
+		str_truncate(ctx->node_path, 0);
+		node = find_next(&ctx->root, ctx->node_path,
+				 ctx->ctx.list->hierarchy_sep);
+                ctx->parent_pos = str_len(ctx->node_path);
+
+		if (node == NULL)
+			return NULL;
+	}
+	ctx->next_node = node->next;
+
+	i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0);
+	node->flags &= ~MAILBOX_FLAG_MATCHED;
+
+	str_truncate(ctx->node_path, ctx->parent_pos);
+	if (ctx->parent_pos != 0)
+		str_append_c(ctx->node_path, ctx->ctx.list->hierarchy_sep);
+	str_append(ctx->node_path, node->name);
+
+	ctx->info.name = str_c(ctx->node_path);
+	ctx->info.flags = node->flags;
+	return &ctx->info;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,276 @@
+/* Copyright (C) 2006 Timo Sirainen */
+
+#include "lib.h"
+#include "hostpid.h"
+#include "home-expand.h"
+#include "subscription-file.h"
+#include "mailbox-list-maildir.h"
+
+#include <sys/stat.h>
+
+extern struct mailbox_list maildir_mailbox_list;
+
+static struct mailbox_list *maildir_list_alloc(void)
+{
+	struct maildir_mailbox_list *list;
+	pool_t pool;
+
+	pool = pool_alloconly_create("maildir++ list", 512);
+
+	list = p_new(pool, struct maildir_mailbox_list, 1);
+	list->list = maildir_mailbox_list;
+	list->list.pool = pool;
+
+	list->temp_prefix =
+		p_strconcat(pool, "temp.", my_hostname, ".", my_pid, ".", NULL);
+	return &list->list;
+}
+
+static void maildir_list_deinit(struct mailbox_list *_list)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+
+	pool_unref(list->list.pool);
+}
+
+static const char *
+maildir_list_get_absolute_path(struct mailbox_list *list, const char *name)
+{
+	const char *p;
+
+	name = home_expand(name);
+
+	p = strrchr(name, '/');
+	if (p == NULL)
+		return name;
+	return t_strdup_printf("%s/%c%s", t_strdup_until(name, p),
+			       list->hierarchy_sep, p+1);
+}
+
+static bool
+maildir_list_is_valid_common(struct mailbox_list *list, const char *name,
+			     size_t *len_r)
+{
+	size_t len;
+
+	/* check that there are no adjacent hierarchy separators */
+	for (len = 0; name[len] != '\0'; len++) {
+		if (name[len] == list->hierarchy_sep &&
+		    name[len+1] == list->hierarchy_sep)
+			return FALSE;
+	}
+
+	if (len == 0 || name[len-1] == '/')
+		return FALSE;
+
+	if (name[0] == list->hierarchy_sep ||
+	    name[len-1] == list->hierarchy_sep)
+		return FALSE;
+
+	*len_r = len;
+	return TRUE;
+}
+
+static bool maildir_list_is_valid_common_nonfs(const char *name)
+{
+	if (*name == '~' || strchr(name, '/') != NULL)
+		return FALSE;
+
+	if (name[0] == '.' && (name[1] == '\0' ||
+			       (name[1] == '.' && name[2] == '\0'))) {
+		/* "." and ".." aren't allowed. */
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static bool
+maildir_is_valid_mask(struct mailbox_list *list __attr_unused__,
+		      const char *mask __attr_unused__)
+{
+	i_unreached();
+	return FALSE;
+}
+
+static bool
+maildir_is_valid_existing_name(struct mailbox_list *list, const char *name)
+{
+	size_t len;
+
+	if (!maildir_list_is_valid_common(list, name, &len))
+		return FALSE;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+		return TRUE;
+
+	return maildir_list_is_valid_common_nonfs(name);
+}
+
+static bool
+maildir_is_valid_create_name(struct mailbox_list *list, const char *name)
+{
+	size_t len;
+
+	if (!maildir_list_is_valid_common(list, name, &len))
+		return FALSE;
+	if (len > MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH)
+		return FALSE;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+		return TRUE;
+
+	if (!maildir_list_is_valid_common_nonfs(name))
+		return FALSE;
+	if (mailbox_list_name_is_too_large(name, list->hierarchy_sep))
+		return FALSE;
+
+	return TRUE;
+}
+
+static const char *
+maildir_list_get_path(struct mailbox_list *_list, const char *name,
+		      enum mailbox_list_path_type type)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+
+	mailbox_list_clear_error(&list->list);
+
+	if (name == NULL) {
+		/* return root directories */
+		switch (type) {
+		case MAILBOX_LIST_PATH_TYPE_DIR:
+		case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+			return _list->set.root_dir;
+		case MAILBOX_LIST_PATH_TYPE_CONTROL:
+			return _list->set.control_dir != NULL ?
+				_list->set.control_dir : _list->set.root_dir;
+		case MAILBOX_LIST_PATH_TYPE_INDEX:
+			return _list->set.index_dir != NULL ?
+				_list->set.index_dir : _list->set.root_dir;
+		}
+		i_unreached();
+	}
+
+	i_assert(mailbox_list_is_valid_existing_name(_list, name));
+
+	if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+	    (*name == '/' || *name == '~'))
+		return maildir_list_get_absolute_path(_list, name);
+
+	switch (type) {
+	case MAILBOX_LIST_PATH_TYPE_DIR:
+	case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+		break;
+	case MAILBOX_LIST_PATH_TYPE_CONTROL:
+		if (_list->set.control_dir != NULL) {
+			return t_strdup_printf("%s/%c%s",
+					       _list->set.control_dir,
+					       _list->hierarchy_sep, name);
+		}
+		break;
+	case MAILBOX_LIST_PATH_TYPE_INDEX:
+		if (_list->set.index_dir != NULL) {
+			if (*_list->set.index_dir == '\0')
+				return "";
+			return t_strdup_printf("%s/%c%s", _list->set.index_dir,
+					       _list->hierarchy_sep, name);
+		}
+		break;
+	}
+
+	if (strcmp(name, "INBOX") == 0) {
+		return _list->set.inbox_path != NULL ?
+			_list->set.inbox_path : _list->set.root_dir;
+	}
+
+	return t_strdup_printf("%s/%c%s", _list->set.root_dir,
+			       _list->hierarchy_sep, name);
+}
+
+static int
+maildir_list_get_mailbox_name_status(struct mailbox_list *_list,
+				     const char *name,
+				     enum mailbox_name_status *status)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+	struct stat st;
+	const char *path;
+
+	mailbox_list_clear_error(&list->list);
+
+	if (!mailbox_list_is_valid_existing_name(_list, name)) {
+		*status = MAILBOX_NAME_INVALID;
+		return 0;
+	}
+
+	path = mailbox_list_get_path(_list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
+		*status = MAILBOX_NAME_EXISTS;
+		return 0;
+	}
+
+	if (!mailbox_list_is_valid_create_name(_list, name)) {
+		*status = MAILBOX_NAME_INVALID;
+		return 0;
+	}
+
+	if (ENOTFOUND(errno) || errno == EACCES) {
+		*status = MAILBOX_NAME_VALID;
+		return 0;
+	} else {
+		mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
+		return -1;
+	}
+}
+
+static const char *
+maildir_list_get_temp_prefix(struct mailbox_list *_list)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+
+	return list->temp_prefix;
+}
+
+static int maildir_list_set_subscribed(struct mailbox_list *_list,
+				       const char *name, bool set)
+{
+	struct maildir_mailbox_list *list =
+		(struct maildir_mailbox_list *)_list;
+	const char *path;
+
+	mailbox_list_clear_error(&list->list);
+
+	path = t_strconcat(_list->set.control_dir != NULL ?
+			   _list->set.control_dir : _list->set.root_dir,
+			   "/", _list->set.subscription_fname, NULL);
+
+	return subsfile_set_subscribed(_list, path, list->temp_prefix,
+				       name, set);
+}
+
+struct mailbox_list maildir_mailbox_list = {
+	MEMBER(name) "maildir++",
+	MEMBER(hierarchy_sep) '.',
+	MEMBER(mailbox_name_max_length) PATH_MAX,
+
+	{
+		maildir_list_alloc,
+		maildir_list_deinit,
+		maildir_is_valid_mask,
+		maildir_is_valid_existing_name,
+		maildir_is_valid_create_name,
+		maildir_list_get_path,
+		maildir_list_get_mailbox_name_status,
+		maildir_list_get_temp_prefix,
+		maildir_list_iter_init,
+		maildir_list_iter_next,
+		maildir_list_iter_deinit,
+		maildir_list_set_subscribed
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-maildir.h	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,24 @@
+#ifndef __MAILBOX_LIST_MAILDIR_H
+#define __MAILBOX_LIST_MAILDIR_H
+
+#include "mailbox-list-private.h"
+
+/* Don't allow creating too long mailbox names. They could start causing
+   problems when they reach the limit. */
+#define MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH (PATH_MAX/2)
+
+struct maildir_mailbox_list {
+	struct mailbox_list list;
+
+	const char *temp_prefix;
+};
+
+struct mailbox_list_iterate_context *
+maildir_list_iter_init(struct mailbox_list *_list,
+		       const char *ref, const char *mask,
+		       enum mailbox_list_iter_flags flags);
+int maildir_list_iter_deinit(struct mailbox_list_iterate_context *ctx);
+struct mailbox_info *
+maildir_list_iter_next(struct mailbox_list_iterate_context *ctx);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/subscription-file.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,247 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "lib.h"
+#include "istream.h"
+#include "ostream.h"
+#include "nfs-workarounds.h"
+#include "file-dotlock.h"
+#include "mailbox-list-private.h"
+#include "subscription-file.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#define SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
+#define SUBSCRIPTION_FILE_LOCK_TIMEOUT 120
+#define SUBSCRIPTION_FILE_CHANGE_TIMEOUT 30
+
+struct subsfile_list_context {
+	pool_t pool;
+
+	struct mailbox_list *list;
+	struct istream *input;
+	const char *path;
+
+	bool failed;
+};
+
+static void subsfile_set_syscall_error(struct mailbox_list *list,
+				       const char *function, const char *path)
+{
+	i_assert(function != NULL);
+
+	if (errno == EACCES)
+		mailbox_list_set_error(list, "Permission denied");
+	else {
+		mailbox_list_set_critical(list,
+			"%s failed with subscription file %s: %m",
+			function, path);
+	}
+}
+
+static const char *next_line(struct mailbox_list *list, const char *path,
+			     struct istream *input, bool *failed_r,
+			     bool ignore_estale)
+{
+	const char *line;
+
+	*failed_r = FALSE;
+	if (input == NULL)
+		return NULL;
+
+	while ((line = i_stream_next_line(input)) == NULL) {
+                switch (i_stream_read(input)) {
+		case -1:
+                        if (input->stream_errno != 0 &&
+                            (input->stream_errno != ESTALE || !ignore_estale)) {
+                                subsfile_set_syscall_error(list,
+                                                           "read()", path);
+                                *failed_r = TRUE;
+                        }
+			return NULL;
+		case -2:
+			/* mailbox name too large */
+			mailbox_list_set_critical(list,
+				"Subscription file %s contains lines longer "
+				"than %u characters", path,
+				list->mailbox_name_max_length);
+			*failed_r = TRUE;
+			return NULL;
+		}
+	}
+
+	return line;
+}
+
+int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
+			    const char *temp_prefix, const char *name, bool set)
+{
+	struct dotlock_settings dotlock_set;
+	struct dotlock *dotlock;
+	const char *line;
+	struct istream *input;
+	struct ostream *output;
+	int fd_in, fd_out;
+	bool found, failed = FALSE;
+
+	if (strcasecmp(name, "INBOX") == 0)
+		name = "INBOX";
+
+	memset(&dotlock_set, 0, sizeof(dotlock_set));
+	dotlock_set.temp_prefix = temp_prefix;
+	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
+	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
+
+	fd_out = file_dotlock_open(&dotlock_set, path, 0, &dotlock);
+	if (fd_out == -1) {
+		if (errno == EAGAIN) {
+			mailbox_list_set_error(list,
+				"Timeout waiting for subscription file lock");
+		} else {
+			subsfile_set_syscall_error(list,
+						   "file_dotlock_open()", path);
+		}
+		return -1;
+	}
+
+	fd_in = nfs_safe_open(path, O_RDONLY);
+	if (fd_in == -1 && errno != ENOENT) {
+		subsfile_set_syscall_error(list, "open()", path);
+		(void)file_dotlock_delete(&dotlock);
+		return -1;
+	}
+
+	input = fd_in == -1 ? NULL :
+		i_stream_create_file(fd_in, default_pool,
+				     list->mailbox_name_max_length+1, TRUE);
+	output = o_stream_create_file(fd_out, default_pool,
+				      list->mailbox_name_max_length+1, FALSE);
+	found = FALSE;
+	while ((line = next_line(list, path, input,
+				 &failed, FALSE)) != NULL) {
+		if (strcmp(line, name) == 0) {
+			found = TRUE;
+			if (!set)
+				continue;
+		}
+
+		if (o_stream_send_str(output, line) < 0 ||
+		    o_stream_send(output, "\n", 1) < 0) {
+			subsfile_set_syscall_error(list, "write()", path);
+			failed = TRUE;
+			break;
+		}
+	}
+
+	if (!failed && set && !found) {
+		/* append subscription */
+		line = t_strconcat(name, "\n", NULL);
+		if (o_stream_send_str(output, line) < 0) {
+			subsfile_set_syscall_error(list, "write()", path);
+			failed = TRUE;
+		}
+	}
+
+	if (input != NULL)
+		i_stream_destroy(&input);
+	o_stream_destroy(&output);
+
+	if (failed || (set && found) || (!set && !found)) {
+		if (file_dotlock_delete(&dotlock) < 0) {
+			subsfile_set_syscall_error(list,
+				"file_dotlock_delete()", path);
+			failed = TRUE;
+		}
+	} else {
+		enum dotlock_replace_flags flags =
+			DOTLOCK_REPLACE_FLAG_VERIFY_OWNER;
+		if (file_dotlock_replace(&dotlock, flags) < 0) {
+			subsfile_set_syscall_error(list,
+				"file_dotlock_replace()", path);
+			failed = TRUE;
+		}
+	}
+	return failed ? -1 : 0;
+}
+
+struct subsfile_list_context *
+subsfile_list_init(struct mailbox_list *list, const char *path)
+{
+	struct subsfile_list_context *ctx;
+	pool_t pool;
+	int fd;
+
+	pool = pool_alloconly_create("subsfile_list",
+				     list->mailbox_name_max_length + 1024);
+
+	ctx = p_new(pool, struct subsfile_list_context, 1);
+	ctx->pool = pool;
+	ctx->list = list;
+
+	fd = nfs_safe_open(path, O_RDONLY);
+	if (fd == -1) {
+		if (errno != ENOENT) {
+			subsfile_set_syscall_error(list, "open()", path);
+			ctx->failed = TRUE;
+		}
+	} else {
+		ctx->input =
+			i_stream_create_file(fd, pool,
+					     list->mailbox_name_max_length+1,
+					     TRUE);
+	}
+	ctx->path = p_strdup(pool, path);
+	return ctx;
+}
+
+int subsfile_list_deinit(struct subsfile_list_context *ctx)
+{
+	int ret = ctx->failed ? -1 : 0;
+
+	if (ctx->input != NULL)
+		i_stream_destroy(&ctx->input);
+	pool_unref(ctx->pool);
+	return ret;
+}
+
+const char *subsfile_list_next(struct subsfile_list_context *ctx)
+{
+        const char *line;
+        unsigned int i;
+        int fd;
+
+        if (ctx->failed || ctx->input == NULL)
+		return NULL;
+
+        for (i = 0;; i++) {
+                line = next_line(ctx->list, ctx->path, ctx->input, &ctx->failed,
+				 i < SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT);
+                if (ctx->input->stream_errno != ESTALE ||
+                    i == SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT)
+                        break;
+
+                /* Reopen the subscription file and re-send everything.
+                   this isn't the optimal behavior, but it's allowed by
+                   IMAP and this way we don't have to read everything into
+                   memory or try to play any guessing games. */
+                i_stream_destroy(&ctx->input);
+
+                fd = nfs_safe_open(ctx->path, O_RDONLY);
+                if (fd == -1) {
+                        /* In case of ENOENT all the subscriptions got lost.
+                           Just return end of subscriptions list in that
+                           case. */
+                        if (errno != ENOENT) {
+                                subsfile_set_syscall_error(ctx->list, "open()",
+                                                           ctx->path);
+                                ctx->failed = TRUE;
+                        }
+                        return NULL;
+                }
+
+		ctx->input = i_stream_create_file(fd, ctx->pool,
+					ctx->list->mailbox_name_max_length+1,
+					TRUE);
+        }
+        return line;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/subscription-file.h	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,20 @@
+#ifndef __SUBSCRIPTION_FILE_H
+#define __SUBSCRIPTION_FILE_H
+
+struct mailbox_list;
+
+/* Initialize new subscription file listing. */
+struct subsfile_list_context *
+subsfile_list_init(struct mailbox_list *list, const char *path);
+
+/* Deinitialize subscription file listing. Returns 0 if ok, or -1 if some
+   error occurred while listing. */
+int subsfile_list_deinit(struct subsfile_list_context *ctx);
+/* Returns the next subscribed mailbox, or NULL. */
+const char *subsfile_list_next(struct subsfile_list_context *ctx);
+
+int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
+			    const char *temp_prefix, const char *name,
+			    bool set);
+
+#endif
--- a/src/lib-storage/mail-storage-private.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/mail-storage-private.h	Thu Nov 16 02:16:31 2006 +0200
@@ -27,8 +27,6 @@
 			      struct mail_storage_callbacks *callbacks,
 			      void *context);
 
-	const char *(*get_mailbox_path)(struct mail_storage *storage,
-					const char *name, bool *is_file_r);
 	const char *(*get_mailbox_control_dir)(struct mail_storage *storage,
 					       const char *name);
 	const char *(*get_mailbox_index_dir)(struct mail_storage *storage,
@@ -45,20 +43,11 @@
 	int (*mailbox_rename)(struct mail_storage *storage, const char *oldname,
 			      const char *newname);
 
-	struct mailbox_list_context *
-		(*mailbox_list_init)(struct mail_storage *storage,
-				     const char *ref, const char *mask,
-				     enum mailbox_list_flags flags);
-	struct mailbox_list *
-		(*mailbox_list_next)(struct mailbox_list_context *ctx);
-	int (*mailbox_list_deinit)(struct mailbox_list_context *ctx);
-
-	int (*set_subscribed)(struct mail_storage *storage,
-			      const char *name, bool set);
-
-	int (*get_mailbox_name_status)(struct mail_storage *storage,
-				       const char *name,
-				       enum mailbox_name_status *status);
+	int (*is_mailbox)(struct mail_storage *storage,
+			  const char *dir, const char *fname,
+			  enum mailbox_list_iter_flags iter_flags,
+			  enum mailbox_info_flags *flags,
+			  enum mailbox_list_file_type type);
 
 	const char *(*get_last_error)(struct mail_storage *storage,
 				      bool *syntax_error_r,
@@ -67,7 +56,7 @@
 
 struct mail_storage {
 	char *name;
-	char hierarchy_sep;
+	bool mailbox_is_file;
 
         struct mail_storage_vfuncs v;
 
@@ -75,6 +64,7 @@
 	pool_t pool;
 
 	char *error;
+	struct mailbox_list *list;
 	enum mail_storage_flags flags;
         enum mail_storage_lock_method lock_method;
 
@@ -287,6 +277,13 @@
 
 const char *mail_storage_class_get_last_error(struct mail_storage *storage,
 					      bool *syntax_error_r);
-bool mailbox_name_is_too_large(const char *name, char sep);
+
+enum mailbox_list_flags
+mail_storage_get_list_flags(enum mail_storage_flags storage_flags);
+int mailbox_storage_list_is_mailbox(const char *dir, const char *fname,
+				    enum mailbox_list_file_type type,
+				    enum mailbox_list_iter_flags iter_flags,
+				    enum mailbox_info_flags *flags,
+				    void *context);
 
 #endif
--- a/src/lib-storage/mail-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/mail-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -16,14 +16,6 @@
 	"Internal error occurred. Refer to server log for more information."
 #define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
 
-/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
-   prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
-   then start renaming them to larger names from end to beginning, which
-   eventually would start causing the failures when trying to use too
-   long mailbox names. */
-#define MAILBOX_MAX_HIERARCHY_LEVELS 20
-#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
-
 unsigned int mail_storage_module_id = 0;
 unsigned int mail_storage_mail_index_module_id = 0;
 
@@ -120,20 +112,20 @@
 }
 
 struct mail_storage *
-mail_storage_create(const char *name, const char *data, const char *user,
+mail_storage_create(const char *driver, const char *data, const char *user,
 		    enum mail_storage_flags flags,
 		    enum mail_storage_lock_method lock_method)
 {
 	struct mail_storage *storage;
 
-	storage = mail_storage_find(name);
+	storage = mail_storage_find(driver);
 	if (storage != NULL)
 		return storage->v.create(data, user, flags, lock_method);
 	else
 		return NULL;
 }
 
-struct mail_storage *
+static struct mail_storage *
 mail_storage_create_default(const char *user, enum mail_storage_flags flags,
 			    enum mail_storage_lock_method lock_method)
 {
@@ -265,6 +257,18 @@
 	storage->temporary_error = TRUE;
 }
 
+void mail_storage_set_list_error(struct mail_storage *storage)
+{
+	bool temp;
+
+	i_free(storage->error);
+	storage->error =
+		i_strdup(mailbox_list_get_last_error(storage->list, &temp));
+
+	storage->syntax_error = FALSE;
+	storage->temporary_error = temp;
+}
+
 void mail_storage_set_critical(struct mail_storage *storage,
 			       const char *fmt, ...)
 {
@@ -285,7 +289,12 @@
 
 char mail_storage_get_hierarchy_sep(struct mail_storage *storage)
 {
-	return storage->hierarchy_sep;
+	return mailbox_list_get_hierarchy_sep(storage->list);
+}
+
+struct mailbox_list *mail_storage_get_list(struct mail_storage *storage)
+{
+	return storage->list;
 }
 
 void mail_storage_set_callbacks(struct mail_storage *storage,
@@ -312,41 +321,6 @@
 	return storage->v.mailbox_rename(storage, oldname, newname);
 }
 
-struct mailbox_list_context *
-mail_storage_mailbox_list_init(struct mail_storage *storage,
-			       const char *ref, const char *mask,
-			       enum mailbox_list_flags flags)
-{
-	return storage->v.mailbox_list_init(storage, ref, mask, flags);
-}
-
-struct mailbox_list *
-mail_storage_mailbox_list_next(struct mailbox_list_context *ctx)
-{
-	return ctx->storage->v.mailbox_list_next(ctx);
-}
-
-int mail_storage_mailbox_list_deinit(struct mailbox_list_context **_ctx)
-{
-	struct mailbox_list_context *ctx = *_ctx;
-
-	*_ctx = NULL;
-	return ctx->storage->v.mailbox_list_deinit(ctx);
-}
-
-int mail_storage_set_subscribed(struct mail_storage *storage,
-				const char *name, bool set)
-{
-	return storage->v.set_subscribed(storage, name, set);
-}
-
-int mail_storage_get_mailbox_name_status(struct mail_storage *storage,
-					 const char *name,
-					 enum mailbox_name_status *status)
-{
-	return storage->v.get_mailbox_name_status(storage, name, status);
-}
-
 const char *mail_storage_get_last_error(struct mail_storage *storage,
 					bool *syntax_error_r,
 					bool *temporary_error_r)
@@ -358,19 +332,61 @@
 const char *mail_storage_get_mailbox_path(struct mail_storage *storage,
 					  const char *name, bool *is_file_r)
 {
-	return storage->v.get_mailbox_path(storage, name, is_file_r);
+	*is_file_r = storage->mailbox_is_file;
+
+	return mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
 }
 
 const char *mail_storage_get_mailbox_control_dir(struct mail_storage *storage,
 						 const char *name)
 {
-	return storage->v.get_mailbox_control_dir(storage, name);
+	return mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_CONTROL);
 }
 
 const char *mail_storage_get_mailbox_index_dir(struct mail_storage *storage,
 					       const char *name)
 {
-	return storage->v.get_mailbox_index_dir(storage, name);
+	return mailbox_list_get_path(storage->list, name,
+				     MAILBOX_LIST_PATH_TYPE_INDEX);
+}
+
+enum mailbox_list_flags
+mail_storage_get_list_flags(enum mail_storage_flags storage_flags)
+{
+	enum mailbox_list_flags list_flags = 0;
+
+	if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
+		list_flags |= MAILBOX_LIST_FLAG_DEBUG;
+	if ((storage_flags & MAIL_STORAGE_FLAG_HAS_INBOX) != 0)
+		list_flags |= MAILBOX_LIST_FLAG_INBOX;
+	if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
+		list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
+	return list_flags;
+}
+
+int mailbox_storage_list_is_mailbox(const char *dir, const char *fname,
+				    enum mailbox_list_file_type type,
+				    enum mailbox_list_iter_flags iter_flags,
+				    enum mailbox_info_flags *flags,
+				    void *context)
+{
+	struct mail_storage *storage = context;
+
+	return mail_storage_is_mailbox(storage, dir, fname, iter_flags,
+				       flags, type);
+}
+
+
+int mail_storage_is_mailbox(struct mail_storage *storage,
+			    const char *dir, const char *fname,
+			    enum mailbox_list_iter_flags iter_flags,
+			    enum mailbox_info_flags *flags,
+			    enum mailbox_list_file_type type)
+{
+	return storage->v.is_mailbox(storage, dir, fname, iter_flags,
+				     flags, type);
 }
 
 struct mailbox *mailbox_open(struct mail_storage *storage, const char *name,
@@ -575,25 +591,3 @@
 {
 	return box->v.is_inconsistent(box);
 }
-
-bool mailbox_name_is_too_large(const char *name, char sep)
-{
-	unsigned int levels = 1, level_len = 0;
-
-	for (; *name != '\0'; name++) {
-		if (*name == sep) {
-			if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
-				return TRUE;
-			levels++;
-			level_len = 0;
-		} else {
-			level_len++;
-		}
-	}
-
-	if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
-		return TRUE;
-	if (levels > MAILBOX_MAX_HIERARCHY_LEVELS)
-		return TRUE;
-	return FALSE;
-}
--- a/src/lib-storage/mail-storage.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/mail-storage.h	Thu Nov 16 02:16:31 2006 +0200
@@ -4,6 +4,7 @@
 struct message_size;
 
 #include "mail-types.h"
+#include "mailbox-list.h"
 
 enum mail_storage_flags {
 	/* Print debugging information while initializing the storage */
@@ -26,7 +27,9 @@
 	MAIL_STORAGE_FLAG_SHARED_NAMESPACE	= 0x80,
 	/* Don't try to autodetect anything, require that the given data 
 	   contains all the necessary information. */
-	MAIL_STORAGE_FLAG_NO_AUTODETECTION	= 0x100
+	MAIL_STORAGE_FLAG_NO_AUTODETECTION	= 0x100,
+	/* Ths storage contains INBOX */
+	MAIL_STORAGE_FLAG_HAS_INBOX		= 0x200
 };
 
 enum mail_storage_lock_method {
@@ -53,26 +56,6 @@
 	MAILBOX_OPEN_MBOX_ONE_MSG_ONLY	= 0x40
 };
 
-enum mailbox_list_flags {
-	MAILBOX_LIST_SUBSCRIBED	= 0x01,
-	MAILBOX_LIST_FAST_FLAGS	= 0x02,
-	MAILBOX_LIST_CHILDREN	= 0x04,
-	MAILBOX_LIST_INBOX	= 0x08
-};
-
-enum mailbox_flags {
-	MAILBOX_NOSELECT	= 0x001,
-	MAILBOX_NONEXISTENT	= 0x002,
-	MAILBOX_PLACEHOLDER	= 0x004,
-	MAILBOX_CHILDREN	= 0x008,
-	MAILBOX_NOCHILDREN	= 0x010,
-	MAILBOX_NOINFERIORS	= 0x020,
-	MAILBOX_MARKED		= 0x040,
-	MAILBOX_UNMARKED	= 0x080,
-
-	MAILBOX_READONLY	= 0x100
-};
-
 enum mailbox_status_items {
 	STATUS_MESSAGES		= 0x01,
 	STATUS_RECENT		= 0x02,
@@ -83,13 +66,6 @@
 	STATUS_KEYWORDS		= 0x40
 };
 
-enum mailbox_name_status {
-	MAILBOX_NAME_EXISTS,
-	MAILBOX_NAME_VALID,
-	MAILBOX_NAME_INVALID,
-	MAILBOX_NAME_NOINFERIORS
-};
-
 enum mail_sort_type {
 /* Maximum size for sort program (each one separately + END) */
 #define MAX_SORT_PROGRAM_SIZE (7 + 1)
@@ -165,14 +141,8 @@
 struct mail_keywords;
 struct mail_save_context;
 struct mailbox;
-struct mailbox_list_context;
 struct mailbox_transaction_context;
 
-struct mailbox_list {
-	const char *name;
-        enum mailbox_flags flags;
-};
-
 struct mailbox_status {
 	uint32_t messages;
 	uint32_t recent;
@@ -232,23 +202,21 @@
 			    enum mail_storage_lock_method *lock_method_r);
 
 /* Create a new instance of registered mail storage class with given
-   storage-specific data. If data is NULL, it tries to use defaults.
+   storage-specific data. If data is NULL, it tries to autodetect defaults.
    May return NULL if anything fails. */
 struct mail_storage *
-mail_storage_create(const char *name, const char *data, const char *user,
+mail_storage_create(const char *driver, const char *data, const char *user,
 		    enum mail_storage_flags flags,
 		    enum mail_storage_lock_method lock_method);
-void mail_storage_destroy(struct mail_storage **storage);
-
-struct mail_storage *
-mail_storage_create_default(const char *user, enum mail_storage_flags flags,
-			    enum mail_storage_lock_method lock_method);
 struct mail_storage *
 mail_storage_create_with_data(const char *data, const char *user,
 			      enum mail_storage_flags flags,
 			      enum mail_storage_lock_method lock_method);
+void mail_storage_destroy(struct mail_storage **storage);
 
 char mail_storage_get_hierarchy_sep(struct mail_storage *storage);
+struct mailbox_list *mail_storage_get_list(struct mail_storage *storage);
+void mail_storage_set_list_error(struct mail_storage *storage);
 
 /* Set storage callback functions to use. */
 void mail_storage_set_callbacks(struct mail_storage *storage,
@@ -274,31 +242,6 @@
 int mail_storage_mailbox_rename(struct mail_storage *storage,
 				const char *oldname, const char *newname);
 
-/* Initialize new mailbox list request. mask may contain '%' and '*'
-   wildcards as defined in RFC3501. Matching against "INBOX" is
-   case-insensitive, but anything else is not. */
-struct mailbox_list_context *
-mail_storage_mailbox_list_init(struct mail_storage *storage,
-			       const char *ref, const char *mask,
-			       enum mailbox_list_flags flags);
-/* Get next mailbox. Returns the mailbox name */
-struct mailbox_list *
-mail_storage_mailbox_list_next(struct mailbox_list_context *ctx);
-/* Deinitialize mailbox list request. Returns FALSE if some error
-   occurred while listing. */
-int mail_storage_mailbox_list_deinit(struct mailbox_list_context **ctx);
-
-/* Subscribe/unsubscribe mailbox. There should be no error when
-   subscribing to already subscribed mailbox. Subscribing to
-   unexisting mailboxes is optional. */
-int mail_storage_set_subscribed(struct mail_storage *storage,
-				const char *name, bool set);
-
-/* Returns mailbox name status */
-int mail_storage_get_mailbox_name_status(struct mail_storage *storage,
-					 const char *name,
-					 enum mailbox_name_status *status);
-
 /* Returns the error message of last occurred error. */
 const char *mail_storage_get_last_error(struct mail_storage *storage,
 					bool *syntax_error_r,
@@ -319,6 +262,12 @@
 const char *mail_storage_get_mailbox_index_dir(struct mail_storage *storage,
 					       const char *name);
 
+int mail_storage_is_mailbox(struct mail_storage *storage,
+			    const char *dir, const char *fname,
+			    enum mailbox_list_iter_flags iter_flags,
+			    enum mailbox_info_flags *flags,
+			    enum mailbox_list_file_type type);
+
 /* Open a mailbox. If input stream is given, mailbox is opened read-only
    using it as a backend. If storage doesn't support stream backends and its
    tried to be used, NULL is returned.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-list-private.h	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,79 @@
+#ifndef __MAILBOX_LIST_PRIVATE_H
+#define __MAILBOX_LIST_PRIVATE_H
+
+#include "mailbox-list.h"
+
+struct dirent;
+
+struct mailbox_list_vfuncs {
+	struct mailbox_list *(*alloc)(void);
+	void (*deinit)(struct mailbox_list *list);
+
+	bool (*is_valid_mask)(struct mailbox_list *list, const char *mask);
+	bool (*is_valid_existing_name)(struct mailbox_list *list,
+				       const char *name);
+	bool (*is_valid_create_name)(struct mailbox_list *list,
+				     const char *name);
+
+	const char *(*get_path)(struct mailbox_list *list, const char *name,
+				enum mailbox_list_path_type type);
+	int (*get_mailbox_name_status)(struct mailbox_list *list,
+				       const char *name,
+				       enum mailbox_name_status *status);
+
+	const char *(*get_temp_prefix)(struct mailbox_list *list);
+
+	struct mailbox_list_iterate_context *
+		(*iter_init)(struct mailbox_list *list,
+			     const char *ref, const char *mask,
+			     enum mailbox_list_iter_flags flags);
+	struct mailbox_info *
+		(*iter_next)(struct mailbox_list_iterate_context *ctx);
+	int (*iter_deinit)(struct mailbox_list_iterate_context *ctx);
+
+	int (*set_subscribed)(struct mailbox_list *list,
+			      const char *name, bool set);
+};
+
+struct mailbox_list {
+	const char *name;
+	char hierarchy_sep;
+	size_t mailbox_name_max_length;
+
+	struct mailbox_list_vfuncs v;
+
+/* private: */
+	pool_t pool;
+	struct mailbox_list_settings set;
+	enum mailbox_list_flags flags;
+
+	char *error;
+	bool temporary_error;
+
+	mailbox_list_is_mailbox_t *callback;
+	void *context;
+
+	ARRAY_DEFINE(module_contexts, void);
+};
+
+struct mailbox_list_iterate_context {
+	struct mailbox_list *list;
+	enum mailbox_list_iter_flags flags;
+	bool failed;
+};
+
+/* Modules should use do "my_id = mailbox_list_module_id++" and
+   use objects' module_contexts[id] for their own purposes. */
+extern unsigned int mailbox_list_module_id;
+
+extern void (*hook_mailbox_list_created)(struct mailbox_list *list);
+
+bool mailbox_list_name_is_too_large(const char *name, char sep);
+enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d);
+
+void mailbox_list_clear_error(struct mailbox_list *list);
+void mailbox_list_set_error(struct mailbox_list *list, const char *error);
+void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...)
+	__attr_format__(2, 3);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-list.c	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,311 @@
+/* Copyright (C) 2006 Timo Sirainen */
+
+#include "lib.h"
+#include "array.h"
+#include "ioloop.h"
+#include "mailbox-list-private.h"
+
+#include <time.h>
+#include <dirent.h>
+
+/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
+   prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
+   then start renaming them to larger names from end to beginning, which
+   eventually would start causing the failures when trying to use too
+   long mailbox names. */
+#define MAILBOX_MAX_HIERARCHY_LEVELS 20
+#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
+
+/* Message to show to users when critical error occurs */
+#define CRITICAL_MSG \
+	"Internal error occurred. Refer to server log for more information."
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
+
+void (*hook_mailbox_list_created)(struct mailbox_list *list);
+
+static ARRAY_DEFINE(mailbox_list_drivers, const struct mailbox_list *);
+
+static bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
+{
+	const struct mailbox_list *const *drivers;
+	unsigned int i, count;
+
+	drivers = array_get(&mailbox_list_drivers, &count);
+	for (i = 0; i < count; i++) {
+		if (strcasecmp(drivers[i]->name, name) == 0) {
+			*idx_r = i;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+void mailbox_list_register(const struct mailbox_list *list)
+{
+	if (!array_is_created(&mailbox_list_drivers))
+		i_array_init(&mailbox_list_drivers, 4);
+	else {
+		unsigned int idx;
+
+		if (mailbox_list_driver_find(list->name, &idx)) {
+			i_fatal("mailbox_list_register(%s): duplicate driver",
+				list->name);
+		}
+	}
+
+	array_append(&mailbox_list_drivers, &list, 1);
+}
+
+void mailbox_list_unregister(const struct mailbox_list *list)
+{
+	unsigned int idx;
+
+	if (!mailbox_list_driver_find(list->name, &idx)) {
+		i_fatal("mailbox_list_unregister(%s): unknown driver",
+			list->name);
+	}
+	array_delete(&mailbox_list_drivers, idx, 1);
+
+	if (array_count(&mailbox_list_drivers) == 0)
+		array_free(&mailbox_list_drivers);
+}
+
+int mailbox_list_init(const char *driver,
+		      const struct mailbox_list_settings *set,
+		      enum mailbox_list_flags flags,
+		      mailbox_list_is_mailbox_t *callback, void *context,
+		      struct mailbox_list **list_r, const char **error_r)
+{
+	const struct mailbox_list *const *class_p;
+	struct mailbox_list *list;
+	unsigned int idx;
+
+	if (!mailbox_list_driver_find(driver, &idx)) {
+		*error_r = "Unknown mailbox list driver";
+		return -1;
+	}
+
+	i_assert(*set->root_dir != '\0');
+	i_assert(*set->subscription_fname != '\0');
+
+	class_p = array_idx(&mailbox_list_drivers, idx);
+	list = (*class_p)->v.alloc();
+
+	list->flags = flags;
+	list->callback = callback;
+	list->context = context;
+
+	/* copy settings */
+	list->set.root_dir = p_strdup(list->pool, set->root_dir);
+	list->set.index_dir = set->index_dir == NULL ||
+		strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
+		p_strdup(list->pool, set->index_dir);
+	list->set.control_dir = set->control_dir == NULL ||
+		strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
+		p_strdup(list->pool, set->control_dir);
+
+	list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
+	list->set.subscription_fname =
+		p_strdup(list->pool, set->subscription_fname);
+	list->set.maildir_name = p_strdup(list->pool, set->maildir_name);
+
+	if ((flags & MAILBOX_LIST_FLAG_DEBUG) != 0) {
+		i_info("%s: root=%s, index=%s, control=%s, inbox=%s",
+		       driver, list->set.root_dir,
+		       list->set.index_dir == NULL ? "" : list->set.index_dir,
+		       list->set.control_dir == NULL ?
+		       "" : list->set.control_dir,
+		       list->set.inbox_path == NULL ?
+		       "" : list->set.inbox_path);
+	}
+
+	array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
+
+	if (hook_mailbox_list_created != NULL)
+		hook_mailbox_list_created(list);
+
+	*list_r = list;
+	return 0;
+}
+
+void mailbox_list_deinit(struct mailbox_list *list)
+{
+	i_free_and_null(list->error);
+
+	list->v.deinit(list);
+}
+
+const char *mailbox_list_get_driver_name(struct mailbox_list *list)
+{
+	return list->name;
+}
+
+char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
+{
+	return list->hierarchy_sep;
+}
+
+bool mailbox_list_is_valid_mask(struct mailbox_list *list, const char *mask)
+{
+	return list->v.is_valid_mask(list, mask);
+}
+
+bool mailbox_list_is_valid_existing_name(struct mailbox_list *list,
+					 const char *name)
+{
+	return list->v.is_valid_existing_name(list, name);
+}
+
+bool mailbox_list_is_valid_create_name(struct mailbox_list *list,
+				       const char *name)
+{
+	return list->v.is_valid_create_name(list, name);
+}
+
+const char *mailbox_list_get_path(struct mailbox_list *list, const char *name,
+				  enum mailbox_list_path_type type)
+{
+	return list->v.get_path(list, name, type);
+}
+
+const char *mailbox_list_get_temp_prefix(struct mailbox_list *list)
+{
+	return list->v.get_temp_prefix(list);
+}
+
+int mailbox_list_get_mailbox_name_status(struct mailbox_list *list,
+					 const char *name,
+					 enum mailbox_name_status *status)
+{
+	return list->v.get_mailbox_name_status(list, name, status);
+}
+
+struct mailbox_list_iterate_context *
+mailbox_list_iter_init(struct mailbox_list *list,
+		       const char *ref, const char *mask,
+		       enum mailbox_list_iter_flags flags)
+{
+	return list->v.iter_init(list, ref, mask, flags);
+}
+
+struct mailbox_info *
+mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
+{
+	return ctx->list->v.iter_next(ctx);
+}
+
+int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
+{
+	struct mailbox_list_iterate_context *ctx = *_ctx;
+
+	*_ctx = NULL;
+
+	return ctx->list->v.iter_deinit(ctx);
+}
+
+int mailbox_list_set_subscribed(struct mailbox_list *list,
+				const char *name, bool set)
+{
+	return list->v.set_subscribed(list, name, set);
+}
+
+bool mailbox_list_name_is_too_large(const char *name, char sep)
+{
+	unsigned int levels = 1, level_len = 0;
+
+	for (; *name != '\0'; name++) {
+		if (*name == sep) {
+			if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
+				return TRUE;
+			levels++;
+			level_len = 0;
+		} else {
+			level_len++;
+		}
+	}
+
+	if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
+		return TRUE;
+	if (levels > MAILBOX_MAX_HIERARCHY_LEVELS)
+		return TRUE;
+	return FALSE;
+}
+
+enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d)
+{
+	enum mailbox_list_file_type type;
+
+#ifdef HAVE_DIRENT_D_TYPE
+	switch (d->d_type) {
+	case DT_UNKNOWN:
+		type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
+		break;
+	case DT_REG:
+		type = MAILBOX_LIST_FILE_TYPE_FILE;
+		break;
+	case DT_DIR:
+		type = MAILBOX_LIST_FILE_TYPE_DIR;
+		break;
+	case DT_LNK:
+		type = MAILBOX_LIST_FILE_TYPE_SYMLINK;
+		break;
+	default:
+		type = MAILBOX_LIST_FILE_TYPE_OTHER;
+		break;
+	}
+#else
+	type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
+#endif
+	return type;
+}
+
+const char *mailbox_list_get_last_error(struct mailbox_list *list,
+					bool *temporary_error_r)
+{
+	*temporary_error_r = list->temporary_error;
+
+	return list->error;
+}
+
+void mailbox_list_clear_error(struct mailbox_list *list)
+{
+	i_free_and_null(list->error);
+
+	list->temporary_error = FALSE;
+}
+
+void mailbox_list_set_error(struct mailbox_list *list, const char *error)
+{
+	i_free(list->error);
+	list->error = i_strdup(error);
+
+	list->temporary_error = FALSE;
+}
+
+static void mailbox_list_set_internal_error(struct mailbox_list *list)
+{
+	struct tm *tm;
+	char str[256];
+
+	tm = localtime(&ioloop_time);
+
+	i_free(list->error);
+	list->error =
+		strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
+		i_strdup(str) : i_strdup(CRITICAL_MSG);
+	list->temporary_error = TRUE;
+}
+
+void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	i_error("%s", t_strdup_vprintf(fmt, va));
+	va_end(va);
+
+	/* critical errors may contain sensitive data, so let user
+	   see only "Internal error" with a timestamp to make it
+	   easier to look from log files the actual error message. */
+	mailbox_list_set_internal_error(list);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-list.h	Thu Nov 16 02:16:31 2006 +0200
@@ -0,0 +1,161 @@
+#ifndef __MAILBOX_LIST_H
+#define __MAILBOX_LIST_H
+
+struct mailbox_list;
+struct mailbox_list_iterate_context;
+
+enum mailbox_list_flags {
+	/* Print debugging information while initializing the driver */
+	MAILBOX_LIST_FLAG_DEBUG			= 0x01,
+	/* This mailbox list contains INBOX. Match case-insensitively for it. */
+	MAILBOX_LIST_FLAG_INBOX			= 0x02,
+	/* Allow full filesystem access with absolute or relative paths. */
+	MAILBOX_LIST_FLAG_FULL_FS_ACCESS	= 0x04
+};
+
+enum mailbox_info_flags {
+	MAILBOX_NOSELECT	= 0x001,
+	MAILBOX_NONEXISTENT	= 0x002,
+	MAILBOX_PLACEHOLDER	= 0x004,
+	MAILBOX_CHILDREN	= 0x008,
+	MAILBOX_NOCHILDREN	= 0x010,
+	MAILBOX_NOINFERIORS	= 0x020,
+	MAILBOX_MARKED		= 0x040,
+	MAILBOX_UNMARKED	= 0x080,
+
+	MAILBOX_READONLY	= 0x100
+};
+
+enum mailbox_name_status {
+	MAILBOX_NAME_EXISTS,
+	MAILBOX_NAME_VALID,
+	MAILBOX_NAME_INVALID,
+	MAILBOX_NAME_NOINFERIORS
+};
+
+enum mailbox_list_iter_flags {
+	/* List only subscribed mailboxes */
+	MAILBOX_LIST_ITER_SUBSCRIBED	= 0x01,
+	/* Don't return any flags unless it can be done without cost */
+	MAILBOX_LIST_ITER_FAST_FLAGS	= 0x02,
+	/* Return children flags */
+	MAILBOX_LIST_ITER_CHILDREN	= 0x04
+};
+
+enum mailbox_list_path_type {
+	/* Return directory's path (eg. /home/user/mail) */
+	MAILBOX_LIST_PATH_TYPE_DIR,
+	/* Return mailbox path (dir + maildir_name) */
+	MAILBOX_LIST_PATH_TYPE_MAILBOX,
+	/* Return control directory */
+	MAILBOX_LIST_PATH_TYPE_CONTROL,
+	/* Return index file directory */
+	MAILBOX_LIST_PATH_TYPE_INDEX
+};
+
+enum mailbox_list_file_type {
+	MAILBOX_LIST_FILE_TYPE_UNKNOWN = 0,
+	MAILBOX_LIST_FILE_TYPE_FILE,
+	MAILBOX_LIST_FILE_TYPE_DIR,
+	MAILBOX_LIST_FILE_TYPE_SYMLINK,
+	MAILBOX_LIST_FILE_TYPE_OTHER
+};
+
+struct mailbox_list_settings {
+	const char *root_dir;
+	const char *index_dir;
+	const char *control_dir;
+
+	const char *inbox_path;
+	const char *subscription_fname;
+	/* If non-empty, it means that mails exist in a maildir_name
+	   subdirectory. eg. if you have a directory containing directories:
+
+	   mail/
+	   mail/foo/
+	   mail/foo/Maildir
+
+	   If mailbox_name is empty, you have mailboxes "mail", "mail/foo" and
+	   "mail/foo/Maildir".
+
+	   If mailbox_name is "Maildir", you have a non-selectable mailbox
+	   "mail" and a selectable mailbox "mail/foo". */
+	const char *maildir_name;
+};
+
+struct mailbox_info {
+	const char *name;
+        enum mailbox_info_flags flags;
+};
+
+/* Returns -1 if error, 0 if it's not a valid mailbox, 1 if it is.
+   flags may be updated (especially the children flags). */
+typedef int mailbox_list_is_mailbox_t(const char *dir, const char *fname,
+				      enum mailbox_list_file_type type,
+				      enum mailbox_list_iter_flags iter_flags,
+				      enum mailbox_info_flags *flags_r,
+				      void *context);
+
+/* register all drivers */
+void mailbox_list_register_all(void);
+
+void mailbox_list_register(const struct mailbox_list *list);
+void mailbox_list_unregister(const struct mailbox_list *list);
+
+/* Returns 0 if ok, -1 if initialization failed. */
+int mailbox_list_init(const char *driver,
+		      const struct mailbox_list_settings *set,
+		      enum mailbox_list_flags flags,
+		      mailbox_list_is_mailbox_t *callback, void *context,
+		      struct mailbox_list **list_r, const char **error_r);
+void mailbox_list_deinit(struct mailbox_list *list);
+
+const char *mailbox_list_get_driver_name(struct mailbox_list *list);
+char mailbox_list_get_hierarchy_sep(struct mailbox_list *list);
+
+/* Returns TRUE if the name doesn't contain any invalid characters.
+   The create name check can be more strict. */
+bool mailbox_list_is_valid_mask(struct mailbox_list *list, const char *mask);
+bool mailbox_list_is_valid_existing_name(struct mailbox_list *list,
+					 const char *name);
+bool mailbox_list_is_valid_create_name(struct mailbox_list *list,
+				       const char *name);
+
+/* Return full path for the given mailbox name. The name must be a valid
+   existing mailbox name, or NULL to get the root directory.
+   For INDEX=MEMORY it returns "" as the path. */
+const char *mailbox_list_get_path(struct mailbox_list *list, const char *name,
+				  enum mailbox_list_path_type type);
+/* Returns mailbox name status */
+int mailbox_list_get_mailbox_name_status(struct mailbox_list *list,
+					 const char *name,
+					 enum mailbox_name_status *status);
+
+/* Returns a prefix that temporary files should use without conflicting
+   with the namespace. */
+const char *mailbox_list_get_temp_prefix(struct mailbox_list *list);
+
+/* Initialize new mailbox list request. mask may contain '%' and '*'
+   wildcards as defined by RFC-3501. */
+struct mailbox_list_iterate_context *
+mailbox_list_iter_init(struct mailbox_list *list,
+		       const char *ref, const char *mask,
+		       enum mailbox_list_iter_flags flags);
+/* Get next mailbox. Returns the mailbox name */
+struct mailbox_info *
+mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx);
+/* Deinitialize mailbox list request. Returns FALSE if some error
+   occurred while listing. */
+int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **ctx);
+
+/* Subscribe/unsubscribe mailbox. There should be no error when
+   subscribing to already subscribed mailbox. Subscribing to
+   unexisting mailboxes is optional. */
+int mailbox_list_set_subscribed(struct mailbox_list *list,
+				const char *name, bool set);
+
+/* Returns the error message of last occurred error. */
+const char *mailbox_list_get_last_error(struct mailbox_list *list,
+					bool *temporary_error_r);
+
+#endif
--- a/src/lib-storage/mailbox-tree.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/mailbox-tree.h	Thu Nov 16 02:16:31 2006 +0200
@@ -1,14 +1,14 @@
 #ifndef __MAILBOX_TREE_H
 #define __MAILBOX_TREE_H
 
-#include "mail-storage.h"
+#include "mailbox-list.h"
 
 struct mailbox_node {
 	struct mailbox_node *next;
 	struct mailbox_node *children;
 
 	char *name;
-	enum mailbox_flags flags;
+	enum mailbox_info_flags flags;
 };
 
 struct mailbox_tree_context *mailbox_tree_init(char separator);
--- a/src/lib-storage/register/.cvsignore	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/register/.cvsignore	Thu Nov 16 02:16:31 2006 +0200
@@ -7,3 +7,4 @@
 Makefile.in
 so_locations
 mail-storage-register.c
+mailbox-list-register.c
--- a/src/lib-storage/register/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/lib-storage/register/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -3,6 +3,8 @@
 BUILT_SOURCES = mail-storage-register.c
 mail_storages = @mail_storages@
 
+mailbox_list_drivers = maildir fs
+
 mail-storage-register.c: Makefile
 	rm -f $@
 	echo '/* this file automatically generated by Makefile */' >$@
@@ -17,15 +19,30 @@
 	done
 	echo '}' >>$@
 
+mailbox-list-register.c: Makefile
+	rm -f $@
+	echo '/* this file automatically generated by Makefile */' >$@
+	echo '#include "lib.h"' >>$@
+	echo '#include "mailbox-list.h"' >>$@
+	for i in $(mailbox_list_drivers) ; do \
+		echo "extern struct mailbox_list $${i}_mailbox_list;" >>$@ ; \
+	done
+	echo 'void mailbox_list_register_all(void) {' >>$@
+	for i in $(mailbox_list_drivers) ; do \
+		echo "mailbox_list_register(&$${i}_mailbox_list);" >>$@ ; \
+	done
+	echo '}' >>$@
+
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-storage
 
 libstorage_register_a_SOURCES = \
-	mail-storage-register.c
+	mail-storage-register.c \
+	mailbox-list-register.c
 
 DISTFILES = $(DIST_COMMON) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
 distclean-generic:
-	rm -f Makefile mail-storage-register.c
+	rm -f Makefile mail-storage-register.c mailbox-list-register.c
--- a/src/lib-storage/subscription-file/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-noinst_LIBRARIES = libstorage_subscription_file.a
-
-AM_CPPFLAGS = \
-	-I$(top_srcdir)/src/lib \
-	-I$(top_srcdir)/src/lib-storage \
-	-I$(top_srcdir)/src/lib-mail
-
-libstorage_subscription_file_a_SOURCES = \
-	subscription-file.c
-
-noinst_HEADERS = \
-	subscription-file.h
-
--- a/src/lib-storage/subscription-file/subscription-file.c	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-/* Copyright (C) 2002-2003 Timo Sirainen */
-
-#include "lib.h"
-#include "istream.h"
-#include "ostream.h"
-#include "nfs-workarounds.h"
-#include "file-dotlock.h"
-#include "mail-storage-private.h"
-#include "subscription-file.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MAX_MAILBOX_LENGTH PATH_MAX
-
-#define SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
-#define SUBSCRIPTION_FILE_LOCK_TIMEOUT 120
-#define SUBSCRIPTION_FILE_CHANGE_TIMEOUT 30
-
-struct subsfile_list_context {
-	pool_t pool;
-
-	struct mail_storage *storage;
-	struct istream *input;
-	const char *path;
-
-	bool failed;
-};
-
-static void subsfile_set_syscall_error(struct mail_storage *storage,
-				       const char *function, const char *path)
-{
-	i_assert(function != NULL);
-
-	if (errno == EACCES)
-		mail_storage_set_error(storage, "Permission denied");
-	else {
-		mail_storage_set_critical(storage,
-			"%s failed with subscription file %s: %m",
-			function, path);
-	}
-}
-
-static const char *next_line(struct mail_storage *storage, const char *path,
-			     struct istream *input, bool *failed_r,
-			     bool ignore_estale)
-{
-	const char *line;
-
-	*failed_r = FALSE;
-	if (input == NULL)
-		return NULL;
-
-	while ((line = i_stream_next_line(input)) == NULL) {
-                switch (i_stream_read(input)) {
-		case -1:
-                        if (input->stream_errno != 0 &&
-                            (input->stream_errno != ESTALE || !ignore_estale)) {
-                                subsfile_set_syscall_error(storage,
-                                                           "read()", path);
-                                *failed_r = TRUE;
-                        }
-			return NULL;
-		case -2:
-			/* mailbox name too large */
-			mail_storage_set_critical(storage,
-				"Subscription file %s contains lines longer "
-				"than %u characters", path,
-				MAX_MAILBOX_LENGTH);
-			*failed_r = TRUE;
-			return NULL;
-		}
-	}
-
-	return line;
-}
-
-int subsfile_set_subscribed(struct mail_storage *storage, const char *path,
-			    const char *temp_prefix, const char *name, bool set)
-{
-	struct dotlock_settings dotlock_set;
-	struct dotlock *dotlock;
-	const char *line;
-	struct istream *input;
-	struct ostream *output;
-	int fd_in, fd_out;
-	bool found, failed = FALSE;
-
-	if (strcasecmp(name, "INBOX") == 0)
-		name = "INBOX";
-
-	memset(&dotlock_set, 0, sizeof(dotlock_set));
-	dotlock_set.temp_prefix = temp_prefix;
-	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
-	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
-
-	fd_out = file_dotlock_open(&dotlock_set, path, 0, &dotlock);
-	if (fd_out == -1) {
-		if (errno == EAGAIN) {
-			mail_storage_set_error(storage,
-				"Timeout waiting for subscription file lock");
-		} else {
-			subsfile_set_syscall_error(storage,
-						   "file_dotlock_open()", path);
-		}
-		return -1;
-	}
-
-	fd_in = nfs_safe_open(path, O_RDONLY);
-	if (fd_in == -1 && errno != ENOENT) {
-		subsfile_set_syscall_error(storage, "open()", path);
-		(void)file_dotlock_delete(&dotlock);
-		return -1;
-	}
-
-	input = fd_in == -1 ? NULL :
-		i_stream_create_file(fd_in, default_pool,
-				     MAX_MAILBOX_LENGTH, TRUE);
-	output = o_stream_create_file(fd_out, default_pool,
-				      MAX_MAILBOX_LENGTH, FALSE);
-	found = FALSE;
-	while ((line = next_line(storage, path, input,
-				 &failed, FALSE)) != NULL) {
-		if (strcmp(line, name) == 0) {
-			found = TRUE;
-			if (!set)
-				continue;
-		}
-
-		if (o_stream_send_str(output, line) < 0 ||
-		    o_stream_send(output, "\n", 1) < 0) {
-			subsfile_set_syscall_error(storage, "write()",
-						   path);
-			failed = TRUE;
-			break;
-		}
-	}
-
-	if (!failed && set && !found) {
-		/* append subscription */
-		line = t_strconcat(name, "\n", NULL);
-		if (o_stream_send_str(output, line) < 0) {
-			subsfile_set_syscall_error(storage, "write()", path);
-			failed = TRUE;
-		}
-	}
-
-	if (input != NULL)
-		i_stream_destroy(&input);
-	o_stream_destroy(&output);
-
-	if (failed || (set && found) || (!set && !found)) {
-		if (file_dotlock_delete(&dotlock) < 0) {
-			subsfile_set_syscall_error(storage,
-				"file_dotlock_delete()", path);
-			failed = TRUE;
-		}
-	} else {
-		enum dotlock_replace_flags flags =
-			DOTLOCK_REPLACE_FLAG_VERIFY_OWNER;
-		if (file_dotlock_replace(&dotlock, flags) < 0) {
-			subsfile_set_syscall_error(storage,
-				"file_dotlock_replace()", path);
-			failed = TRUE;
-		}
-	}
-	return failed ? -1 : 0;
-}
-
-struct subsfile_list_context *
-subsfile_list_init(struct mail_storage *storage, const char *path)
-{
-	struct subsfile_list_context *ctx;
-	pool_t pool;
-	int fd;
-
-	fd = nfs_safe_open(path, O_RDONLY);
-	if (fd == -1 && errno != ENOENT) {
-		subsfile_set_syscall_error(storage, "open()", path);
-		return NULL;
-	}
-
-	pool = pool_alloconly_create("subsfile_list", MAX_MAILBOX_LENGTH+1024);
-
-	ctx = p_new(pool, struct subsfile_list_context, 1);
-	ctx->pool = pool;
-	ctx->storage = storage;
-	ctx->input = fd == -1 ? NULL :
-		i_stream_create_file(fd, pool, MAX_MAILBOX_LENGTH, TRUE);
-	ctx->path = p_strdup(pool, path);
-	return ctx;
-}
-
-int subsfile_list_deinit(struct subsfile_list_context *ctx)
-{
-	bool failed;
-
-	failed = ctx->failed;
-	if (ctx->input != NULL)
-		i_stream_destroy(&ctx->input);
-	pool_unref(ctx->pool);
-
-	return failed ? -1 : 0;
-}
-
-const char *subsfile_list_next(struct subsfile_list_context *ctx)
-{
-        const char *line;
-        unsigned int i;
-        int fd;
-
-        if (ctx->failed || ctx->input == NULL)
-		return NULL;
-
-        for (i = 0;; i++) {
-                line = next_line(ctx->storage, ctx->path, ctx->input,
-				 &ctx->failed,
-				 i < SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT);
-                if (ctx->input->stream_errno != ESTALE ||
-                    i == SUBSCRIPTION_FILE_ESTALE_RETRY_COUNT)
-                        break;
-
-                /* Reopen the subscription file and re-send everything.
-                   this isn't the optimal behavior, but it's allowed by
-                   IMAP and this way we don't have to read everything into
-                   memory or try to play any guessing games. */
-                i_stream_destroy(&ctx->input);
-
-                fd = nfs_safe_open(ctx->path, O_RDONLY);
-                if (fd == -1) {
-                        /* In case of ENOENT all the subscriptions got lost.
-                           Just return end of subscriptions list in that
-                           case. */
-                        if (errno != ENOENT) {
-                                subsfile_set_syscall_error(ctx->storage,
-                                                           "open()",
-                                                           ctx->path);
-                                ctx->failed = TRUE;
-                        }
-                        return NULL;
-                }
-
-                ctx->input = i_stream_create_file(fd, ctx->pool,
-                                                  MAX_MAILBOX_LENGTH, TRUE);
-        }
-        return line;
-}
--- a/src/lib-storage/subscription-file/subscription-file.h	Wed Nov 15 21:14:08 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#ifndef __SUBSCRIPTION_FILE_H
-#define __SUBSCRIPTION_FILE_H
-
-#include "mail-storage.h"
-
-/* Initialize new subscription file listing. Returns NULL if failed. */
-struct subsfile_list_context *
-subsfile_list_init(struct mail_storage *storage, const char *path);
-
-/* Deinitialize subscription file listing. Returns 0 if ok, or -1 if some
-   error occurred while listing. */
-int subsfile_list_deinit(struct subsfile_list_context *ctx);
-/* Returns the next subscribed mailbox, or NULL. */
-const char *subsfile_list_next(struct subsfile_list_context *ctx);
-
-int subsfile_set_subscribed(struct mail_storage *storage, const char *path,
-			    const char *temp_prefix, const char *name,
-			    bool set);
-
-#endif
--- a/src/plugins/acl/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/acl/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -14,6 +14,7 @@
 	acl-backend-vfile.c \
 	acl-cache.c \
 	acl-mailbox.c \
+	acl-mailbox-list.c \
 	acl-plugin.c \
 	acl-storage.c
 
--- a/src/plugins/acl/acl-plugin.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/acl/acl-plugin.c	Thu Nov 16 02:16:31 2006 +0200
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "mail-storage.h"
+#include "mailbox-list-private.h"
 #include "acl-api.h"
 #include "acl-plugin.h"
 
@@ -11,6 +12,7 @@
 extern void (*hook_mail_storage_created)(struct mail_storage *storage);
 
 void (*acl_next_hook_mail_storage_created)(struct mail_storage *storage);
+void (*acl_next_hook_mailbox_list_created)(struct mailbox_list *list);
 
 void acl_plugin_init(void)
 {
@@ -18,6 +20,9 @@
 		acl_next_hook_mail_storage_created =
 			hook_mail_storage_created;
 		hook_mail_storage_created = acl_mail_storage_created;
+
+		acl_next_hook_mailbox_list_created = hook_mailbox_list_created;
+		hook_mailbox_list_created = acl_mailbox_list_created;
 	}
 }
 
@@ -26,5 +31,7 @@
 	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;
 	}
 }
--- a/src/plugins/acl/acl-plugin.h	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/acl/acl-plugin.h	Thu Nov 16 02:16:31 2006 +0200
@@ -30,12 +30,21 @@
 
 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 unsigned int acl_storage_module_id;
 
 void acl_mail_storage_created(struct mail_storage *storage);
+void acl_mailbox_list_created(struct mailbox_list *list);
 
 struct mailbox *acl_mailbox_open_box(struct mailbox *box);
 
+const char *
+acl_storage_get_parent_mailbox_name(struct mail_storage *storage,
+				    const char *name);
+int acl_storage_have_right(struct mail_storage *storage, const char *name,
+			   unsigned int acl_storage_right_idx, bool *can_see_r);
+void acl_mailbox_list_set_storage(struct mail_storage *storage);
+
 void acl_plugin_init(void);
 void acl_plugin_deinit(void);
 
--- a/src/plugins/acl/acl-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/acl/acl-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -25,9 +25,8 @@
 	MAIL_ACL_ADMIN
 };
 
-static int
-acl_storage_have_right(struct mail_storage *storage, const char *name,
-		       unsigned int acl_storage_right_idx, bool *can_see_r)
+int acl_storage_have_right(struct mail_storage *storage, const char *name,
+			   unsigned int acl_storage_right_idx, bool *can_see_r)
 {
 	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
 	const unsigned int *idx_arr = astorage->acl_storage_right_idx;
@@ -49,8 +48,9 @@
 	return ret;
 }
 
-static const char *
-get_parent_mailbox_name(struct mail_storage *storage, const char *name)
+const char *
+acl_storage_get_parent_mailbox_name(struct mail_storage *storage,
+				    const char *name)
 {
 	const char *p;
 	char sep;
@@ -115,8 +115,8 @@
 
 	t_push();
 	ret = acl_storage_have_right(storage,
-				     get_parent_mailbox_name(storage, name),
-				     ACL_STORAGE_RIGHT_CREATE, NULL);
+			acl_storage_get_parent_mailbox_name(storage, name),
+			ACL_STORAGE_RIGHT_CREATE, NULL);
 	t_pop();
 
 	if (ret <= 0) {
@@ -183,8 +183,8 @@
 	/* and create the new one under the parent mailbox */
 	t_push();
 	ret = acl_storage_have_right(storage,
-				     get_parent_mailbox_name(storage, newname),
-				     ACL_STORAGE_RIGHT_CREATE, NULL);
+			acl_storage_get_parent_mailbox_name(storage, newname),
+			ACL_STORAGE_RIGHT_CREATE, NULL);
 	t_pop();
 
 	if (ret <= 0) {
@@ -201,83 +201,6 @@
 	return astorage->super.mailbox_rename(storage, oldname, newname);
 }
 
-static struct mailbox_list *
-acl_mailbox_list_next(struct mailbox_list_context *ctx)
-{
-	struct acl_mail_storage *astorage = ACL_CONTEXT(ctx->storage);
-	struct mailbox_list *list;
-	int ret;
-
-	for (;;) {
-		list = astorage->super.mailbox_list_next(ctx);
-		if (list == NULL)
-			return NULL;
-
-		ret = acl_storage_have_right(ctx->storage, list->name,
-					     ACL_STORAGE_RIGHT_LOOKUP, NULL);
-		if (ret > 0)
-			return list;
-		if (ret < 0) {
-			ctx->failed = TRUE;
-			return NULL;
-		}
-
-		/* no permission to see this mailbox */
-		if ((ctx->flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-			/* it's subscribed, show it as non-existent */
-			if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0)
-				list->flags = MAILBOX_NONEXISTENT;
-			return list;
-		}
-
-		/* skip to next one */
-	}
-}
-
-static int acl_get_mailbox_name_status(struct mail_storage *storage,
-				       const char *name,
-				       enum mailbox_name_status *status)
-{
-	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
-	int ret;
-
-	ret = acl_storage_have_right(storage, name,
-				     ACL_STORAGE_RIGHT_LOOKUP, NULL);
-	if (ret < 0)
-		return -1;
-
-	if (astorage->super.get_mailbox_name_status(storage, name, status) < 0)
-		return -1;
-	if (ret > 0)
-		return 0;
-
-	/* we shouldn't reveal this mailbox's existance */
-	switch (*status) {
-	case MAILBOX_NAME_EXISTS:
-		*status = MAILBOX_NAME_VALID;
-		break;
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-		break;
-	case MAILBOX_NAME_NOINFERIORS:
-		/* have to check if we are allowed to see the parent */
-		t_push();
-		ret = acl_storage_have_right(storage,
-				get_parent_mailbox_name(storage, name),
-				ACL_STORAGE_RIGHT_LOOKUP, NULL);
-		t_pop();
-
-		if (ret < 0)
-			return -1;
-		if (ret == 0) {
-			/* no permission to see the parent */
-			*status = MAILBOX_NAME_VALID;
-		}
-		break;
-	}
-	return 0;
-}
-
 void acl_mail_storage_created(struct mail_storage *storage)
 {
 	struct acl_mail_storage *astorage;
@@ -301,7 +224,7 @@
 		(storage->flags & MAIL_STORAGE_FLAG_SHARED_NAMESPACE) == 0 ?
 		getenv("USER") : NULL;
 	backend = acl_backend_init(acl_env, storage, user_env, NULL,
-				  owner_username);
+				   owner_username);
 	if (backend == NULL)
 		i_fatal("ACL backend initialization failed");
 
@@ -319,8 +242,8 @@
 	storage->v.mailbox_create = acl_mailbox_create;
 	storage->v.mailbox_delete = acl_mailbox_delete;
 	storage->v.mailbox_rename = acl_mailbox_rename;
-	storage->v.mailbox_list_next = acl_mailbox_list_next;
-	storage->v.get_mailbox_name_status = acl_get_mailbox_name_status;
+
+	acl_mailbox_list_set_storage(storage);
 
 	/* build ACL right lookup table */
 	for (i = 0; i < ACL_STORAGE_RIGHT_COUNT; i++) {
@@ -337,3 +260,4 @@
 	array_idx_set(&storage->module_contexts,
 		      acl_storage_module_id, &astorage);
 }
+
--- a/src/plugins/convert/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/convert/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -29,7 +29,7 @@
 	$(top_builddir)/src/lib-storage/register/libstorage-register.a \
 	$(STORAGE_LIBS) \
 	$(top_builddir)/src/lib-storage/libstorage.a \
-	$(top_builddir)/src/lib-storage/subscription-file/libstorage_subscription_file.a \
+	$(top_builddir)/src/lib-storage/list/libstorage_list.a \
 	$(top_builddir)/src/lib-imap/libimap.a \
 	$(top_builddir)/src/lib-mail/libmail.a \
 	$(top_builddir)/src/lib-charset/libcharset.a \
--- a/src/plugins/convert/convert-storage.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/convert/convert-storage.c	Thu Nov 16 02:16:31 2006 +0200
@@ -100,18 +100,18 @@
 
 static int mailbox_convert_list_item(struct mail_storage *source_storage,
 				     struct mail_storage *dest_storage,
-				     struct mailbox_list *list,
+				     struct mailbox_info *info,
 				     struct dotlock *dotlock)
 {
 	const char *name;
 	struct mailbox *srcbox, *destbox;
 	int ret = 0;
 
-	if ((list->flags & (MAILBOX_NONEXISTENT|MAILBOX_PLACEHOLDER)) != 0)
+	if ((info->flags & (MAILBOX_NONEXISTENT|MAILBOX_PLACEHOLDER)) != 0)
 		return 0;
 
-	name = strcasecmp(list->name, "INBOX") == 0 ? "INBOX" : list->name;
-	if ((list->flags & MAILBOX_NOSELECT) != 0) {
+	name = strcasecmp(info->name, "INBOX") == 0 ? "INBOX" : info->name;
+	if ((info->flags & MAILBOX_NOSELECT) != 0) {
 		if (mail_storage_mailbox_create(dest_storage, name, TRUE) < 0) {
 			i_error("Mailbox conversion: Couldn't create mailbox "
 				"directory %s", name);
@@ -158,15 +158,15 @@
 			     struct mail_storage *dest_storage,
 			     struct dotlock *dotlock)
 {
-	struct mailbox_list_context *iter;
-	struct mailbox_list *list;
+	struct mailbox_list_iterate_context *iter;
+	struct mailbox_info *info;
 	int ret = 0;
 
-	iter = mail_storage_mailbox_list_init(source_storage, "", "*",
-                                              MAILBOX_LIST_FAST_FLAGS);
-	while ((list = mail_storage_mailbox_list_next(iter)) != NULL) {
+	iter = mailbox_list_iter_init(mail_storage_get_list(source_storage),
+				      "", "*", MAILBOX_LIST_ITER_FAST_FLAGS);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
 		if (mailbox_convert_list_item(source_storage, dest_storage,
-					      list, dotlock) < 0) {
+					      info, dotlock) < 0) {
 			ret = -1;
 			break;
 		}
@@ -175,7 +175,7 @@
 		   is done only after 100 mails. */
 		(void)file_dotlock_touch(dotlock);
 	}
-	if (mail_storage_mailbox_list_deinit(&iter) < 0)
+	if (mailbox_list_iter_deinit(&iter) < 0)
 		ret = -1;
 	return ret;
 }
@@ -183,21 +183,23 @@
 static int mailbox_list_copy_subscriptions(struct mail_storage *source_storage,
 					   struct mail_storage *dest_storage)
 {
-	struct mailbox_list_context *iter;
-	struct mailbox_list *list;
+	struct mailbox_list_iterate_context *iter;
+	struct mailbox_info *info;
+	struct mailbox_list *dest_list;
 	int ret = 0;
 
-	iter = mail_storage_mailbox_list_init(source_storage, "", "*",
-					      MAILBOX_LIST_SUBSCRIBED |
-					      MAILBOX_LIST_FAST_FLAGS);
-	while ((list = mail_storage_mailbox_list_next(iter)) != NULL) {
-		if (mail_storage_set_subscribed(dest_storage, list->name,
+	dest_list = mail_storage_get_list(dest_storage);
+	iter = mailbox_list_iter_init(mail_storage_get_list(source_storage),
+				      "", "*", MAILBOX_LIST_ITER_SUBSCRIBED |
+				      MAILBOX_LIST_ITER_FAST_FLAGS);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
+		if (mailbox_list_set_subscribed(dest_list, info->name,
 						TRUE) < 0) {
 			ret = -1;
 			break;
 		}
 	}
-	if (mail_storage_mailbox_list_deinit(&iter) < 0)
+	if (mailbox_list_iter_deinit(&iter) < 0)
 		ret = -1;
 	return ret;
 }
--- a/src/plugins/expire/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/expire/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -33,7 +33,7 @@
 	$(top_builddir)/src/lib-storage/register/libstorage-register.a \
 	$(STORAGE_LIBS) \
 	$(top_builddir)/src/lib-storage/libstorage.a \
-	$(top_builddir)/src/lib-storage/subscription-file/libstorage_subscription_file.a \
+	$(top_builddir)/src/lib-storage/list/libstorage_list.a \
 	$(top_builddir)/src/lib-imap/libimap.a \
 	$(top_builddir)/src/lib-mail/libmail.a \
 	$(top_builddir)/src/lib-dict/libdict.a \
--- a/src/plugins/quota/quota-maildir.c	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/plugins/quota/quota-maildir.c	Thu Nov 16 02:16:31 2006 +0200
@@ -36,8 +36,9 @@
 };
 
 struct maildir_list_context {
-	struct mailbox_list_context *ctx;
-	struct mailbox_list *list;
+	struct mail_storage *storage;
+	struct mailbox_list_iterate_context *iter;
+	struct mailbox_info *info;
 
 	string_t *path;
 	int state;
@@ -131,10 +132,11 @@
 	struct maildir_list_context *ctx;
 
 	ctx = i_new(struct maildir_list_context, 1);
+	ctx->storage = storage;
 	ctx->path = str_new(default_pool, 512);
-	ctx->ctx = mail_storage_mailbox_list_init(storage, "", "*",
-						  MAILBOX_LIST_FAST_FLAGS |
-						  MAILBOX_LIST_INBOX);
+	ctx->iter = mailbox_list_iter_init(mail_storage_get_list(storage),
+					   "", "*",
+					   MAILBOX_LIST_ITER_FAST_FLAGS);
 	return ctx;
 }
 
@@ -147,14 +149,14 @@
 
 	for (;;) {
 		if (ctx->state == 0) {
-			ctx->list = mail_storage_mailbox_list_next(ctx->ctx);
-			if (ctx->list == NULL)
+			ctx->info = mailbox_list_iter_next(ctx->iter);
+			if (ctx->info == NULL)
 				return NULL;
 		}
 
 		t_push();
-		path = mail_storage_get_mailbox_path(ctx->ctx->storage,
-						     ctx->list->name,
+		path = mail_storage_get_mailbox_path(ctx->storage,
+						     ctx->info->name,
 						     &is_file);
 		str_truncate(ctx->path, 0);
 		str_append(ctx->path, path);
@@ -180,7 +182,7 @@
 
 static int maildir_list_deinit(struct maildir_list_context *ctx)
 {
-	int ret = mail_storage_mailbox_list_deinit(&ctx->ctx);
+	int ret = mailbox_list_iter_deinit(&ctx->iter);
 
 	str_free(&ctx->path);
 	i_free(ctx);
--- a/src/pop3/Makefile.am	Wed Nov 15 21:14:08 2006 +0200
+++ b/src/pop3/Makefile.am	Thu Nov 16 02:16:31 2006 +0200
@@ -20,7 +20,7 @@
 	../lib-storage/register/libstorage-register.a \
 	$(STORAGE_LIBS) \
 	../lib-storage/libstorage.a \
-	../lib-storage/subscription-file/libstorage_subscription_file.a \
+	../lib-storage/list/libstorage_list.a \
 	../lib-imap/libimap.a \
 	../lib-mail/libmail.a \
 	../lib-dict/libdict.a \