changeset 5448:beabd433cdae HEAD

Moved delete/rename operations to mailbox_list API. Fixed mbox/maildir to work with either fs/maildir++ directory layout. They can be changed by appending :LAYOUT=fs|maildir++ to mail_location.
author Timo Sirainen <tss@iki.fi>
date Thu, 29 Mar 2007 10:59:11 +0300
parents 04404fd54cf6
children bca896b09c7a
files src/imap/cmd-delete.c src/imap/cmd-rename.c src/lib-storage/index/dbox/dbox-file.c src/lib-storage/index/dbox/dbox-storage.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/dbox/dbox-sync-expunge.c src/lib-storage/index/dbox/dbox-sync-full.c src/lib-storage/index/dbox/dbox-uidlist.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/list/index-mailbox-list-sync.c src/lib-storage/list/mailbox-list-fs-iter.c src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-maildir-iter.c src/lib-storage/list/mailbox-list-maildir.c 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/plugins/acl/acl-mailbox-list.c src/plugins/acl/acl-mailbox.c src/plugins/acl/acl-storage.c src/plugins/lazy-expunge/lazy-expunge-plugin.c src/plugins/mail-log/mail-log-plugin.c src/plugins/quota/quota-plugin.c src/plugins/quota/quota-plugin.h src/plugins/quota/quota-storage.c src/plugins/quota/quota.c
diffstat 31 files changed, 1383 insertions(+), 1011 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/cmd-delete.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/imap/cmd-delete.c	Thu Mar 29 10:59:11 2007 +0300
@@ -7,6 +7,7 @@
 {
 	struct client *client = cmd->client;
 	struct mail_storage *storage;
+	struct mailbox_list *list;
 	struct mailbox *mailbox;
 	const char *name;
 
@@ -41,9 +42,12 @@
 		name = t_strndup(name, strlen(name)-1);
 	}
 
-	if (mail_storage_mailbox_delete(storage, name) < 0)
+	list = mail_storage_get_list(storage);
+	if (mailbox_list_delete_mailbox(list, name) < 0) {
+		mail_storage_set_list_error(storage);
 		client_send_storage_error(cmd, storage);
-	else
+	} else {
 		client_send_tagline(cmd, "OK Delete completed.");
+	}
 	return TRUE;
 }
--- a/src/imap/cmd-rename.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/imap/cmd-rename.c	Thu Mar 29 10:59:11 2007 +0300
@@ -6,6 +6,7 @@
 bool cmd_rename(struct client_command_context *cmd)
 {
 	struct mail_storage *old_storage, *new_storage;
+	struct mailbox_list *list;
 	const char *oldname, *newname;
 
 	/* <old name> <new name> */
@@ -29,10 +30,12 @@
 		return TRUE;
 	}
 
-	if (mail_storage_mailbox_rename(old_storage, oldname, newname) < 0)
+	list = mail_storage_get_list(old_storage);
+	if (mailbox_list_rename_mailbox(list, oldname, newname) < 0) {
+		mail_storage_set_list_error(old_storage);
 		client_send_storage_error(cmd, old_storage);
-	else
+	} else {
 		client_send_tagline(cmd, "OK Rename completed.");
-
+	}
 	return TRUE;
 }
--- a/src/lib-storage/index/dbox/dbox-file.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-file.c	Thu Mar 29 10:59:11 2007 +0300
@@ -117,8 +117,7 @@
 		mbox->file->fd = -1;
 
 		mbox->file->path =
-			i_strdup_printf("%s/"DBOX_MAILDIR_NAME"/"
-					DBOX_MAIL_FILE_FORMAT,
+			i_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
 					mbox->path, file_seq);
 	}
 
--- a/src/lib-storage/index/dbox/dbox-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -1,6 +1,7 @@
 /* Copyright (C) 2005-2007 Timo Sirainen */
 
 #include "lib.h"
+#include "array.h"
 #include "ioloop.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
@@ -21,6 +22,10 @@
 /* How often to touch the uidlist lock file when using KEEP_LOCKED flag */
 #define DBOX_LOCK_TOUCH_MSECS (10*1000)
 
+#define DBOX_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					dbox_mailbox_list_module_id))
+
 const struct dotlock_settings default_uidlist_dotlock_set = {
 	MEMBER(temp_prefix) NULL,
 	MEMBER(lock_suffix) NULL,
@@ -63,17 +68,51 @@
 extern struct mail_storage dbox_storage;
 extern struct mailbox dbox_mailbox;
 
-static bool dbox_handle_errors(struct mail_storage *storage)
+static unsigned int dbox_mailbox_list_module_id = 0;
+
+static int
+dbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
+static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags);
+
+static bool
+dbox_storage_is_valid_existing_name(struct mailbox_list *list, const char *name)
 {
-	if (ENOACCESS(errno))
-		mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION);
-	else if (ENOSPACE(errno))
-		mail_storage_set_error(storage, "Not enough disk space");
-	else if (ENOTFOUND(errno))
-		mail_storage_set_error(storage, "Directory structure is broken");
-	else
+	struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
+	const char *p;
+
+	if (!storage->list_super.is_valid_existing_name(list, name))
 		return FALSE;
-	return TRUE;
+
+	/* Don't allow the mailbox name to end in dbox-Mails */
+	p = strrchr(name, '/');
+	if (p != NULL)
+		name = p + 1;
+	return strcmp(name, DBOX_MAILDIR_NAME) != 0;
+}
+
+static bool
+dbox_storage_is_valid_create_name(struct mailbox_list *list, const char *name)
+{
+	struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
+	const char *const *tmp;
+	bool ret = TRUE;
+
+	if (!storage->list_super.is_valid_create_name(list, name))
+		return FALSE;
+
+	/* Don't allow creating mailboxes under dbox-Mails */
+	t_push();
+	for (tmp = t_strsplit(name, "/"); *tmp != NULL; tmp++) {
+		if (strcmp(*tmp, DBOX_MAILDIR_NAME) == 0) {
+			ret = FALSE;
+			break;
+		}
+	}
+	t_pop();
+	return ret;
 }
 
 static int
@@ -86,7 +125,7 @@
 
 	memset(list_set, 0, sizeof(*list_set));
 	list_set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
-	list_set->maildir_name = "";
+	list_set->maildir_name = DBOX_MAILDIR_NAME;
 
 	if (data == NULL || *data == '\0') {
 		/* we won't do any guessing for this format. */
@@ -162,12 +201,19 @@
 
 	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;
 	}
+	storage->list_super = list->v;
+	list->v.is_valid_existing_name = dbox_storage_is_valid_existing_name;
+	list->v.is_valid_create_name = dbox_storage_is_valid_create_name;
+	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
+	list->v.delete_mailbox = dbox_list_delete_mailbox;
+
+	array_idx_set(&list->module_contexts,
+		      dbox_mailbox_list_module_id, &storage);
 
 	storage->uidlist_dotlock_set = default_uidlist_dotlock_set;
 	storage->file_dotlock_set = default_file_dotlock_set;
@@ -204,7 +250,7 @@
 
 	data = t_strcut(data, ':');
 
-	path = t_strconcat(data, "/inbox/"DBOX_MAILDIR_NAME, NULL);
+	path = t_strconcat(data, "/INBOX/"DBOX_MAILDIR_NAME, NULL);
 	if (stat(path, &st) < 0) {
 		if (debug)
 			i_info("dbox autodetect: stat(%s) failed: %m", path);
@@ -221,9 +267,13 @@
 
 static int create_dbox(struct mail_storage *storage, const char *path)
 {
+	const char *error;
+
 	if (mkdir_parents(path, CREATE_MODE) < 0 && errno != EEXIST) {
-		if (dbox_handle_errors(storage))
+		if (mail_storage_errno2str(&error)) {
+			mail_storage_set_error(storage, "%s", error);
 			return -1;
+		}
 
 		mail_storage_set_critical(storage, "mkdir(%s) failed: %m",
 					  path);
@@ -367,7 +417,7 @@
 		return dbox_open(storage, name, flags);
 	} else if (errno == ENOENT) {
 		mail_storage_set_error(_storage,
-			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
 		return NULL;
 	} else {
 		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
@@ -400,34 +450,30 @@
 	return create_dbox(_storage, path);
 }
 
-static int dbox_mailbox_delete(struct mail_storage *_storage,
-			       const char *name)
+static int
+dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
-	const char *path, *mail_path;
+	struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
 	struct stat st;
-
-	mail_storage_clear_error(_storage);
+	const char *path, *mail_path, *error;
 
-	if (strcmp(name, "INBOX") == 0) {
-		mail_storage_set_error(_storage, "INBOX can't be deleted.");
+	/* make sure the indexes are closed before trying to delete the
+	   directory that contains them */
+	index_storage_destroy_unrefed();
+
+	/* delete the index and control directories */
+	if (storage->list_super.delete_mailbox(list, name) < 0)
 		return -1;
-	}
 
-	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
-		mail_storage_set_error(_storage, "Invalid mailbox name");
-		return -1;
-	}
-
-	path = mailbox_list_get_path(_storage->list, name,
-				     MAILBOX_LIST_PATH_TYPE_DIR);
-	mail_path = mailbox_list_get_path(_storage->list, name,
+	path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
+	mail_path = mailbox_list_get_path(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);
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
 			return -1;
 		}
 
@@ -436,23 +482,23 @@
 			return 0;
 
 		if (errno == ENOTEMPTY) {
-			mail_storage_set_error(_storage,
-				"Mailbox has only submailboxes: %s", name);
+			mailbox_list_set_error(list, t_strdup_printf(
+				"Directory %s isn't empty, can't delete it.",
+				name));
 		} else {
-			mail_storage_set_critical(_storage,
+			mailbox_list_set_critical(list,
 				"rmdir() failed for %s: %m", path);
 		}
 
 		return -1;
 	}
 
-	/* make sure the indexes are closed before trying to delete the
-	   directory that contains them */
-	index_storage_destroy_unrefed();
 
 	if (unlink_directory(mail_path, TRUE) < 0) {
-		if (!dbox_handle_errors(_storage)) {
-			mail_storage_set_critical(_storage,
+		if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
 				"unlink_directory() failed for %s: %m",
 				mail_path);
 		}
@@ -467,79 +513,12 @@
 			break;
 
 		name = t_strdup_until(name, p);
-		path = mailbox_list_get_path(_storage->list, name,
+		path = mailbox_list_get_path(list, name,
 					     MAILBOX_LIST_PATH_TYPE_DIR);
 	}
 	return 0;
 }
 
-static int dbox_mailbox_rename(struct mail_storage *_storage,
-			       const char *oldname, const char *newname)
-{
-	const char *oldpath, *newpath, *p;
-	struct stat st;
-
-	mail_storage_clear_error(_storage);
-
-	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 = 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))
-				return -1;
-
-			mail_storage_set_critical(_storage,
-				"mkdir_parents(%s) failed: %m", p);
-			return -1;
-		}
-	}
-
-	/* first check that the destination mailbox doesn't exist.
-	   this is racy, but we need to be atomic and there's hardly any
-	   possibility that someone actually tries to rename two mailboxes
-	   to same new one */
-	if (lstat(newpath, &st) == 0) {
-		mail_storage_set_error(_storage,
-				       "Target mailbox already exists");
-		return -1;
-	} else if (errno == ENOTDIR) {
-		mail_storage_set_error(_storage,
-			"Target mailbox doesn't allow inferior mailboxes");
-		return -1;
-	} else if (errno != ENOENT && errno != EACCES) {
-		mail_storage_set_critical(_storage, "lstat(%s) failed: %m",
-					  newpath);
-		return -1;
-	}
-
-	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
-	   the next time it's needed. */
-	if (rename(oldpath, newpath) < 0) {
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-		} else if (!dbox_handle_errors(_storage)) {
-			mail_storage_set_critical(_storage,
-				"rename(%s, %s) failed: %m", oldpath, newpath);
-		}
-		return -1;
-	}
-
-	return 0;
-}
-
 static int dbox_storage_close(struct mailbox *box)
 {
 	struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
@@ -571,18 +550,15 @@
 		return;
 	}
 
-	index_mailbox_check_add(&mbox->ibox,
-		t_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME, NULL));
+	index_mailbox_check_add(&mbox->ibox, mbox->path);
 }
 
-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)
+static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags)
 {
-	const char *path, *mail_path;
-	size_t len;
+	const char *mail_path;
 	struct stat st;
 	int ret = 1;
 
@@ -591,18 +567,11 @@
 		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) {
+	    (ctx->flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
 		/* it's a file */
 		*flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
 		return 0;
@@ -610,8 +579,7 @@
 
 	/* need to stat() then */
 	t_push();
-	path = t_strconcat(dir, "/", fname, NULL);
-	mail_path = t_strconcat(path, "/"DBOX_MAILDIR_NAME, NULL);
+	mail_path = t_strconcat(dir, "/", fname, "/"DBOX_MAILDIR_NAME, NULL);
 
 	if (stat(mail_path, &st) == 0) {
 		if (!S_ISDIR(st.st_mode)) {
@@ -621,17 +589,9 @@
 		}
 	} else {
 		/* non-selectable, but may contain subdirs */
+		if (errno != ENOTDIR)
+			*flags |= MAILBOX_CHILDREN;
 		*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();
 
@@ -640,6 +600,7 @@
 
 static void dbox_class_init(void)
 {
+	dbox_mailbox_list_module_id = mailbox_list_module_id++;
 	dbox_transaction_class_init();
 }
 
@@ -661,9 +622,6 @@
 		index_storage_set_callbacks,
 		dbox_mailbox_open,
 		dbox_mailbox_create,
-		dbox_mailbox_delete,
-		dbox_mailbox_rename,
-		dbox_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/index/dbox/dbox-storage.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Thu Mar 29 10:59:11 2007 +0300
@@ -2,6 +2,7 @@
 #define __DBOX_STORAGE_H
 
 #include "index-storage.h"
+#include "mailbox-list-private.h"
 #include "dbox-format.h"
 
 #define DBOX_STORAGE_NAME "dbox"
@@ -14,6 +15,7 @@
 
 struct dbox_storage {
 	struct index_storage storage;
+	struct mailbox_list_vfuncs list_super;
 
 	struct dotlock_settings uidlist_dotlock_set;
 	struct dotlock_settings file_dotlock_set;
--- a/src/lib-storage/index/dbox/dbox-sync-expunge.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-sync-expunge.c	Thu Mar 29 10:59:11 2007 +0300
@@ -116,8 +116,7 @@
 	file_seq = dbox_uidlist_get_new_file_seq(mbox->uidlist);
 
 	for (;; file_seq++) {
-		path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/"
-				       DBOX_MAIL_FILE_FORMAT,
+		path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
 				       mbox->path, file_seq);
 		fd = file_dotlock_open(&mbox->storage->new_file_dotlock_set,
 				       path, DOTLOCK_CREATE_FLAG_NONBLOCK,
@@ -352,8 +351,7 @@
 
 	if (!skipped_expunges) {
 		/* all mails expunged from file, unlink it. */
-		path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/"
-				       DBOX_MAIL_FILE_FORMAT,
+		path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
 				       mbox->path, entry->file_seq);
 		if (unlink(path) < 0) {
 			mail_storage_set_critical(STORAGE(mbox->storage),
@@ -469,8 +467,7 @@
 	if (ctx->dotlock_failed_file_seq != sync_entry->file_seq) {
 		/* we need to have the file locked in case another process is
 		   appending there already. */
-		path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/"
-				       DBOX_MAIL_FILE_FORMAT,
+		path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
 				       mbox->path, sync_entry->file_seq);
 		ret = file_dotlock_create(&mbox->storage->new_file_dotlock_set,
 					  path, DOTLOCK_CREATE_FLAG_NONBLOCK,
--- a/src/lib-storage/index/dbox/dbox-sync-full.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-sync-full.c	Thu Mar 29 10:59:11 2007 +0300
@@ -193,7 +193,6 @@
 	struct dbox_mailbox *mbox = ctx->mbox;
 	const struct mail_index_header *hdr;
 	unsigned int file_prefix_len = strlen(DBOX_MAIL_FILE_PREFIX);
-	const char *path;
 	uint32_t file_seq;
 	DIR *dirp;
 	struct dirent *dp;
@@ -201,11 +200,10 @@
 
 	/* go through msg.* files, sync them to index and based on it
 	   write dbox's index file */
-	path = t_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME, NULL);
-	dirp = opendir(path);
+	dirp = opendir(mbox->path);
 	if (dirp == NULL) {
 		mail_storage_set_critical(STORAGE(mbox->storage),
-					  "opendir(%s) failed: %m", path);
+					  "opendir(%s) failed: %m", mbox->path);
 		return -1;
 	}
 
@@ -231,7 +229,7 @@
 	}
 	if (closedir(dirp) < 0) {
 		mail_storage_set_critical(STORAGE(mbox->storage),
-					  "closedir(%s) failed: %m", path);
+			"closedir(%s) failed: %m", mbox->path);
 		ret = -1;
 	}
 
--- a/src/lib-storage/index/dbox/dbox-uidlist.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-uidlist.c	Thu Mar 29 10:59:11 2007 +0300
@@ -93,9 +93,7 @@
 	uidlist->lock_fd = -1;
 	uidlist->entry_pool =
 		pool_alloconly_create("uidlist entry pool", 1024*32);
-	uidlist->path =
-		i_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME"/"
-			    DBOX_UIDLIST_FILENAME, NULL);
+	uidlist->path = i_strconcat(mbox->path, "/"DBOX_UIDLIST_FILENAME, NULL);
 	i_array_init(&uidlist->entries, 64);
 	return uidlist;
 }
@@ -1054,8 +1052,8 @@
 
 		/* try locking the file. */
 		str_truncate(path, 0);
-		str_printfa(path, "%s/"DBOX_MAILDIR_NAME"/"
-			    DBOX_MAIL_FILE_FORMAT, mbox->path, file_seq);
+		str_printfa(path, "%s/"DBOX_MAIL_FILE_FORMAT,
+			    mbox->path, file_seq);
 		ret = file_dotlock_create(&mbox->storage->file_dotlock_set,
 					  str_c(path),
 					  DOTLOCK_CREATE_FLAG_NONBLOCK,
--- a/src/lib-storage/index/maildir/maildir-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "array.h"
 #include "hostpid.h"
+#include "str.h"
 #include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
@@ -15,6 +16,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <dirent.h>
 #include <unistd.h>
 #include <sys/stat.h>
 
@@ -23,6 +25,10 @@
 #define MAILDIR_PLUSPLUS_DRIVER_NAME "maildir++"
 #define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
 
+#define MAILDIR_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					maildir_mailbox_list_module_id))
+
 struct rename_context {
 	bool found;
 	size_t oldnamelen;
@@ -32,8 +38,26 @@
 extern struct mail_storage maildir_storage;
 extern struct mailbox maildir_mailbox;
 
+static unsigned int maildir_mailbox_list_module_id = 0;
+
 static int verify_inbox(struct mail_storage *storage,
 			enum mailbox_open_flags *flags);
+static int
+maildir_list_delete_mailbox(struct mailbox_list *list, const char *name);
+static int
+maildir_list_rename_mailbox(struct mailbox_list *list,
+			    const char *oldname, const char *newname);
+static int
+maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
+			     	__attr_unused__,
+			     const char *dir, const char *fname,
+			     enum mailbox_list_file_type type,
+			     enum mailbox_info_flags *flags_r);
+static int
+maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				const char *dir, const char *fname,
+				enum mailbox_list_file_type type,
+				enum mailbox_info_flags *flags_r);
 
 static const char *strip_tail_slash(const char *path)
 {
@@ -52,11 +76,14 @@
 
 static int
 maildir_get_list_settings(struct mailbox_list_settings *list_set,
-			  const char *data, enum mail_storage_flags flags)
+			  const char *data, enum mail_storage_flags flags,
+			  const char **layout_r)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 	const char *home, *path, *p;
 
+	*layout_r = MAILDIR_PLUSPLUS_DRIVER_NAME;
+
 	memset(list_set, 0, sizeof(*list_set));
 	list_set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME;
 	list_set->maildir_name = "";
@@ -114,6 +141,9 @@
 				} else if (strncmp(p, "CONTROL=", 8) == 0) {
 					list_set->control_dir =
 						strip_tail_slash_and_cut(p+8);
+				} else if (strncmp(p, "LAYOUT=", 7) == 0) {
+					*layout_r =
+						strip_tail_slash_and_cut(p+7);
 				}
 				p = strchr(p, ':');
 			} while (p != NULL);
@@ -126,6 +156,8 @@
 		return -1;
 	}
 	list_set->root_dir = strip_tail_slash(list_set->root_dir);
+	if (list_set->inbox_path == NULL)
+		list_set->inbox_path = list_set->root_dir;
 
 	if (list_set->index_dir != NULL &&
 	    strcmp(list_set->index_dir, "MEMORY") == 0)
@@ -133,6 +165,51 @@
 	return 0;
 }
 
+static bool maildir_is_internal_name(const char *name)
+{
+	return strcmp(name, "cur") == 0 ||
+		strcmp(name, "new") == 0 ||
+		strcmp(name, "tmp") == 0;
+}
+
+static bool maildir_storage_is_valid_existing_name(struct mailbox_list *list,
+						   const char *name)
+{
+	struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list);
+	const char *p;
+
+	if (!storage->list_super.is_valid_existing_name(list, name))
+		return FALSE;
+
+	/* Don't allow the mailbox name to end in cur/new/tmp */
+	p = strrchr(name, '/');
+	if (p != NULL)
+		name = p + 1;
+	return !maildir_is_internal_name(name);
+}
+
+static bool maildir_storage_is_valid_create_name(struct mailbox_list *list,
+						 const char *name)
+{
+	struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list);
+	const char *const *tmp;
+	bool ret = TRUE;
+
+	if (!storage->list_super.is_valid_create_name(list, name))
+		return FALSE;
+
+	/* Don't allow creating mailboxes under cur/new/tmp */
+	t_push();
+	for (tmp = t_strsplit(name, "/"); *tmp != NULL; tmp++) {
+		if (maildir_is_internal_name(*tmp)) {
+			ret = FALSE;
+			break;
+		}
+	}
+	t_pop();
+	return ret;
+}
+
 static struct mail_storage *
 maildir_create(const char *data, const char *user,
 	       enum mail_storage_flags flags,
@@ -143,11 +220,11 @@
 	struct mailbox_list_settings list_set;
 	struct mailbox_list *list;
 	enum mailbox_open_flags open_flags;
-	const char *error;
+	const char *layout, *error;
 	struct stat st;
 	pool_t pool;
 
-	if (maildir_get_list_settings(&list_set, data, flags) < 0)
+	if (maildir_get_list_settings(&list_set, data, flags, &layout) < 0)
 		return NULL;
 	list_set.mail_storage_flags = &flags;
 	list_set.lock_method = &lock_method;
@@ -166,14 +243,28 @@
 	pool = pool_alloconly_create("storage", 512+256);
 	storage = p_new(pool, struct maildir_storage, 1);
 
-	if (mailbox_list_init("maildir++", &list_set,
+	if (mailbox_list_init(layout, &list_set,
 			      mail_storage_get_list_flags(flags),
-			      mailbox_storage_list_is_mailbox, storage,
 			      &list, &error) < 0) {
-		i_error("maildir++: %s", error);
+		i_error("maildir %s: %s", layout, error);
 		pool_unref(pool);
 		return NULL;
 	}
+	storage->list_super = list->v;
+	if (strcmp(layout, MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) {
+		list->v.iter_is_mailbox = maildirplusplus_iter_is_mailbox;
+	} else {
+		list->v.is_valid_existing_name =
+			maildir_storage_is_valid_existing_name;
+		list->v.is_valid_create_name =
+			maildir_storage_is_valid_create_name;
+		list->v.iter_is_mailbox = maildir_list_iter_is_mailbox;
+	}
+	list->v.delete_mailbox = maildir_list_delete_mailbox;
+	list->v.rename_mailbox = maildir_list_rename_mailbox;
+
+	array_idx_set(&list->module_contexts,
+		      maildir_mailbox_list_module_id, &storage);
 
 	storage->copy_with_hardlinks =
 		getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
@@ -231,27 +322,6 @@
 	return TRUE;
 }
 
-static const char *
-maildir_get_unlink_dest(struct mail_storage *storage, const char *name)
-{
-	const char *root_dir;
-
-	if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
-	    (*name == '/' || *name == '~'))
-		return NULL;
-
-	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 int mkdir_verify(struct mail_storage *storage,
 			const char *dir, bool verify)
 {
@@ -510,7 +580,7 @@
 		return maildir_open(storage, name, flags);
 	} else if (errno == ENOENT) {
 		mail_storage_set_error(_storage,
-			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
 		return NULL;
 	} else {
 		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
@@ -608,33 +678,117 @@
 	return 0;
 }
 
-static int maildir_mailbox_delete(struct mail_storage *_storage,
-				  const char *name)
+static const char *
+maildir_get_unlink_dest(struct mailbox_list *list, const char *name)
 {
-	struct stat st;
-	const char *src, *dest, *index_dir, *control_dir;
-	int count;
+	const char *root_dir;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+	    (*name == '/' || *name == '~'))
+		return NULL;
+
+	if (strcmp(mailbox_list_get_driver_name(list),
+		   MAILDIR_PLUSPLUS_DRIVER_NAME) != 0) {
+		/* Not maildir++ driver. Don't use this trick. */
+		return NULL;
+	}
+
+	root_dir = mailbox_list_get_path(list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	return t_strdup_printf("%s/%c"MAILDIR_UNLINK_DIRNAME, root_dir,
+			       mailbox_list_get_hierarchy_sep(list));
+}
 
-	mail_storage_clear_error(_storage);
+static int
+maildir_delete_nonrecursive(struct mailbox_list *list, const char *path,
+			    const char *name)
+{
+	DIR *dir;
+	struct dirent *d;
+	string_t *full_path;
+	unsigned int dir_len;
+	bool unlinked_something = FALSE;
 
-	if (strcmp(name, "INBOX") == 0) {
-		mail_storage_set_error(_storage, "INBOX can't be deleted.");
+	dir = opendir(path);
+	if (dir == NULL) {
+		if (errno == ENOENT) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else {
+			mailbox_list_set_critical(list,
+				"opendir(%s) failed: %m", path);
+		}
 		return -1;
 	}
 
-	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
-		mail_storage_set_error(_storage, "Invalid mailbox name");
+	full_path = t_str_new(256);
+	str_append(full_path, path);
+	str_append_c(full_path, '/');
+	dir_len = str_len(full_path);
+
+	errno = 0;
+	while ((d = readdir(dir)) != NULL) {
+		if (d->d_name[0] == '.') {
+			/* skip . and .. */
+			if (d->d_name[1] == '\0')
+				continue;
+			if (d->d_name[1] == '.' && d->d_name[2] == '\0')
+				continue;
+		}
+
+		str_truncate(full_path, dir_len);
+		str_append(full_path, d->d_name);
+
+		if (maildir_is_internal_name(d->d_name)) {
+			if (unlink_directory(str_c(full_path), TRUE) < 0) {
+				mailbox_list_set_critical(list,
+					"unlink_directory(%s) failed: %m",
+					str_c(full_path));
+			} else {
+				unlinked_something = TRUE;
+			}
+			continue;
+		}
+
+		/* trying to unlink() a directory gives either EPERM or EISDIR
+		   (non-POSIX). it doesn't really work anywhere in practise,
+		   so don't bother stat()ing the file first */
+		if (unlink(str_c(full_path)) == 0)
+			unlinked_something = TRUE;
+		else if (errno != ENOENT && errno != EISDIR && errno != EPERM) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m",
+				str_c(full_path));
+		}
+	}
+
+	if (closedir(dir) < 0) {
+		mailbox_list_set_critical(list, "closedir(%s) failed: %m",
+					  path);
+	}
+
+	if (rmdir(path) == 0)
+		unlinked_something = TRUE;
+	else if (errno != ENOENT && errno != ENOTEMPTY) {
+		mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
 		return -1;
 	}
 
-	/* 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);
+	if (!unlinked_something) {
+		mailbox_list_set_error(list, t_strdup_printf(
+			"Directory %s isn't empty, can't delete it.", name));
 		return -1;
 	}
+	return 0;
+}
+
+static int
+maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list);
+	struct stat st;
+	const char *src, *dest;
+	int count;
 
 	/* Make sure the indexes are closed before trying to delete the
 	   directory that contains them. It can still fail with some NFS
@@ -642,241 +796,84 @@
 	   that can't really be helped. */
 	index_storage_destroy_unrefed();
 
-	/* 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 != '~');
+	/* delete the index and control directories */
+	if (storage->list_super.delete_mailbox(list, name) < 0)
+		return -1;
+
+	/* check if the mailbox actually exists */
+	src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (stat(src, &st) != 0 && errno == ENOENT) {
+		mailbox_list_set_error(list, t_strdup_printf(
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		return -1;
+	}
 
-		if (unlink_directory(index_dir, TRUE) < 0 &&
-		    errno != ENOTEMPTY) {
-			mail_storage_set_critical(_storage,
-				"unlink_directory(%s) failed: %m", index_dir);
+	dest = maildir_get_unlink_dest(list, name);
+	if (dest == NULL) {
+		/* delete the directory directly without any renaming */
+		return maildir_delete_nonrecursive(list, src, name);
+	}
+
+	/* 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) {
+		if (errno == ENOENT) {
+			/* it was just deleted under us by
+			   another process */
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+			return -1;
+		}
+		if (!EDESTDIREXISTS(errno)) {
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", src, dest);
 			return -1;
 		}
+
+		/* already existed, delete it and try again */
+		if (unlink_directory(dest, TRUE) < 0) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m", dest);
+			return -1;
+		}
+		count++;
 	}
-	control_dir = mailbox_list_get_path(_storage->list, name,
-					    MAILBOX_LIST_PATH_TYPE_CONTROL);
-	if (strcmp(control_dir, src) != 0 &&
-	    strcmp(control_dir, index_dir) != 0) {
-		i_assert(*name != '/' && *name != '~');
+
+	if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
+		mailbox_list_set_critical(list,
+			"unlink_directory(%s) failed: %m", dest);
+
+		/* it's already renamed to ..dir, which means it's
+		   deleted as far as the client is concerned. Report
+		   success. */
+	}
+	return 0;
+}
 
-		if (unlink_directory(control_dir, TRUE) < 0 &&
-		    errno != ENOTEMPTY) {
-			mail_storage_set_critical(_storage,
-				"unlink_directory(%s) failed: %m", control_dir);
+static int maildir_list_rename_mailbox(struct mailbox_list *list,
+				       const char *oldname, const char *newname)
+{
+	struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list);
+	const char *path1, *path2;
+
+	if (strcmp(oldname, "INBOX") == 0) {
+		/* INBOX often exists as the root ~/Maildir.
+		   We can't rename it then. */
+		path1 = mailbox_list_get_path(list, oldname,
+					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		path2 = mailbox_list_get_path(list, NULL,
+					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		if (strcmp(path1, path2) == 0) {
+			mailbox_list_set_error(list,
+				"Renaming INBOX isn't supported.");
 			return -1;
 		}
 	}
 
-	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) {
-			if (errno == ENOENT) {
-				/* it was just deleted under us by
-				   another process */
-				mail_storage_set_error(_storage,
-					MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND,
-					name);
-				return -1;
-			}
-			if (!EDESTDIREXISTS(errno)) {
-				mail_storage_set_critical(_storage,
-					"rename(%s, %s) failed: %m", src, dest);
-				return -1;
-			}
-
-			/* already existed, delete it and try again */
-			if (unlink_directory(dest, TRUE) < 0) {
-				mail_storage_set_critical(_storage,
-					"unlink_directory(%s) failed: %m",
-					dest);
-				return -1;
-			}
-			count++;
-		}
-	}
-
-	if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
-		mail_storage_set_critical(_storage,
-			"unlink_directory(%s) failed: %m", dest);
-
-		/* it's already renamed to ..dir, which means it's deleted
-		   as far as the client is concerned. Report success. */
-	}
-
-	return 0;
-}
-
-static int rename_dir(struct mail_storage *storage,
-		      enum mailbox_list_path_type type,
-		      const char *oldname, const char *newname)
-{
-	const char *oldpath, *newpath;
-
-	oldpath = mailbox_list_get_path(storage->list, oldname, type);
-	newpath = mailbox_list_get_path(storage->list, newname, type);
-	if (strcmp(oldpath, newpath) == 0)
-		return 0;
-
-	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
-		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
-					  oldpath, newpath);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int rename_subfolders(struct mail_storage *storage,
-			     const char *oldname, const char *newname)
-{
-	struct mailbox_list_iterate_context *iter;
-        struct mailbox_info *info;
-	ARRAY_DEFINE(names_arr, const char *);
-	const char *mask, *oldpath, *newpath, *old_listname, *new_listname;
-	const char *const *names;
-	unsigned int i, count;
-	size_t oldnamelen;
-	pool_t pool;
-	int ret;
-
-	ret = 0;
-	oldnamelen = strlen(oldname);
-
-	/* first get a list of the subfolders and save them to memory, because
-	   we can't rely on readdir() not skipping files while the directory
-	   is being modified. this doesn't protect against modifications by
-	   other processes though. */
-	pool = pool_alloconly_create("Maildir subfolders list", 1024);
-	i_array_init(&names_arr, 64);
-
-	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(info->name));
-
-		name = p_strdup(pool, info->name + oldnamelen);
-		array_append(&names_arr, &name, 1);
-	}
-	if (mailbox_list_iter_deinit(&iter) < 0) {
-		ret = -1;
-		names = NULL; count = 0;
-	} else {
-		names = array_get(&names_arr, &count);
-	}
-
-	for (i = 0; i < count; i++) {
-		t_push();
-
-		old_listname = t_strconcat(oldname, names[i], NULL);
-		new_listname = t_strconcat(newname, names[i], NULL);
-		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
-		   but I'm not sure if it's worth it. It could be even
-		   considered as a feature.
-
-		   Anyway, the bug with merging is that if both folders have
-		   identically named subfolder they conflict. Just ignore those
-		   and leave them under the old folder. */
-		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
-			ret = 1;
-		else {
-			mail_storage_set_critical(storage,
-				"rename(%s, %s) failed: %m", oldpath, newpath);
-			ret = -1;
-			t_pop();
-			break;
-		}
-
-		(void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_CONTROL,
-				 old_listname, new_listname);
-		(void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_INDEX,
-				 old_listname, new_listname);
-		t_pop();
-	}
-	array_free(&names_arr);
-	pool_unref(pool);
-
-	return ret;
-}
-
-static int maildir_mailbox_rename(struct mail_storage *_storage,
-				  const char *oldname, const char *newname)
-{
-	const char *oldpath, *newpath;
-	int ret;
-        bool found;
-
-	mail_storage_clear_error(_storage);
-
-	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;
-	}
-
-	if (strcmp(oldname, "INBOX") == 0) {
-		mail_storage_set_error(_storage,
-				       "Renaming INBOX isn't supported.");
-		return -1;
-	}
-
-	/* NOTE: it's possible to rename a nonexisting folder which has
-	   subfolders. In that case we should ignore the rename() error. */
-	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_dir(_storage, MAILBOX_LIST_PATH_TYPE_CONTROL,
-				 oldname, newname);
-		(void)rename_dir(_storage, MAILBOX_LIST_PATH_TYPE_INDEX,
-				 oldname, newname);
-
-		found = ret == 0;
-		ret = rename_subfolders(_storage, oldname, newname);
-		if (ret < 0)
-			return -1;
-		if (!found && ret == 0) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-			return -1;
-		}
-
-		return 0;
-	}
-
-	if (EDESTDIREXISTS(errno)) {
-		mail_storage_set_error(_storage,
-				       "Target mailbox already exists");
-		return -1;
-	} else {
-		mail_storage_set_critical(_storage, "rename(%s, %s) failed: %m",
-					  oldpath, newpath);
-		return -1;
-	}
+	return storage->list_super.rename_mailbox(list, oldname, newname);
 }
 
 static int maildir_storage_close(struct mailbox *box)
@@ -922,20 +919,71 @@
 }
 
 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)
+maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
+			     	__attr_unused__,
+			     const char *dir, const char *fname,
+			     enum mailbox_list_file_type type,
+			     enum mailbox_info_flags *flags_r)
 {
-	struct maildir_storage *storage = (struct maildir_storage *)_storage;
+	struct stat st;
+	const char *path;
+	int ret;
+
+	if (maildir_is_internal_name(fname)) {
+		*flags_r = MAILBOX_NONEXISTENT;
+		return 0;
+	}
+
+	switch (type) {
+	case MAILBOX_LIST_FILE_TYPE_FILE:
+	case MAILBOX_LIST_FILE_TYPE_OTHER:
+		/* non-directories are not */
+		*flags_r = MAILBOX_NOSELECT;
+		return 0;
+
+	case MAILBOX_LIST_FILE_TYPE_DIR:
+	case MAILBOX_LIST_FILE_TYPE_UNKNOWN:
+	case MAILBOX_LIST_FILE_TYPE_SYMLINK:
+		break;
+	}
+
+	t_push();
+	path = t_strdup_printf("%s/%s", dir, fname);
+	if (stat(path, &st) == 0) {
+		if (S_ISDIR(st.st_mode))
+			ret = 1;
+		else if (strncmp(fname, ".nfs", 4) == 0) {
+			/* temporary NFS file */
+			*flags_r = MAILBOX_NONEXISTENT;
+			ret = 0;
+		} else {
+			*flags_r = MAILBOX_NOSELECT;
+			ret = 0;
+		}
+	} else if (errno == ENOENT) {
+		/* this was a directory. maybe it has children. */
+		*flags_r = MAILBOX_NOSELECT;
+		ret = 1;
+	} else {
+		*flags_r = MAILBOX_NOSELECT;
+		ret = 0;
+	}
+	t_pop();
+	return ret;
+}
+
+static int
+maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				const char *dir, const char *fname,
+				enum mailbox_list_file_type type,
+				enum mailbox_info_flags *flags_r)
+{
+	struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(ctx->list);
+	struct mail_storage *_storage = &storage->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) {
+	    strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0) {
 		const char *path;
 		struct stat st;
 
@@ -950,7 +998,7 @@
 			(void)unlink_directory(path, TRUE);
 		t_pop();
 
-		*flags = MAILBOX_NONEXISTENT;
+		*flags_r = MAILBOX_NONEXISTENT;
 		return 0;
 	}
 
@@ -962,7 +1010,7 @@
 	case MAILBOX_LIST_FILE_TYPE_FILE:
 	case MAILBOX_LIST_FILE_TYPE_OTHER:
 		/* non-directories are not */
-		*flags = MAILBOX_NOSELECT;
+		*flags_r = MAILBOX_NOSELECT;
 		return 0;
 
 	case MAILBOX_LIST_FILE_TYPE_UNKNOWN:
@@ -979,11 +1027,25 @@
 
 		t_push();
 		path = t_strdup_printf("%s/%s", dir, fname);
-		ret = (stat(path, &st) < 0 || !S_ISDIR(st.st_mode)) ? 0 : 1;
+		if (stat(path, &st) == 0) {
+			if (S_ISDIR(st.st_mode))
+				ret = 1;
+			else {
+				if (strncmp(fname, ".nfs", 4) == 0)
+					*flags_r = MAILBOX_NONEXISTENT;
+				else
+					*flags_r = MAILBOX_NOSELECT;
+				ret = 0;
+			}
+		} else if (errno == ENOENT) {
+			/* just deleted? */
+			*flags_r = MAILBOX_NONEXISTENT;
+			ret = 0;
+		} else {
+			*flags_r = MAILBOX_NOSELECT;
+			ret = 0;
+		}
 		t_pop();
-
-		if (ret == 0)
-			*flags = MAILBOX_NONEXISTENT;
 	} else {
 		ret = 1;
 	}
@@ -992,6 +1054,7 @@
 
 static void maildir_class_init(void)
 {
+	maildir_mailbox_list_module_id = mailbox_list_module_id++;
 	maildir_transaction_class_init();
 }
 
@@ -1013,9 +1076,6 @@
 		index_storage_set_callbacks,
 		maildir_mailbox_open,
 		maildir_mailbox_create,
-		maildir_mailbox_delete,
-		maildir_mailbox_rename,
-		maildir_storage_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/index/maildir/maildir-storage.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Thu Mar 29 10:59:11 2007 +0300
@@ -43,6 +43,7 @@
 #define MAILDIR_SAVE_FLAG_DELETED  0x20000000
 
 #include "index-storage.h"
+#include "mailbox-list-private.h"
 
 #define STORAGE(maildir_storage) \
 	(&(maildir_storage)->storage.storage)
@@ -56,6 +57,7 @@
 struct maildir_storage {
 	struct index_storage storage;
 
+	struct mailbox_list_vfuncs list_super;
 	const char *temp_prefix;
 
 	unsigned int copy_with_hardlinks:1;
--- a/src/lib-storage/index/mbox/mbox-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -31,6 +31,10 @@
 	((st).st_size == 0 ? MAILBOX_UNMARKED : \
 	 (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED)
 
+#define MBOX_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					mbox_mailbox_list_module_id))
+
 /* 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
    client's point of view. So if you do it, change all mailboxes' UIDVALIDITY
@@ -65,8 +69,14 @@
 extern struct mail_storage mbox_storage;
 extern struct mailbox mbox_mailbox;
 
-static bool mbox_mailbox_list_module_id_set = FALSE;
-static unsigned int mbox_mailbox_list_module_id;
+static unsigned int mbox_mailbox_list_module_id = 0;
+
+static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags);
+static int mbox_list_delete_mailbox(struct mailbox_list *list,
+				    const char *name);
 
 int mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function)
 {
@@ -83,21 +93,6 @@
 	return -1;
 }
 
-static bool mbox_handle_errors(struct index_storage *istorage)
-{
-	struct mail_storage *storage = &istorage->storage;
-
-	if (ENOACCESS(errno))
-		mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION);
-	else if (ENOSPACE(errno))
-		mail_storage_set_error(storage, "Not enough disk space");
-	else if (ENOTFOUND(errno))
-		mail_storage_set_error(storage, "Directory structure is broken");
-	else
-		return FALSE;
-	return TRUE;
-}
-
 static bool mbox_is_file(const char *path, const char *name, bool debug)
 {
 	struct stat st;
@@ -282,13 +277,16 @@
 
 static int
 mbox_get_list_settings(struct mailbox_list_settings *list_set,
-		       const char *data, enum mail_storage_flags flags)
+		       const char *data, enum mail_storage_flags flags,
+		       const char **layout_r)
 {
 	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 	const char *p;
 	struct stat st;
 	bool autodetect;
 
+	*layout_r = "fs";
+
 	memset(list_set, 0, sizeof(*list_set));
 	list_set->subscription_fname = MBOX_SUBSCRIPTION_FILE_NAME;
 	list_set->maildir_name = "";
@@ -329,6 +327,8 @@
 				} else if (strncmp(p, "INDEX=", 6) == 0) {
 					list_set->index_dir =
 						t_strcut(p+6, ':');
+				} else if (strncmp(p, "LAYOUT=", 7) == 0) {
+					*layout_r = t_strcut(p+7, ':');
 				}
 				p = strchr(p, ':');
 			} while (p != NULL);
@@ -384,9 +384,7 @@
 mbox_list_get_path(struct mailbox_list *list, const char *name,
 		   enum mailbox_list_path_type type)
 {
-	struct mbox_storage *storage =
-		*((void **)array_idx_modifiable(&list->module_contexts,
-						mbox_mailbox_list_module_id));
+	struct mbox_storage *storage = MBOX_LIST_CONTEXT(list);
 	const char *path, *p;
 
 	path = storage->list_super.get_path(list, name, type);
@@ -410,10 +408,10 @@
 	struct index_storage *istorage;
 	struct mailbox_list_settings list_set;
 	struct mailbox_list *list;
-	const char *error;
+	const char *layout, *error;
 	pool_t pool;
 
-	if (mbox_get_list_settings(&list_set, data, flags) < 0)
+	if (mbox_get_list_settings(&list_set, data, flags, &layout) < 0)
 		return NULL;
 	list_set.mail_storage_flags = &flags;
 	list_set.lock_method = &lock_method;
@@ -421,22 +419,21 @@
 	pool = pool_alloconly_create("storage", 512+256);
 	storage = p_new(pool, struct mbox_storage, 1);
 
-	if (mailbox_list_init("fs", &list_set,
+	if (mailbox_list_init(layout, &list_set,
 			      mail_storage_get_list_flags(flags),
-			      mailbox_storage_list_is_mailbox, storage,
 			      &list, &error) < 0) {
-		i_error("mbox fs: %s", error);
+		i_error("mbox %s: %s", layout, error);
 		pool_unref(pool);
 		return NULL;
 	}
-
 	storage->list_super = list->v;
-	list->v.get_path = mbox_list_get_path;
+	if (strcmp(layout, "fs") == 0 && *list_set.maildir_name == '\0') {
+		/* have to use .imap/ directories */
+		list->v.get_path = mbox_list_get_path;
+	}
+	list->v.iter_is_mailbox = mbox_list_iter_is_mailbox;
+	list->v.delete_mailbox = mbox_list_delete_mailbox;
 
-	if (!mbox_mailbox_list_module_id_set) {
-		mbox_mailbox_list_module_id_set = TRUE;
-		mbox_mailbox_list_module_id = mailbox_list_module_id++;
-	}
 	array_idx_set(&list->module_contexts,
 		      mbox_mailbox_list_module_id, &storage);
 
@@ -667,8 +664,7 @@
 		  struct istream *input, enum mailbox_open_flags flags)
 {
 	struct mbox_storage *storage = (struct mbox_storage *)_storage;
-	struct index_storage *istorage = INDEX_STORAGE(storage);
-	const char *path;
+	const char *path, *error;
 	struct stat st;
 
 	mail_storage_clear_error(_storage);
@@ -702,8 +698,10 @@
 
 	if (ENOTFOUND(errno)) {
 		mail_storage_set_error(_storage,
-			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-	} else if (!mbox_handle_errors(istorage)) {
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
+	} else if (mail_storage_errno2str(&error))
+		mail_storage_set_error(_storage, "%s", error);
+	else {
 		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
 					  path);
 	}
@@ -714,8 +712,7 @@
 static int mbox_mailbox_create(struct mail_storage *_storage, const char *name,
 			       bool directory)
 {
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *path, *p;
+	const char *path, *p, *error;
 	struct stat st;
 	int fd;
 
@@ -726,17 +723,6 @@
 		return -1;
 	}
 
-	if (strncasecmp(name, "INBOX/", 6) == 0) {
-		/* We might actually be able to create mailboxes under INBOX
-		   because the real INBOX file isn't usually named as INBOX
-		   in the root mail directory. that would anyway be a special
-		   case which would require special handling elsewhere, so just
-		   don't allow it. */
-		mail_storage_set_error(_storage,
-				"Mailbox doesn't allow inferior mailboxes");
-		return -1;
-	}
-
 	/* make sure it doesn't exist already */
 	path = mailbox_list_get_path(_storage->list, name,
 				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
@@ -749,7 +735,9 @@
 		if (errno == ENOTDIR) {
 			mail_storage_set_error(_storage,
 				"Mailbox doesn't allow inferior mailboxes");
-		} else if (!mbox_handle_errors(storage)) {
+		} else if (mail_storage_errno2str(&error))
+			mail_storage_set_error(_storage, "%s", error);
+		else {
 			mail_storage_set_critical(_storage,
 				"stat() failed for mbox file %s: %m", path);
 		}
@@ -761,11 +749,12 @@
 	if (p != NULL) {
 		p = t_strdup_until(path, p);
 		if (mkdir_parents(p, CREATE_MODE) < 0) {
-			if (mbox_handle_errors(storage))
-				return -1;
-
-			mail_storage_set_critical(_storage,
-				"mkdir_parents(%s) failed: %m", p);
+			if (mail_storage_errno2str(&error))
+				mail_storage_set_error(_storage, "%s", error);
+			else {
+				mail_storage_set_critical(_storage,
+					"mkdir_parents(%s) failed: %m", p);
+			}
 			return -1;
 		}
 
@@ -785,194 +774,15 @@
 	if (errno == EEXIST) {
 		/* mailbox was just created between stat() and open() call.. */
 		mail_storage_set_error(_storage, "Mailbox already exists");
-	} else if (!mbox_handle_errors(storage)) {
+	} else if (mail_storage_errno2str(&error))
+		mail_storage_set_error(_storage, "%s", error);
+	else {
 		mail_storage_set_critical(_storage,
 			"Can't create mailbox %s: %m", name);
 	}
 	return -1;
 }
 
-static int mbox_mailbox_delete(struct mail_storage *_storage, const char *name)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *index_dir, *path;
-	struct stat st;
-
-	mail_storage_clear_error(_storage);
-
-	if (strcmp(name, "INBOX") == 0) {
-		mail_storage_set_error(_storage, "INBOX can't be deleted.");
-		return -1;
-	}
-
-	if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
-		mail_storage_set_error(_storage, "Invalid mailbox name");
-		return -1;
-	}
-
-	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,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"lstat() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	if (S_ISDIR(st.st_mode)) {
-		/* 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 != '\0' && rmdir(index_dir) < 0 &&
-		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
-			if (!mbox_handle_errors(storage)) {
-				mail_storage_set_critical(_storage,
-					"rmdir() failed for %s: %m", index_dir);
-				return -1;
-			}
-		}
-
-		if (rmdir(path) == 0)
-			return 0;
-
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (errno == ENOTEMPTY) {
-			mail_storage_set_error(_storage,
-				"Folder %s isn't empty, can't delete it.",
-				name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"rmdir() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	/* delete the index directory first, so that if we crash we don't
-	   leave indexes for deleted mailboxes lying around */
-	index_dir = mailbox_list_get_path(_storage->list, name,
-					  MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*index_dir != '\0') {
-		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,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"unlink() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	return 0;
-}
-
-static int mbox_mailbox_rename(struct mail_storage *_storage,
-			       const char *oldname, const char *newname)
-{
-	struct index_storage *storage = (struct index_storage *)_storage;
-	const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p;
-	struct stat st;
-
-	mail_storage_clear_error(_storage);
-
-	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;
-	}
-
-	if (strncasecmp(newname, "INBOX/", 6) == 0) {
-		/* Not allowed - see explanation in mbox_mailbox_create */
-		mail_storage_set_error(_storage,
-			"Target mailbox doesn't allow inferior mailboxes");
-		return -1;
-	}
-
-	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, '/');
-	if (p != NULL) {
-		p = t_strdup_until(newpath, p);
-		if (mkdir_parents(p, CREATE_MODE) < 0) {
-			if (mbox_handle_errors(storage))
-				return -1;
-
-			mail_storage_set_critical(_storage,
-				"mkdir_parents(%s) failed: %m", p);
-			return -1;
-		}
-	}
-
-	/* first check that the destination mailbox doesn't exist.
-	   this is racy, but we need to be atomic and there's hardly any
-	   possibility that someone actually tries to rename two mailboxes
-	   to same new one */
-	if (lstat(newpath, &st) == 0) {
-		mail_storage_set_error(_storage,
-				       "Target mailbox already exists");
-		return -1;
-	} else if (errno == ENOTDIR) {
-		mail_storage_set_error(_storage,
-			"Target mailbox doesn't allow inferior mailboxes");
-		return -1;
-	} else if (errno != ENOENT && errno != EACCES) {
-		mail_storage_set_critical(_storage, "lstat(%s) failed: %m",
-					  newpath);
-		return -1;
-	}
-
-	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
-	   the next time it's needed. */
-	if (rename(oldpath, newpath) < 0) {
-		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(_storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(_storage,
-				"rename(%s, %s) failed: %m", oldpath, newpath);
-		}
-		return -1;
-	}
-
-	/* we need to rename the index directory as well */
-	old_indexdir = mailbox_list_get_path(_storage->list, oldname,
-					     MAILBOX_LIST_PATH_TYPE_INDEX);
-	new_indexdir = mailbox_list_get_path(_storage->list, newname,
-					     MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*old_indexdir != '\0') {
-		if (rename(old_indexdir, new_indexdir) < 0 &&
-		    errno != ENOENT) {
-			mail_storage_set_critical(_storage,
-						  "rename(%s, %s) failed: %m",
-						  old_indexdir, new_indexdir);
-		}
-	}
-
-	return 0;
-}
-
 static int mbox_storage_close(struct mailbox *box)
 {
 	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
@@ -1019,26 +829,39 @@
 		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)
+static bool
+is_inbox_file(struct mailbox_list *list, const char *path, const char *fname)
 {
+	const char *inbox_path;
+
+	if (strcasecmp(fname, "INBOX") != 0)
+		return FALSE;
+
+	inbox_path = mailbox_list_get_path(list, "INBOX",
+					   MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	return strcmp(inbox_path, path) == 0;
+}
+
+static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
+				     const char *dir, const char *fname,
+				     enum mailbox_list_file_type type,
+				     enum mailbox_info_flags *flags_r)
+{
+	struct mail_storage *storage = MBOX_LIST_CONTEXT(ctx->list);
 	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;
+		*flags_r = 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;
+			*flags_r = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
 			return 0;
 		}
 	}
@@ -1046,19 +869,19 @@
 	/* skip all .lock files */
 	len = strlen(fname);
 	if (len > 5 && strcmp(fname+len-5, ".lock") == 0) {
-		*flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS;
+		*flags_r = 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;
+		*flags_r = 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;
+	    (ctx->flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) {
+		*flags_r = MAILBOX_NOINFERIORS;
 		return 1;
 	}
 
@@ -1067,11 +890,19 @@
 	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);
+			*flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		else {
+			*flags_r = MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+			if (is_inbox_file(ctx->list, path, fname) &&
+			    strcmp(fname, "INBOX") != 0) {
+				/* it's possible for INBOX to have child
+				   mailboxes as long as the inbox file itself
+				   isn't in <mail root>/INBOX */
+				*flags_r &= ~MAILBOX_NOINFERIORS;
+			}
+		}
 	} else if (errno == EACCES || errno == ELOOP)
-		*flags |= MAILBOX_NOSELECT;
+		*flags_r = MAILBOX_NOSELECT;
 	else if (ENOTFOUND(errno))
 		ret = 0;
 	else {
@@ -1083,8 +914,91 @@
 	return ret;
 }
 
+static int mbox_list_delete_mailbox(struct mailbox_list *list,
+				    const char *name)
+{
+	struct mbox_storage *storage = MBOX_LIST_CONTEXT(list);
+	struct stat st;
+	const char *path, *index_dir, *error;
+
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (lstat(path, &st) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"lstat() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	if (S_ISDIR(st.st_mode)) {
+		/* 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(list, name,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		index_dir = *index_dir == '\0' ? "" :
+			t_strconcat(index_dir, "/"MBOX_INDEX_DIR_NAME, NULL);
+
+		if (*index_dir != '\0' && rmdir(index_dir) < 0 &&
+		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
+			if (mail_storage_errno2str(&error))
+				mailbox_list_set_error(list, error);
+			else {
+				mailbox_list_set_critical(list,
+					"rmdir() failed for %s: %m", index_dir);
+			}
+			return -1;
+		}
+
+		if (rmdir(path) == 0)
+			return 0;
+
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (errno == ENOTEMPTY) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				"Directory %s isn't empty, can't delete it.",
+				name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"rmdir() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	/* delete index / control files first */
+	index_storage_destroy_unrefed();
+	if (storage->list_super.delete_mailbox(list, name) < 0)
+		return -1;
+
+	if (unlink(path) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
+		} else if (mail_storage_errno2str(&error))
+			mailbox_list_set_error(list, error);
+		else {
+			mailbox_list_set_critical(list,
+				"unlink() failed for %s: %m", path);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
 static void mbox_class_init(void)
 {
+	mbox_mailbox_list_module_id = mailbox_list_module_id++;
 	mbox_transaction_class_init();
 }
 
@@ -1106,9 +1020,6 @@
 		index_storage_set_callbacks,
 		mbox_mailbox_open,
 		mbox_mailbox_create,
-		mbox_mailbox_delete,
-		mbox_mailbox_rename,
-		mbox_is_mailbox,
 		index_storage_get_last_error
 	}
 };
--- a/src/lib-storage/list/index-mailbox-list-sync.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/list/index-mailbox-list-sync.c	Thu Mar 29 10:59:11 2007 +0300
@@ -470,7 +470,7 @@
 		index_list_next_hook_mailbox_created(box);
 
 	/* FIXME: maildir-only for now */
-	if (strcmp(box->storage->name, "maildir") != 0)
+	if (strcmp(box->storage->list->name, "maildir++") != 0)
 		return;
 
 	ibox = p_new(box->pool, struct index_list_mailbox, 1);
--- a/src/lib-storage/list/mailbox-list-fs-iter.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/list/mailbox-list-fs-iter.c	Thu Mar 29 10:59:11 2007 +0300
@@ -22,7 +22,8 @@
 	struct imap_match_glob *glob;
 	struct subsfile_list_context *subsfile_ctx;
 
-	bool inbox_found;
+	bool inbox_found, inbox_listed;
+	enum mailbox_info_flags inbox_flags;
 
 	struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx);
 
@@ -121,7 +122,7 @@
 	virtual_path = mask_get_dir(mask);
 
 	path = mailbox_list_get_path(_list, virtual_path,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+				     MAILBOX_LIST_PATH_TYPE_DIR);
 	if (list_opendir(_list, path, TRUE, &dirp) < 0)
 		return &ctx->ctx;
 	/* if user gave invalid directory, we just don't show any results. */
@@ -189,6 +190,41 @@
 	return ctx->next(ctx);
 }
 
+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_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_DIR);
+	path_split(inbox_path, &dir, &fname);
+	if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname,
+					     MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+					     &ctx->info.flags) < 0)
+		ctx->ctx.failed = TRUE;
+	t_pop();
+
+	return &ctx->info;
+}
+
 static int
 list_file(struct fs_list_iterate_context *ctx, const struct dirent *d)
 {
@@ -218,23 +254,50 @@
 
 	/* 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);
+	ret = ctx->ctx.list->v.
+		iter_is_mailbox(&ctx->ctx, ctx->dir->real_path, fname,
+				mailbox_list_get_file_type(d),
+				&ctx->info.flags);
 	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) {
+	if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
+	    strcasecmp(list_path, "INBOX") == 0) {
+		if (ctx->inbox_listed) {
+			/* already listed the INBOX */
+			return 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)
+						   MAILBOX_LIST_PATH_TYPE_DIR);
+		if (strcmp(real_path, inbox_path) == 0) {
+			/* delay listing in case there's a INBOX/ directory */
+			ctx->inbox_found = TRUE;
+			ctx->inbox_flags = ctx->info.flags;
+			return 0;
+		}
+		if (strcmp(fname, "INBOX") != 0 ||
+		    (ctx->info.flags & MAILBOX_NOINFERIORS) != 0) {
+			/* duplicate INBOX, can't show this */
 			return 0;
+		}
 
-		ctx->inbox_found = TRUE;
+		/* INBOX/ directory. show the INBOX list now */
+		if (!ctx->inbox_found) {
+			enum mailbox_info_flags dir_flags = ctx->info.flags;
+
+			(void)fs_list_inbox(ctx);
+			ctx->info.flags &= ~(MAILBOX_NOINFERIORS |
+					     MAILBOX_NOCHILDREN);
+			ctx->info.flags |= dir_flags;
+			ctx->inbox_found = TRUE;
+		} else {
+			ctx->info.flags &= ~MAILBOX_NOSELECT;
+			ctx->info.flags |= ctx->inbox_flags;
+		}
+		ctx->inbox_listed = TRUE;
 	}
 
 	if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) {
@@ -270,21 +333,6 @@
 	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;
@@ -321,12 +369,11 @@
 
 	t_push();
 	path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+				     MAILBOX_LIST_PATH_TYPE_DIR);
 	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)
+	if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname,
+					     MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+					     &ctx->info.flags) < 0)
 		ctx->ctx.failed = TRUE;
 	t_pop();
 	return &ctx->info;
@@ -346,27 +393,6 @@
 		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;
@@ -399,9 +425,16 @@
 	    (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
 	    ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) {
 		/* show inbox */
+		ctx->inbox_listed = TRUE;
 		ctx->inbox_found = TRUE;
 		return fs_list_inbox(ctx);
 	}
+	if (!ctx->inbox_listed && ctx->inbox_found) {
+		ctx->inbox_listed = TRUE;
+		ctx->info.flags = ctx->inbox_flags;
+		ctx->info.name = "INBOX";
+		return &ctx->info;
+	}
 
 	/* finished */
 	return NULL;
--- a/src/lib-storage/list/mailbox-list-fs.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/list/mailbox-list-fs.c	Thu Mar 29 10:59:11 2007 +0300
@@ -1,13 +1,18 @@
-/* Copyright (C) 2006 Timo Sirainen */
+/* Copyright (C) 2006-2007 Timo Sirainen */
 
 #include "lib.h"
 #include "hostpid.h"
 #include "home-expand.h"
+#include "mkdir-parents.h"
 #include "subscription-file.h"
 #include "mailbox-list-fs.h"
 
+#include <stdio.h>
+#include <unistd.h>
 #include <sys/stat.h>
 
+#define CREATE_MODE 0770 /* umask() should limit it more */
+
 extern struct mailbox_list fs_mailbox_list;
 
 static struct mailbox_list *fs_list_alloc(void)
@@ -28,8 +33,7 @@
 
 static void fs_list_deinit(struct mailbox_list *_list)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 
 	pool_unref(list->list.pool);
 }
@@ -136,8 +140,7 @@
 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;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	const struct mailbox_list_settings *set = &_list->set;
 
 	mailbox_list_clear_error(&list->list);
@@ -158,7 +161,7 @@
 		i_unreached();
 	}
 
-	i_assert(mailbox_list_is_valid_existing_name(_list, name));
+	i_assert(mailbox_list_is_valid_mask(_list, name));
 
 	if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
 	    (*name == '/' || *name == '~')) {
@@ -188,10 +191,8 @@
 		break;
 	}
 
-	if (strcmp(name, "INBOX") == 0) {
-		return set->inbox_path != NULL ?
-			set->inbox_path : set->root_dir;
-	}
+	if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL)
+		return set->inbox_path;
 
 	if (*set->maildir_name == '\0')
 		return t_strdup_printf("%s/%s", set->root_dir, name);
@@ -205,8 +206,7 @@
 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 fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	struct stat st;
 	const char *path;
 
@@ -245,8 +245,7 @@
 static const char *
 fs_list_get_temp_prefix(struct mailbox_list *_list)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 
 	return list->temp_prefix;
 }
@@ -267,8 +266,7 @@
 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;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	const char *path;
 
 	mailbox_list_clear_error(&list->list);
@@ -280,6 +278,103 @@
 				       name, set);
 }
 
+static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	/* let the backend handle the rest */
+	return mailbox_list_delete_index_control(list, name);
+}
+
+static bool fs_handle_errors(struct mailbox_list *list)
+{
+	if (ENOACCESS(errno))
+		mailbox_list_set_error(list, MAILBOX_LIST_ERR_NO_PERMISSION);
+	else if (ENOSPACE(errno))
+		mailbox_list_set_error(list, "Not enough disk space");
+	else if (ENOTFOUND(errno))
+		mailbox_list_set_error(list, "Directory structure is broken");
+	else
+		return FALSE;
+	return TRUE;
+}
+
+static int fs_list_rename_mailbox(struct mailbox_list *list,
+				  const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p;
+	struct stat st;
+
+	if (!mailbox_list_is_valid_existing_name(list, oldname) ||
+	    !mailbox_list_is_valid_create_name(list, newname)) {
+		mailbox_list_set_error(list, "Invalid mailbox name");
+		return -1;
+	}
+
+	oldpath = mailbox_list_get_path(list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	/* create the hierarchy */
+	p = strrchr(newpath, '/');
+	if (p != NULL) {
+		p = t_strdup_until(newpath, p);
+		if (mkdir_parents(p, CREATE_MODE) < 0) {
+			if (fs_handle_errors(list))
+				return -1;
+
+			mailbox_list_set_critical(list,
+				"mkdir_parents(%s) failed: %m", p);
+			return -1;
+		}
+	}
+
+	/* first check that the destination mailbox doesn't exist.
+	   this is racy, but we need to be atomic and there's hardly any
+	   possibility that someone actually tries to rename two mailboxes
+	   to same new one */
+	if (lstat(newpath, &st) == 0) {
+		mailbox_list_set_error(list, "Target mailbox already exists");
+		return -1;
+	} else if (errno == ENOTDIR) {
+		mailbox_list_set_error(list,
+			"Target mailbox doesn't allow inferior mailboxes");
+		return -1;
+	} else if (errno != ENOENT && errno != EACCES) {
+		mailbox_list_set_critical(list, "lstat(%s) failed: %m",
+					  newpath);
+		return -1;
+	}
+
+	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
+	   the next time it's needed. */
+	if (rename(oldpath, newpath) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname));
+		} else if (!fs_handle_errors(list)) {
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
+		}
+		return -1;
+	}
+
+	/* we need to rename the index directory as well */
+	old_indexdir = mailbox_list_get_path(list, oldname,
+					     MAILBOX_LIST_PATH_TYPE_INDEX);
+	new_indexdir = mailbox_list_get_path(list, newname,
+					     MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*old_indexdir != '\0') {
+		if (rename(old_indexdir, new_indexdir) < 0 &&
+		    errno != ENOENT) {
+			mailbox_list_set_critical(list,
+						  "rename(%s, %s) failed: %m",
+						  old_indexdir, new_indexdir);
+		}
+	}
+
+	return 0;
+}
+
 struct mailbox_list fs_mailbox_list = {
 	MEMBER(name) "fs",
 	MEMBER(hierarchy_sep) '/',
@@ -298,6 +393,9 @@
 		fs_list_iter_init,
 		fs_list_iter_next,
 		fs_list_iter_deinit,
-		fs_list_set_subscribed
+		NULL,
+		fs_list_set_subscribed,
+		fs_list_delete_mailbox,
+		fs_list_rename_mailbox
 	}
 };
--- a/src/lib-storage/list/mailbox-list-maildir-iter.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/list/mailbox-list-maildir-iter.c	Thu Mar 29 10:59:11 2007 +0300
@@ -98,10 +98,9 @@
 
 		/* 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);
+		ret = ctx->ctx.list->v.
+			iter_is_mailbox(&ctx->ctx, ctx->dir, fname,
+					mailbox_list_get_file_type(d), &flags);
 		if (ret < 0) {
 			t_pop();
 			return -1;
@@ -149,6 +148,11 @@
 				node->flags |= MAILBOX_FLAG_MATCHED;
 			}
 		}
+		if (node != NULL) {
+			node->flags |= flags & ~(MAILBOX_NOINFERIORS |
+						 MAILBOX_CHILDREN |
+						 MAILBOX_NOCHILDREN);
+		}
 	}
 	t_pop();
 
--- a/src/lib-storage/list/mailbox-list-maildir.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Thu Mar 29 10:59:11 2007 +0300
@@ -1,11 +1,13 @@
-/* Copyright (C) 2006 Timo Sirainen */
+/* Copyright (C) 2006-2007 Timo Sirainen */
 
 #include "lib.h"
+#include "array.h"
 #include "hostpid.h"
 #include "home-expand.h"
 #include "subscription-file.h"
 #include "mailbox-list-maildir.h"
 
+#include <stdio.h>
 #include <sys/stat.h>
 
 extern struct mailbox_list maildir_mailbox_list;
@@ -270,6 +272,164 @@
 				       name, set);
 }
 
+static int rename_dir(struct mailbox_list *list,
+		      enum mailbox_list_path_type type,
+		      const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath;
+
+	oldpath = mailbox_list_get_path(list, oldname, type);
+	newpath = mailbox_list_get_path(list, newname, type);
+
+	if (strcmp(oldpath, newpath) == 0)
+		return 0;
+
+	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
+		mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rename_children(struct mailbox_list *list,
+			   const char *oldname, const char *newname)
+{
+	struct mailbox_list_iterate_context *iter;
+        struct mailbox_info *info;
+	ARRAY_DEFINE(names_arr, const char *);
+	const char *mask, *oldpath, *newpath, *old_listname, *new_listname;
+	const char *const *names;
+	unsigned int i, count;
+	size_t oldnamelen;
+	pool_t pool;
+	int ret;
+
+	ret = 0;
+	oldnamelen = strlen(oldname);
+
+	/* first get the list of the children and save them to memory, because
+	   we can't rely on readdir() not skipping files while the directory
+	   is being modified. this doesn't protect against modifications by
+	   other processes though. */
+	pool = pool_alloconly_create("Maildir++ children list", 1024);
+	i_array_init(&names_arr, 64);
+
+	mask = t_strdup_printf("%s%c*", oldname,
+			       mailbox_list_get_hierarchy_sep(list));
+	iter = mailbox_list_iter_init(list, mask, MAILBOX_LIST_ITER_FAST_FLAGS);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
+		const char *name;
+
+		i_assert(oldnamelen <= strlen(info->name));
+
+		name = p_strdup(pool, info->name + oldnamelen);
+		array_append(&names_arr, &name, 1);
+	}
+	if (mailbox_list_iter_deinit(&iter) < 0) {
+		ret = -1;
+		names = NULL; count = 0;
+	} else {
+		names = array_get(&names_arr, &count);
+	}
+
+	for (i = 0; i < count; i++) {
+		t_push();
+
+		old_listname = t_strconcat(oldname, names[i], NULL);
+		new_listname = t_strconcat(newname, names[i], NULL);
+		oldpath = mailbox_list_get_path(list, old_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		newpath = mailbox_list_get_path(list, new_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+		/* FIXME: it's possible to merge two mailboxes if either one of
+		   them doesn't have existing root mailbox. We could check this
+		   but I'm not sure if it's worth it. It could be even
+		   considered as a feature.
+
+		   Anyway, the bug with merging is that if both mailboxes have
+		   identically named child mailbox they conflict. Just ignore
+		   those and leave them under the old mailbox. */
+		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
+			ret = 1;
+		else {
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
+			ret = -1;
+			t_pop();
+			break;
+		}
+
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
+				 old_listname, new_listname);
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
+				 old_listname, new_listname);
+		t_pop();
+	}
+	array_free(&names_arr);
+	pool_unref(pool);
+
+	return ret;
+}
+
+static int
+maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	/* let the backend handle the rest */
+	return mailbox_list_delete_index_control(list, name);
+}
+
+static int maildir_list_rename_mailbox(struct mailbox_list *list,
+				       const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath;
+	int ret;
+        bool found;
+
+	if (!mailbox_list_is_valid_existing_name(list, oldname) ||
+	    !mailbox_list_is_valid_create_name(list, newname)) {
+		mailbox_list_set_error(list, "Invalid mailbox name");
+		return -1;
+	}
+
+	/* NOTE: it's possible to rename a nonexisting mailbox which has
+	   children. In that case we should ignore the rename() error. */
+	oldpath = mailbox_list_get_path(list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	ret = rename(oldpath, newpath);
+	if (ret == 0 || errno == ENOENT) {
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
+				 oldname, newname);
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
+				 oldname, newname);
+
+		found = ret == 0;
+		ret = rename_children(list, oldname, newname);
+		if (ret < 0)
+			return -1;
+		if (!found && ret == 0) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname));
+			return -1;
+		}
+
+		return 0;
+	}
+
+	if (EDESTDIREXISTS(errno)) {
+		mailbox_list_set_error(list, "Target mailbox already exists");
+	} else {
+		mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+	}
+	return -1;
+}
+
 struct mailbox_list maildir_mailbox_list = {
 	MEMBER(name) "maildir++",
 	MEMBER(hierarchy_sep) '.',
@@ -288,6 +448,9 @@
 		maildir_list_iter_init,
 		maildir_list_iter_next,
 		maildir_list_iter_deinit,
-		maildir_list_set_subscribed
+		NULL,
+		maildir_list_set_subscribed,
+		maildir_list_delete_mailbox,
+		maildir_list_rename_mailbox
 	}
 };
--- a/src/lib-storage/mail-storage-private.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mail-storage-private.h	Thu Mar 29 10:59:11 2007 +0300
@@ -4,11 +4,6 @@
 #include "file-lock.h"
 #include "mail-storage.h"
 
-/* Some error strings that should be used everywhere to avoid
-   permissions checks from revealing mailbox's existence */
-#define MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND "Mailbox doesn't exist: %s"
-#define MAIL_STORAGE_ERR_NO_PERMISSION "Permission denied"
-
 /* Called after mail storage has been created */
 extern void (*hook_mail_storage_created)(struct mail_storage *storage);
 /* Called after mailbox has been opened */
@@ -37,15 +32,6 @@
 
 	int (*mailbox_create)(struct mail_storage *storage, const char *name,
 			      bool directory);
-	int (*mailbox_delete)(struct mail_storage *storage, const char *name);
-	int (*mailbox_rename)(struct mail_storage *storage, const char *oldname,
-			      const char *newname);
-
-	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,
@@ -289,10 +275,6 @@
 
 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);
+bool mail_storage_errno2str(const char **error_r);
 
 #endif
--- a/src/lib-storage/mail-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mail-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -1,10 +1,11 @@
-/* Copyright (C) 2002-2006 Timo Sirainen */
+/* Copyright (C) 2002-2007 Timo Sirainen */
 
 #include "lib.h"
 #include "ioloop.h"
 #include "array.h"
 #include "var-expand.h"
 #include "mail-index-private.h"
+#include "mailbox-list-private.h"
 #include "mail-storage-private.h"
 
 #include <stdlib.h>
@@ -318,17 +319,6 @@
 	return storage->v.mailbox_create(storage, name, directory);
 }
 
-int mail_storage_mailbox_delete(struct mail_storage *storage, const char *name)
-{
-	return storage->v.mailbox_delete(storage, name);
-}
-
-int mail_storage_mailbox_rename(struct mail_storage *storage,
-				const char *oldname, const char *newname)
-{
-	return storage->v.mailbox_rename(storage, oldname, newname);
-}
-
 const char *mail_storage_get_last_error(struct mail_storage *storage,
 					bool *syntax_error_r,
 					bool *temporary_error_r)
@@ -385,27 +375,17 @@
 	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)
+bool mail_storage_errno2str(const char **error_r)
 {
-	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);
+	if (ENOACCESS(errno))
+		*error_r = MAILBOX_LIST_ERR_NO_PERMISSION;
+	else if (ENOSPACE(errno))
+		*error_r = "Not enough disk space";
+	else if (ENOTFOUND(errno))
+		*error_r = "Directory structure is broken";
+	else
+		return FALSE;
+	return TRUE;
 }
 
 struct mailbox *mailbox_open(struct mail_storage *storage, const char *name,
--- a/src/lib-storage/mail-storage.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mail-storage.h	Thu Mar 29 10:59:11 2007 +0300
@@ -229,18 +229,6 @@
    created as long as it shows in LIST. */
 int mail_storage_mailbox_create(struct mail_storage *storage, const char *name,
 				bool directory);
-/* Only the specified mailbox is deleted, ie. folders under the
-   specified mailbox must not be deleted. */
-int mail_storage_mailbox_delete(struct mail_storage *storage, const char *name);
-/* If the name has inferior hierarchical names, then the inferior
-   hierarchical names MUST also be renamed (ie. foo -> bar renames
-   also foo/bar -> bar/bar). newname may contain multiple new
-   hierarchies.
-
-   If oldname is case-insensitively "INBOX", the mails are moved
-   into new folder but the INBOX folder must not be deleted. */
-int mail_storage_mailbox_rename(struct mail_storage *storage,
-				const char *oldname, const char *newname);
 
 /* Returns the error message of last occurred error. */
 const char *mail_storage_get_last_error(struct mail_storage *storage,
@@ -262,12 +250,6 @@
 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.
--- a/src/lib-storage/mailbox-list-private.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mailbox-list-private.h	Thu Mar 29 10:59:11 2007 +0300
@@ -1,6 +1,11 @@
 #ifndef __MAILBOX_LIST_PRIVATE_H
 #define __MAILBOX_LIST_PRIVATE_H
 
+/* Some error strings that should be used everywhere to avoid
+   permissions checks from revealing mailbox's existence */
+#define MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND "Mailbox doesn't exist: %s"
+#define MAILBOX_LIST_ERR_NO_PERMISSION "Permission denied"
+
 #include "mailbox-list.h"
 
 struct dirent;
@@ -32,8 +37,18 @@
 		(*iter_next)(struct mailbox_list_iterate_context *ctx);
 	int (*iter_deinit)(struct mailbox_list_iterate_context *ctx);
 
+	/* Returns -1 if error, 0 if it's not a valid mailbox, 1 if it is.
+	   flags may be updated (especially the children flags). */
+	int (*iter_is_mailbox)(struct mailbox_list_iterate_context *ctx,
+			       const char *dir, const char *fname,
+			       enum mailbox_list_file_type type,
+			       enum mailbox_info_flags *flags_r);
+
 	int (*set_subscribed)(struct mailbox_list *list,
 			      const char *name, bool set);
+	int (*delete_mailbox)(struct mailbox_list *list, const char *name);
+	int (*rename_mailbox)(struct mailbox_list *list, const char *oldname,
+			      const char *newname);
 };
 
 struct mailbox_list {
@@ -51,9 +66,6 @@
 	char *error;
 	bool temporary_error;
 
-	mailbox_list_is_mailbox_t *callback;
-	void *context;
-
 	ARRAY_DEFINE(module_contexts, void);
 };
 
@@ -69,6 +81,9 @@
 
 extern void (*hook_mailbox_list_created)(struct mailbox_list *list);
 
+int mailbox_list_delete_index_control(struct mailbox_list *list,
+				      const char *name);
+
 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);
 
--- a/src/lib-storage/mailbox-list.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mailbox-list.c	Thu Mar 29 10:59:11 2007 +0300
@@ -1,9 +1,10 @@
-/* Copyright (C) 2006 Timo Sirainen */
+/* Copyright (C) 2006-2007 Timo Sirainen */
 
 #include "lib.h"
 #include "array.h"
 #include "ioloop.h"
 #include "mkdir-parents.h"
+#include "unlink-directory.h"
 #include "mailbox-list-private.h"
 
 #include <time.h>
@@ -76,7 +77,6 @@
 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;
@@ -95,8 +95,6 @@
 	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);
@@ -234,6 +232,60 @@
 	return list->v.set_subscribed(list, name, set);
 }
 
+int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	return list->v.delete_mailbox(list, name);
+}
+
+int mailbox_list_rename_mailbox(struct mailbox_list *list,
+				const char *oldname, const char *newname)
+{
+	return list->v.rename_mailbox(list, oldname, newname);
+}
+
+int mailbox_list_delete_index_control(struct mailbox_list *list,
+				      const char *name)
+{
+	const char *path, *index_dir, *dir;
+
+	if (strcmp(name, "INBOX") == 0) {
+		mailbox_list_set_error(list, "INBOX can't be deleted.");
+		return -1;
+	}
+
+	if (!mailbox_list_is_valid_existing_name(list, name)) {
+		mailbox_list_set_error(list, "Invalid mailbox name");
+		return -1;
+	}
+
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	/* delete the index directory first, so that if we crash we don't
+	   leave indexes for deleted mailboxes lying around */
+	index_dir = mailbox_list_get_path(list, name,
+					  MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*index_dir != '\0' && strcmp(index_dir, path) != 0) {
+		if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m", index_dir);
+			return -1;
+		}
+	}
+
+	/* control directory next */
+	dir = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL);
+	if (*dir != '\0' && strcmp(dir, path) != 0 &&
+	    strcmp(dir, index_dir) != 0) {
+		if (unlink_directory(dir, TRUE) < 0 && errno != ENOENT) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m", dir);
+			return -1;
+		}
+	}
+	return 0;
+}
+
 bool mailbox_list_name_is_too_large(const char *name, char sep)
 {
 	unsigned int levels = 1, level_len = 0;
--- a/src/lib-storage/mailbox-list.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/lib-storage/mailbox-list.h	Thu Mar 29 10:59:11 2007 +0300
@@ -42,9 +42,9 @@
 };
 
 enum mailbox_list_path_type {
-	/* Return directory's path (eg. /home/user/mail) */
+	/* Return directory's path (eg. ~/dbox/INBOX) */
 	MAILBOX_LIST_PATH_TYPE_DIR,
-	/* Return mailbox path (dir + maildir_name) */
+	/* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */
 	MAILBOX_LIST_PATH_TYPE_MAILBOX,
 	/* Return control directory */
 	MAILBOX_LIST_PATH_TYPE_CONTROL,
@@ -92,14 +92,6 @@
         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);
 
@@ -110,7 +102,6 @@
 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);
 
@@ -161,6 +152,18 @@
 int mailbox_list_set_subscribed(struct mailbox_list *list,
 				const char *name, bool set);
 
+/* Delete the given mailbox. If it has children, they aren't deleted. */
+int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
+/* If the name has inferior hierarchical names, then the inferior
+   hierarchical names MUST also be renamed (ie. foo -> bar renames
+   also foo/bar -> bar/bar). newname may contain multiple new
+   hierarchies.
+
+   If oldname is case-insensitively "INBOX", the mails are moved
+   into new mailbox but the INBOX mailbox must not be deleted. */
+int mailbox_list_rename_mailbox(struct mailbox_list *list,
+				const char *oldname, const char *newname);
+
 /* Returns the error message of last occurred error. */
 const char *mailbox_list_get_last_error(struct mailbox_list *list,
 					bool *temporary_error_r);
--- a/src/plugins/acl/acl-mailbox-list.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/acl/acl-mailbox-list.c	Thu Mar 29 10:59:11 2007 +0300
@@ -102,6 +102,76 @@
 	return 0;
 }
 
+static int
+acl_mailbox_list_delete(struct mailbox_list *list, const char *name)
+{
+	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+	bool can_see;
+	int ret;
+
+	ret = acl_storage_have_right(alist->storage, name,
+				     ACL_STORAGE_RIGHT_DELETE, &can_see);
+	if (ret <= 0) {
+		if (ret < 0)
+			return -1;
+		if (can_see) {
+			mail_storage_set_error(alist->storage,
+					       MAILBOX_LIST_ERR_NO_PERMISSION);
+		} else {
+			mail_storage_set_error(alist->storage,
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
+		}
+		return -1;
+	}
+
+	return alist->super.delete_mailbox(list, name);
+}
+
+static int
+acl_mailbox_list_rename(struct mailbox_list *list,
+			const char *oldname, const char *newname)
+{
+	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+	bool can_see;
+	int ret;
+
+	/* renaming requires rights to delete the old mailbox */
+	ret = acl_storage_have_right(alist->storage, oldname,
+				     ACL_STORAGE_RIGHT_DELETE, &can_see);
+	if (ret <= 0) {
+		if (ret < 0)
+			return -1;
+		if (can_see) {
+			mail_storage_set_error(alist->storage,
+					       MAILBOX_LIST_ERR_NO_PERMISSION);
+		} else {
+			mail_storage_set_error(alist->storage,
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname);
+		}
+		return 0;
+	}
+
+	/* and create the new one under the parent mailbox */
+	t_push();
+	ret = acl_storage_have_right(alist->storage,
+		acl_storage_get_parent_mailbox_name(alist->storage, newname),
+		ACL_STORAGE_RIGHT_CREATE, NULL);
+	t_pop();
+
+	if (ret <= 0) {
+		if (ret == 0) {
+			/* Note that if the mailbox didn't have LOOKUP
+			   permission, this not reveals to user the mailbox's
+			   existence. Can't help it. */
+			mail_storage_set_error(alist->storage,
+					       MAILBOX_LIST_ERR_NO_PERMISSION);
+		}
+		return -1;
+	}
+
+	return alist->super.rename_mailbox(list, oldname, newname);
+}
+
 void acl_mailbox_list_created(struct mailbox_list *list)
 {
 	struct acl_mailbox_list *alist;
@@ -113,6 +183,8 @@
 	alist->super = list->v;
 	list->v.iter_next = acl_mailbox_list_iter_next;
 	list->v.get_mailbox_name_status = acl_get_mailbox_name_status;
+	list->v.delete_mailbox = acl_mailbox_list_delete;
+	list->v.rename_mailbox = acl_mailbox_list_rename;
 
 	if (!acl_mailbox_list_module_id_set) {
 		acl_mailbox_list_module_id = mailbox_list_module_id++;
--- a/src/plugins/acl/acl-mailbox.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/acl/acl-mailbox.c	Thu Mar 29 10:59:11 2007 +0300
@@ -7,7 +7,7 @@
 #include "lib.h"
 #include "array.h"
 #include "istream.h"
-#include "mail-storage-private.h"
+#include "mailbox-list-private.h"
 #include "acl-api-private.h"
 #include "acl-plugin.h"
 
@@ -49,7 +49,7 @@
 	if (ret < 0)
 		return -1;
 
-	mail_storage_set_error(box->storage, MAIL_STORAGE_ERR_NO_PERMISSION);
+	mail_storage_set_error(box->storage, MAILBOX_LIST_ERR_NO_PERMISSION);
 	return 0;
 }
 
--- a/src/plugins/acl/acl-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/acl/acl-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "istream.h"
 #include "acl-api-private.h"
+#include "mailbox-list-private.h"
 #include "acl-plugin.h"
 
 #include <stdlib.h>
@@ -92,10 +93,10 @@
 			return NULL;
 		if (can_see) {
 			mail_storage_set_error(storage,
-					       MAIL_STORAGE_ERR_NO_PERMISSION);
+					       MAILBOX_LIST_ERR_NO_PERMISSION);
 		} else {
 			mail_storage_set_error(storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name);
 		}
 		return NULL;
 	}
@@ -125,7 +126,7 @@
 			   permission, this not reveals to user the mailbox's
 			   existence. Can't help it. */
 			mail_storage_set_error(storage,
-					       MAIL_STORAGE_ERR_NO_PERMISSION);
+					       MAILBOX_LIST_ERR_NO_PERMISSION);
 		}
 		return -1;
 	}
@@ -133,74 +134,6 @@
 	return astorage->super.mailbox_create(storage, name, directory);
 }
 
-static int acl_mailbox_delete(struct mail_storage *storage, const char *name)
-{
-	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
-	bool can_see;
-	int ret;
-
-	ret = acl_storage_have_right(storage, name, ACL_STORAGE_RIGHT_DELETE,
-				     &can_see);
-	if (ret <= 0) {
-		if (ret < 0)
-			return -1;
-		if (can_see) {
-			mail_storage_set_error(storage,
-					       MAIL_STORAGE_ERR_NO_PERMISSION);
-		} else {
-			mail_storage_set_error(storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
-		}
-		return -1;
-	}
-
-	return astorage->super.mailbox_delete(storage, name);
-}
-
-static int acl_mailbox_rename(struct mail_storage *storage, const char *oldname,
-			      const char *newname)
-{
-	struct acl_mail_storage *astorage = ACL_CONTEXT(storage);
-	bool can_see;
-	int ret;
-
-	/* renaming requires rights to delete the old mailbox */
-	ret = acl_storage_have_right(storage, oldname,
-				     ACL_STORAGE_RIGHT_DELETE, &can_see);
-	if (ret <= 0) {
-		if (ret < 0)
-			return -1;
-		if (can_see) {
-			mail_storage_set_error(storage,
-					       MAIL_STORAGE_ERR_NO_PERMISSION);
-		} else {
-			mail_storage_set_error(storage,
-				MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
-		}
-		return 0;
-	}
-
-	/* and create the new one under the parent mailbox */
-	t_push();
-	ret = acl_storage_have_right(storage,
-			acl_storage_get_parent_mailbox_name(storage, newname),
-			ACL_STORAGE_RIGHT_CREATE, NULL);
-	t_pop();
-
-	if (ret <= 0) {
-		if (ret == 0) {
-			/* Note that if the mailbox didn't have LOOKUP
-			   permission, this not reveals to user the mailbox's
-			   existence. Can't help it. */
-			mail_storage_set_error(storage,
-					       MAIL_STORAGE_ERR_NO_PERMISSION);
-		}
-		return -1;
-	}
-
-	return astorage->super.mailbox_rename(storage, oldname, newname);
-}
-
 void acl_mail_storage_created(struct mail_storage *storage)
 {
 	struct acl_mail_storage *astorage;
@@ -240,8 +173,6 @@
 	storage->v.destroy = acl_storage_destroy;
 	storage->v.mailbox_open = acl_mailbox_open;
 	storage->v.mailbox_create = acl_mailbox_create;
-	storage->v.mailbox_delete = acl_mailbox_delete;
-	storage->v.mailbox_rename = acl_mailbox_rename;
 
 	acl_mailbox_list_set_storage(storage);
 
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Thu Mar 29 10:59:11 2007 +0300
@@ -19,6 +19,9 @@
 #define LAZY_EXPUNGE_CONTEXT(obj) \
 	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
 					lazy_expunge_storage_module_id))
+#define LAZY_EXPUNGE_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					lazy_expunge_mailbox_list_module_id))
 
 enum lazy_namespace {
 	LAZY_NAMESPACE_EXPUNGE,
@@ -28,6 +31,12 @@
 	LAZY_NAMESPACE_COUNT
 };
 
+struct lazy_expunge_mailbox_list {
+	struct mailbox_list_vfuncs super;
+
+	struct mail_storage *storage;
+};
+
 struct lazy_expunge_mail_storage {
 	struct mail_storage_vfuncs super;
 	bool internal_namespace;
@@ -48,13 +57,18 @@
 
 const char *lazy_expunge_plugin_version = PACKAGE_VERSION;
 
+static void (*lazy_expunge_next_hook_client_created)(struct client **client);
 static void (*lazy_expunge_next_hook_mail_storage_created)
 	(struct mail_storage *storage);
-static void (*lazy_expunge_next_hook_client_created)(struct client **client);
+static void (*lazy_expunge_next_hook_mailbox_list_created)
+	(struct mailbox_list *list);
 
 static unsigned int lazy_expunge_storage_module_id = 0;
 static bool lazy_expunge_storage_module_id_set = FALSE;
 
+static unsigned int lazy_expunge_mailbox_list_module_id = 0;
+static bool lazy_expunge_mailbox_list_module_id_set = FALSE;
+
 static struct namespace *lazy_namespaces[LAZY_NAMESPACE_COUNT];
 
 static struct mailbox *
@@ -275,7 +289,7 @@
 	return box;
 }
 
-static int dir_move_or_merge(struct mail_storage *storage,
+static int dir_move_or_merge(struct mailbox_list *list,
 			     const char *srcdir, const char *destdir)
 {
 	DIR *dir;
@@ -288,14 +302,14 @@
 		return 0;
 
 	if (!EDESTDIREXISTS(errno)) {
-		mail_storage_set_critical(storage,
+		mailbox_list_set_critical(list,
 			"rename(%s, %s) failed: %m", srcdir, destdir);
 	}
 
 	/* rename all the files separately */
 	dir = opendir(srcdir);
 	if (dir == NULL) {
-		mail_storage_set_critical(storage,
+		mailbox_list_set_critical(list,
 			"opendir(%s) failed: %m", srcdir);
 		return -1;
 	}
@@ -323,20 +337,20 @@
 
 		if (rename(str_c(src_path), str_c(dest_path)) < 0 &&
 		    errno != ENOENT) {
-			mail_storage_set_critical(storage,
+			mailbox_list_set_critical(list,
 				"rename(%s, %s) failed: %m",
 				str_c(src_path), str_c(dest_path));
 			ret = -1;
 		}
 	}
 	if (closedir(dir) < 0) {
-		mail_storage_set_critical(storage,
+		mailbox_list_set_critical(list,
 			"closedir(%s) failed: %m", srcdir);
 		ret = -1;
 	}
 	if (ret == 0) {
 		if (rmdir(srcdir) < 0) {
-			mail_storage_set_critical(storage,
+			mailbox_list_set_critical(list,
 				"rmdir(%s) failed: %m", srcdir);
 			ret = -1;
 		}
@@ -345,22 +359,22 @@
 }
 
 static int
-mailbox_move(struct mail_storage *src_storage, const char *src_name,
-	     struct mail_storage *dest_storage, const char **_dest_name)
+mailbox_move(struct mailbox_list *src_list, const char *src_name,
+	     struct mailbox_list *dest_list, const char **_dest_name)
 {
 	const char *dest_name = *_dest_name;
 	const char *srcdir, *src2dir, *src3dir, *destdir;
-	bool is_file;
 
-	srcdir = mail_storage_get_mailbox_path(src_storage, src_name, &is_file);
-	destdir = mail_storage_get_mailbox_path(dest_storage, dest_name,
-						&is_file);
+	srcdir = mailbox_list_get_path(src_list, src_name,
+				       MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	destdir = mailbox_list_get_path(dest_list, dest_name,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	while (rename(srcdir, destdir) < 0) {
 		if (errno == ENOENT)
 			return 0;
 
 		if (!EDESTDIREXISTS(errno)) {
-			mail_storage_set_critical(src_storage,
+			mailbox_list_set_critical(src_list,
 				"rename(%s, %s) failed: %m", srcdir, destdir);
 			return -1;
 		}
@@ -369,22 +383,24 @@
 		   update the filename. */
 		dest_name = t_strdup_printf("%s-%04u", *_dest_name,
 					    (uint32_t)random());
-		destdir = mail_storage_get_mailbox_path(dest_storage, dest_name,
-							&is_file);
+		destdir = mailbox_list_get_path(dest_list, dest_name,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
 	}
 
 	t_push();
-	src2dir = mail_storage_get_mailbox_control_dir(src_storage, src_name);
+	src2dir = mailbox_list_get_path(src_list, src_name,
+					MAILBOX_LIST_PATH_TYPE_CONTROL);
 	if (strcmp(src2dir, srcdir) != 0) {
-		destdir = mail_storage_get_mailbox_control_dir(dest_storage,
-							       dest_name);
-		(void)dir_move_or_merge(src_storage, src2dir, destdir);
+		destdir = mailbox_list_get_path(dest_list, dest_name,
+						MAILBOX_LIST_PATH_TYPE_CONTROL);
+		(void)dir_move_or_merge(src_list, src2dir, destdir);
 	}
-	src3dir = mail_storage_get_mailbox_index_dir(src_storage, src_name);
+	src3dir = mailbox_list_get_path(src_list, src_name,
+					MAILBOX_LIST_PATH_TYPE_INDEX);
 	if (strcmp(src3dir, srcdir) != 0 && strcmp(src3dir, src2dir) != 0) {
-		destdir = mail_storage_get_mailbox_index_dir(dest_storage,
-							     dest_name);
-		(void)dir_move_or_merge(src_storage, src3dir, destdir);
+		destdir = mailbox_list_get_path(dest_list, dest_name,
+						MAILBOX_LIST_PATH_TYPE_INDEX);
+		(void)dir_move_or_merge(src_list, src3dir, destdir);
 	}
 	t_pop();
 
@@ -393,33 +409,37 @@
 }
 
 static int
-lazy_expunge_mailbox_delete(struct mail_storage *storage, const char *name)
+lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
-	struct lazy_expunge_mail_storage *lstorage =
-		LAZY_EXPUNGE_CONTEXT(storage);
-	struct mail_storage *dest_storage;
+	struct lazy_expunge_mailbox_list *llist =
+		LAZY_EXPUNGE_LIST_CONTEXT(list);
+	struct lazy_expunge_mail_storage *lstorage;
+	struct mailbox_list *dest_list;
 	enum mailbox_name_status status;
 	const char *destname;
 	struct tm *tm;
 	char timestamp[256];
 	int ret;
 
+	if (llist->storage == NULL) {
+		/* not a maildir storage */
+		return llist->super.delete_mailbox(list, name);
+	}
+
+	lstorage = LAZY_EXPUNGE_CONTEXT(llist->storage);
 	if (lstorage->internal_namespace)
-		return lstorage->super.mailbox_delete(storage, name);
-
-	mail_storage_clear_error(storage);
+		return llist->super.delete_mailbox(list, name);
 
 	/* first do the normal sanity checks */
 	if (strcmp(name, "INBOX") == 0) {
-		mail_storage_set_error(storage, "INBOX can't be deleted.");
+		mailbox_list_set_error(list, "INBOX can't be deleted.");
 		return -1;
 	}
 
-	if (mailbox_list_get_mailbox_name_status(storage->list, name,
-						 &status) < 0)
+	if (mailbox_list_get_mailbox_name_status(list, name, &status) < 0)
 		return -1;
 	if (status == MAILBOX_NAME_INVALID) {
-		mail_storage_set_error(storage, "Invalid mailbox name");
+		mailbox_list_set_error(list, "Invalid mailbox name");
 		return -1;
 	}
 
@@ -430,24 +450,27 @@
 	destname = t_strconcat(name, "-", timestamp, NULL);
 
 	/* first move the actual mailbox */
-	dest_storage = lazy_namespaces[LAZY_NAMESPACE_DELETE]->storage;
-	if ((ret = mailbox_move(storage, name, dest_storage, &destname)) < 0)
+	dest_list = lazy_namespaces[LAZY_NAMESPACE_DELETE]->storage->list;
+	if ((ret = mailbox_move(list, name, dest_list, &destname)) < 0)
 		return -1;
 	if (ret == 0) {
-		mail_storage_set_error(storage,
-			MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
+		mailbox_list_set_error(list, t_strdup_printf(
+			MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name));
 		return -1;
 	}
 
 	/* next move the expunged messages mailbox, if it exists */
-	storage = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage;
-	dest_storage = lazy_namespaces[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage;
-	(void)mailbox_move(storage, name, dest_storage, &destname);
+	list = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage->list;
+	dest_list =
+		lazy_namespaces[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage->list;
+	(void)mailbox_move(list, name, dest_list, &destname);
 	return 0;
 }
 
 static void lazy_expunge_mail_storage_created(struct mail_storage *storage)
 {
+	struct lazy_expunge_mailbox_list *llist =
+		LAZY_EXPUNGE_LIST_CONTEXT(storage->list);
 	struct lazy_expunge_mail_storage *lstorage;
 
 	if (lazy_expunge_next_hook_mail_storage_created != NULL)
@@ -457,10 +480,11 @@
 	if (strcmp(storage->name, "maildir") != 0)
 		return;
 
+	llist->storage = storage;
+
 	lstorage = p_new(storage->pool, struct lazy_expunge_mail_storage, 1);
 	lstorage->super = storage->v;
 	storage->v.mailbox_open = lazy_expunge_mailbox_open;
-	storage->v.mailbox_delete = lazy_expunge_mailbox_delete;
 
 	if (!lazy_expunge_storage_module_id_set) {
 		lazy_expunge_storage_module_id = mail_storage_module_id++;
@@ -471,6 +495,26 @@
 		      lazy_expunge_storage_module_id, &lstorage);
 }
 
+static void lazy_expunge_mailbox_list_created(struct mailbox_list *list)
+{
+	struct lazy_expunge_mailbox_list *llist;
+
+	if (lazy_expunge_next_hook_mailbox_list_created != NULL)
+		lazy_expunge_next_hook_mailbox_list_created(list);
+
+	llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1);
+	llist->super = list->v;
+	list->v.delete_mailbox = lazy_expunge_mailbox_list_delete;
+
+	if (!lazy_expunge_mailbox_list_module_id_set) {
+		lazy_expunge_mailbox_list_module_id = mailbox_list_module_id++;
+		lazy_expunge_mailbox_list_module_id_set = TRUE;
+	}
+
+	array_idx_set(&list->module_contexts,
+		      lazy_expunge_mailbox_list_module_id, &llist);
+}
+
 static void lazy_expunge_hook_client_created(struct client **client)
 {
 	struct lazy_expunge_mail_storage *lstorage;
@@ -517,14 +561,17 @@
 	lazy_expunge_next_hook_mail_storage_created =
 		hook_mail_storage_created;
 	hook_mail_storage_created = lazy_expunge_mail_storage_created;
+
+	lazy_expunge_next_hook_mailbox_list_created = hook_mailbox_list_created;
+	hook_mailbox_list_created = lazy_expunge_mailbox_list_created;
 }
 
 void lazy_expunge_plugin_deinit(void)
 {
-	if (lazy_expunge_storage_module_id_set) {
-		hook_client_created = lazy_expunge_hook_client_created;
+	if (getenv("LAZY_EXPUNGE") == NULL)
+		return;
 
-		hook_mail_storage_created =
-			lazy_expunge_next_hook_mail_storage_created;
-	}
+	hook_client_created = lazy_expunge_hook_client_created;
+	hook_mail_storage_created = lazy_expunge_next_hook_mail_storage_created;
+	hook_mailbox_list_created = lazy_expunge_next_hook_mailbox_list_created;
 }
--- a/src/plugins/mail-log/mail-log-plugin.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/mail-log/mail-log-plugin.c	Thu Mar 29 10:59:11 2007 +0300
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "str-sanitize.h"
 #include "mail-storage-private.h"
+#include "mailbox-list-private.h"
 #include "mail-log-plugin.h"
 
 #define MAILBOX_NAME_LOG_LEN 64
@@ -12,6 +13,13 @@
 #define MAIL_LOG_CONTEXT(obj) \
 	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
 					mail_log_storage_module_id))
+#define MAIL_LOG_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					mail_log_mailbox_list_module_id))
+
+struct mail_log_mailbox_list {
+	struct mailbox_list_vfuncs super;
+};
 
 struct mail_log_mail_storage {
 	struct mail_storage_vfuncs super;
@@ -29,10 +37,15 @@
 
 static void (*mail_log_next_hook_mail_storage_created)
 	(struct mail_storage *storage);
+static void (*mail_log_next_hook_mailbox_list_created)
+	(struct mailbox_list *list);
 
 static unsigned int mail_log_storage_module_id = 0;
 static bool mail_log_storage_module_id_set = FALSE;
 
+static unsigned int mail_log_mailbox_list_module_id = 0;
+static bool mail_log_mailbox_list_module_id_set = FALSE;
+
 static void mail_log_action(struct mail *mail, const char *action)
 {
 	const char *msgid, *mailbox_str;
@@ -160,11 +173,11 @@
 }
 
 static int
-mail_log_mailbox_delete(struct mail_storage *storage, const char *name)
+mail_log_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
-	struct mail_log_mail_storage *lstorage = MAIL_LOG_CONTEXT(storage);
+	struct mail_log_mailbox_list *llist = MAIL_LOG_LIST_CONTEXT(list);
 
-	if (lstorage->super.mailbox_delete(storage, name) < 0)
+	if (llist->super.delete_mailbox(list, name) < 0)
 		return -1;
 
 	i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN));
@@ -181,7 +194,6 @@
 	lstorage = p_new(storage->pool, struct mail_log_mail_storage, 1);
 	lstorage->super = storage->v;
 	storage->v.mailbox_open = mail_log_mailbox_open;
-	storage->v.mailbox_delete = mail_log_mailbox_delete;
 
 	if (!mail_log_storage_module_id_set) {
 		mail_log_storage_module_id = mail_storage_module_id++;
@@ -192,17 +204,37 @@
 		      mail_log_storage_module_id, &lstorage);
 }
 
+static void mail_log_mailbox_list_created(struct mailbox_list *list)
+{
+	struct mail_log_mailbox_list *llist;
+
+	if (mail_log_next_hook_mailbox_list_created != NULL)
+		mail_log_next_hook_mailbox_list_created(list);
+
+	llist = p_new(list->pool, struct mail_log_mailbox_list, 1);
+	llist->super = list->v;
+	list->v.delete_mailbox = mail_log_mailbox_list_delete;
+
+	if (!mail_log_mailbox_list_module_id_set) {
+		mail_log_mailbox_list_module_id = mailbox_list_module_id++;
+		mail_log_mailbox_list_module_id_set = TRUE;
+	}
+
+	array_idx_set(&list->module_contexts,
+		      mail_log_mailbox_list_module_id, &llist);
+}
+
 void mail_log_plugin_init(void)
 {
-	mail_log_next_hook_mail_storage_created =
-		hook_mail_storage_created;
+	mail_log_next_hook_mail_storage_created = hook_mail_storage_created;
 	hook_mail_storage_created = mail_log_mail_storage_created;
+
+	mail_log_next_hook_mailbox_list_created = hook_mailbox_list_created;
+	hook_mailbox_list_created = mail_log_mailbox_list_created;
 }
 
 void mail_log_plugin_deinit(void)
 {
-	if (mail_log_storage_module_id_set) {
-		hook_mail_storage_created =
-			mail_log_next_hook_mail_storage_created;
-	}
+	hook_mail_storage_created = mail_log_next_hook_mail_storage_created;
+	hook_mailbox_list_created = mail_log_next_hook_mailbox_list_created;
 }
--- a/src/plugins/quota/quota-plugin.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/quota/quota-plugin.c	Thu Mar 29 10:59:11 2007 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "mail-storage.h"
+#include "mailbox-list-private.h"
 #include "quota.h"
 #include "quota-plugin.h"
 
@@ -11,6 +12,7 @@
 extern void (*hook_mail_storage_created)(struct mail_storage *storage);
 
 void (*quota_next_hook_mail_storage_created)(struct mail_storage *storage);
+void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list);
 
 const char *quota_plugin_version = PACKAGE_VERSION;
 struct quota *quota_set;
@@ -74,9 +76,11 @@
 	}
 	t_pop();
 
-	quota_next_hook_mail_storage_created =
-		hook_mail_storage_created;
+	quota_next_hook_mail_storage_created = hook_mail_storage_created;
 	hook_mail_storage_created = quota_mail_storage_created;
+
+	quota_next_hook_mailbox_list_created = hook_mailbox_list_created;
+	hook_mailbox_list_created = quota_mailbox_list_created;
 }
 
 void quota_plugin_deinit(void)
@@ -84,6 +88,8 @@
 	if (quota_set != NULL) {
 		hook_mail_storage_created =
 			quota_next_hook_mail_storage_created;
+		hook_mailbox_list_created =
+			quota_next_hook_mailbox_list_created;
 		quota_deinit(quota_set);
 	}
 }
--- a/src/plugins/quota/quota-plugin.h	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/quota/quota-plugin.h	Thu Mar 29 10:59:11 2007 +0300
@@ -5,11 +5,14 @@
 
 extern void (*quota_next_hook_mail_storage_created)
 	(struct mail_storage *storage);
+extern void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list);
+
 /* "quota" symbol already exists in OSX, so we'll use this slightly uglier
    name. */
 extern struct quota *quota_set;
 
 void quota_mail_storage_created(struct mail_storage *storage);
+void quota_mailbox_list_created(struct mailbox_list *list);
 
 void quota_plugin_init(void);
 void quota_plugin_deinit(void);
--- a/src/plugins/quota/quota-storage.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/quota/quota-storage.c	Thu Mar 29 10:59:11 2007 +0300
@@ -5,6 +5,7 @@
 #include "istream.h"
 #include "mail-search.h"
 #include "mail-storage-private.h"
+#include "mailbox-list-private.h"
 #include "quota-private.h"
 #include "quota-plugin.h"
 
@@ -13,6 +14,15 @@
 #define QUOTA_CONTEXT(obj) \
 	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
 					quota_storage_module_id))
+#define QUOTA_LIST_CONTEXT(obj) \
+	*((void **)array_idx_modifiable(&(obj)->module_contexts, \
+					quota_mailbox_list_module_id))
+
+struct quota_mailbox_list {
+	struct mailbox_list_vfuncs super;
+
+	struct mail_storage *storage;
+};
 
 struct quota_mail_storage {
 	struct mail_storage_vfuncs super;
@@ -31,6 +41,9 @@
 static unsigned int quota_storage_module_id = 0;
 static bool quota_storage_module_id_set = FALSE;
 
+static unsigned int quota_mailbox_list_module_id = 0;
+static bool quota_mailbox_list_module_id_set = FALSE;
+
 static int quota_mail_expunge(struct mail *_mail)
 {
 	struct mail_private *mail = (struct mail_private *)_mail;
@@ -247,9 +260,10 @@
 	return box;
 }
 
-static int quota_mailbox_delete(struct mail_storage *storage, const char *name)
+static int
+quota_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
-	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
+	struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
 	struct mailbox *box;
 	struct mail_search_context *ctx;
         struct mailbox_transaction_context *t;
@@ -262,7 +276,7 @@
 	   and free the quota for all the messages existing in it. Open the
 	   mailbox locked so that other processes can't mess up the quota
 	   calculations by adding/removing mails while we're doing this. */
-	box = mailbox_open(storage, name, NULL, MAILBOX_OPEN_FAST |
+	box = mailbox_open(qlist->storage, name, NULL, MAILBOX_OPEN_FAST |
 			   MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_KEEP_LOCKED);
 	if (box == NULL)
 		return -1;
@@ -287,7 +301,7 @@
 	mailbox_close(&box);
 	/* FIXME: here's an unfortunate race condition */
 	return ret < 0 ? -1 :
-		qstorage->super.mailbox_delete(storage, name);
+		qlist->super.delete_mailbox(list, name);
 }
 
 static void quota_storage_destroy(struct mail_storage *storage)
@@ -301,16 +315,18 @@
 
 void quota_mail_storage_created(struct mail_storage *storage)
 {
+	struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(storage->list);
 	struct quota_mail_storage *qstorage;
 
 	if (quota_next_hook_mail_storage_created != NULL)
 		quota_next_hook_mail_storage_created(storage);
 
+	qlist->storage = storage;
+
 	qstorage = p_new(storage->pool, struct quota_mail_storage, 1);
 	qstorage->super = storage->v;
 	storage->v.destroy = quota_storage_destroy;
 	storage->v.mailbox_open = quota_mailbox_open;
-	storage->v.mailbox_delete = quota_mailbox_delete;
 
 	if (!quota_storage_module_id_set) {
 		quota_storage_module_id = mail_storage_module_id++;
@@ -325,3 +341,23 @@
 		quota_add_user_storage(quota_set, storage);
 	}
 }
+
+void quota_mailbox_list_created(struct mailbox_list *list)
+{
+	struct quota_mailbox_list *qlist;
+
+	if (quota_next_hook_mailbox_list_created != NULL)
+		quota_next_hook_mailbox_list_created(list);
+
+	qlist = p_new(list->pool, struct quota_mailbox_list, 1);
+	qlist->super = list->v;
+	list->v.delete_mailbox = quota_mailbox_list_delete;
+
+	if (!quota_mailbox_list_module_id_set) {
+		quota_mailbox_list_module_id = mailbox_list_module_id++;
+		quota_mailbox_list_module_id_set = TRUE;
+	}
+
+	array_idx_set(&list->module_contexts,
+		      quota_mailbox_list_module_id, &qlist);
+}
--- a/src/plugins/quota/quota.c	Wed Mar 28 23:07:38 2007 +0300
+++ b/src/plugins/quota/quota.c	Thu Mar 29 10:59:11 2007 +0300
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "array.h"
 #include "hash.h"
+#include "mailbox-list-private.h"
 #include "quota-private.h"
 #include "quota-fs.h"
 #include <stdlib.h>
@@ -410,7 +411,7 @@
 	/* the quota information comes from userdb (or even config file),
 	   so there's really no way to support this until some major changes
 	   are done */
-	*error_r = MAIL_STORAGE_ERR_NO_PERMISSION;
+	*error_r = MAILBOX_LIST_ERR_NO_PERMISSION;
 	return -1;
 }