(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) {
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);
}
{
struct client *client = cmd->client;
struct mail_namespace *ns;
- struct mailbox *mailbox;
+ struct mailbox *box;
const char *name;
/* <mailbox> */
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;
}
/* 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;
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;
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 = {
index_storage_mailbox_close,
cydir_mailbox_create,
index_storage_mailbox_update,
+ index_storage_mailbox_delete,
index_storage_get_status,
NULL,
NULL,
#ifndef DBOX_MAIL_H
#define DBOX_MAIL_H
+#include "index-mail.h"
+
struct dbox_mail {
struct index_mail imail;
#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"
}
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;
-}
-
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
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;
/* 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,
#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)
}
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;
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) {
}
/* 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
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;
index_storage_mailbox_close,
dbox_mailbox_create,
mdbox_mailbox_update,
+ mdbox_mailbox_delete,
mdbox_storage_get_status,
NULL,
NULL,
/* 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)
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,
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;
index_storage_mailbox_close,
dbox_mailbox_create,
dbox_mailbox_update,
+ index_storage_mailbox_delete,
dbox_storage_get_status,
NULL,
NULL,
#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"
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;
}
{
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;
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);
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 ||
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);
_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;
#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;
};
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))
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))
}
}
-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) {
}
}
+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,
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;
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 {
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);
}
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,
#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:
#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 */
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,
}
}
-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)
{
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;
mbox_mailbox_close,
mbox_mailbox_create,
mbox_mailbox_update,
+ index_storage_mailbox_delete,
mbox_storage_get_status,
NULL,
NULL,
/* 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"
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)
{
}
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 = {
index_storage_mailbox_close,
raw_mailbox_create,
raw_mailbox_update,
+ index_storage_mailbox_delete,
index_storage_get_status,
NULL,
NULL,
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;
shared_list_iter_next,
shared_list_iter_deinit,
NULL,
+ NULL,
shared_list_set_subscribed,
shared_list_create_mailbox_dir,
shared_list_delete_mailbox,
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 \
headers = \
index-mailbox-list.h \
+ mailbox-list-delete.h \
mailbox-list-fs.h \
mailbox-list-maildir.h \
mailbox-list-subscriptions.h \
--- /dev/null
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "unlink-directory.h"
+#include "mailbox-list-private.h"
+#include "mailbox-list-delete.h"
+
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+
+static int
+mailbox_list_check_root_delete(struct mailbox_list *list, const char *name,
+ const char *path)
+{
+ const char *root_dir;
+
+ root_dir = mailbox_list_get_path(list, NULL,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ if (strcmp(root_dir, path) != 0)
+ return 0;
+
+ if (strcmp(name, "INBOX") == 0 &&
+ (list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ "INBOX can't be deleted.");
+ return -1;
+ }
+ mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ "Mail storage root can't be deleted.");
+ return -1;
+}
+
+int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
+ const char *name,
+ const char *trash_dir)
+{
+ const char *src;
+ unsigned int count;
+
+ src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ if (mailbox_list_check_root_delete(list, name, src) < 0)
+ return -1;
+
+ /* rename the mailbox dir to trash dir, which atomically
+ marks it as being deleted. */
+ count = 0;
+ while (rename(src, trash_dir) < 0) {
+ if (ENOTFOUND(errno)) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+ return -1;
+ }
+ if (errno == EXDEV) {
+ /* can't do this the fast way */
+ return 0;
+ }
+ if (!EDESTDIREXISTS(errno)) {
+ if (mailbox_list_set_error_from_errno(list))
+ return -1;
+ mailbox_list_set_critical(list,
+ "rename(%s, %s) failed: %m", src, trash_dir);
+ return -1;
+ }
+
+ /* already existed, delete it and try again */
+ if (unlink_directory(trash_dir, TRUE) < 0 &&
+ (errno != ENOTEMPTY || count >= 5)) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m", trash_dir);
+ return -1;
+ }
+ count++;
+ }
+
+ if (unlink_directory(trash_dir, TRUE) < 0 && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m", trash_dir);
+
+ /* it's already renamed to trash dir, which means it's
+ deleted as far as the client is concerned. Report
+ success. */
+ }
+ return 1;
+}
+
+int mailbox_list_delete_mailbox_file(struct mailbox_list *list,
+ const char *name)
+{
+ const char *path;
+
+ path = mailbox_list_get_path(list, name,
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+ /* we can simply unlink() the file */
+ if (unlink(path) == 0)
+ return 0;
+ else if (ENOTFOUND(errno)) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+ return -1;
+ } else {
+ if (!mailbox_list_set_error_from_errno(list)) {
+ mailbox_list_set_critical(list,
+ "unlink(%s) failed: %m", path);
+ }
+ return -1;
+ }
+}
+
+int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
+ const char *name, const char *path,
+ bool rmdir_path)
+{
+ DIR *dir;
+ struct dirent *d;
+ string_t *full_path;
+ unsigned int dir_len;
+ bool mailbox_dir, unlinked_something = FALSE;
+
+ if (mailbox_list_check_root_delete(list, name, path) < 0)
+ return -1;
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ if (errno == ENOENT) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
+ } else {
+ if (!mailbox_list_set_error_from_errno(list)) {
+ mailbox_list_set_critical(list,
+ "opendir(%s) failed: %m", path);
+ }
+ }
+ return -1;
+ }
+
+ full_path = t_str_new(256);
+ str_append(full_path, path);
+ str_append_c(full_path, '/');
+ dir_len = str_len(full_path);
+
+ for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
+ if (d->d_name[0] == '.') {
+ /* skip . and .. */
+ if (d->d_name[1] == '\0')
+ continue;
+ if (d->d_name[1] == '.' && d->d_name[2] == '\0')
+ continue;
+ }
+
+ str_truncate(full_path, dir_len);
+ mailbox_dir = list->v.is_mailbox_dir != NULL &&
+ list->v.is_mailbox_dir(list, str_c(full_path),
+ d->d_name);
+ str_append(full_path, d->d_name);
+
+ if (mailbox_dir) {
+ if (unlink_directory(str_c(full_path), TRUE) < 0) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m",
+ str_c(full_path));
+ } else {
+ unlinked_something = TRUE;
+ }
+ continue;
+ }
+
+ /* trying to unlink() a directory gives either EPERM or EISDIR
+ (non-POSIX). it doesn't really work anywhere in practise,
+ so don't bother stat()ing the file first */
+ if (unlink(str_c(full_path)) == 0)
+ unlinked_something = TRUE;
+ else if (errno != ENOENT && errno != EISDIR && errno != EPERM) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m",
+ str_c(full_path));
+ }
+ }
+ if (errno != 0)
+ mailbox_list_set_critical(list, "readdir(%s) failed: %m", path);
+ if (closedir(dir) < 0) {
+ mailbox_list_set_critical(list, "closedir(%s) failed: %m",
+ path);
+ }
+
+ if (rmdir_path) {
+ if (rmdir(path) == 0)
+ unlinked_something = TRUE;
+ else if (errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
+ path);
+ return -1;
+ }
+ }
+
+ if (!unlinked_something) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ "Mailbox has children, can't delete it");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+mailbox_list_delete_until_root(struct mailbox_list *list, const char *path,
+ enum mailbox_list_path_type type)
+{
+ const char *root_dir, *p;
+ unsigned int len;
+
+ root_dir = mailbox_list_get_path(list, NULL, type);
+ if (strncmp(path, root_dir, strlen(root_dir)) != 0) {
+ /* mbox workaround: name=child/box, root_dir=mail/.imap/,
+ path=mail/child/.imap/box. we'll want to try to delete
+ the .imap/ part, but no further. */
+ len = strlen(path);
+ while (len > 0 && path[len-1] != '/')
+ len--;
+ if (len == 0)
+ return;
+ len--;
+ while (len > 0 && path[len-1] != '/')
+ len--;
+ if (len == 0)
+ return;
+
+ root_dir = t_strndup(path, len-1);
+ }
+ while (strcmp(path, root_dir) != 0) {
+ if (rmdir(path) < 0 && errno != ENOENT) {
+ if (errno == ENOTEMPTY)
+ return;
+
+ mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
+ path);
+ return;
+ }
+ p = strrchr(path, '/');
+ if (p == NULL)
+ break;
+
+ path = t_strdup_until(path, p);
+ }
+}
+
+static void mailbox_list_try_delete(struct mailbox_list *list, const char *name,
+ enum mailbox_list_path_type type)
+{
+ const char *mailbox_path, *path;
+
+ mailbox_path = mailbox_list_get_path(list, name,
+ MAILBOX_LIST_PATH_TYPE_MAILBOX);
+ path = mailbox_list_get_path(list, name, type);
+ if (path == NULL || *path == '\0' || strcmp(path, mailbox_path) == 0)
+ return;
+
+ if (*list->set.maildir_name == '\0' &&
+ (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) {
+ /* this directory may contain also child mailboxes' data.
+ we don't want to delete that. */
+ bool rmdir_path = *list->set.maildir_name != '\0';
+ if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
+ rmdir_path) < 0)
+ return;
+ } else {
+ if (unlink_directory(path, TRUE) < 0 &&
+ errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(list,
+ "unlink_directory(%s) failed: %m", path);
+ }
+ }
+
+ /* avoid leaving empty directories lying around */
+ mailbox_list_delete_until_root(list, path, type);
+}
+
+void mailbox_list_delete_finish(struct mailbox_list *list, const char *name)
+{
+ mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_INDEX);
+ mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL);
+ mailbox_list_try_delete(list, name, MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
+}
--- /dev/null
+#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
#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>
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)
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);
}
fs_list_iter_next,
fs_list_iter_deinit,
NULL,
+ NULL,
fs_list_set_subscribed,
fs_list_create_mailbox_dir,
fs_list_delete_mailbox,
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;
#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>
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)
maildir_list_iter_next,
maildir_list_iter_deinit,
NULL,
+ NULL,
maildir_list_set_subscribed,
maildir_list_create_mailbox_dir,
maildir_list_delete_mailbox,
maildir_list_iter_next,
maildir_list_iter_deinit,
NULL,
+ NULL,
maildir_list_set_subscribed,
maildir_list_create_mailbox_dir,
maildir_list_delete_mailbox,
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;
#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
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);
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);
}
}
+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))
{
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");
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);
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;
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 {
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);
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);
#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"
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') {
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
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
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)
test_mailbox_close,
test_mailbox_create,
test_mailbox_update,
+ test_mailbox_delete,
test_mailbox_get_status,
NULL,
NULL,
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,
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);
struct acl_mailbox {
union mailbox_module_context module_ctx;
struct acl_object *aclobj;
+ bool skip_acl_checks;
};
struct acl_transaction_context {
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)
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)
/* 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) {
}
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)
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;
union mailbox_list_module_context module_ctx;
unsigned int internal_namespace:1;
- unsigned int deleting:1;
};
struct lazy_expunge_transaction {
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)
}
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;
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 "
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);
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);
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)
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);
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,
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;
}
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
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,
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,
}
}
-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;
}
}
struct mail_storage;
struct mailbox_transaction_context;
struct mailbox_list;
+struct mailbox;
struct notify_context;
struct module;
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,
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,
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;
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,
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);
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);
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);
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);
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 */
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 */
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);
}
virtual_mailbox_close,
virtual_mailbox_create,
virtual_mailbox_update,
+ index_storage_mailbox_delete,
index_storage_get_status,
NULL,
NULL,