bool cmd_rename(struct client_command_context *cmd)
{
struct mail_storage *old_storage, *new_storage;
- struct mailbox_list *list;
- struct mail_namespace *ns;
+ struct mailbox_list *old_list, *new_list;
+ struct mail_namespace *old_ns;
const char *oldname, *newname;
unsigned int oldlen;
old_storage = client_find_storage(cmd, &oldname);
if (old_storage == NULL)
return TRUE;
+ old_list = mail_storage_get_list(old_storage);
new_storage = client_find_storage(cmd, &newname);
if (new_storage == NULL)
return TRUE;
-
- if (old_storage != new_storage) {
- client_send_tagline(cmd,
- "NO Can't rename mailbox to another storage type.");
- return TRUE;
- }
-
- /* disallow box -> box/child, because it may break clients and there's
- really no point in doing it anyway. */
- list = mail_storage_get_list(old_storage);
- ns = mailbox_list_get_namespace(list);
- oldlen = strlen(oldname);
- if (strncmp(oldname, newname, oldlen) == 0 &&
- newname[oldlen] == ns->real_sep) {
- client_send_tagline(cmd,
- "NO Can't rename mailbox under its own child.");
- return TRUE;
+ new_list = mail_storage_get_list(new_storage);
+
+ if (old_storage == new_storage) {
+ /* disallow box -> box/child, because it may break clients and
+ there's really no point in doing it anyway. */
+ old_ns = mailbox_list_get_namespace(old_list);
+ oldlen = strlen(oldname);
+ if (strncmp(oldname, newname, oldlen) == 0 &&
+ newname[oldlen] == old_ns->real_sep) {
+ client_send_tagline(cmd,
+ "NO Can't rename mailbox under its own child.");
+ return TRUE;
+ }
}
- if (mailbox_list_rename_mailbox(list, oldname, newname) < 0)
- client_send_list_error(cmd, list);
- else {
+ if (mailbox_list_rename_mailbox(old_list, oldname,
+ new_list, newname, TRUE) < 0)
+ client_send_list_error(cmd, old_list);
+ else
client_send_tagline(cmd, "OK Rename completed.");
- }
return TRUE;
}
static int
dbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
static int
-dbox_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname);
+dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children);
static int
-dbox_list_rename_mailbox_pre(struct mailbox_list *list,
- const char *oldname, const char *newname);
+dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname);
static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
const char *dir, const char *fname,
const char *mailbox_name,
return -1;
}
-static bool
-dbox_list_rename_get_alt_paths(struct mailbox_list *list,
- const char *oldname, const char *newname,
+static int
+dbox_list_rename_get_alt_paths(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname,
+ enum mailbox_list_path_type path_type,
const char **oldpath_r, const char **newpath_r)
{
- struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
+ struct dbox_storage *oldstorage = DBOX_LIST_CONTEXT(oldlist);
+ struct dbox_storage *newstorage = DBOX_LIST_CONTEXT(newlist);
const char *path;
- if (storage->alt_dir == NULL)
- return FALSE;
-
- path = mailbox_list_get_path(list, oldname, MAILBOX_LIST_PATH_TYPE_DIR);
- *oldpath_r = dbox_get_alt_path(storage, path);
+ path = mailbox_list_get_path(oldlist, oldname, path_type);
+ *oldpath_r = dbox_get_alt_path(oldstorage, path);
if (*oldpath_r == NULL)
- return FALSE;
+ return 0;
- path = mailbox_list_get_path(list, newname, MAILBOX_LIST_PATH_TYPE_DIR);
- *newpath_r = dbox_get_alt_path(storage, path);
- i_assert(*newpath_r != NULL);
- return TRUE;
+ path = mailbox_list_get_path(newlist, newname, path_type);
+ *newpath_r = dbox_get_alt_path(newstorage, path);
+ if (*newpath_r == NULL) {
+ /* destination dbox storage doesn't have alt-path defined.
+ we can't do the rename easily. */
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailboxes across specified storages.");
+ return -1;
+ }
+ return 1;
}
static int
-dbox_list_rename_mailbox_pre(struct mailbox_list *list,
- const char *oldname, const char *newname)
+dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname)
{
const char *alt_oldpath, *alt_newpath;
struct stat st;
+ int ret;
- if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
- &alt_oldpath, &alt_newpath))
- return 0;
+ ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_DIR,
+ &alt_oldpath, &alt_newpath);
+ if (ret <= 0)
+ return ret;
if (stat(alt_newpath, &st) == 0) {
/* race condition or a directory left there lying around?
safest to just report error. */
- mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
return -1;
} else if (errno != ENOENT) {
- mailbox_list_set_critical(list, "stat(%s) failed: %m",
+ mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
alt_newpath);
return -1;
}
}
static int
-dbox_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
- struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
- const char *alt_oldpath, *alt_newpath;
+ struct dbox_storage *oldstorage = DBOX_LIST_CONTEXT(oldlist);
+ enum mailbox_list_path_type path_type;
+ const char *alt_oldpath, *alt_newpath, *path;
+ int ret;
- if (storage->list_module_ctx.super.rename_mailbox(list, oldname,
- newname) < 0)
+ if (oldstorage->list_module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children) < 0)
return -1;
- if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
- &alt_oldpath, &alt_newpath))
- return 0;
+ path_type = rename_children ? MAILBOX_LIST_PATH_TYPE_DIR :
+ MAILBOX_LIST_PATH_TYPE_MAILBOX;
+ ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
+ path_type, &alt_oldpath,
+ &alt_newpath);
+ if (ret <= 0)
+ return ret;
if (rename(alt_oldpath, alt_newpath) == 0) {
/* ok */
+ if (!rename_children) {
+ path = mailbox_list_get_path(oldlist, oldname,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ if (rmdir(path) < 0 &&
+ errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(oldlist,
+ "rmdir(%s) failed: %m", path);
+ }
+ }
} else if (errno != ENOENT) {
/* renaming is done already, so just log the error */
- mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
alt_oldpath, alt_newpath);
}
return 0;
static int
maildir_list_delete_mailbox(struct mailbox_list *list, const char *name);
static int
-maildir_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname);
+maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children);
static int
maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
const char *dir, const char *fname,
return 0;
}
-static int maildir_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+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_storage *storage = MAILDIR_LIST_CONTEXT(list);
+ struct maildir_storage *oldstorage = MAILDIR_LIST_CONTEXT(oldlist);
const char *path1, *path2;
if (strcmp(oldname, "INBOX") == 0) {
/* INBOX often exists as the root ~/Maildir.
We can't rename it then. */
- path1 = mailbox_list_get_path(list, oldname,
+ path1 = mailbox_list_get_path(oldlist, oldname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
- path2 = mailbox_list_get_path(list, NULL,
+ path2 = mailbox_list_get_path(oldlist, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(path1, path2) == 0) {
- mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
"Renaming INBOX isn't supported.");
return -1;
}
}
- return storage->list_module_ctx.super.
- rename_mailbox(list, oldname, newname);
+ return oldstorage->list_module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
}
static int maildir_storage_mailbox_close(struct mailbox *box)
return ret;
}
-static int shared_list_rename_get_ns(struct mailbox_list *list,
- const char **oldname, const char **newname,
+static int shared_list_rename_get_ns(struct mailbox_list *oldlist,
+ const char **oldname,
+ struct mailbox_list *newlist,
+ const char **newname,
struct mail_namespace **ns_r)
{
struct mail_namespace *old_ns, *new_ns;
- if (shared_storage_get_namespace(list->ns->storage,
+ if (shared_storage_get_namespace(oldlist->ns->storage,
oldname, &old_ns) < 0 ||
- shared_storage_get_namespace(list->ns->storage,
+ shared_storage_get_namespace(newlist->ns->storage,
newname, &new_ns) < 0)
return -1;
if (old_ns != new_ns) {
- mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
- "Can't rename mailboxes across storages");
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename shared mailboxes across storages.");
return -1;
}
*ns_r = old_ns;
return 0;
}
-static int shared_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+static int
+shared_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
struct mail_namespace *ns;
int ret;
- if (shared_list_rename_get_ns(list, &oldname, &newname, &ns) < 0)
+ if (shared_list_rename_get_ns(oldlist, &oldname,
+ newlist, &newname, &ns) < 0)
return -1;
- ret = mailbox_list_rename_mailbox(ns->list, oldname, newname);
+ ret = mailbox_list_rename_mailbox(ns->list, oldname, ns->list, newname,
+ rename_children);
if (ret < 0)
- shared_list_copy_error(list, ns);
+ shared_list_copy_error(oldlist, ns);
return ret;
}
static int
-shared_list_rename_mailbox_pre(struct mailbox_list *list,
- const char *oldname, const char *newname)
+shared_list_rename_mailbox_pre(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname)
{
struct mail_namespace *ns;
int ret;
- if (shared_list_rename_get_ns(list, &oldname, &newname, &ns) < 0)
+ if (shared_list_rename_get_ns(oldlist, &oldname,
+ newlist, &newname, &ns) < 0)
return -1;
- ret = ns->list->v.rename_mailbox_pre(ns->list, oldname, newname);
+ ret = ns->list->v.rename_mailbox_pre(ns->list, oldname,
+ ns->list, newname);
if (ret < 0)
- shared_list_copy_error(list, ns);
+ shared_list_copy_error(oldlist, ns);
return ret;
}
#include "hostpid.h"
#include "mkdir-parents.h"
#include "subscription-file.h"
+#include "mail-storage.h"
#include "mailbox-list-fs.h"
#include <stdio.h>
return mailbox_list_delete_index_control(list, name);
}
-static int rename_dir(struct mailbox_list *list,
- enum mailbox_list_path_type type,
- const char *oldname, const char *newname)
+static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ enum mailbox_list_path_type type, bool rmdir_parent)
{
- const char *oldpath, *newpath;
+ const char *oldpath, *newpath, *p;
- oldpath = mailbox_list_get_path(list, oldname, type);
- newpath = mailbox_list_get_path(list, newname, type);
+ oldpath = mailbox_list_get_path(oldlist, oldname, type);
+ newpath = mailbox_list_get_path(newlist, newname, type);
if (strcmp(oldpath, newpath) == 0)
return 0;
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
- mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
+ if (rmdir_parent && (p = strrchr(oldpath, '/')) != NULL) {
+ oldpath = t_strdup_until(oldpath, p);
+ if (rmdir(oldpath) < 0 &&
+ errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(oldlist,
+ "rmdir(%s) failed: %m", oldpath);
+ }
+ }
return 0;
}
-static int fs_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+static int fs_list_rename_mailbox(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname, bool rename_children)
{
const char *oldpath, *newpath, *p;
+ enum mailbox_list_path_type path_type;
struct stat st;
mode_t mode;
gid_t gid;
+ bool isfile, rmdir_parent = FALSE;
+
+ (void)mail_storage_get_mailbox_path(oldlist->ns->storage,
+ oldname, &isfile);
+ if (rename_children)
+ path_type = MAILBOX_LIST_PATH_TYPE_DIR;
+ else if (isfile || *oldlist->set.maildir_name != '\0')
+ path_type = MAILBOX_LIST_PATH_TYPE_MAILBOX;
+ else {
+ /* we can't do this, our children would get renamed with us */
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailbox without its children.");
+ return -1;
+ }
- oldpath = mailbox_list_get_path(list, oldname,
- MAILBOX_LIST_PATH_TYPE_DIR);
- newpath = mailbox_list_get_path(list, newname,
- MAILBOX_LIST_PATH_TYPE_DIR);
+ oldpath = mailbox_list_get_path(oldlist, oldname, path_type);
+ newpath = mailbox_list_get_path(newlist, newname, path_type);
/* create the hierarchy */
p = strrchr(newpath, '/');
if (p != NULL) {
- mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
+ mailbox_list_get_dir_permissions(newlist, NULL, &mode, &gid);
p = t_strdup_until(newpath, p);
if (mkdir_parents_chown(p, mode, (uid_t)-1, gid) < 0 &&
errno != EEXIST) {
- if (mailbox_list_set_error_from_errno(list))
+ if (mailbox_list_set_error_from_errno(oldlist))
return -1;
- mailbox_list_set_critical(list,
+ mailbox_list_set_critical(oldlist,
"mkdir_parents(%s) failed: %m", p);
return -1;
}
possibility that someone actually tries to rename two mailboxes
to same new one */
if (lstat(newpath, &st) == 0) {
- mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
return -1;
} else if (errno == ENOTDIR) {
- mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
"Target mailbox doesn't allow inferior mailboxes");
return -1;
} else if (errno != ENOENT && errno != EACCES) {
- mailbox_list_set_critical(list, "lstat(%s) failed: %m",
+ mailbox_list_set_critical(oldlist, "lstat(%s) failed: %m",
newpath);
return -1;
}
- if (list->v.rename_mailbox_pre != NULL) {
- if (list->v.rename_mailbox_pre(list, oldname, newname) < 0)
+ if (oldlist->v.rename_mailbox_pre != NULL) {
+ if (oldlist->v.rename_mailbox_pre(oldlist, oldname,
+ newlist, newname) < 0)
return -1;
}
the next time it's needed. */
if (rename(oldpath, newpath) < 0) {
if (ENOTFOUND(errno)) {
- mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
- } else if (!mailbox_list_set_error_from_errno(list)) {
- mailbox_list_set_critical(list,
+ } else if (!mailbox_list_set_error_from_errno(oldlist)) {
+ mailbox_list_set_critical(oldlist,
"rename(%s, %s) failed: %m", oldpath, newpath);
}
return -1;
}
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
- oldname, newname);
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX, oldname, newname);
+ if (!rename_children) {
+ /* if there are no child mailboxes, get rid of the mailbox
+ directory entirely. */
+ oldpath = mailbox_list_get_path(oldlist, oldname,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ if (rmdir(oldpath) == 0)
+ rmdir_parent = TRUE;
+ else if (errno != ENOENT && errno != ENOTEMPTY) {
+ mailbox_list_set_critical(oldlist,
+ "rmdir(%s) failed: %m", oldpath);
+ }
+ }
+
+ (void)rename_dir(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_CONTROL, rmdir_parent);
+ (void)rename_dir(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_INDEX, rmdir_parent);
return 0;
}
name, set);
}
-static int rename_dir(struct mailbox_list *list,
- enum mailbox_list_path_type type,
- const char *oldname, const char *newname)
+static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ enum mailbox_list_path_type type)
{
const char *oldpath, *newpath;
- oldpath = mailbox_list_get_path(list, oldname, type);
- newpath = mailbox_list_get_path(list, newname, type);
+ oldpath = mailbox_list_get_path(oldlist, oldname, type);
+ newpath = mailbox_list_get_path(newlist, newname, type);
if (strcmp(oldpath, newpath) == 0)
return 0;
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
- mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
-
return 0;
}
-static int rename_children(struct mailbox_list *list,
- const char *oldname, const char *newname)
+static int
+maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname)
{
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
unsigned int i, count;
size_t oldnamelen;
pool_t pool;
+ char old_sep;
int ret;
ret = 0;
pool = pool_alloconly_create("Maildir++ children list", 1024);
i_array_init(&names_arr, 64);
- pattern = t_strdup_printf("%s%c*", oldname,
- mailbox_list_get_hierarchy_sep(list));
- iter = mailbox_list_iter_init(list, pattern,
+ old_sep = mailbox_list_get_hierarchy_sep(oldlist);
+ pattern = t_strdup_printf("%s%c*", oldname, old_sep);
+ iter = mailbox_list_iter_init(oldlist, pattern,
MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
while ((info = mailbox_list_iter_next(iter)) != NULL) {
const char *name;
/* verify that the prefix matches, otherwise we could have
problems with mailbox names containing '%' and '*' chars */
if (strncmp(info->name, oldname, oldnamelen) == 0 &&
- info->name[oldnamelen] ==
- mailbox_list_get_hierarchy_sep(list)) {
+ info->name[oldnamelen] == old_sep) {
name = p_strdup(pool, info->name + oldnamelen);
array_append(&names_arr, &name, 1);
}
}
new_listname = t_strconcat(newname, names[i], NULL);
- oldpath = mailbox_list_get_path(list, old_listname,
+ oldpath = mailbox_list_get_path(oldlist, old_listname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
- newpath = mailbox_list_get_path(list, new_listname,
+ newpath = mailbox_list_get_path(newlist, new_listname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
/* FIXME: it's possible to merge two mailboxes if either one of
if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
ret = 1;
else {
- mailbox_list_set_critical(list,
+ mailbox_list_set_critical(oldlist,
"rename(%s, %s) failed: %m", oldpath, newpath);
ret = -1;
break;
}
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
- old_listname, new_listname);
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
- old_listname, new_listname);
+ (void)rename_dir(oldlist, old_listname, newlist, new_listname,
+ MAILBOX_LIST_PATH_TYPE_CONTROL);
+ (void)rename_dir(oldlist, old_listname, newlist, new_listname,
+ MAILBOX_LIST_PATH_TYPE_INDEX);
}
array_free(&names_arr);
pool_unref(&pool);
return mailbox_list_delete_index_control(list, name);
}
-static int maildir_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+static int
+maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
const char *oldpath, *newpath;
int ret;
/* NOTE: it's possible to rename a nonexisting mailbox which has
children. In that case we should ignore the rename() error. */
- oldpath = mailbox_list_get_path(list, oldname,
+ oldpath = mailbox_list_get_path(oldlist, oldname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
- newpath = mailbox_list_get_path(list, newname,
+ newpath = mailbox_list_get_path(newlist, newname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
ret = rename(oldpath, newpath);
if (ret == 0 || errno == ENOENT) {
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
- oldname, newname);
- (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
- oldname, newname);
+ (void)rename_dir(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_CONTROL);
+ (void)rename_dir(oldlist, oldname, newlist, newname,
+ MAILBOX_LIST_PATH_TYPE_INDEX);
found = ret == 0;
- T_BEGIN {
- ret = rename_children(list, oldname, newname);
+ if (!rename_children)
+ ret = 0;
+ else T_BEGIN {
+ ret = maildir_rename_children(oldlist, oldname,
+ newlist, newname);
} T_END;
if (ret < 0)
return -1;
if (!found && ret == 0) {
- mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
return -1;
}
}
if (EDESTDIREXISTS(errno)) {
- mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
} else {
- mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+ mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
oldpath, newpath);
}
return -1;
int (*set_subscribed)(struct mailbox_list *list,
const char *name, bool set);
int (*delete_mailbox)(struct mailbox_list *list, const char *name);
- int (*rename_mailbox)(struct mailbox_list *list, const char *oldname,
- const char *newname);
+ int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children);
/* called by rename_mailbox() just before running the actual rename() */
- int (*rename_mailbox_pre)(struct mailbox_list *list,
- const char *oldname, const char *newname);
+ int (*rename_mailbox_pre)(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname);
};
struct mailbox_list_module_register {
#include "imap-match.h"
#include "imap-utf7.h"
#include "mailbox-tree.h"
+#include "mail-storage-private.h"
#include "mailbox-list-private.h"
#include <time.h>
return list->v.delete_mailbox(list, name);
}
-int mailbox_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname)
+static bool nullequals(const void *p1, const void *p2)
{
- if (!mailbox_list_is_valid_existing_name(list, oldname) ||
- !mailbox_list_is_valid_create_name(list, newname)) {
- mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
+ return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL);
+}
+
+int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname, bool rename_children)
+{
+ if (!mailbox_list_is_valid_existing_name(oldlist, oldname) ||
+ !mailbox_list_is_valid_create_name(newlist, newname)) {
+ mailbox_list_set_error(oldlist, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
+ if (strcmp(oldlist->ns->storage->name,
+ newlist->ns->storage->name) != 0) {
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailbox to another storage type.");
+ return -1;
+ }
+ if (!nullequals(oldlist->set.index_dir, newlist->set.index_dir) ||
+ !nullequals(oldlist->set.control_dir, newlist->set.control_dir)) {
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailboxes across specified storages.");
+ return -1;
+ }
+ if (oldlist->ns->type != NAMESPACE_PRIVATE ||
+ newlist->ns->type != NAMESPACE_PRIVATE) {
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+ "Renaming not supported across non-private namespaces.");
+ return -1;
+ }
- return list->v.rename_mailbox(list, oldname, newname);
+ return oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
}
static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir)
/* Delete the given mailbox. If it has children, they aren't deleted. */
int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
-/* If the name has inferior hierarchical names, then the inferior
- hierarchical names MUST also be renamed (ie. foo -> bar renames
- also foo/bar -> bar/bar). newname may contain multiple new
- hierarchies.
-
- If oldname is case-insensitively "INBOX", the mails are moved
- into new mailbox but the INBOX mailbox must not be deleted. */
-int mailbox_list_rename_mailbox(struct mailbox_list *list,
- const char *oldname, const char *newname);
+/* Rename mailbox. Renaming across different mailbox lists is possible only
+ between private namespaces and storages of the same type. If the rename
+ fails, the error is set to oldlist. */
+int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
+ const char *oldname,
+ struct mailbox_list *newlist,
+ const char *newname, bool rename_children);
/* Returns the error message of last occurred error. */
const char *mailbox_list_get_last_error(struct mailbox_list *list,
}
static int
-acl_mailbox_list_rename(struct mailbox_list *list,
- const char *oldname, const char *newname)
+acl_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
- struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+ struct acl_mailbox_list *old_alist = ACL_LIST_CONTEXT(oldlist);
+ struct acl_mailbox_list *new_alist = ACL_LIST_CONTEXT(newlist);
bool can_see;
int ret;
/* renaming requires rights to delete the old mailbox */
- ret = acl_mailbox_list_have_right(list, oldname,
+ ret = acl_mailbox_list_have_right(oldlist, oldname,
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,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_PERM,
MAIL_ERRSTR_NO_PERMISSION);
} else {
- mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
}
return 0;
/* and create the new one under the parent mailbox */
T_BEGIN {
- ret = acl_storage_rights_ctx_have_right(&alist->rights, newname,
- TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
+ ret = acl_storage_rights_ctx_have_right(&new_alist->rights,
+ newname, TRUE, ACL_STORAGE_RIGHT_CREATE, NULL);
} T_END;
if (ret <= 0) {
/* Note that if the mailbox didn't have LOOKUP
permission, this not reveals to user the mailbox's
existence. Can't help it. */
- mailbox_list_set_error(list, MAIL_ERROR_PERM,
+ mailbox_list_set_error(oldlist, MAIL_ERROR_PERM,
MAIL_ERRSTR_NO_PERMISSION);
} else {
- mailbox_list_set_internal_error(list);
+ mailbox_list_set_internal_error(oldlist);
}
return -1;
}
- return alist->module_ctx.super.rename_mailbox(list, oldname, newname);
+ return old_alist->module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
}
static void acl_mailbox_list_init_shared(struct mailbox_list *list)
}
static int
-listescape_rename_mailbox(struct mailbox_list *list, const char *oldname,
- const char *newname)
+listescape_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
- struct listescape_mailbox_list *mlist = LIST_ESCAPE_LIST_CONTEXT(list);
-
- oldname = list_escape(list->ns, oldname, TRUE);
- newname = list_escape(list->ns, newname, TRUE);
- return mlist->module_ctx.super.rename_mailbox(list, oldname, newname);
+ struct listescape_mailbox_list *old_mlist =
+ LIST_ESCAPE_LIST_CONTEXT(oldlist);
+
+ oldname = list_escape(oldlist->ns, oldname, TRUE);
+ newname = list_escape(newlist->ns, newname, TRUE);
+ return old_mlist->module_ctx.super.
+ rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children);
}
static int listescape_set_subscribed(struct mailbox_list *list,
}
static int
-mail_log_mailbox_list_rename(struct mailbox_list *list, const char *oldname,
- const char *newname)
+mail_log_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname,
+ struct mailbox_list *newlist, const char *newname,
+ bool rename_children)
{
- union mailbox_list_module_context *llist = MAIL_LOG_LIST_CONTEXT(list);
- struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(list->ns->user);
+ union mailbox_list_module_context *llist =
+ MAIL_LOG_LIST_CONTEXT(oldlist);
+ struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(oldlist->ns->user);
- if (llist->super.rename_mailbox(list, oldname, newname) < 0)
+ if (llist->super.rename_mailbox(oldlist, oldname, newlist, newname,
+ rename_children) < 0)
return -1;
if ((muser->events & MAIL_LOG_EVENT_MAILBOX_RENAME) == 0)