]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Mailbox deletion API changed.
authorTimo Sirainen <tss@iki.fi>
Tue, 9 Feb 2010 02:11:53 +0000 (04:11 +0200)
committerTimo Sirainen <tss@iki.fi>
Tue, 9 Feb 2010 02:11:53 +0000 (04:11 +0200)
Mailbox deletion should now be free of race conditions. The actual file
deletion code is now responsibility of mailbox_list backend.

--HG--
branch : HEAD

45 files changed:
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 [new file with mode: 0644]
src/lib-storage/list/mailbox-list-delete.h [new file with mode: 0644]
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

index 9e81ff72133047fca4c279fee7ae8b008388ff54..5e2aca01db10e4fbfccd2cdade4e849e6cb35131 100644 (file)
@@ -1105,6 +1105,7 @@ local_worker_delete_mailbox(struct dsync_worker *_worker,
                (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 @@ local_worker_delete_mailbox(struct dsync_worker *_worker,
 
        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);
 }
 
index fa24af0e664e7a7997eac28c0228eeca03c57493..666c9c60b67044fdb0d90b09f3fcb784839f1b59 100644 (file)
@@ -7,7 +7,7 @@ bool cmd_delete(struct client_command_context *cmd)
 {
        struct client *client = cmd->client;
        struct mail_namespace *ns;
-       struct mailbox *mailbox;
+       struct mailbox *box;
        const char *name;
 
        /* <mailbox> */
@@ -25,19 +25,18 @@ bool cmd_delete(struct client_command_context *cmd)
        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;
 }
index 21178d5b6a439ec345c503735f6ee12cd2378c26..3368d256e2ff89e1fa456dec0e95d87276abb262 100644 (file)
@@ -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 "index-mail.h"
 #include "mail-copy.h"
+#include "index-mail.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 @@ cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                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 int cydir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx
 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 @@ struct mailbox cydir_mailbox = {
                index_storage_mailbox_close,
                cydir_mailbox_create,
                index_storage_mailbox_update,
+               index_storage_mailbox_delete,
                index_storage_get_status,
                NULL,
                NULL,
index 1775ee19e83b898fb131347769cb2bc5e78c9bf4..72e78b4a581ff8f578d6deebcbc8693ee123662c 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef DBOX_MAIL_H
 #define DBOX_MAIL_H
 
+#include "index-mail.h"
+
 struct dbox_mail {
        struct index_mail imail;
 
index 2da7e625f52aff243116c4fe47fab6a23eadc6a7..6c394d12e98a7fc7e2e04245d24fe898a83d0807 100644 (file)
@@ -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 @@ int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
        }
        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;
-}
-
index 0d3248833aeb9868d3a70c345bb625c327c9510b..2f3e7d216f764f6cc997426a47172bd82c3d91fc 100644 (file)
@@ -62,9 +62,4 @@ int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
                             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
index ed3d8b60afd38654db71b8ae9db3b3498b2fe111..cb521bc34d6159a8738dd8f50e8076f686fb7317 100644 (file)
@@ -371,6 +371,11 @@ int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx)
        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;
index 65e2933d5fe3cbd5cc09958900e8171beebe3fe3..47eafec50095e7a45e0bfc1339b1fe333fa0a0a1 100644 (file)
@@ -53,6 +53,7 @@ dbox_map_transaction_begin(struct dbox_map *map, bool external);
 /* 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,
index 2f934270bc59202882c7ef8303c0df9ea45cb133..e50402050236b964c0011c12400936cfbbfb6969 100644 (file)
@@ -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"
 #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 @@ static int mdbox_write_index_header(struct mailbox *box,
        }
 
        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 @@ mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
        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 @@ mdbox_mailbox_unref_mails(struct mailbox_list *list, const char *path)
        }
 
        /* 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 @@ static void dbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
        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 @@ struct mailbox mdbox_mailbox = {
                index_storage_mailbox_close,
                dbox_mailbox_create,
                mdbox_mailbox_update,
+               mdbox_mailbox_delete,
                mdbox_storage_get_status,
                NULL,
                NULL,
index dd50cf5bd1d04bb00a40d4dc63740393838cfd1d..6ef283599d5e7147849e11af55eb66323da259d1 100644 (file)
@@ -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)
 
@@ -231,22 +218,6 @@ dbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
        return sdbox_write_index_header(box, update);
 }
 
-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,
@@ -271,7 +242,6 @@ static void sdbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
        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 @@ struct mailbox sdbox_mailbox = {
                index_storage_mailbox_close,
                dbox_mailbox_create,
                dbox_mailbox_update,
+               index_storage_mailbox_delete,
                dbox_storage_get_status,
                NULL,
                NULL,
index 38218c76ce8d7ec8b32b185f68fdafd7f8e7c644..7ef313577af6cbdbf3ad619ae85e6011bb018c40 100644 (file)
@@ -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 @@ int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
        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 @@ int index_storage_mailbox_enable(struct mailbox *box,
 {
        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 @@ int index_storage_mailbox_update(struct mailbox *box,
        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 @@ int index_storage_mailbox_update(struct mailbox *box,
        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 ||
index fd81f084cd1adabc51d3512d933b56bb30dbc28b..7731c6a9e3ccf64cf2bc9a528019878ad4761d1b 100644 (file)
@@ -76,6 +76,8 @@ int index_storage_mailbox_enable(struct mailbox *box,
 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);
index e23ccf38913e438ec547f409ab3f2c74f9d60917..d9501fd3a310d49b4163d860b01e9244c16cffba 100644 (file)
@@ -121,8 +121,10 @@ int index_transaction_commit(struct mailbox_transaction_context *_t,
        _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;
index 1acf26bbfc75c8dde0c25a8d7152a0d7418bf592..26bde02b3cf845b7773e6595cdda92050cb82421 100644 (file)
@@ -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_is_internal_name(const char *name)
 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_existing_name(struct mailbox_list *list,
 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 @@ maildir_storage_get_status(struct mailbox *box, enum mailbox_status_items items,
        }
 }
 
-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 void maildir_notify_changes(struct mailbox *box)
        }
 }
 
+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 @@ maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
                                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 @@ uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list)
 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 @@ static void maildir_storage_add_list(struct mail_storage *storage,
                        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 @@ struct mailbox maildir_mailbox = {
                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,
index cf9ff67d2f36e20c397f14cfe45ecd03be1c139d..71bc3a737972f65814c7fac0e78328c995bbaeb2 100644 (file)
@@ -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:
index f1e5d1793856e5ac12416421857a63a738c395af..21251725fec605ab2e47e6cfcec11e09c3b69662 100644 (file)
@@ -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"
 #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 @@ static int mbox_mailbox_open(struct mailbox *box)
        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_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
        }
 }
 
-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 @@ static void mbox_storage_add_list(struct mail_storage *storage,
                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 @@ struct mailbox mbox_mailbox = {
                mbox_mailbox_close,
                mbox_mailbox_create,
                mbox_mailbox_update,
+               index_storage_mailbox_delete,
                mbox_storage_get_status,
                NULL,
                NULL,
index e3eb38a4b795a82574f9584035a7f34c0b77eeb5..c9bba57858d7f626e4ac8e9a08e3aedaab99f337 100644 (file)
@@ -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 @@ raw_mailbox_update(struct mailbox *box,
        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 @@ static void raw_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
                                 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 @@ struct mailbox raw_mailbox = {
                index_storage_mailbox_close,
                raw_mailbox_create,
                raw_mailbox_update,
+               index_storage_mailbox_delete,
                index_storage_get_status,
                NULL,
                NULL,
index b87ba6230d26db4d11e412e9a15e29aead6ec7b7..2d39ef10d6d864f290ae1d6d9c7b7ab55aaddb1a 100644 (file)
@@ -255,7 +255,7 @@ shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
 
        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 @@ struct mailbox_list shared_mailbox_list = {
                shared_list_iter_next,
                shared_list_iter_deinit,
                NULL,
+               NULL,
                shared_list_set_subscribed,
                shared_list_create_mailbox_dir,
                shared_list_delete_mailbox,
index eebefc312265e79436af930a2ecd53972a595d71..a646539a154b31092fa617d342f913e1f8e2899a 100644 (file)
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
 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 @@ libstorage_list_la_SOURCES = \
 
 headers = \
        index-mailbox-list.h \
+       mailbox-list-delete.h \
        mailbox-list-fs.h \
        mailbox-list-maildir.h \
        mailbox-list-subscriptions.h \
diff --git a/src/lib-storage/list/mailbox-list-delete.c b/src/lib-storage/list/mailbox-list-delete.c
new file mode 100644 (file)
index 0000000..c9fcb40
--- /dev/null
@@ -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);
+}
diff --git a/src/lib-storage/list/mailbox-list-delete.h b/src/lib-storage/list/mailbox-list-delete.h
new file mode 100644 (file)
index 0000000..edc0509
--- /dev/null
@@ -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
index 2fb7a22e902a93af16f2fc516c9b93869a850caf..a1fa01566ed706cd2eeb434a71a5852ab3f3760e 100644 (file)
@@ -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 @@ fs_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
        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 @@ static int fs_list_delete_dir(struct mailbox_list *list, const char *name)
                        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 @@ struct mailbox_list fs_mailbox_list = {
                fs_list_iter_next,
                fs_list_iter_deinit,
                NULL,
+               NULL,
                fs_list_set_subscribed,
                fs_list_create_mailbox_dir,
                fs_list_delete_mailbox,
index 95b5dba639642d524070942510efd90b737f6a91..e089b2fbf8ed8920e4c306515d53a8c3b2882723 100644 (file)
@@ -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;
 
index ab95f7150f0e04c2c93302770d2e3f84b1d80ae8..f0b7d1d758086aef9de25c51063be97840e44b80 100644 (file)
@@ -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_mailbox_dir(struct mailbox_list *list, const char *name,
                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 @@ struct mailbox_list maildir_mailbox_list = {
                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 @@ struct mailbox_list imapdir_mailbox_list = {
                maildir_list_iter_next,
                maildir_list_iter_deinit,
                NULL,
+               NULL,
                maildir_list_set_subscribed,
                maildir_list_create_mailbox_dir,
                maildir_list_delete_mailbox,
index 277f820c74423ad6b4bd3de6d0e092e2c2d58498..f6bb39a2cbe75cbbd26345bcdaa145f9ef439328 100644 (file)
@@ -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;
 
index c769082db2413e63b3eb8e126db52fbe635afd30..0f937ffe250a79f09be817ff82ed2a01e357c3b7 100644 (file)
@@ -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
index 89e730cb855c50e311f97e5e1c9278355dbd2dbb..f1cde503d9dac44a7d6449f060eba4fceaa34bb5 100644 (file)
@@ -96,6 +96,7 @@ struct mailbox_vfuncs {
        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_critical(struct mail_storage *storage,
 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);
index e73c851523712a0659d6a972bbf97420e35307a2..4490b820da12bbbefcbf57771589aca764707462 100644 (file)
@@ -381,6 +381,16 @@ void mail_storage_set_critical(struct mail_storage *storage,
        }
 }
 
+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 mailbox_open(struct mailbox *box)
 {
        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 @@ int mailbox_create(struct mailbox *box, const struct mailbox_update *update,
 
        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 @@ int mailbox_update(struct mailbox *box, const struct mailbox_update *update)
        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;
index c9ad7999a9d685c27c0f0e7f049ebc7fe460b4e9..2a300517694e9ff15da370bc00b12c8e9fcde440 100644 (file)
@@ -41,7 +41,9 @@ enum mailbox_flags {
           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 @@ int mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                   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);
index 60fd7682df391dabdc455686c0a8f423fd4dccb1..7d3441a0f7cccc50c5fe886ba8635c5cafcfa46f 100644 (file)
@@ -54,6 +54,10 @@ struct mailbox_list_vfuncs {
                               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);
index 5a41b1ea79229035248aefcaac4c562990fca3ce..b8dd6a45f330da40ef0dff66467747b73becc379 100644 (file)
@@ -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 @@ int mailbox_list_set_subscribed(struct mailbox_list *list,
        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 @@ void mailbox_list_set_changelog_timestamp(struct mailbox_list *list,
        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
index 933cf17254a52f66d0f26cb6df94d9cbf8502f9b..4addc28bbf06bed28c762e1f40a0637d82323a39 100644 (file)
@@ -248,8 +248,6 @@ int mailbox_list_mailbox(struct mailbox_list *list, const char *name,
 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
index 5716a58a20af1d9ceccc2091ceae4eb12bb20bd1..c12e53d917a71f6009da16b290bc8ddf4333d315 100644 (file)
@@ -52,6 +52,13 @@ test_mailbox_update(struct mailbox *box,
        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 @@ struct mailbox test_mailbox = {
                test_mailbox_close,
                test_mailbox_create,
                test_mailbox_update,
+               test_mailbox_delete,
                test_mailbox_get_status,
                NULL,
                NULL,
index 15f1befd534ea1af1b497a8bce914dfa3a83ea7c..ff83179fa8ee1b6d2b33a0ec02267da4255d8730 100644 (file)
@@ -487,31 +487,6 @@ acl_mailbox_list_create_dir(struct mailbox_list *list, const char *name,
                create_mailbox_dir(list, name, directory);
 }
 
-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,
@@ -626,7 +601,6 @@ static void acl_mailbox_list_init_default(struct mailbox_list *list)
        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);
index 53fcf274740056c6a06eb1c1aeb896d190aeb5bf..6ab4b9b9ea8f2699f82f3a4b05e697683a0d6874 100644 (file)
@@ -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 @@ int acl_mailbox_right_lookup(struct mailbox *box, unsigned int right_idx)
        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 @@ acl_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
        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 @@ static int acl_mailbox_open_check_acl(struct mailbox *box)
 
        /* 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 @@ static int acl_mailbox_open_check_acl(struct mailbox *box)
        }
 
        ret = acl_object_have_right(abox->aclobj, idx_arr[open_right]);
-       if (ret > 0)
-               return 0;
-       if (ret < 0)
-               return -1;
-
-       /* no access. */
-       ret = acl_object_have_right(abox->aclobj,
-                                   idx_arr[ACL_STORAGE_RIGHT_LOOKUP]);
-       if (ret < 0)
+       if (ret <= 0) {
+               if (ret == 0) {
+                       /* no access. */
+                       acl_mailbox_fail_not_found(box);
+               }
                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 @@ void acl_mailbox_allocated(struct mailbox *box)
                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;
index 3dca420c4ddedd94de39fb76f274324b58844de4..7a231a7b1a7f4873b29234abd2cb737095634504 100644 (file)
@@ -46,7 +46,6 @@ struct lazy_expunge_mailbox_list {
        union mailbox_list_module_context module_ctx;
 
        unsigned int internal_namespace:1;
-       unsigned int deleting:1;
 };
 
 struct lazy_expunge_transaction {
@@ -231,25 +230,6 @@ lazy_expunge_mail_alloc(struct mailbox_transaction_context *t,
        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 @@ mailbox_move(struct mailbox_list *src_list, const char *src_name,
 }
 
 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 @@ mailbox_move_all_mails(struct mailbox_list *list,
        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 @@ mailbox_move_all_mails(struct mailbox_list *list,
                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 @@ mailbox_move_all_mails(struct mailbox_list *list,
        else
                mailbox_transaction_rollback(&dest_trans);
 
-       mailbox_free(&src_box);
-       mailbox_free(&dest_box);
-
        if (ret == 0)
-               ret = mailbox_list_delete_mailbox(list, src_name);
+               ret = mailbox_delete(src_box);
+
+       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 @@ lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name)
                        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 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);
        }
-       return 0;
 }
 
 static void lazy_expunge_mail_namespace_storage_added(struct mail_namespace *ns)
@@ -458,7 +461,6 @@ static void lazy_expunge_mail_namespace_storage_added(struct mail_namespace *ns)
        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);
index 4b5a8c89a64e24e9cde6a2fa8e6904cd6c0c0994..299e15482dc58a8b1d96b916ffd2b73422ae7f86 100644 (file)
@@ -215,20 +215,6 @@ listescape_mailbox_alloc(struct mail_storage *storage,
                mailbox_alloc(storage, list, name, input, flags);
 }
 
-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,
@@ -318,7 +304,6 @@ static void listescape_mail_namespace_storage_added(struct mail_namespace *ns)
        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;
index 9127767d817c8cdcc6502ce227516d7081757d22..f9892df1300b8887c8bf43f30b9503335cbd0aca 100644 (file)
@@ -379,14 +379,13 @@ static void mail_log_mail_transaction_rollback(void *txn)
 }
 
 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
index c3b2fca8615823c3b3b5a493640b19a18f02b245..d89e37f49b8cfd1d3c1222f3da1d63c74da6d416 100644 (file)
@@ -19,11 +19,9 @@ void notify_noop_mail_update_keywords(void *txn ATTR_UNUSED,
 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,
index a5f0c529a9a849fb4d582560e3b9ec911585db5f..34363c1b2e02f7c02b79d751dc9332623dea6beb 100644 (file)
@@ -14,10 +14,8 @@ void notify_contexts_mail_update_keywords(struct mail *mail,
 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,
index c912a294016d6dd591a3d732ee8f9ca48712a863..2ff5d787c322da44d17c4043008969472edb55c4 100644 (file)
@@ -132,25 +132,20 @@ void notify_contexts_mail_transaction_rollback(struct mailbox_transaction_contex
        }
 }
 
-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(list, name);
-       }
+       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_list *list,
-                                          const char *name)
+void notify_contexts_mailbox_delete_commit(struct mailbox *box)
 {
        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;
        }
 }
index 560b22e3d48c287ec80a110d6811e3dbf173467f..4d477d0a2787d8fbfb6efdca71ea110f42b1c1e1 100644 (file)
@@ -7,6 +7,7 @@ struct mail_transaction_commit_changes;
 struct mail_storage;
 struct mailbox_transaction_context;
 struct mailbox_list;
+struct mailbox;
 struct notify_context;
 struct module;
 
@@ -22,10 +23,8 @@ struct notify_vfuncs {
        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_update_keywords(void *txn, struct mail *mail,
 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,
index 93d64da9feeb22bb56998c773228f83cf50f4e22..eba7c84c7dfe8dcd64e6d5fd27b3d779c962208d 100644 (file)
@@ -199,6 +199,20 @@ notify_transaction_rollback(struct mailbox_transaction_context *t)
        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,23 +227,10 @@ static void notify_mailbox_allocated(struct mailbox *box)
        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,
@@ -254,7 +255,6 @@ static void notify_mail_namespace_storage_added(struct mail_namespace *ns)
 
        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);
index c1403c9ed893f945000ccf2f48915744acd855b1..738491a38b8bdbf85faa7fcb97ab6d3cd4fcfa79 100644 (file)
@@ -331,6 +331,48 @@ static int quota_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
        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 @@ void quota_mailbox_allocated(struct mailbox *box)
        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 @@ static void quota_mailbox_list_deinit(struct mailbox_list *list)
        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 @@ void quota_mail_namespace_storage_added(struct mail_namespace *ns)
                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 */
index bd840c15ad3f3bb9a9c3449929ea96ae90fe7b18..7a299c13d9c904afbfb8bcfe6eabb1e0f8085764 100644 (file)
@@ -305,98 +305,6 @@ virtual_mailbox_update(struct mailbox *box,
        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 @@ static void virtual_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
        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 @@ struct mailbox virtual_mailbox = {
                virtual_mailbox_close,
                virtual_mailbox_create,
                virtual_mailbox_update,
+               index_storage_mailbox_delete,
                index_storage_get_status,
                NULL,
                NULL,