]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Implemented initial support for renaming mailboxes across namespaces.
authorTimo Sirainen <tss@iki.fi>
Fri, 22 May 2009 23:16:03 +0000 (19:16 -0400)
committerTimo Sirainen <tss@iki.fi>
Fri, 22 May 2009 23:16:03 +0000 (19:16 -0400)
--HG--
branch : HEAD

12 files changed:
src/imap/cmd-rename.c
src/lib-storage/index/dbox/dbox-storage.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/shared/shared-list.c
src/lib-storage/list/mailbox-list-fs.c
src/lib-storage/list/mailbox-list-maildir.c
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c
src/lib-storage/mailbox-list.h
src/plugins/acl/acl-mailbox-list.c
src/plugins/listescape/listescape-plugin.c
src/plugins/mail-log/mail-log-plugin.c

index bcbbd745ff1c82b653be701eef4ccf43911f2529..fb7d208f9a025bcb7d5defb41ecd34c79a5036ff 100644 (file)
@@ -7,8 +7,8 @@
 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;
 
@@ -22,33 +22,30 @@ bool cmd_rename(struct client_command_context *cmd)
        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;
 }
index adcd9973225f116a6ea352c55e3cb2923e70a378..0d0c6b626b0d90276777b024eacaa9022352d72f 100644 (file)
@@ -39,11 +39,14 @@ static MODULE_CONTEXT_DEFINE_INIT(dbox_mailbox_list_module,
 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,
@@ -605,47 +608,59 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *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;
        }
@@ -653,25 +668,42 @@ dbox_list_rename_mailbox_pre(struct mailbox_list *list,
 }
 
 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;
index 0f7d63fda03e9961174cb97e5d97f5ab19af23d0..711f445dd21c2882264630a0e3d7cc31aec90528 100644 (file)
@@ -43,8 +43,9 @@ static const char *maildir_subdirs[] = { "cur", "new", "tmp" };
 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,
@@ -829,28 +830,31 @@ maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
        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)
index ea70e1ef2674dcd8f11232e0e460e1e1e6c1f73e..52ec9a77720b69d07701beca78b7ac0104b1b69f 100644 (file)
@@ -245,52 +245,62 @@ shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
        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;
 }
 
index 39f5be7b19c19e00eea62eee9f265428f7d5d22b..0cabff94ce0ac5279bdeb9c1e092a2f8b380f687 100644 (file)
@@ -4,6 +4,7 @@
 #include "hostpid.h"
 #include "mkdir-parents.h"
 #include "subscription-file.h"
+#include "mail-storage.h"
 #include "mailbox-list-fs.h"
 
 #include <stdio.h>
@@ -279,50 +280,73 @@ static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
        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;
                }
@@ -333,21 +357,22 @@ static int fs_list_rename_mailbox(struct mailbox_list *list,
           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;
        }
 
@@ -355,18 +380,32 @@ static int fs_list_rename_mailbox(struct mailbox_list *list,
           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;
 }
 
index 3c45d177859b19ba3a565435ea157674aac1b574..61795562147a2bc0d6748882c6df3d1bdc04f441 100644 (file)
@@ -277,29 +277,29 @@ static int maildir_list_set_subscribed(struct mailbox_list *_list,
                                       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;
@@ -309,6 +309,7 @@ static int rename_children(struct mailbox_list *list,
        unsigned int i, count;
        size_t oldnamelen;
        pool_t pool;
+       char old_sep;
        int ret;
 
        ret = 0;
@@ -321,9 +322,9 @@ static int rename_children(struct mailbox_list *list,
        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;
@@ -331,8 +332,7 @@ static int rename_children(struct mailbox_list *list,
                /* 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);
                }
@@ -353,9 +353,9 @@ static int rename_children(struct mailbox_list *list,
                }
 
                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
@@ -369,16 +369,16 @@ static int rename_children(struct mailbox_list *list,
                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);
@@ -393,8 +393,10 @@ maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
        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;
@@ -402,26 +404,29 @@ static int maildir_list_rename_mailbox(struct mailbox_list *list,
 
        /* 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;
                }
@@ -430,10 +435,10 @@ static int maildir_list_rename_mailbox(struct mailbox_list *list,
        }
 
        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;
index 7c37315abda0d66acf1d89438851ecf93dfa241e..1dae0fa6b00660c81090e1b46006731ab63964bb 100644 (file)
@@ -51,11 +51,14 @@ struct mailbox_list_vfuncs {
        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 {
index cbd4eb83281ecb1e97e5594859f77c6e5804f59b..d4f1785616d02bf9d76871a9dfc43128be565f67 100644 (file)
@@ -9,6 +9,7 @@
 #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>
@@ -572,17 +573,43 @@ int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
        return list->v.delete_mailbox(list, name);
 }
 
-int mailbox_list_rename_mailbox(struct mailbox_list *list,
-                               const char *oldname, const char *newname)
+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)
index d6cf93a49113c3056c770b455cedbbaab6b4b5e9..804cccd20d9d0a25d624ce1c9f8e8f1df8877a00 100644 (file)
@@ -212,15 +212,13 @@ int mailbox_list_set_subscribed(struct mailbox_list *list,
 
 /* 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,
index 69c7df482c31b0f7f391289122f55bee2baf60b3..d0405a868dbdfba07ffa14d14aa94582151625a8 100644 (file)
@@ -455,24 +455,26 @@ acl_mailbox_list_delete(struct mailbox_list *list, const char *name)
 }
 
 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;
@@ -480,8 +482,8 @@ acl_mailbox_list_rename(struct mailbox_list *list,
 
        /* 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) {
@@ -489,15 +491,17 @@ acl_mailbox_list_rename(struct mailbox_list *list,
                        /* 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)
index ad54edade5a7e355fa125e8419c4a41b14182428..5770d696da475dbc9ea2cf36fa16891ea9a7f280 100644 (file)
@@ -204,14 +204,18 @@ listescape_delete_mailbox(struct mailbox_list *list, const char *name)
 }
 
 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, 
index 56fdc2a5b1119fef2346aa77ba11af67ec650df1..90d86df074eee90d252354b3409e43458d84afbf 100644 (file)
@@ -597,13 +597,16 @@ mail_log_mailbox_list_delete(struct mailbox_list *list, const char *name)
 }
 
 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)