changeset 10676:1fd7833c16ca HEAD

lib-storage: Mailbox deletion API changed. Mailbox deletion should now be free of race conditions. The actual file deletion code is now responsibility of mailbox_list backend.
author Timo Sirainen <tss@iki.fi>
date Tue, 09 Feb 2010 04:11:53 +0200
parents 9cefc944d82a
children 93fe3aa23bdb
files src/dsync/dsync-worker-local.c src/imap/cmd-delete.c src/lib-storage/index/cydir/cydir-storage.c src/lib-storage/index/dbox-common/dbox-mail.h src/lib-storage/index/dbox-common/dbox-storage.c src/lib-storage/index/dbox-common/dbox-storage.h src/lib-storage/index/dbox-multi/mdbox-map.c src/lib-storage/index/dbox-multi/mdbox-map.h src/lib-storage/index/dbox-multi/mdbox-storage.c src/lib-storage/index/dbox-single/sdbox-storage.c src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/index-transaction.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/index/raw/raw-storage.c src/lib-storage/index/shared/shared-list.c src/lib-storage/list/Makefile.am src/lib-storage/list/mailbox-list-delete.c src/lib-storage/list/mailbox-list-delete.h src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-fs.h src/lib-storage/list/mailbox-list-maildir.c src/lib-storage/list/mailbox-list-maildir.h src/lib-storage/mail-copy.h src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c src/lib-storage/mailbox-list.h src/lib-storage/test-mailbox.c src/plugins/acl/acl-mailbox-list.c src/plugins/acl/acl-mailbox.c src/plugins/lazy-expunge/lazy-expunge-plugin.c src/plugins/listescape/listescape-plugin.c src/plugins/mail-log/mail-log-plugin.c src/plugins/notify/notify-noop.c src/plugins/notify/notify-plugin-private.h src/plugins/notify/notify-plugin.c src/plugins/notify/notify-plugin.h src/plugins/notify/notify-storage.c src/plugins/quota/quota-storage.c src/plugins/virtual/virtual-storage.c
diffstat 45 files changed, 819 insertions(+), 1140 deletions(-) [+]
line wrap: on
line diff
--- a/src/dsync/dsync-worker-local.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/dsync/dsync-worker-local.c	Tue Feb 09 04:11:53 2010 +0200
@@ -1105,6 +1105,7 @@
 		(struct local_dsync_worker *)_worker;
 	struct local_dsync_mailbox *lbox;
 	const mailbox_guid_t *mailbox = &dsync_box->mailbox_guid;
+	struct mailbox *box;
 
 	lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
 	if (lbox == NULL) {
@@ -1116,12 +1117,15 @@
 
 	mailbox_list_set_changelog_timestamp(lbox->ns->list,
 					     dsync_box->last_change);
-	if (mailbox_list_delete_mailbox(lbox->ns->list,
-					lbox->storage_name) < 0) {
+	box = mailbox_alloc(lbox->ns->list, lbox->storage_name, NULL, 0);
+	if (mailbox_delete(box) < 0) {
+		struct mail_storage *storage = mailbox_get_storage(box);
+
 		i_error("Can't delete mailbox %s: %s", lbox->storage_name,
-			mailbox_list_get_last_error(lbox->ns->list, NULL));
+			mail_storage_get_last_error(storage, NULL));
 		dsync_worker_set_failure(_worker);
 	}
+	mailbox_free(&box);
 	mailbox_list_set_changelog_timestamp(lbox->ns->list, (time_t)-1);
 }
 
--- a/src/imap/cmd-delete.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/imap/cmd-delete.c	Tue Feb 09 04:11:53 2010 +0200
@@ -7,7 +7,7 @@
 {
 	struct client *client = cmd->client;
 	struct mail_namespace *ns;
-	struct mailbox *mailbox;
+	struct mailbox *box;
 	const char *name;
 
 	/* <mailbox> */
@@ -25,19 +25,18 @@
 	if (ns == NULL)
 		return TRUE;
 
-	mailbox = mailbox_alloc(ns->list, name, NULL, 0);
+	box = mailbox_alloc(ns->list, name, NULL, 0);
 	if (client->mailbox != NULL &&
-	    mailbox_backends_equal(mailbox, client->mailbox)) {
+	    mailbox_backends_equal(box, client->mailbox)) {
 		/* deleting selected mailbox. close it first */
 		client_search_updates_free(client);
 		mailbox_free(&client->mailbox);
 	}
-	mailbox_free(&mailbox);
 
-	if (mailbox_list_delete_mailbox(ns->list, name) < 0)
-		client_send_list_error(cmd, ns->list);
-	else {
+	if (mailbox_delete(box) < 0)
+		client_send_storage_error(cmd, mailbox_get_storage(box));
+	else
 		client_send_tagline(cmd, "OK Delete completed.");
-	}
+	mailbox_free(&box);
 	return TRUE;
 }
--- a/src/lib-storage/index/cydir/cydir-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -1,31 +1,16 @@
 /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "mkdir-parents.h"
+#include "mail-copy.h"
 #include "index-mail.h"
-#include "mail-copy.h"
 #include "cydir-sync.h"
 #include "cydir-storage.h"
 
-#include <unistd.h>
-#include <dirent.h>
 #include <sys/stat.h>
 
-#define CYDIR_LIST_CONTEXT(obj) \
-	MODULE_CONTEXT(obj, cydir_mailbox_list_module)
-
-struct cydir_mailbox_list {
-	union mailbox_list_module_context module_ctx;
-};
-
 extern struct mail_storage cydir_storage;
 extern struct mailbox cydir_mailbox;
 
-static MODULE_CONTEXT_DEFINE_INIT(cydir_mailbox_list_module,
-				  &mailbox_list_module_register);
-
 static struct mail_storage *cydir_storage_alloc(void)
 {
 	struct cydir_storage *storage;
@@ -128,97 +113,6 @@
 		index_storage_mailbox_update(box, update);
 }
 
-static int
-cydir_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;
-
-	dir = opendir(path);
-	if (dir == NULL) {
-		if (!mailbox_list_set_error_from_errno(list)) {
-			mailbox_list_set_critical(list,
-				"opendir(%s) failed: %m", path);
-		}
-		return -1;
-	}
-
-	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);
-
-		/* 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(%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;
-	}
-
-	if (!unlinked_something) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-			t_strdup_printf("Directory %s isn't empty, "
-					"can't delete it.", name));
-		return -1;
-	}
-	return 0;
-}
-
-static int
-cydir_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	struct cydir_mailbox_list *mlist = CYDIR_LIST_CONTEXT(list);
-	struct stat st;
-	const char *src;
-
-	/* delete the index and control directories */
-	if (mlist->module_ctx.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, MAIL_ERROR_NOTFOUND,
-			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		return -1;
-	}
-
-	return cydir_delete_nonrecursive(list, src, name);
-}
-
 static void cydir_notify_changes(struct mailbox *box)
 {
 	struct cydir_mailbox *mbox = (struct cydir_mailbox *)box;
@@ -286,15 +180,7 @@
 static void cydir_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
 				   struct mailbox_list *list)
 {
-	struct cydir_mailbox_list *mlist;
-
-	mlist = p_new(list->pool, struct cydir_mailbox_list, 1);
-	mlist->module_ctx.super = list->v;
-
 	list->v.iter_is_mailbox = cydir_list_iter_is_mailbox;
-	list->v.delete_mailbox = cydir_list_delete_mailbox;
-
-	MODULE_CONTEXT_SET(list, cydir_mailbox_list_module, mlist);
 }
 
 struct mail_storage cydir_storage = {
@@ -323,6 +209,7 @@
 		index_storage_mailbox_close,
 		cydir_mailbox_create,
 		index_storage_mailbox_update,
+		index_storage_mailbox_delete,
 		index_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/dbox-common/dbox-mail.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-mail.h	Tue Feb 09 04:11:53 2010 +0200
@@ -1,6 +1,8 @@
 #ifndef DBOX_MAIL_H
 #define DBOX_MAIL_H
 
+#include "index-mail.h"
+
 struct dbox_mail {
 	struct index_mail imail;
 
--- a/src/lib-storage/index/dbox-common/dbox-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -2,10 +2,7 @@
 
 #include "lib.h"
 #include "ioloop.h"
-#include "randgen.h"
-#include "hex-binary.h"
 #include "mkdir-parents.h"
-#include "unlink-directory.h"
 #include "unlink-old-files.h"
 #include "mailbox-uidvalidity.h"
 #include "mailbox-list-private.h"
@@ -326,123 +323,3 @@
 	}
 	return 0;
 }
-
-static const char *dbox_get_trash_dest(const char *trash_dir)
-{
-	const char *path;
-	unsigned char randbuf[16];
-	struct stat st;
-
-	do {
-		random_fill_weak(randbuf, sizeof(randbuf));
-		path = t_strconcat(trash_dir, "/",
-			binary_to_hex(randbuf, sizeof(randbuf)), NULL);
-	} while (lstat(path, &st) == 0);
-	return path;
-}
-
-int dbox_list_delete_mailbox1(struct mailbox_list *list, const char *name,
-			      const char **trash_dest_r)
-{
-	struct stat st;
-	const char *path, *trash_dir, *trash_dest;
-	int ret;
-
-	path = mailbox_list_get_path(list, name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	trash_dir = mailbox_list_get_path(list, NULL,
-					  MAILBOX_LIST_PATH_TYPE_DIR);
-	trash_dir = t_strconcat(trash_dir, "/"DBOX_TRASH_DIR_NAME, NULL);
-	trash_dest = *trash_dest_r = dbox_get_trash_dest(trash_dir);
-
-	/* first try renaming the actual mailbox to trash directory */
-	ret = rename(path, trash_dest);
-	if (ret < 0 && errno == ENOENT) {
-		/* either source mailbox doesn't exist or trash directory
-		   doesn't exist. try creating the trash and retrying. */
-		const char *origin;
-		mode_t mode;
-		gid_t gid;
-
-		mailbox_list_get_dir_permissions(list, NULL, &mode,
-						 &gid, &origin);
-		if (mkdir_parents_chgrp(trash_dir, mode, gid, origin) < 0 &&
-		    errno != EEXIST) {
-			mailbox_list_set_critical(list,
-				"mkdir(%s) failed: %m", trash_dir);
-			return -1;
-		}
-		ret = rename(path, trash_dest);
-	}
-	if (ret == 0)
-		return 1;
-	else if (errno != ENOENT) {
-		mailbox_list_set_critical(list, "stat(%s) failed: %m", path);
-		return -1;
-	} else {
-		/* mailbox not found - what about the directory? */
-		path = mailbox_list_get_path(list, name,
-					     MAILBOX_LIST_PATH_TYPE_DIR);
-		if (stat(path, &st) == 0) {
-			/* delete the directory */
-		} else if (errno == ENOENT) {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-			return -1;
-		} else if (!mailbox_list_set_error_from_errno(list)) {
-			mailbox_list_set_critical(list, "stat(%s) failed: %m",
-						  path);
-			return -1;
-		}
-		return 0;
-	}
-}
-
-int dbox_list_delete_mailbox2(struct mailbox_list *list, const char *name,
-			      int ret, const char *trash_dest)
-{
-	const char *path, *alt_path;
-	bool deleted = FALSE;
-
-	path = mailbox_list_get_path(list, name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (ret > 0) {
-		if (unlink_directory(trash_dest, TRUE) < 0) {
-			mailbox_list_set_critical(list,
-				"unlink_directory(%s) failed: %m", trash_dest);
-			ret = -1;
-		}
-		/* if there's an alt path, delete it too */
-		alt_path = dbox_get_alt_path(list, path);
-		if (alt_path != NULL) {
-			if (unlink_directory(alt_path, TRUE) < 0) {
-				mailbox_list_set_critical(list,
-					"unlink_directory(%s) failed: %m", alt_path);
-				ret = -1;
-			}
-		}
-		/* try to delete the parent directory also */
-		deleted = TRUE;
-		path = mailbox_list_get_path(list, name,
-					     MAILBOX_LIST_PATH_TYPE_DIR);
-	}
-
-	alt_path = dbox_get_alt_path(list, path);
-	if (alt_path != NULL)
-		(void)rmdir(alt_path);
-
-	if (rmdir(path) == 0)
-		return ret;
-	else if (errno == ENOTEMPTY) {
-		if (deleted)
-			return ret;
-		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-			t_strdup_printf("Directory %s isn't empty, "
-					"can't delete it.", name));
-	} else if (!mailbox_list_set_error_from_errno(list)) {
-		mailbox_list_set_critical(list, "rmdir() failed for %s: %m",
-					  path);
-	}
-	return -1;
-}
-
--- a/src/lib-storage/index/dbox-common/dbox-storage.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.h	Tue Feb 09 04:11:53 2010 +0200
@@ -62,9 +62,4 @@
 			     struct mailbox_list *newlist, const char *newname,
 			     bool rename_children);
 
-int dbox_list_delete_mailbox1(struct mailbox_list *list, const char *name,
-			      const char **trash_dest_r);
-int dbox_list_delete_mailbox2(struct mailbox_list *list, const char *name,
-			      int ret, const char *trash_dest);
-
 #endif
--- a/src/lib-storage/index/dbox-multi/mdbox-map.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-map.c	Tue Feb 09 04:11:53 2010 +0200
@@ -371,6 +371,11 @@
 	return 0;
 }
 
+void dbox_map_transaction_set_failed(struct dbox_map_transaction_context *ctx)
+{
+	ctx->success = FALSE;
+}
+
 void dbox_map_transaction_free(struct dbox_map_transaction_context **_ctx)
 {
 	struct dbox_map_transaction_context *ctx = *_ctx;
--- a/src/lib-storage/index/dbox-multi/mdbox-map.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-map.h	Tue Feb 09 04:11:53 2010 +0200
@@ -53,6 +53,7 @@
 /* Write transaction to map and leave it locked. Call _free() to update tail
    offset and unlock. */
 int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx);
+void dbox_map_transaction_set_failed(struct dbox_map_transaction_context *ctx);
 void dbox_map_transaction_free(struct dbox_map_transaction_context **ctx);
 
 int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -2,17 +2,10 @@
 
 #include "lib.h"
 #include "array.h"
-#include "ioloop.h"
-#include "str.h"
-#include "hex-binary.h"
-#include "randgen.h"
 #include "mkdir-parents.h"
-#include "unlink-directory.h"
-#include "unlink-old-files.h"
-#include "index-mail.h"
-#include "mail-copy.h"
 #include "mail-index-modseq.h"
-#include "mailbox-uidvalidity.h"
+#include "mail-index-alloc-cache.h"
+#include "mailbox-log.h"
 #include "dbox-mail.h"
 #include "dbox-save.h"
 #include "mdbox-map.h"
@@ -21,12 +14,6 @@
 #include "mdbox-storage-rebuild.h"
 #include "mdbox-storage.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-
 #define MDBOX_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, mdbox_mailbox_list_module)
 
@@ -234,8 +221,7 @@
 	}
 
 	if (mail_index_transaction_commit(&trans) < 0) {
-		mail_storage_set_internal_error(box->storage);
-		mail_index_reset_error(box->index);
+		mail_storage_set_index_error(box);
 		return -1;
 	}
 	return 0;
@@ -292,44 +278,22 @@
 	return mdbox_write_index_header(box, update);
 }
 
-static int
-mdbox_mailbox_unref_mails(struct mailbox_list *list, const char *path)
+static int mdbox_mailbox_unref_mails(struct mdbox_mailbox *mbox)
 {
-	struct mdbox_storage *storage =
-		(struct mdbox_storage *)list->ns->storage;
-	const struct mail_storage_settings *old_set;
-	struct mail_storage_settings tmp_set;
-	struct mailbox *box;
-	struct mdbox_mailbox *mbox;
+	struct dbox_map_transaction_context *map_trans;
 	const struct mail_index_header *hdr;
 	const struct mdbox_mail_index_record *dbox_rec;
-	struct dbox_map_transaction_context *map_trans;
 	ARRAY_TYPE(uint32_t) map_uids;
 	const void *data;
 	bool expunged;
 	uint32_t seq;
 	int ret;
 
-	old_set = list->mail_set;
-	tmp_set = *list->mail_set;
-	tmp_set.mail_full_filesystem_access = TRUE;
-	list->mail_set = &tmp_set;
-	box = mdbox_mailbox_alloc(&storage->storage.storage, list, path, NULL,
-				  MAILBOX_FLAG_IGNORE_ACLS |
-				  MAILBOX_FLAG_KEEP_RECENT);
-	ret = mailbox_open(box);
-	list->mail_set = old_set;
-	if (ret < 0) {
-		mailbox_free(&box);
-		return -1;
-	}
-	mbox = (struct mdbox_mailbox *)box;
-
 	/* get a list of all map_uids in this mailbox */
 	i_array_init(&map_uids, 128);
-	hdr = mail_index_get_header(box->view);
+	hdr = mail_index_get_header(mbox->box.view);
 	for (seq = 1; seq <= hdr->messages_count; seq++) {
-		mail_index_lookup_ext(box->view, seq, mbox->ext_id,
+		mail_index_lookup_ext(mbox->box.view, seq, mbox->ext_id,
 				      &data, &expunged);
 		dbox_rec = data;
 		if (dbox_rec == NULL) {
@@ -341,37 +305,24 @@
 	}
 
 	/* unreference the map_uids */
-	map_trans = dbox_map_transaction_begin(storage->map, FALSE);
+	map_trans = dbox_map_transaction_begin(mbox->storage->map, FALSE);
 	ret = dbox_map_update_refcounts(map_trans, &map_uids, -1);
 	if (ret == 0)
 		ret = dbox_map_transaction_commit(map_trans);
 	dbox_map_transaction_free(&map_trans);
 	array_free(&map_uids);
-	mailbox_free(&box);
 	return ret;
 }
 
-static int
-mdbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
+static int mdbox_mailbox_delete(struct mailbox *box)
 {
-	struct mdbox_mailbox_list *mlist = MDBOX_LIST_CONTEXT(list);
-	const char *trash_dest;
-	int ret;
-
-	/* delete the index and control directories */
-	if (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
-		return -1;
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
 
-	if ((ret = dbox_list_delete_mailbox1(list, name, &trash_dest)) < 0)
-		return -1;
-	if (ret > 0) {
-		if (mdbox_mailbox_unref_mails(list, trash_dest) < 0) {
-			/* we've already renamed it. there's no going back. */
-			mailbox_list_set_internal_error(list);
-			ret = -1;
-		}
+	if (box->opened) {
+		if (mdbox_mailbox_unref_mails(mbox) < 0)
+			return -1;
 	}
-	return dbox_list_delete_mailbox2(list, name, ret, trash_dest);
+	return index_storage_mailbox_delete(box);
 }
 
 static int
@@ -398,7 +349,6 @@
 	mlist->module_ctx.super = list->v;
 
 	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
-	list->v.delete_mailbox = mdbox_list_delete_mailbox;
 	list->v.rename_mailbox = mdbox_list_rename_mailbox;
 	list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
 
@@ -431,6 +381,7 @@
 		index_storage_mailbox_close,
 		dbox_mailbox_create,
 		mdbox_mailbox_update,
+		mdbox_mailbox_delete,
 		mdbox_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -1,26 +1,13 @@
 /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
-#include "ioloop.h"
-#include "hex-binary.h"
-#include "randgen.h"
-#include "mkdir-parents.h"
-#include "unlink-directory.h"
-#include "unlink-old-files.h"
-#include "index-mail.h"
 #include "mail-index-modseq.h"
-#include "mailbox-uidvalidity.h"
 #include "dbox-mail.h"
 #include "dbox-save.h"
 #include "sdbox-file.h"
 #include "sdbox-sync.h"
 #include "sdbox-storage.h"
 
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
 #define SDBOX_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, sdbox_mailbox_list_module)
 
@@ -232,22 +219,6 @@
 }
 
 static int
-sdbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	struct sdbox_mailbox_list *mlist = SDBOX_LIST_CONTEXT(list);
-	const char *trash_dest;
-	int ret;
-
-	/* delete the index and control directories */
-	if (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
-		return -1;
-
-	if ((ret = dbox_list_delete_mailbox1(list, name, &trash_dest)) < 0)
-		return -1;
-	return dbox_list_delete_mailbox2(list, name, ret, trash_dest);
-}
-
-static int
 sdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
 			  struct mailbox_list *newlist, const char *newname,
 			  bool rename_children)
@@ -271,7 +242,6 @@
 	mlist->module_ctx.super = list->v;
 
 	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
-	list->v.delete_mailbox = sdbox_list_delete_mailbox;
 	list->v.rename_mailbox = sdbox_list_rename_mailbox;
 	list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
 
@@ -304,6 +274,7 @@
 		index_storage_mailbox_close,
 		dbox_mailbox_create,
 		dbox_mailbox_update,
+		index_storage_mailbox_delete,
 		dbox_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/index-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/index-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -10,6 +10,7 @@
 #include "mail-index-alloc-cache.h"
 #include "mail-index-private.h"
 #include "mail-index-modseq.h"
+#include "mailbox-log.h"
 #include "mailbox-list-private.h"
 #include "index-storage.h"
 #include "index-mail.h"
@@ -245,9 +246,11 @@
 	if (hook_mailbox_opened != NULL)
 		hook_mailbox_opened(box);
 
-	if (mail_index_is_deleted(box->index)) {
-		mailbox_set_deleted(box);
-		return -1;
+	if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) {
+		if (mail_index_is_deleted(box->index)) {
+			mailbox_set_deleted(box);
+			return -1;
+		}
 	}
 	return 0;
 }
@@ -305,10 +308,8 @@
 {
 	if ((feature & MAILBOX_FEATURE_CONDSTORE) != 0) {
 		box->enabled_features |= MAILBOX_FEATURE_CONDSTORE;
-		if (!box->opened) {
-			if (mailbox_open(box) < 0)
-				return -1;
-		}
+		if (mailbox_open(box) < 0)
+			return -1;
 		T_BEGIN {
 			mail_index_modseq_enable(box->index);
 		} T_END;
@@ -393,10 +394,8 @@
 	struct mail_index_transaction *trans;
 	int ret;
 
-	if (!box->opened) {
-		if (mailbox_open(box) < 0)
-			return -1;
-	}
+	if (mailbox_open(box) < 0)
+		return -1;
 	if (update->cache_fields != NULL)
 		index_storage_mailbox_update_cache_fields(box, update);
 
@@ -435,6 +434,56 @@
 	return ret;
 }
 
+int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted)
+{
+	uint8_t dir_sha128[MAIL_GUID_128_SIZE];
+	enum mail_error error;
+
+	if (mailbox_list_delete_dir(box->list, box->name) == 0)
+		return 0;
+
+	(void)mailbox_list_get_last_error(box->list, &error);
+	if (error != MAIL_ERROR_NOTFOUND || !mailbox_deleted) {
+		mail_storage_copy_list_error(box->storage, box->list);
+		return -1;
+	}
+	/* failed directory deletion, but mailbox deletion succeeded.
+	   this was probably maildir++, which internally deleted the
+	   directory as well. add changelog record about that too. */
+	mailbox_name_get_sha128(box->name, dir_sha128);
+	mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_DIR,
+				dir_sha128);
+	return 0;
+}
+
+int index_storage_mailbox_delete(struct mailbox *box)
+{
+	struct mailbox_status status;
+
+	if (!box->opened) {
+		/* \noselect mailbox, try deleting only the directory */
+		return index_storage_mailbox_delete_dir(box, FALSE);
+	}
+
+	mailbox_get_status(box, STATUS_GUID, &status);
+
+	/* Make sure the indexes are closed before trying to delete the
+	   directory that contains them. It can still fail with some NFS
+	   implementations if indexes are opened by another session, but
+	   that can't really be helped. */
+	mailbox_close(box);
+	mail_index_alloc_cache_destroy_unrefed();
+
+	if (box->list->v.delete_mailbox(box->list, box->name) < 0) {
+		mail_storage_copy_list_error(box->storage, box->list);
+		return -1;
+	} 
+
+	mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_MAILBOX,
+				status.mailbox_guid);
+	return index_storage_mailbox_delete_dir(box, TRUE);
+}
+
 bool index_storage_is_readonly(struct mailbox *box)
 {
 	return (box->flags & MAILBOX_FLAG_READONLY) != 0 ||
--- a/src/lib-storage/index/index-storage.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/index-storage.h	Tue Feb 09 04:11:53 2010 +0200
@@ -76,6 +76,8 @@
 void index_storage_mailbox_close(struct mailbox *box);
 int index_storage_mailbox_update(struct mailbox *box,
 				 const struct mailbox_update *update);
+int index_storage_mailbox_delete(struct mailbox *box);
+int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted);
 
 bool index_storage_is_readonly(struct mailbox *box);
 bool index_storage_allow_new_keywords(struct mailbox *box);
--- a/src/lib-storage/index/index-transaction.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/index-transaction.c	Tue Feb 09 04:11:53 2010 +0200
@@ -121,8 +121,10 @@
 	_t->changes = changes_r;
 
 	ret = mail_index_transaction_commit_full(&itrans, &result);
-	if (ret < 0 && mail_index_is_deleted(_t->box->index))
-		mailbox_set_deleted(_t->box);
+	_t = NULL;
+
+	if (ret < 0 && mail_index_is_deleted(box->index))
+		mailbox_set_deleted(box);
 
 	changes_r->ignored_uid_changes = result.ignored_uid_changes;
 	changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
--- a/src/lib-storage/index/maildir/maildir-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -2,31 +2,24 @@
 
 #include "lib.h"
 #include "ioloop.h"
-#include "array.h"
-#include "hostpid.h"
-#include "str.h"
 #include "mkdir-parents.h"
 #include "eacces-error.h"
 #include "unlink-directory.h"
 #include "unlink-old-files.h"
-#include "mailbox-log.h"
 #include "mailbox-uidvalidity.h"
+#include "list/mailbox-list-maildir.h"
 #include "maildir-storage.h"
 #include "maildir-uidlist.h"
 #include "maildir-keywords.h"
 #include "maildir-sync.h"
 #include "index-mail.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <unistd.h>
 #include <sys/stat.h>
 
 #define MAILDIR_LIST_CONTEXT(obj) \
 	MODULE_CONTEXT(obj, maildir_mailbox_list_module)
 
-struct maildir_mailbox_list {
+struct maildir_mailbox_list_context {
 	union mailbox_list_module_context module_ctx;
 	const struct maildir_settings *set;
 };
@@ -54,7 +47,7 @@
 static bool maildir_storage_is_valid_existing_name(struct mailbox_list *list,
 						   const char *name)
 {
-	struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(list);
+	struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list);
 	const char *p;
 
 	if (!mlist->module_ctx.super.is_valid_existing_name(list, name))
@@ -70,7 +63,7 @@
 static bool maildir_storage_is_valid_create_name(struct mailbox_list *list,
 						 const char *name)
 {
-	struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(list);
+	struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list);
 	bool ret = TRUE;
 
 	if (!mlist->module_ctx.super.is_valid_create_name(list, name))
@@ -526,247 +519,13 @@
 	}
 }
 
-static const char *
-maildir_get_unlink_dest(struct mailbox_list *list, const char *name)
-{
-	const char *root_dir;
-	char sep;
-
-	if (list->mail_set->mail_full_filesystem_access &&
-	    (*name == '/' || *name == '~'))
-		return NULL;
-
-	if (strcmp(mailbox_list_get_driver_name(list),
-		   MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0) {
-		/* Not maildir++ driver. Don't use this trick. */
-		return NULL;
-	}
-
-	root_dir = mailbox_list_get_path(list, NULL,
-					 MAILBOX_LIST_PATH_TYPE_DIR);
-	sep = mailbox_list_get_hierarchy_sep(list);
-	return t_strdup_printf("%s/%c%c"MAILDIR_UNLINK_DIRNAME, root_dir,
-			       sep, sep);
-}
-
-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;
-
-	dir = opendir(path);
-	if (dir == NULL) {
-		if (errno == ENOENT) {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		} else {
-			mailbox_list_set_critical(list,
-				"opendir(%s) failed: %m", path);
-		}
-		return -1;
-	}
-
-	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;
-	}
-
-	if (!unlinked_something) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-			t_strdup_printf("Directory %s isn't empty, "
-					"can't delete it.", name));
-		return -1;
-	}
-	return 0;
-}
-
-static int
-maildir_delete_with_trash(struct mailbox_list *list, const char *src,
-			  const char *dest, const char *name)
-{
-	unsigned int count;
-
-	/* 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) {
-		if (errno == ENOENT) {
-			/* it was just deleted under us by
-			   another process */
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				T_MAIL_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 &&
-		    (errno != ENOTEMPTY || count >= 5)) {
-			mailbox_list_set_critical(list,
-				"unlink_directory(%s) failed: %m", dest);
-			return -1;
-		}
-		count++;
-	}
-
-	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;
-}
-
-static void mailbox_get_guid(struct mailbox_list *list, const char *name,
-			     uint8_t mailbox_guid[MAIL_GUID_128_SIZE])
-{
-	struct mailbox *box;
-	struct mailbox_status status;
-
-	box = mailbox_alloc(list, name, NULL, MAILBOX_FLAG_KEEP_RECENT);
-	if (mailbox_open(box) < 0)
-		memset(mailbox_guid, 0, MAIL_GUID_128_SIZE);
-	else {
-		mailbox_get_status(box, STATUS_GUID, &status);
-		memcpy(mailbox_guid, status.mailbox_guid, MAIL_GUID_128_SIZE);
-	}
-	mailbox_free(&box);
-}
-
-static int
-maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	union mailbox_list_module_context *mlist = MAILDIR_LIST_CONTEXT(list);
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
-	uint8_t dir_sha128[MAIL_GUID_128_SIZE];
-	struct stat st;
-	const char *src, *dest, *base;
-	int ret;
-
-	mailbox_get_guid(list, name, mailbox_guid);
-
-	/* delete the index and control directories */
-	if (mlist->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 (lstat(src, &st) != 0 && errno == ENOENT) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		return -1;
-	}
-
-	if (!S_ISDIR(st.st_mode)) {
-		/* a symlink most likely */
-		if (unlink(src) < 0 && errno != ENOENT) {
-			mailbox_list_set_critical(list,
-				"unlink(%s) failed: %m", src);
-			return -1;
-		}
-		return 0;
-	}
-
-	if (strcmp(name, "INBOX") == 0) {
-		/* we shouldn't get this far if this is the actual INBOX.
-		   more likely we're just deleting a namespace/INBOX.
-		   be anyway sure that we don't accidentally delete the entire
-		   maildir (INBOX explicitly configured to maildir root). */
-		base = mailbox_list_get_path(list, NULL,
-					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-		if (strcmp(base, src) == 0) {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
-					       "INBOX can't be deleted.");
-			return -1;
-		}
-	}
-
-	dest = maildir_get_unlink_dest(list, name);
-	if (dest == NULL) {
-		/* delete the directory directly without any renaming */
-		ret = maildir_delete_nonrecursive(list, src, name);
-	} else {
-		ret = maildir_delete_with_trash(list, src, dest, name);
-	}
-
-	if (ret == 0) {
-		mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_MAILBOX,
-					mailbox_guid);
-		mailbox_name_get_sha128(name, dir_sha128);
-		mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR,
-					dir_sha128);
-	}
-	return 0;
-}
-
 static int
 maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
 			    struct mailbox_list *newlist, const char *newname,
 			    bool rename_children)
 {
-	struct maildir_mailbox_list *oldmlist = MAILDIR_LIST_CONTEXT(oldlist);
+	struct maildir_mailbox_list_context *oldmlist =
+		MAILDIR_LIST_CONTEXT(oldlist);
 	const char *path1, *path2;
 
 	if (strcmp(oldname, "INBOX") == 0) {
@@ -819,6 +578,13 @@
 	}
 }
 
+static bool
+maildir_is_mailbox_dir(struct mailbox_list *list ATTR_UNUSED,
+		       const char *dir ATTR_UNUSED, const char *name)
+{
+	return maildir_is_internal_name(name);
+}
+
 static int
 maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
 			     	ATTR_UNUSED,
@@ -917,11 +683,12 @@
 				enum mailbox_list_file_type type,
 				enum mailbox_info_flags *flags)
 {
-	struct maildir_mailbox_list *mlist = MAILDIR_LIST_CONTEXT(ctx->list);
+	struct maildir_mailbox_list_context *mlist =
+		MAILDIR_LIST_CONTEXT(ctx->list);
 	int ret;
 
 	if (fname[1] == mailbox_list_get_hierarchy_sep(ctx->list) &&
-	    strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0) {
+	    strcmp(fname+2, MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME) == 0) {
 		const char *path;
 		struct stat st;
 
@@ -1002,12 +769,13 @@
 static void maildir_storage_add_list(struct mail_storage *storage,
 				     struct mailbox_list *list)
 {
-	struct maildir_mailbox_list *mlist;
+	struct maildir_mailbox_list_context *mlist;
 
-	mlist = p_new(list->pool, struct maildir_mailbox_list, 1);
+	mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1);
 	mlist->module_ctx.super = list->v;
 	mlist->set = mail_storage_get_driver_settings(storage);
 
+	list->v.is_mailbox_dir = maildir_is_mailbox_dir;
 	if (strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0) {
 		list->v.iter_is_mailbox = maildirplusplus_iter_is_mailbox;
 	} else {
@@ -1017,7 +785,6 @@
 			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;
 	MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist);
 }
@@ -1048,6 +815,7 @@
 		maildir_mailbox_close,
 		maildir_mailbox_create,
 		maildir_mailbox_update,
+		index_storage_mailbox_delete,
 		maildir_storage_get_status,
 		maildir_list_index_has_changed,
 		maildir_list_index_update_sync,
--- a/src/lib-storage/index/maildir/maildir-storage.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Tue Feb 09 04:11:53 2010 +0200
@@ -6,7 +6,6 @@
 #define MAILDIR_STORAGE_NAME "maildir"
 #define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions"
 #define MAILDIR_INDEX_PREFIX "dovecot.index"
-#define MAILDIR_UNLINK_DIRNAME "DOVECOT-TRASHED"
 #define MAILDIR_UIDVALIDITY_FNAME "dovecot-uidvalidity"
 
 /* "base,S=123:2," means:
--- a/src/lib-storage/index/mbox/mbox-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -2,11 +2,8 @@
 
 #include "lib.h"
 #include "ioloop.h"
-#include "array.h"
 #include "istream.h"
 #include "restrict-access.h"
-#include "mkdir-parents.h"
-#include "unlink-directory.h"
 #include "mbox-storage.h"
 #include "mbox-lock.h"
 #include "mbox-file.h"
@@ -15,10 +12,6 @@
 #include "mail-copy.h"
 #include "index-mail.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <sys/stat.h>
 
 /* How often to touch the dotlock file when using KEEP_LOCKED flag */
@@ -478,9 +471,8 @@
 	if ((ret = stat(box->path, &st)) == 0 && !S_ISDIR(st.st_mode))
 		return mbox_mailbox_open_existing(mbox);
 	else if (ret == 0) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
-			t_strdup_printf("Mailbox isn't selectable: %s",
-					box->name));
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
+				       "Mailbox isn't selectable");
 		return -1;
 	} else if (ENOTFOUND(errno)) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
@@ -703,79 +695,6 @@
 	}
 }
 
-static int mbox_list_delete_mailbox(struct mailbox_list *list,
-				    const char *name)
-{
-	struct mbox_mailbox_list *mlist = MBOX_LIST_CONTEXT(list);
-	struct stat st;
-	const char *path, *index_dir;
-
-	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, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		} else if (!mailbox_list_set_error_from_errno(list)) {
-			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 (!mailbox_list_set_error_from_errno(list)) {
-				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, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		} else if (errno == ENOTEMPTY) {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				t_strdup_printf("Directory %s isn't empty, "
-						"can't delete it.", name));
-		} else if (!mailbox_list_set_error_from_errno(list)) {
-			mailbox_list_set_critical(list,
-				"rmdir() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	/* delete index / control files first */
-	if (mlist->module_ctx.super.delete_mailbox(list, name) < 0)
-		return -1;
-
-	if (unlink(path) < 0) {
-		if (ENOTFOUND(errno)) {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		} else if (!mailbox_list_set_error_from_errno(list)) {
-			mailbox_list_set_critical(list,
-				"unlink() failed for %s: %m", path);
-		}
-		return -1;
-	}
-
-	return 0;
-}
-
 static void mbox_storage_add_list(struct mail_storage *storage,
 				  struct mailbox_list *list)
 {
@@ -791,7 +710,6 @@
 		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;
 	list->v.is_valid_existing_name = mbox_is_valid_existing_name;
 	list->v.is_valid_create_name = mbox_is_valid_create_name;
 
@@ -877,6 +795,7 @@
 		mbox_mailbox_close,
 		mbox_mailbox_create,
 		mbox_mailbox_update,
+		index_storage_mailbox_delete,
 		mbox_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/raw/raw-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/raw/raw-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -1,7 +1,6 @@
 /* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
-#include "array.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "index-mail.h"
@@ -109,14 +108,6 @@
 	return -1;
 }
 
-static int raw_list_delete_mailbox(struct mailbox_list *list,
-				   const char *name ATTR_UNUSED)
-{
-	mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
-			       "Raw mailbox deletion isn't supported");
-	return -1;
-}
-
 static void raw_notify_changes(struct mailbox *box ATTR_UNUSED)
 {
 }
@@ -167,7 +158,6 @@
 				 struct mailbox_list *list)
 {
 	list->v.iter_is_mailbox = raw_list_iter_is_mailbox;
-	list->v.delete_mailbox = raw_list_delete_mailbox;
 }
 
 struct mail_storage raw_storage = {
@@ -196,6 +186,7 @@
 		index_storage_mailbox_close,
 		raw_mailbox_create,
 		raw_mailbox_update,
+		index_storage_mailbox_delete,
 		index_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/shared/shared-list.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/index/shared/shared-list.c	Tue Feb 09 04:11:53 2010 +0200
@@ -255,7 +255,7 @@
 
 	if (shared_storage_get_namespace(&ns, &name) < 0)
 		return -1;
-	ret = mailbox_list_delete_mailbox(ns->list, name);
+	ret = ns->list->v.delete_mailbox(ns->list, name);
 	if (ret < 0)
 		shared_list_copy_error(list, ns);
 	return ret;
@@ -353,6 +353,7 @@
 		shared_list_iter_next,
 		shared_list_iter_deinit,
 		NULL,
+		NULL,
 		shared_list_set_subscribed,
 		shared_list_create_mailbox_dir,
 		shared_list_delete_mailbox,
--- a/src/lib-storage/list/Makefile.am	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/list/Makefile.am	Tue Feb 09 04:11:53 2010 +0200
@@ -11,6 +11,7 @@
 libstorage_list_la_SOURCES = \
 	index-mailbox-list.c \
 	index-mailbox-list-sync.c \
+	mailbox-list-delete.c \
 	mailbox-list-fs.c \
 	mailbox-list-fs-iter.c \
 	mailbox-list-maildir.c \
@@ -20,6 +21,7 @@
 
 headers = \
 	index-mailbox-list.h \
+	mailbox-list-delete.h \
 	mailbox-list-fs.h \
 	mailbox-list-maildir.h \
 	mailbox-list-subscriptions.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-delete.c	Tue Feb 09 04:11:53 2010 +0200
@@ -0,0 +1,284 @@
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "unlink-directory.h"
+#include "mailbox-list-private.h"
+#include "mailbox-list-delete.h"
+
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+
+static int
+mailbox_list_check_root_delete(struct mailbox_list *list, const char *name,
+			       const char *path)
+{
+	const char *root_dir;
+
+	root_dir = mailbox_list_get_path(list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	if (strcmp(root_dir, path) != 0)
+		return 0;
+
+	if (strcmp(name, "INBOX") == 0 &&
+	    (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
+		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+				       "INBOX can't be deleted.");
+		return -1;
+	}
+	mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+			       "Mail storage root can't be deleted.");
+	return -1;
+}
+
+int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
+					  const char *name,
+					  const char *trash_dir)
+{
+	const char *src;
+	unsigned int count;
+
+	src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (mailbox_list_check_root_delete(list, name, src) < 0)
+		return -1;
+
+	/* rename the mailbox dir to trash dir, which atomically
+	   marks it as being deleted. */
+	count = 0;
+	while (rename(src, trash_dir) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+			return -1;
+		}
+		if (errno == EXDEV) {
+			/* can't do this the fast way */
+			return 0;
+		}
+		if (!EDESTDIREXISTS(errno)) {
+			if (mailbox_list_set_error_from_errno(list))
+				return -1;
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", src, trash_dir);
+			return -1;
+		}
+
+		/* already existed, delete it and try again */
+		if (unlink_directory(trash_dir, TRUE) < 0 &&
+		    (errno != ENOTEMPTY || count >= 5)) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m", trash_dir);
+			return -1;
+		}
+		count++;
+	}
+
+	if (unlink_directory(trash_dir, TRUE) < 0 && errno != ENOTEMPTY) {
+		mailbox_list_set_critical(list,
+			"unlink_directory(%s) failed: %m", trash_dir);
+
+		/* it's already renamed to trash dir, which means it's
+		   deleted as far as the client is concerned. Report
+		   success. */
+	}
+	return 1;
+}
+
+int mailbox_list_delete_mailbox_file(struct mailbox_list *list,
+				     const char *name)
+{
+	const char *path;
+
+	path = mailbox_list_get_path(list, name,
+				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	/* we can simply unlink() the file */
+	if (unlink(path) == 0)
+		return 0;
+	else if (ENOTFOUND(errno)) {
+		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+				       T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+		return -1;
+	} else {
+		if (!mailbox_list_set_error_from_errno(list)) {
+			mailbox_list_set_critical(list,
+				"unlink(%s) failed: %m", path);
+		}
+		return -1;
+	}
+}
+
+int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
+					     const char *name, const char *path,
+					     bool rmdir_path)
+{
+	DIR *dir;
+	struct dirent *d;
+	string_t *full_path;
+	unsigned int dir_len;
+	bool mailbox_dir, unlinked_something = FALSE;
+
+	if (mailbox_list_check_root_delete(list, name, path) < 0)
+		return -1;
+
+	dir = opendir(path);
+	if (dir == NULL) {
+		if (errno == ENOENT) {
+			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+		} else {
+			if (!mailbox_list_set_error_from_errno(list)) {
+				mailbox_list_set_critical(list,
+					"opendir(%s) failed: %m", path);
+			}
+		}
+		return -1;
+	}
+
+	full_path = t_str_new(256);
+	str_append(full_path, path);
+	str_append_c(full_path, '/');
+	dir_len = str_len(full_path);
+
+	for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
+		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);
+		mailbox_dir = list->v.is_mailbox_dir != NULL &&
+			list->v.is_mailbox_dir(list, str_c(full_path),
+					       d->d_name);
+		str_append(full_path, d->d_name);
+
+		if (mailbox_dir) {
+			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 (errno != 0)
+		mailbox_list_set_critical(list, "readdir(%s) failed: %m", path);
+	if (closedir(dir) < 0) {
+		mailbox_list_set_critical(list, "closedir(%s) failed: %m",
+					  path);
+	}
+
+	if (rmdir_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;
+		}
+	}
+
+	if (!unlinked_something) {
+		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+				       "Mailbox has children, can't delete it");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
+			       enum mailbox_list_path_type type)
+{
+	const char *root_dir, *p;
+	unsigned int len;
+
+	root_dir = mailbox_list_get_path(list, NULL, type);
+	if (strncmp(path, root_dir, strlen(root_dir)) != 0) {
+		/* mbox workaround: name=child/box, root_dir=mail/.imap/,
+		   path=mail/child/.imap/box. we'll want to try to delete
+		   the .imap/ part, but no further. */
+		len = strlen(path);
+		while (len > 0 && path[len-1] != '/')
+			len--;
+		if (len == 0)
+			return;
+		len--;
+		while (len > 0 && path[len-1] != '/')
+			len--;
+		if (len == 0)
+			return;
+
+		root_dir = t_strndup(path, len-1);
+	}
+	while (strcmp(path, root_dir) != 0) {
+		if (rmdir(path) < 0 && errno != ENOENT) {
+			if (errno == ENOTEMPTY)
+				return;
+
+			mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
+						  path);
+			return;
+		}
+		p = strrchr(path, '/');
+		if (p == NULL)
+			break;
+
+		path = t_strdup_until(path, p);
+	}
+}
+
+static void mailbox_list_try_delete(struct mailbox_list *list, const char *name,
+				    enum mailbox_list_path_type type)
+{
+	const char *mailbox_path, *path;
+
+	mailbox_path = mailbox_list_get_path(list, name,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	path = mailbox_list_get_path(list, name, type);
+	if (path == NULL || *path == '\0' || strcmp(path, mailbox_path) == 0)
+		return;
+
+	if (*list->set.maildir_name == '\0' &&
+	    (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) {
+		/* this directory may contain also child mailboxes' data.
+		   we don't want to delete that. */
+		bool rmdir_path = *list->set.maildir_name != '\0';
+		if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
+							     rmdir_path) < 0)
+			return;
+	} else {
+		if (unlink_directory(path, TRUE) < 0 &&
+		    errno != ENOENT && errno != ENOTEMPTY) {
+			mailbox_list_set_critical(list,
+				"unlink_directory(%s) failed: %m", path);
+		}
+	}
+
+	/* avoid leaving empty directories lying around */
+	mailbox_list_delete_until_root(list, path, type);
+}
+
+void mailbox_list_delete_finish(struct mailbox_list *list, const char *name)
+{
+	mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX);
+	mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL);
+	mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/list/mailbox-list-delete.h	Tue Feb 09 04:11:53 2010 +0200
@@ -0,0 +1,14 @@
+#ifndef MAILBOX_LIST_DELETE_H
+#define MAILBOX_LIST_DELETE_H
+
+int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
+					  const char *name,
+					  const char *trash_dir);
+int mailbox_list_delete_mailbox_file(struct mailbox_list *list,
+				     const char *name);
+int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
+					     const char *name, const char *path,
+					     bool rmdir_path);
+void mailbox_list_delete_finish(struct mailbox_list *list, const char *name);
+
+#endif
--- a/src/lib-storage/list/mailbox-list-fs.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-fs.c	Tue Feb 09 04:11:53 2010 +0200
@@ -6,6 +6,7 @@
 #include "mailbox-log.h"
 #include "subscription-file.h"
 #include "mail-storage.h"
+#include "mailbox-list-delete.h"
 #include "mailbox-list-fs.h"
 
 #include <stdio.h>
@@ -361,10 +362,54 @@
 	return -1;
 }
 
+static const char *mailbox_list_fs_get_trash_dir(struct mailbox_list *list)
+{
+	const char *root_dir;
+
+	root_dir = mailbox_list_get_path(list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	return t_strdup_printf("%s/"MAILBOX_LIST_FS_TRASH_DIR_NAME, root_dir);
+}
+
 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);
+	const char *path, *trash_dir;
+	int ret = 0;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
+		if (mailbox_list_delete_mailbox_file(list, name) < 0)
+			return -1;
+		ret = 1;
+	}
+
+	if (*list->set.maildir_name != '\0' &&
+	    *list->set.mailbox_dir_name != '\0' && ret == 0) {
+		trash_dir = mailbox_list_fs_get_trash_dir(list);
+		ret = mailbox_list_delete_maildir_via_trash(list, name,
+							    trash_dir);
+		if (ret < 0)
+			return -1;
+
+		/* try to delete the parent directory */
+		path = mailbox_list_get_path(list, name,
+					     MAILBOX_LIST_PATH_TYPE_DIR);
+		if (rmdir(path) < 0 && errno != ENOENT && errno != ENOTEMPTY) {
+			mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
+						  path);
+		}
+	}
+
+	if (ret == 0) {
+		bool rmdir_path = *list->set.maildir_name != '\0';
+
+		path = mailbox_list_get_path(list, name,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
+							     rmdir_path) < 0)
+			return -1;
+	}
+	mailbox_list_delete_finish(list, name);
+	return 0;
 }
 
 static int fs_list_delete_dir(struct mailbox_list *list, const char *name)
@@ -385,7 +430,7 @@
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
 	} else if (errno == ENOTEMPTY) {
 		mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
-				       "Mailbox exists");
+			"Mailbox has children, delete them first");
 	} else {
 		mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
 	}
@@ -546,6 +591,7 @@
 		fs_list_iter_next,
 		fs_list_iter_deinit,
 		NULL,
+		NULL,
 		fs_list_set_subscribed,
 		fs_list_create_mailbox_dir,
 		fs_list_delete_mailbox,
--- a/src/lib-storage/list/mailbox-list-fs.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-fs.h	Tue Feb 09 04:11:53 2010 +0200
@@ -7,6 +7,10 @@
    problems when they reach the limit. */
 #define FS_MAX_CREATE_MAILBOX_NAME_LENGTH (MAILBOX_LIST_NAME_MAX_LENGTH/2)
 
+/* When doing deletion via renaming it first to trash directory, use this as
+   the trash directory name */
+#define MAILBOX_LIST_FS_TRASH_DIR_NAME "..DOVECOT-TrasH"
+
 struct fs_mailbox_list {
 	struct mailbox_list list;
 
--- a/src/lib-storage/list/mailbox-list-maildir.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Tue Feb 09 04:11:53 2010 +0200
@@ -6,6 +6,7 @@
 #include "eacces-error.h"
 #include "mkdir-parents.h"
 #include "subscription-file.h"
+#include "mailbox-list-delete.h"
 #include "mailbox-list-maildir.h"
 
 #include <stdio.h>
@@ -484,11 +485,46 @@
 		maildir_list_create_maildirfolder_file(list, path);
 }
 
+static const char *mailbox_list_maildir_get_trash_dir(struct mailbox_list *list)
+{
+	const char *root_dir;
+
+	root_dir = mailbox_list_get_path(list, NULL,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	return t_strdup_printf("%s/%c%c"MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME,
+			       root_dir, list->hierarchy_sep,
+			       list->hierarchy_sep);
+}
+
 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);
+	const char *path, *trash_dir;
+	int ret = 0;
+
+	if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
+		if (mailbox_list_delete_mailbox_file(list, name) < 0)
+			return -1;
+		ret = 1;
+	}
+
+	trash_dir = mailbox_list_maildir_get_trash_dir(list);
+	ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir);
+	if (ret < 0)
+		return -1;
+
+	if (ret == 0) {
+		/* we could actually use just unlink_directory()
+		   but error handling is easier this way :) */
+		path = mailbox_list_get_path(list, name,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		if (mailbox_list_delete_mailbox_nonrecursive(list, name,
+							     path, TRUE) < 0)
+			return -1;
+	}
+
+	mailbox_list_delete_finish(list, name);
+	return 0;
 }
 
 static int maildir_list_delete_dir(struct mailbox_list *list, const char *name)
@@ -584,6 +620,7 @@
 		maildir_list_iter_next,
 		maildir_list_iter_deinit,
 		NULL,
+		NULL,
 		maildir_list_set_subscribed,
 		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
@@ -615,6 +652,7 @@
 		maildir_list_iter_next,
 		maildir_list_iter_deinit,
 		NULL,
+		NULL,
 		maildir_list_set_subscribed,
 		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
--- a/src/lib-storage/list/mailbox-list-maildir.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-maildir.h	Tue Feb 09 04:11:53 2010 +0200
@@ -7,6 +7,10 @@
    problems when they reach the limit. */
 #define MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH (MAILBOX_LIST_NAME_MAX_LENGTH/2)
 
+/* When doing deletion via renaming it first to trash directory, use this as
+   the trash directory name */
+#define MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME "DOVECOT-TRASHED"
+
 struct maildir_mailbox_list {
 	struct mailbox_list list;
 
--- a/src/lib-storage/mail-copy.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mail-copy.h	Tue Feb 09 04:11:53 2010 +0200
@@ -1,6 +1,9 @@
 #ifndef MAIL_COPY_H
 #define MAIL_COPY_H
 
+struct mail;
+struct mail_save_context;
+
 int mail_storage_copy(struct mail_save_context *ctx, struct mail *mail);
 
 #endif
--- a/src/lib-storage/mail-storage-private.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mail-storage-private.h	Tue Feb 09 04:11:53 2010 +0200
@@ -96,6 +96,7 @@
 	int (*create)(struct mailbox *box, const struct mailbox_update *update,
 		      bool directory);
 	int (*update)(struct mailbox *box, const struct mailbox_update *update);
+	int (*delete)(struct mailbox *box);
 
 	void (*get_status)(struct mailbox *box, enum mailbox_status_items items,
 			   struct mailbox_status *status_r);
@@ -436,6 +437,8 @@
 void mail_storage_set_internal_error(struct mail_storage *storage);
 void mail_storage_set_index_error(struct mailbox *box);
 bool mail_storage_set_error_from_errno(struct mail_storage *storage);
+void mail_storage_copy_list_error(struct mail_storage *storage,
+				  struct mailbox_list *list);
 
 int mail_set_aborted(struct mail *mail);
 void mail_set_expunged(struct mail *mail);
--- a/src/lib-storage/mail-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mail-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -381,6 +381,16 @@
 	}
 }
 
+void mail_storage_copy_list_error(struct mail_storage *storage,
+				  struct mailbox_list *list)
+{
+	const char *str;
+	enum mail_error error;
+
+	str = mailbox_list_get_last_error(list, &error);
+	mail_storage_set_error(storage, error, str);
+}
+
 void mail_storage_set_index_error(struct mailbox *box)
 {
 	if (mail_index_is_deleted(box->index))
@@ -489,6 +499,9 @@
 {
 	int ret;
 
+	if (box->opened)
+		return 0;
+
 	if (!mailbox_list_is_valid_existing_name(box->list, box->name)) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
 				       "Invalid mailbox name");
@@ -555,11 +568,7 @@
 
 	if (box->list->v.create_mailbox_dir(box->list, box->name,
 					    directory) < 0) {
-		const char *str;
-		enum mail_error error;
-
-		str = mailbox_list_get_last_error(box->list, &error);
-		mail_storage_set_error(box->storage, error, str);
+		mail_storage_copy_list_error(box->storage, box->list);
 		return -1;
 	}
 	mailbox_refresh_permissions(box);
@@ -572,6 +581,54 @@
 	return box->v.update(box, update);
 }
 
+static int mailbox_mark_index_deleted(struct mailbox *box)
+{
+	struct mail_index_transaction *trans;
+
+	trans = mail_index_transaction_begin(box->view, 0);
+	mail_index_set_deleted(trans);
+	if (mail_index_transaction_commit(&trans) < 0) {
+		mail_storage_set_index_error(box);
+		return -1;
+	}
+
+	/* sync the mailbox. this finishes the index deletion and it can
+	   succeed only for a single session. we do it here, so the rest of
+	   the deletion code doesn't have to worry about race conditions. */
+	return mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ);
+}
+
+int mailbox_delete(struct mailbox *box)
+{
+	enum mail_error error;
+	int ret;
+
+	if (*box->name == '\0') {
+		mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
+				       "Storage root can't be deleted");
+		return -1;
+	}
+	if (strcmp(box->name, "INBOX") == 0 &&
+	    (box->list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+				       "INBOX can't be deleted.");
+		return -1;
+	}
+
+	if (mailbox_open(box) < 0) {
+		(void)mail_storage_get_last_error(box->storage, &error);
+		if (error != MAIL_ERROR_NOTFOUND)
+			return -1;
+		/* \noselect mailbox */
+	} else {
+		if (mailbox_mark_index_deleted(box) < 0)
+			return -1;
+	}
+	ret = box->v.delete(box);
+	mailbox_close(box);
+	return ret;
+}
+
 struct mail_storage *mailbox_get_storage(const struct mailbox *box)
 {
 	return box->storage;
--- a/src/lib-storage/mail-storage.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mail-storage.h	Tue Feb 09 04:11:53 2010 +0200
@@ -41,7 +41,9 @@
 	   This causes ACL plugin to use POST right rather than INSERT. */
 	MAILBOX_FLAG_POST_SESSION	= 0x80,
 	/* Force opening mailbox and ignoring any ACLs */
-	MAILBOX_FLAG_IGNORE_ACLS	= 0x100
+	MAILBOX_FLAG_IGNORE_ACLS	= 0x100,
+	/* Open mailbox even if it's already marked as deleted */
+	MAILBOX_FLAG_OPEN_DELETED	= 0x200
 };
 
 enum mailbox_feature {
@@ -345,6 +347,8 @@
 		   bool directory);
 /* Update existing mailbox's metadata. */
 int mailbox_update(struct mailbox *box, const struct mailbox_update *update);
+/* Delete mailbox (and its parent directory, if it has no siblings) */
+int mailbox_delete(struct mailbox *box);
 
 /* Enable the given feature for the mailbox. */
 int mailbox_enable(struct mailbox *box, enum mailbox_feature features);
--- a/src/lib-storage/mailbox-list-private.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mailbox-list-private.h	Tue Feb 09 04:11:53 2010 +0200
@@ -54,6 +54,10 @@
 			       const char *mailbox_name,
 			       enum mailbox_list_file_type type,
 			       enum mailbox_info_flags *flags_r);
+	/* Returns TRUE if dir/name points to mailbox's internal directory.
+	   If it does, mailbox deletion assumes it can safely delete it. */
+	bool (*is_mailbox_dir)(struct mailbox_list *list, const char *dir,
+			       const char *name);
 
 	int (*set_subscribed)(struct mailbox_list *list,
 			      const char *name, bool set);
--- a/src/lib-storage/mailbox-list.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mailbox-list.c	Tue Feb 09 04:11:53 2010 +0200
@@ -15,7 +15,6 @@
 #include "unlink-directory.h"
 #include "imap-match.h"
 #include "imap-utf7.h"
-#include "mail-index-alloc-cache.h"
 #include "mailbox-log.h"
 #include "mailbox-tree.h"
 #include "mail-storage-private.h"
@@ -732,29 +731,6 @@
 	return 0;
 }
 
-int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') {
-		mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
-				       "Invalid mailbox name");
-		return -1;
-	}
-	if (strcmp(name, "INBOX") == 0 &&
-	    (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
-				       "INBOX can't be deleted.");
-		return -1;
-	}
-
-	/* Make sure the indexes are closed before trying to delete the
-	   directory that contains them. It can still fail with some NFS
-	   implementations if indexes are opened by another session, but
-	   that can't really be helped. */
-	mail_index_alloc_cache_destroy_unrefed();
-
-	return list->v.delete_mailbox(list, name);
-}
-
 int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
 {
 	if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') {
@@ -840,51 +816,6 @@
 	list->changelog_timestamp = stamp;
 }
 
-static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir)
-{
-	if (unlink_directory(dir, TRUE) == 0 || errno == ENOENT)
-		return 0;
-
-	if (errno == ENOTEMPTY) {
-		/* We're most likely using NFS and we can't delete
-		   .nfs* files. */
-		mailbox_list_set_error(list, MAIL_ERROR_INUSE,
-			"Mailbox is still open in another session, "
-			"can't delete it.");
-	} else {
-		mailbox_list_set_critical(list,
-			"unlink_directory(%s) failed: %m", dir);
-	}
-	return -1;
-}
-
-int mailbox_list_delete_index_control(struct mailbox_list *list,
-				      const char *name)
-{
-	const char *path, *index_dir, *dir;
-
-	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 (mailbox_list_try_delete(list, index_dir) < 0)
-			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 (mailbox_list_try_delete(list, dir) < 0)
-			return -1;
-	}
-	return 0;
-}
-
 static void node_fix_parents(struct mailbox_node *node)
 {
 	/* If we happened to create any of the parents, we need to mark them
--- a/src/lib-storage/mailbox-list.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/mailbox-list.h	Tue Feb 09 04:11:53 2010 +0200
@@ -248,8 +248,6 @@
 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);
 /* Delete a non-selectable mailbox. Fail if the mailbox is selectable. */
 int mailbox_list_delete_dir(struct mailbox_list *list, const char *name);
 /* Rename mailbox. Renaming across different mailbox lists is possible only
--- a/src/lib-storage/test-mailbox.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/lib-storage/test-mailbox.c	Tue Feb 09 04:11:53 2010 +0200
@@ -52,6 +52,13 @@
 	return -1;
 }
 
+static int test_mailbox_delete(struct mailbox *box)
+{
+	mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+			       "Test mailbox delete isn't supported");
+	return -1;
+}
+
 static void test_mailbox_get_status(struct mailbox *box ATTR_UNUSED,
 				    enum mailbox_status_items items ATTR_UNUSED,
 				    struct mailbox_status *status_r)
@@ -308,6 +315,7 @@
 		test_mailbox_close,
 		test_mailbox_create,
 		test_mailbox_update,
+		test_mailbox_delete,
 		test_mailbox_get_status,
 		NULL,
 		NULL,
--- a/src/plugins/acl/acl-mailbox-list.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/acl/acl-mailbox-list.c	Tue Feb 09 04:11:53 2010 +0200
@@ -488,31 +488,6 @@
 }
 
 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_mailbox_list_have_right(list, name, FALSE,
-					  ACL_STORAGE_RIGHT_DELETE, &can_see);
-	if (ret <= 0) {
-		if (ret < 0)
-			return -1;
-		if (can_see) {
-			mailbox_list_set_error(list, MAIL_ERROR_PERM,
-					       MAIL_ERRSTR_NO_PERMISSION);
-		} else {
-			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		}
-		return -1;
-	}
-
-	return alist->module_ctx.super.delete_mailbox(list, name);
-}
-
-static int
 acl_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname,
 			struct mailbox_list *newlist, const char *newname,
 			bool rename_children)
@@ -626,7 +601,6 @@
 	list->v.iter_deinit = acl_mailbox_list_iter_deinit;
 	list->v.get_mailbox_name_status = acl_get_mailbox_name_status;
 	list->v.create_mailbox_dir = acl_mailbox_list_create_dir;
-	list->v.delete_mailbox = acl_mailbox_list_delete;
 	list->v.rename_mailbox = acl_mailbox_list_rename;
 
 	acl_storage_rights_ctx_init(&alist->rights, backend);
--- a/src/plugins/acl/acl-mailbox.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/acl/acl-mailbox.c	Tue Feb 09 04:11:53 2010 +0200
@@ -19,6 +19,7 @@
 struct acl_mailbox {
 	union mailbox_module_context module_ctx;
 	struct acl_object *aclobj;
+	bool skip_acl_checks;
 };
 
 struct acl_transaction_context {
@@ -41,6 +42,9 @@
 	struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(box->list);
 	int ret;
 
+	if (abox->skip_acl_checks)
+		return 1;
+
 	ret = acl_object_have_right(abox->aclobj,
 			alist->rights.acl_storage_right_idx[right_idx]);
 	if (ret > 0)
@@ -151,6 +155,41 @@
 	return abox->module_ctx.super.update(box, update);
 }
 
+static void acl_mailbox_fail_not_found(struct mailbox *box)
+{
+	int ret;
+
+	ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP);
+	if (ret > 0) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_PERM,
+				       MAIL_ERRSTR_NO_PERMISSION);
+	} else if (ret == 0) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
+				T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
+	}
+}
+
+static int
+acl_mailbox_delete(struct mailbox *box)
+{
+	struct acl_mailbox *abox = ACL_CONTEXT(box);
+	int ret;
+
+	ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_DELETE);
+	if (ret <= 0) {
+		if (ret == 0)
+			acl_mailbox_fail_not_found(box);
+		return -1;
+	}
+
+	/* deletion might internally open the mailbox. let it succeed even if
+	   we don't have READ permission. */
+	abox->skip_acl_checks = TRUE;
+	ret = abox->module_ctx.super.delete(box);
+	abox->skip_acl_checks = FALSE;
+	return ret;
+}
+
 static int
 acl_get_write_rights(struct mailbox *box,
 		     bool *flags_r, bool *flag_seen_r, bool *flag_del_r)
@@ -391,7 +430,8 @@
 
 	/* mailbox can be opened either for reading or appending new messages */
 	if ((box->flags & MAILBOX_FLAG_IGNORE_ACLS) != 0 ||
-	    (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0)
+	    (box->list->ns->flags & NAMESPACE_FLAG_NOACL) != 0 ||
+	    abox->skip_acl_checks)
 		return 0;
 
 	if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0) {
@@ -402,24 +442,14 @@
 	}
 
 	ret = acl_object_have_right(abox->aclobj, idx_arr[open_right]);
-	if (ret > 0)
-		return 0;
-	if (ret < 0)
+	if (ret <= 0) {
+		if (ret == 0) {
+			/* no access. */
+			acl_mailbox_fail_not_found(box);
+		}
 		return -1;
-
-	/* no access. */
-	ret = acl_object_have_right(abox->aclobj,
-				    idx_arr[ACL_STORAGE_RIGHT_LOOKUP]);
-	if (ret < 0)
-		return -1;
-	if (ret > 0) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_PERM,
-				       MAIL_ERRSTR_NO_PERMISSION);
-	} else {
-		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
-				T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
 	}
-	return -1;
+	return 0;
 }
 
 static int acl_mailbox_open(struct mailbox *box)
@@ -454,6 +484,7 @@
 		box->v.close = acl_mailbox_close;
 		box->v.create = acl_mailbox_create;
 		box->v.update = acl_mailbox_update;
+		box->v.delete = acl_mailbox_delete;
 		box->v.mail_alloc = acl_mail_alloc;
 		box->v.save_begin = acl_save_begin;
 		box->v.keywords_create = acl_keywords_create;
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Tue Feb 09 04:11:53 2010 +0200
@@ -46,7 +46,6 @@
 	union mailbox_list_module_context module_ctx;
 
 	unsigned int internal_namespace:1;
-	unsigned int deleting:1;
 };
 
 struct lazy_expunge_transaction {
@@ -231,25 +230,6 @@
 	return _mail;
 }
 
-static void lazy_expunge_mailbox_allocated(struct mailbox *box)
-{
-	struct lazy_expunge_mailbox_list *llist =
-		LAZY_EXPUNGE_LIST_CONTEXT(box->list);
-	union mailbox_module_context *mbox;
-
-	if (llist != NULL && !llist->internal_namespace) {
-		mbox = p_new(box->pool, union mailbox_module_context, 1);
-		mbox->super = box->v;
-
-		box->v.transaction_begin = lazy_expunge_transaction_begin;
-		box->v.transaction_commit = lazy_expunge_transaction_commit;
-		box->v.transaction_rollback = lazy_expunge_transaction_rollback;
-		box->v.mail_alloc = lazy_expunge_mail_alloc;
-		MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module,
-					mbox);
-	}
-}
-
 static int
 mailbox_move(struct mailbox_list *src_list, const char *src_name,
 	     struct mailbox_list *dest_list, const char **_dest_name)
@@ -291,10 +271,9 @@
 }
 
 static int
-mailbox_move_all_mails(struct mailbox_list *list,
-		       const char *src_name, const char *dest_name)
+mailbox_move_all_mails(struct mailbox *src_box, const char *dest_name)
 {
-	struct mailbox *src_box, *dest_box;
+	struct mailbox *dest_box;
 	struct mail_search_args *search_args;
         struct mailbox_transaction_context *src_trans, *dest_trans;
 	struct mail_search_context *search_ctx;
@@ -304,7 +283,7 @@
 	enum mail_error error;
 	int ret;
 
-	dest_box = mailbox_alloc(list, dest_name, NULL, 0);
+	dest_box = mailbox_alloc(src_box->list, dest_name, NULL, 0);
 	if (mailbox_open(dest_box) < 0) {
 		errstr = mail_storage_get_last_error(dest_box->storage, &error);
 		i_error("lazy_expunge: Couldn't open DELETE dest mailbox "
@@ -313,19 +292,6 @@
 		return -1;
 	}
 
-	src_box = mailbox_alloc(list, src_name, NULL, MAILBOX_FLAG_KEEP_LOCKED);
-	if (mailbox_open(src_box) < 0) {
-		errstr = mail_storage_get_last_error(src_box->storage, &error);
-		mailbox_free(&src_box);
-		mailbox_free(&dest_box);
-
-		if (error == MAIL_ERROR_NOTFOUND)
-			return 0;
-		i_error("lazy_expunge: Couldn't open DELETE source mailbox "
-			"%s: %s", src_name, errstr);
-		return -1;
-	}
-
 	src_trans = mailbox_transaction_begin(src_box, 0);
 	dest_trans = mailbox_transaction_begin(dest_box,
 					MAILBOX_TRANSACTION_FLAG_EXTERNAL);
@@ -357,50 +323,49 @@
 	else
 		mailbox_transaction_rollback(&dest_trans);
 
-	mailbox_free(&src_box);
-	mailbox_free(&dest_box);
+	if (ret == 0)
+		ret = mailbox_delete(src_box);
 
-	if (ret == 0)
-		ret = mailbox_list_delete_mailbox(list, src_name);
+	mailbox_free(&dest_box);
 	return ret;
 }
 
-static int
-lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name)
+static int mailbox_mark_index_undeleted(struct mailbox *box)
 {
+	struct mail_index_transaction *trans;
+
+	trans = mail_index_transaction_begin(box->view,
+				MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
+	mail_index_set_undeleted(trans);
+	if (mail_index_transaction_commit(&trans) < 0) {
+		mail_storage_set_index_error(box);
+		return -1;
+	}
+	return 0;
+}
+
+static int lazy_expunge_mailbox_delete(struct mailbox *box)
+{
+	struct mailbox_list *list = box->list;
 	struct lazy_expunge_mailbox_list *llist =
 		LAZY_EXPUNGE_LIST_CONTEXT(list);
 	struct mail_namespace *expunge_ns, *dest_ns;
-	enum mailbox_name_status status;
-	const char *destname;
+	struct mailbox *expunge_box;
+	const char *destname, *str;
+	enum mail_error error;
 	struct tm *tm;
 	char timestamp[256];
 	int ret;
 
-	if (llist->internal_namespace || llist->deleting)
-		return llist->module_ctx.super.delete_mailbox(list, name);
-
-	/* first do the normal sanity checks */
-	if (strcmp(name, "INBOX") == 0) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
-				       "INBOX can't be deleted.");
-		return -1;
-	}
-
-	if (mailbox_list_get_mailbox_name_status(list, name, &status) < 0)
-		return -1;
-	if (status == MAILBOX_NAME_INVALID) {
-		mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
-				       "Invalid mailbox name");
-		return -1;
-	}
+	if (llist->internal_namespace)
+		return llist->module_ctx.super.delete_mailbox(list, box->name);
 
 	expunge_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_EXPUNGE);
 	dest_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_DELETE);
 	if (expunge_ns == dest_ns) {
 		/* if there are no expunged messages in this mailbox,
 		   we can simply rename the mailbox to the destination name */
-		destname = name;
+		destname = box->name;
 	} else {
 		/* destination mailbox name needs to contain a timestamp */
 		tm = localtime(&ioloop_time);
@@ -409,31 +374,69 @@
 			i_strocpy(timestamp, dec2str(ioloop_time),
 				  sizeof(timestamp));
 		}
-		destname = t_strconcat(name, "-", timestamp, NULL);
+		destname = t_strconcat(box->name, "-", timestamp, NULL);
 	}
 
 	/* first move the actual mailbox */
-	if ((ret = mailbox_move(list, name, dest_ns->list, &destname)) < 0)
+	if ((ret = mailbox_move(list, box->name, dest_ns->list, &destname)) < 0)
 		return -1;
 	if (ret == 0) {
 		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
-			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+			T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
 		return -1;
 	}
 
-	if (expunge_ns == dest_ns && strcmp(destname, name) != 0) {
-		llist->deleting = TRUE;
-		(void)mailbox_move_all_mails(dest_ns->list, destname, name);
-		llist->deleting = FALSE;
+	/* other sessions now see the mailbox completely deleted.
+	   since it's not really deleted in the lazy-expunge namespace,
+	   we might want to change it again. so mark the index undeleted. */
+	expunge_box = mailbox_alloc(dest_ns->list, destname, NULL,
+				    MAILBOX_FLAG_OPEN_DELETED);
+	if (mailbox_open(expunge_box) < 0) {
+		str = mail_storage_get_last_error(expunge_box->storage, &error);
+		i_error("lazy_expunge: Couldn't open DELETEd mailbox "
+			"%s: %s", destname, str);
+		mailbox_free(&expunge_box);
+		return -1;
 	}
+	if (mailbox_mark_index_undeleted(expunge_box) < 0) {
+		mailbox_free(&expunge_box);
+		return -1;
+	}
+
+	if (expunge_ns == dest_ns && strcmp(destname, box->name) != 0)
+		ret = mailbox_move_all_mails(expunge_box, box->name);
+	else
+		ret = 0;
+	mailbox_free(&expunge_box);
 
 	/* next move the expunged messages mailbox, if it exists */
 	dest_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_DELETE_EXPUNGE);
 	if (expunge_ns != dest_ns) {
-		(void)mailbox_move(expunge_ns->list, name,
-				   dest_ns->list, &destname);
+		if (mailbox_move(expunge_ns->list, box->name,
+				 dest_ns->list, &destname) < 0)
+			ret = -1;
 	}
-	return 0;
+	return ret;
+}
+
+static void lazy_expunge_mailbox_allocated(struct mailbox *box)
+{
+	struct lazy_expunge_mailbox_list *llist =
+		LAZY_EXPUNGE_LIST_CONTEXT(box->list);
+	union mailbox_module_context *mbox;
+
+	if (llist != NULL && !llist->internal_namespace) {
+		mbox = p_new(box->pool, union mailbox_module_context, 1);
+		mbox->super = box->v;
+
+		box->v.transaction_begin = lazy_expunge_transaction_begin;
+		box->v.transaction_commit = lazy_expunge_transaction_commit;
+		box->v.transaction_rollback = lazy_expunge_transaction_rollback;
+		box->v.mail_alloc = lazy_expunge_mail_alloc;
+		box->v.delete = lazy_expunge_mailbox_delete;
+		MODULE_CONTEXT_SET_SELF(box, lazy_expunge_mail_storage_module,
+					mbox);
+	}
 }
 
 static void lazy_expunge_mail_namespace_storage_added(struct mail_namespace *ns)
@@ -458,7 +461,6 @@
 	if (luser != NULL && ns->type == NAMESPACE_PRIVATE) {
 		llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1);
 		llist->module_ctx.super = list->v;
-		list->v.delete_mailbox = lazy_expunge_mailbox_list_delete;
 
 		MODULE_CONTEXT_SET(list, lazy_expunge_mailbox_list_module,
 				   llist);
--- a/src/plugins/listescape/listescape-plugin.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/listescape/listescape-plugin.c	Tue Feb 09 04:11:53 2010 +0200
@@ -216,20 +216,6 @@
 }
 
 static int
-listescape_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
-	int ret;
-
-	/* at least quota plugin opens the mailbox when deleting it */
-	name = list_escape(list->ns, name, FALSE);
-	mlist->name_escaped = TRUE;
-	ret = mlist->module_ctx.super.delete_mailbox(list, name);
-	mlist->name_escaped = FALSE;
-	return ret;
-}
-
-static int
 listescape_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
 			  struct mailbox_list *newlist, const char *newname,
 			  bool rename_children)
@@ -318,7 +304,6 @@
 	list->v.iter_init = listescape_mailbox_list_iter_init;
 	list->v.iter_next = listescape_mailbox_list_iter_next;
 	list->v.iter_deinit = listescape_mailbox_list_iter_deinit;
-	list->v.delete_mailbox = listescape_delete_mailbox;
 	list->v.rename_mailbox = listescape_rename_mailbox;
 	list->v.set_subscribed = listescape_set_subscribed;
 	list->v.get_mailbox_name_status = listescape_get_mailbox_name_status;
--- a/src/plugins/mail-log/mail-log-plugin.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/mail-log/mail-log-plugin.c	Tue Feb 09 04:11:53 2010 +0200
@@ -379,14 +379,13 @@
 }
 
 static void
-mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, 
-			       struct mailbox_list *list ATTR_UNUSED,
-			       const char *name)
+mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, struct mailbox *box)
 {
 	if ((mail_log_set.events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0)
 		return;
 
-	i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN));
+	i_info("Mailbox deleted: %s",
+	       str_sanitize(box->name, MAILBOX_NAME_LOG_LEN));
 }
 
 static void
--- a/src/plugins/notify/notify-noop.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/notify/notify-noop.c	Tue Feb 09 04:11:53 2010 +0200
@@ -19,11 +19,9 @@
 void notify_noop_mail_transaction_commit(void *txn ATTR_UNUSED,
 					 struct mail_transaction_commit_changes *changes ATTR_UNUSED) {}
 void notify_noop_mail_transaction_rollback(void *txn ATTR_UNUSED) {}
-void *notify_noop_mailbox_delete_begin(struct mailbox_list *list ATTR_UNUSED,
-				       const char *name ATTR_UNUSED) { return NULL; }
+void *notify_noop_mailbox_delete_begin(struct mailbox *box ATTR_UNUSED) { return NULL; }
 void notify_noop_mailbox_delete_commit(void *txn ATTR_UNUSED,
-				       struct mailbox_list *list ATTR_UNUSED,
-				       const char *name ATTR_UNUSED) {}
+				       struct mailbox *box ATTR_UNUSED) {}
 void notify_noop_mailbox_delete_rollback(void *txn ATTR_UNUSED) {}
 void notify_noop_mailbox_rename(struct mailbox_list *oldlist ATTR_UNUSED,
 				const char *oldname ATTR_UNUSED,
--- a/src/plugins/notify/notify-plugin-private.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/notify/notify-plugin-private.h	Tue Feb 09 04:11:53 2010 +0200
@@ -14,10 +14,8 @@
 void notify_contexts_mail_transaction_commit(struct mailbox_transaction_context *t,
 					     struct mail_transaction_commit_changes *changes);
 void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_context *t);
-void notify_contexts_mailbox_delete_begin(struct mailbox_list *list,
-					  const char *name);
-void notify_contexts_mailbox_delete_commit(struct mailbox_list *list,
-					   const char *name);
+void notify_contexts_mailbox_delete_begin(struct mailbox *box);
+void notify_contexts_mailbox_delete_commit(struct mailbox *box);
 void notify_contexts_mailbox_delete_rollback(void);
 void notify_contexts_mailbox_rename(struct mailbox_list *oldlist,
 				    const char *oldname,
--- a/src/plugins/notify/notify-plugin.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/notify/notify-plugin.c	Tue Feb 09 04:11:53 2010 +0200
@@ -132,25 +132,20 @@
 	}
 }
 
-void notify_contexts_mailbox_delete_begin(struct mailbox_list *list,
-					  const char *name)
+void notify_contexts_mailbox_delete_begin(struct mailbox *box)
+{
+	struct notify_context *ctx;
+
+	for (ctx = ctx_list; ctx != NULL; ctx = ctx->next)
+		ctx->mailbox_delete_txn = ctx->v.mailbox_delete_begin(box);
+}
+
+void notify_contexts_mailbox_delete_commit(struct mailbox *box)
 {
 	struct notify_context *ctx;
 
 	for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
-		ctx->mailbox_delete_txn =
-			ctx->v.mailbox_delete_begin(list, name);
-	}
-}
-
-void notify_contexts_mailbox_delete_commit(struct mailbox_list *list,
-					   const char *name)
-{
-	struct notify_context *ctx;
-
-	for (ctx = ctx_list; ctx != NULL; ctx = ctx->next) {
-		ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn,
-					     list, name);
+		ctx->v.mailbox_delete_commit(ctx->mailbox_delete_txn, box);
 		ctx->mailbox_delete_txn = NULL;
 	}
 }
--- a/src/plugins/notify/notify-plugin.h	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/notify/notify-plugin.h	Tue Feb 09 04:11:53 2010 +0200
@@ -7,6 +7,7 @@
 struct mail_storage;
 struct mailbox_transaction_context;
 struct mailbox_list;
+struct mailbox;
 struct notify_context;
 struct module;
 
@@ -22,10 +23,8 @@
 	void (*mail_transaction_commit)(void *txn,
 			struct mail_transaction_commit_changes *changes);
 	void (*mail_transaction_rollback)(void *txn);
-	void *(*mailbox_delete_begin)(struct mailbox_list *list, 
-				      const char *name);
-	void (*mailbox_delete_commit)(void *txn, struct mailbox_list *list,
-				      const char *name);
+	void *(*mailbox_delete_begin)(struct mailbox *box);
+	void (*mailbox_delete_commit)(void *txn, struct mailbox *box);
 	void (*mailbox_delete_rollback)(void *txn);
 	void (*mailbox_rename)(struct mailbox_list *oldlist,
 			       const char *oldname,
@@ -44,10 +43,8 @@
 void notify_noop_mail_transaction_commit(void *txn,
 					 struct mail_transaction_commit_changes *changes);
 void notify_noop_mail_transaction_rollback(void *txn);
-void *notify_noop_mailbox_delete_begin(struct mailbox_list *list,
-				       const char *name);
-void notify_noop_mailbox_delete_commit(void *txn, struct mailbox_list *list,
-				       const char *name);
+void *notify_noop_mailbox_delete_begin(struct mailbox *box);
+void notify_noop_mailbox_delete_commit(void *txn, struct mailbox *box);
 void notify_noop_mailbox_delete_rollback(void *txn);
 void notify_noop_mailbox_rename(struct mailbox_list *oldlist,
 				const char *oldname,
--- a/src/plugins/notify/notify-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/notify/notify-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -199,6 +199,20 @@
 	lbox->super.transaction_rollback(t);
 }
 
+static int
+notify_mailbox_delete(struct mailbox *box)
+{
+	union mailbox_module_context *lbox = NOTIFY_CONTEXT(box);
+
+	notify_contexts_mailbox_delete_begin(box);
+	if (lbox->super.delete(box) < 0) {
+		notify_contexts_mailbox_delete_rollback();
+		return -1;
+	}
+	notify_contexts_mailbox_delete_commit(box);
+	return 0;
+}
+
 static void notify_mailbox_allocated(struct mailbox *box)
 {
 	union mailbox_module_context *lbox;
@@ -213,24 +227,11 @@
 	box->v.transaction_begin = notify_transaction_begin;
 	box->v.transaction_commit = notify_transaction_commit;
 	box->v.transaction_rollback = notify_transaction_rollback;
+	box->v.delete = notify_mailbox_delete;
 	MODULE_CONTEXT_SET_SELF(box, notify_storage_module, lbox);
 }
 
 static int
-notify_mailbox_list_delete(struct mailbox_list *list, const char *name)
-{
-	union mailbox_list_module_context *llist = NOTIFY_LIST_CONTEXT(list);
-
-	notify_contexts_mailbox_delete_begin(list, name);
-	if (llist->super.delete_mailbox(list, name) < 0) {
-		notify_contexts_mailbox_delete_rollback();
-		return -1;
-	}
-	notify_contexts_mailbox_delete_commit(list, name);
-	return 0;
-}
-
-static int
 notify_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname,
 			   struct mailbox_list *newlist, const char *newname,
 			   bool rename_children)
@@ -254,7 +255,6 @@
 
 	llist = p_new(list->pool, union mailbox_list_module_context, 1);
 	llist->super = list->v;
-	list->v.delete_mailbox = notify_mailbox_list_delete;
 	list->v.rename_mailbox = notify_mailbox_list_rename;
 
 	MODULE_CONTEXT_SET_SELF(list, notify_mailbox_list_module, llist);
--- a/src/plugins/quota/quota-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/quota/quota-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -331,6 +331,48 @@
 	return ret;
 }
 
+static int
+quota_mailbox_delete_shrink_quota(struct mailbox *box)
+{
+	struct mail_search_context *ctx;
+        struct mailbox_transaction_context *t;
+	struct quota_transaction_context *qt;
+	struct mail *mail;
+	struct mail_search_args *search_args;
+
+	t = mailbox_transaction_begin(box, 0);
+	qt = quota_transaction_begin(box);
+
+	search_args = mail_search_build_init();
+	mail_search_build_add_all(search_args);
+	ctx = mailbox_search_init(t, search_args, NULL);
+	mail_search_args_unref(&search_args);
+
+	mail = mail_alloc(t, 0, NULL);
+	while (mailbox_search_next(ctx, mail))
+		quota_free(qt, mail);
+	mail_free(&mail);
+
+	if (mailbox_search_deinit(&ctx) < 0) {
+		/* maybe we missed some mails. */
+		quota_recalculate(qt);
+	}
+	(void)quota_transaction_commit(&qt);
+	mailbox_transaction_rollback(&t);
+	return 0;
+}
+
+static int quota_mailbox_delete(struct mailbox *box)
+{
+	struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
+
+	if (box->opened) {
+		if (quota_mailbox_delete_shrink_quota(box) < 0)
+			return -1;
+	}
+	return qbox->module_ctx.super.delete(box);
+}
+
 static void quota_mailbox_close(struct mailbox *box)
 {
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
@@ -364,44 +406,11 @@
 	box->v.copy = quota_copy;
 	box->v.sync_notify = quota_mailbox_sync_notify;
 	box->v.sync_deinit = quota_mailbox_sync_deinit;
+	box->v.delete = quota_mailbox_delete;
 	box->v.close = quota_mailbox_close;
 	MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
 }
 
-static int
-quota_mailbox_delete_shrink_quota(struct mailbox *box)
-{
-	struct mail_search_context *ctx;
-        struct mailbox_transaction_context *t;
-	struct quota_transaction_context *qt;
-	struct mail *mail;
-	struct mail_search_args *search_args;
-	int ret;
-
-	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0)
-		return -1;
-
-	t = mailbox_transaction_begin(box, 0);
-	qt = QUOTA_CONTEXT(t);
-
-	search_args = mail_search_build_init();
-	mail_search_build_add_all(search_args);
-	ctx = mailbox_search_init(t, search_args, NULL);
-	mail_search_args_unref(&search_args);
-
-	mail = mail_alloc(t, 0, NULL);
-	while (mailbox_search_next(ctx, mail))
-		quota_free(qt, mail);
-	mail_free(&mail);
-
-	ret = mailbox_search_deinit(&ctx);
-	if (ret < 0)
-		mailbox_transaction_rollback(&t);
-	else
-		ret = mailbox_transaction_commit(&t);
-	return ret;
-}
-
 static void quota_mailbox_list_deinit(struct mailbox_list *list)
 {
 	struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
@@ -410,44 +419,6 @@
 	qlist->module_ctx.super.deinit(list);
 }
 
-static int
-quota_mailbox_list_delete(struct mailbox_list *list, const char *name)
-{
-	struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list);
-	struct mailbox *box;
-	enum mail_error error;
-	const char *str;
-	int ret;
-
-	/* This is a bit annoying to handle. We'll have to open the mailbox
-	   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_alloc(list, name, NULL, MAILBOX_FLAG_KEEP_RECENT |
-			    MAILBOX_FLAG_KEEP_LOCKED);
-	if (mailbox_open(box) < 0) {
-		str = mail_storage_get_last_error(mailbox_get_storage(box),
-						  &error);
-		if (error != MAIL_ERROR_NOTPOSSIBLE) {
-			ret = -1;
-		} else {
-			/* mailbox isn't selectable */
-			ret = 0;
-		}
-	} else {
-		if ((ret = quota_mailbox_delete_shrink_quota(box)) < 0) {
-			str = mail_storage_get_last_error(box->storage, &error);
-			mailbox_list_set_error(list, error, str);
-		}
-	}
-	if (box != NULL)
-		mailbox_free(&box);
-
-	/* FIXME: here's an unfortunate race condition */
-	return ret < 0 ? -1 :
-		qlist->module_ctx.super.delete_mailbox(list, name);
-}
-
 struct quota *quota_get_mail_user_quota(struct mail_user *user)
 {
 	struct quota_user *quser = QUOTA_USER_CONTEXT(user);
@@ -522,7 +493,6 @@
 		qlist = p_new(list->pool, struct quota_mailbox_list, 1);
 		qlist->module_ctx.super = list->v;
 		list->v.deinit = quota_mailbox_list_deinit;
-		list->v.delete_mailbox = quota_mailbox_list_delete;
 		MODULE_CONTEXT_SET(list, quota_mailbox_list_module, qlist);
 
 		/* register to owner's quota roots */
--- a/src/plugins/virtual/virtual-storage.c	Tue Feb 09 04:09:28 2010 +0200
+++ b/src/plugins/virtual/virtual-storage.c	Tue Feb 09 04:11:53 2010 +0200
@@ -305,98 +305,6 @@
 	return -1;
 }
 
-static int
-virtual_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;
-
-	dir = opendir(path);
-	if (dir == NULL) {
-		if (!mailbox_list_set_error_from_errno(list)) {
-			mailbox_list_set_critical(list,
-				"opendir(%s) failed: %m", path);
-		}
-		return -1;
-	}
-
-	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);
-
-		/* 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(%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;
-	}
-
-	if (!unlinked_something) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
-			t_strdup_printf("Directory %s isn't empty, "
-					"can't delete it.", name));
-		return -1;
-	}
-	return 0;
-}
-
-static int
-virtual_list_delete_mailbox(struct mailbox_list *list, const char *name)
-{
-	struct virtual_mailbox_list *mlist = VIRTUAL_LIST_CONTEXT(list);
-	struct stat st;
-	const char *src;
-
-	/* delete the index and control directories */
-	if (mlist->module_ctx.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, MAIL_ERROR_NOTFOUND,
-			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
-		return -1;
-	}
-
-	return virtual_delete_nonrecursive(list, src, name);
-}
-
 static void virtual_notify_changes(struct mailbox *box ATTR_UNUSED)
 {
 	/* FIXME: maybe some day */
@@ -548,9 +456,7 @@
 	mlist->module_ctx.super = list->v;
 
 	list->ns->flags |= NAMESPACE_FLAG_NOQUOTA;
-
 	list->v.iter_is_mailbox = virtual_list_iter_is_mailbox;
-	list->v.delete_mailbox = virtual_list_delete_mailbox;
 
 	MODULE_CONTEXT_SET(list, virtual_mailbox_list_module, mlist);
 }
@@ -581,6 +487,7 @@
 		virtual_mailbox_close,
 		virtual_mailbox_create,
 		virtual_mailbox_update,
+		index_storage_mailbox_delete,
 		index_storage_get_status,
 		NULL,
 		NULL,