]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Moved mailbox directory creation code to mailbox_list backend.
authorTimo Sirainen <tss@iki.fi>
Sat, 6 Feb 2010 23:50:49 +0000 (01:50 +0200)
committerTimo Sirainen <tss@iki.fi>
Sat, 6 Feb 2010 23:50:49 +0000 (01:50 +0200)
--HG--
branch : HEAD

14 files changed:
src/lib-storage/index/cydir/cydir-storage.c
src/lib-storage/index/dbox-common/dbox-storage.c
src/lib-storage/index/index-storage.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/mbox/mbox-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/mail-storage-private.h
src/lib-storage/mail-storage.c
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.h
src/plugins/acl/acl-mailbox-list.c
src/plugins/acl/acl-mailbox.c

index 61a3646373c41dfa2e7456577a3f0fadd9c437e9..393aa04c0f17566159b8c75e8e313faec6c345bd 100644 (file)
@@ -48,25 +48,6 @@ cydir_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
                set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME;
 }
 
-static int create_cydir(struct mail_storage *storage, struct mailbox_list *list,
-                       const char *path)
-{
-       const char *origin;
-       mode_t mode;
-       gid_t gid;
-
-       mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
-       if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 &&
-           errno != EEXIST) {
-               if (!mail_storage_set_error_from_errno(storage)) {
-                       mail_storage_set_critical(storage,
-                               "mkdir(%s) failed: %m", path);
-               }
-               return -1;
-       }
-       return 0;
-}
-
 static struct mailbox *
 cydir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
                    const char *name, struct istream *input,
@@ -114,7 +95,8 @@ static int cydir_mailbox_open(struct mailbox *box)
                /* exists, open it */
        } else if (errno == ENOENT && strcmp(box->name, "INBOX") == 0) {
                /* INBOX always exists, create it */
-               if (create_cydir(box->storage, box->list, box->path) < 0)
+               if (box->list->v.create_mailbox_dir(box->list,
+                                                   box->name, FALSE) < 0)
                        return -1;
        } else if (errno == ENOENT) {
                mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
@@ -136,21 +118,11 @@ static int
 cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                     bool directory)
 {
-       const char *path;
-       struct stat st;
-
-       path = mailbox_list_get_path(box->list, box->name,
-                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
-       if (stat(path, &st) == 0) {
-               mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-                                      "Mailbox already exists");
-               return -1;
-       }
-
-       if (create_cydir(box->storage, box->list, path) < 0)
-               return -1;
+       if (directory &&
+           (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+               return 0;
 
-       return directory || update == NULL ? 0 :
+       return update == NULL ? 0 :
                index_storage_mailbox_update(box, update);
 }
 
index f74061143ed43951722477947e0000989a81e5a1..142e9c6d7b730a540910ea0fdd49a4940ee9070e 100644 (file)
@@ -157,48 +157,17 @@ dbox_get_alt_path(struct mailbox_list *list, const char *path)
 int dbox_mailbox_create(struct mailbox *box,
                        const struct mailbox_update *update, bool directory)
 {
-       const char *path, *alt_path, *origin;
-       struct stat st;
-
-       path = mailbox_list_get_path(box->list, box->name,
-                                    directory ? MAILBOX_LIST_PATH_TYPE_DIR :
-                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
-       if (stat(path, &st) == 0) {
-               mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-                                      "Mailbox already exists");
-               return -1;
-       }
+       struct dbox_storage *storage = (struct dbox_storage *)box->storage;
 
-       if (directory) {
-               mode_t mode;
-               gid_t gid;
+       if (directory &&
+           (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+               return 0;
 
-               mailbox_list_get_dir_permissions(box->list, NULL, &mode,
-                                                &gid, &origin);
-               if (mkdir_parents_chgrp(path, mode, gid, origin) == 0)
-                       return 0;
-               else if (errno == EEXIST) {
-                       mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-                                              "Mailbox already exists");
-               } else if (!mail_storage_set_error_from_errno(box->storage)) {
-                       mail_storage_set_critical(box->storage,
-                                                 "mkdir(%s) failed: %m", path);
-               }
+       if (index_storage_mailbox_open(box) < 0)
                return -1;
-       }
-
-       /* make sure the alt path doesn't exist yet. it shouldn't (except with
-          race conditions with RENAME/DELETE), but if something crashed and
-          left it lying around we don't want to start overwriting files in
-          it. */
-       alt_path = dbox_get_alt_path(box->list, path);
-       if (alt_path != NULL && stat(alt_path, &st) == 0) {
-               mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
-                                      "Mailbox already exists");
+       if (storage->v.mailbox_create_indexes(box, update) < 0)
                return -1;
-       }
-
-       return dbox_mailbox_create_indexes(box, update);
+       return 0;
 }
 
 int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED,
index f5a4e0869386e87ee2f8a9bb3cf88d25c112f783..8f30abe5516cbc1d96ec314f08824b7814cdc05a 100644 (file)
@@ -451,8 +451,6 @@ void index_storage_mailbox_alloc(struct index_mailbox *ibox, const char *name,
 {
        struct mailbox *box = &ibox->box;
        const char *path;
-       gid_t dir_gid;
-       const char *origin, *dir_origin;
        string_t *vname;
 
        if (name != NULL) {
@@ -487,18 +485,11 @@ void index_storage_mailbox_alloc(struct index_mailbox *ibox, const char *name,
        ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
        ibox->index = index_storage_alloc(box->list, name, flags, index_prefix);
 
-       if (box->file_create_mode == 0) {
-               mailbox_list_get_permissions(box->list, name,
-                                            &box->file_create_mode,
-                                            &box->file_create_gid, &origin);
-               box->file_create_gid_origin = p_strdup(box->pool, origin);
-               mailbox_list_get_dir_permissions(box->list, name,
-                                                &box->dir_create_mode,
-                                                &dir_gid, &dir_origin);
-               mail_index_set_permissions(ibox->index,
-                                          box->file_create_mode,
-                                          box->file_create_gid, origin);
-       }
+       if (box->file_create_mode == 0)
+               mailbox_refresh_permissions(box);
+       mail_index_set_permissions(ibox->index, box->file_create_mode,
+                                  box->file_create_gid,
+                                  box->file_create_gid_origin);
 }
 
 int index_storage_mailbox_enable(struct mailbox *box,
index 68fbf747584b6849fc5db32cf202ab3cf47f392b..4ff20e9628db10e4981761a778253edf19024623 100644 (file)
@@ -23,8 +23,6 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
-#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
-
 #define MAILDIR_LIST_CONTEXT(obj) \
        MODULE_CONTEXT(obj, maildir_mailbox_list_module)
 
@@ -293,19 +291,16 @@ static int maildir_check_tmp(struct mail_storage *storage, const char *dir)
 }
 
 /* create or fix maildir, ignore if it already exists */
-static int
-create_maildir(struct mail_storage *storage, struct mail_namespace *ns,
-              const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
-              bool verify)
+static int create_maildir(struct mailbox *box, bool verify)
 {
        const char *path;
        unsigned int i;
        int ret;
 
        if (!verify) {
-               ret = maildir_check_tmp(storage, dir);
+               ret = maildir_check_tmp(box->storage, box->path);
                if (ret > 0) {
-                       mail_storage_set_error(storage,
+                       mail_storage_set_error(box->storage,
                                MAIL_ERROR_EXISTS, "Mailbox already exists");
                        return -1;
                }
@@ -314,9 +309,10 @@ create_maildir(struct mail_storage *storage, struct mail_namespace *ns,
        }
 
        for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
-               path = t_strconcat(dir, "/", maildir_subdirs[i], NULL);
-               if (mkdir_verify(storage, ns, path, mode, gid,
-                                gid_origin, verify) < 0)
+               path = t_strconcat(box->path, "/", maildir_subdirs[i], NULL);
+               if (mkdir_verify(box->storage, box->list->ns, path,
+                                box->dir_create_mode, box->file_create_gid,
+                                box->file_create_gid_origin, verify) < 0)
                        return -1;
        }
        return 0;
@@ -327,15 +323,6 @@ static void maildir_lock_touch_timeout(struct maildir_mailbox *mbox)
        (void)maildir_uidlist_lock_touch(mbox->uidlist);
 }
 
-static mode_t get_dir_mode(mode_t mode)
-{
-       /* add the execute bit if either read or write bit is set */
-       if ((mode & 0600) != 0) mode |= 0100;
-       if ((mode & 0060) != 0) mode |= 0010;
-       if ((mode & 0006) != 0) mode |= 0001;
-       return mode;
-}
-
 static struct mailbox *
 maildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
                      const char *name, struct istream *input,
@@ -374,26 +361,9 @@ static int maildir_mailbox_open_existing(struct mailbox *box)
        struct stat st;
        const char *shared_path;
 
-       /* for shared mailboxes get the create mode from the
-          permissions of dovecot-shared file. */
        shared_path = t_strconcat(box->path, "/dovecot-shared", NULL);
-       if (stat(shared_path, &st) == 0) {
-               if ((st.st_mode & S_ISGID) != 0 ||
-                   (st.st_mode & 0060) == 0) {
-                       /* Ignore GID */
-                       st.st_gid = (gid_t)-1;
-               }
-               mail_index_set_permissions(mbox->ibox.index,
-                                          st.st_mode & 0666, st.st_gid,
-                                          shared_path);
-
-               box->file_create_mode = st.st_mode & 0666;
-               box->dir_create_mode = get_dir_mode(st.st_mode & 0666);
-               box->file_create_gid = st.st_gid;
-               mbox->ibox.box.file_create_gid_origin =
-                       p_strdup(box->pool, shared_path);
+       if (stat(shared_path, &st) == 0)
                box->private_flags_mask = MAIL_SEEN;
-       }
 
        if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
                if (maildir_uidlist_lock(mbox->uidlist) <= 0)
@@ -412,9 +382,6 @@ static int maildir_mailbox_open_existing(struct mailbox *box)
 static int maildir_mailbox_open(struct mailbox *box)
 {
        struct stat st;
-       const char *gid_origin;
-       mode_t mode;
-       gid_t gid;
        int ret;
        bool inbox;
 
@@ -440,10 +407,7 @@ static int maildir_mailbox_open(struct mailbox *box)
        /* tmp/ directory doesn't exist. does the maildir? */
        if (inbox || (*box->name != '\0' && stat(box->path, &st) == 0)) {
                /* yes, we'll need to create the missing dirs */
-               mailbox_list_get_dir_permissions(box->list, box->name,
-                                                &mode, &gid, &gid_origin);
-               if (create_maildir(box->storage, box->list->ns, box->path,
-                                  mode, gid, gid_origin, TRUE) < 0)
+               if (create_maildir(box, TRUE) < 0)
                        return -1;
 
                return maildir_mailbox_open_existing(box);
@@ -458,40 +422,31 @@ static int maildir_mailbox_open(struct mailbox *box)
        }
 }
 
-static int
-maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
-                     const char *dir, mode_t mode, gid_t gid,
-                     const char *gid_origin)
+static int maildir_create_shared(struct mailbox *box)
 {
        const char *path;
        mode_t old_mask;
        int fd;
 
-       /* add the execute bit if either read or write bit is set */
-       if ((mode & 0600) != 0) mode |= 0100;
-       if ((mode & 0060) != 0) mode |= 0010;
-       if ((mode & 0006) != 0) mode |= 0001;
-
-       if (create_maildir(storage, ns, dir, mode, gid, gid_origin, FALSE) < 0)
-               return -1;
-
-       old_mask = umask(0777 ^ mode);
-       path = t_strconcat(dir, "/dovecot-shared", NULL);
-       fd = open(path, O_WRONLY | O_CREAT, mode & 0666);
+       old_mask = umask(0);
+       path = t_strconcat(box->path, "/dovecot-shared", NULL);
+       fd = open(path, O_WRONLY | O_CREAT, box->file_create_mode);
        umask(old_mask);
 
        if (fd == -1) {
-               mail_storage_set_critical(storage, "open(%s) failed: %m", path);
+               mail_storage_set_critical(box->storage, "open(%s) failed: %m",
+                                         path);
                return -1;
        }
 
-       if (fchown(fd, (uid_t)-1, gid) < 0) {
+       if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
                if (errno == EPERM) {
-                       mail_storage_set_critical(storage, "%s",
+                       mail_storage_set_critical(box->storage, "%s",
                                eperm_error_get_chgrp("fchown", path,
-                                                     gid, gid_origin));
+                                       box->file_create_gid,
+                                       box->file_create_gid_origin));
                } else {
-                       mail_storage_set_critical(storage,
+                       mail_storage_set_critical(box->storage,
                                "fchown(%s) failed: %m", path);
                }
        }
@@ -533,65 +488,27 @@ static int
 maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                       bool directory)
 {
+       const char *root_dir, *shared_path;
        struct stat st;
-       const char *path, *root_dir, *shared_path, *gid_origin;
-       mode_t old_mask;
-       int fd;
 
-       path = mailbox_list_get_path(box->list, box->name,
-                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
+       if (directory &&
+           (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+               return 0;
+
+       if (create_maildir(box, FALSE) < 0)
+               return -1;
+
+       /* if dovecot-shared exists in the root dir, copy it to newly
+          created mailboxes */
        root_dir = mailbox_list_get_path(box->list, NULL,
                                         MAILBOX_LIST_PATH_TYPE_MAILBOX);
-
-       /* if dovecot-shared exists in the root dir, create the mailbox using
-          its permissions and gid, and copy the dovecot-shared inside it. */
        shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
        if (stat(shared_path, &st) == 0) {
-               gid_origin = shared_path;
-               if (maildir_create_shared(box->storage, box->list->ns, path,
-                                         st.st_mode & 0666, st.st_gid,
-                                         gid_origin) < 0)
-                       return -1;
-       } else {
-               mailbox_list_get_dir_permissions(box->list, NULL, &st.st_mode,
-                                                &st.st_gid, &gid_origin);
-               if (create_maildir(box->storage, box->list->ns, path,
-                                  st.st_mode, st.st_gid, gid_origin,
-                                  FALSE) < 0)
+               if (maildir_create_shared(box) < 0)
                        return -1;
        }
 
-       /* Maildir++ spec wants that maildirfolder named file is created for
-          all subfolders. */
-       path = t_strconcat(path, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
-       old_mask = umask(0777 ^ (st.st_mode & 0666));
-       fd = open(path, O_CREAT | O_WRONLY, 0666);
-       umask(old_mask);
-       if (fd != -1) {
-               /* if dovecot-shared exists, use the same group */
-               if (st.st_gid == (gid_t)-1) {
-                       /* doesn't exist */
-               } else if (fchown(fd, (uid_t)-1, st.st_gid) == 0) {
-                       /* ok */
-               } else if (errno == EPERM) {
-                       mail_storage_set_critical(box->storage, "%s",
-                               eperm_error_get_chgrp("fchown", path,
-                                                    st.st_gid, gid_origin));
-               } else {
-                       mail_storage_set_critical(box->storage,
-                               "fchown(%s) failed: %m", path);
-               }
-               (void)close(fd);
-       } else if (errno == ENOENT) {
-               mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
-                       "Mailbox was deleted while it was being created");
-               return -1;
-       } else {
-               mail_storage_set_critical(box->storage,
-                       "open(%s, O_CREAT) failed: %m", path);
-       }
-       return directory || update == NULL ? 0 :
-               maildir_mailbox_update(box, update);
+       return update == NULL ? 0 : maildir_mailbox_update(box, update);
 }
 
 static void
index cbc0ccae7f1f361ce43927852ba4e9d03a9afbbd..b513b7ec8e4a64744ff4d956125ec0b2d79366a1 100644 (file)
@@ -515,70 +515,23 @@ static int
 mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                    bool directory)
 {
-       struct mail_storage *storage = box->storage;
-       const char *path, *p, *origin;
-       struct stat st;
-       mode_t mode;
-       gid_t gid;
-       int fd;
+       int fd, ret;
 
-       /* make sure it doesn't exist already */
-       path = mailbox_list_get_path(box->list, box->name,
-                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
-       if (stat(path, &st) == 0) {
-               mail_storage_set_error(storage, MAIL_ERROR_EXISTS,
-                                      "Mailbox already exists");
-               return -1;
-       }
+       if (directory &&
+           (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
+               return 0;
 
-       if (errno != ENOENT) {
-               if (errno == ENOTDIR) {
-                       mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
-                               "Mailbox doesn't allow inferior mailboxes");
-               } else if (!mail_storage_set_error_from_errno(storage)) {
-                       mail_storage_set_critical(storage,
-                               "stat() failed for mbox file %s: %m", path);
+       /* create the mbox file */
+       ret = mailbox_create_fd(box, box->path, O_RDWR | O_CREAT | O_EXCL, &fd);
+       if (ret <= 0) {
+               if (ret == 0) {
+                       mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+                                              "Mailbox already exists");
                }
                return -1;
        }
-
-       /* create the hierarchy if needed */
-       p = directory ? path + strlen(path) : strrchr(path, '/');
-       if (p != NULL) {
-               p = t_strdup_until(path, p);
-               mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid,
-                                                &origin);
-               if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
-                   errno != EEXIST) {
-                       if (!mail_storage_set_error_from_errno(storage)) {
-                               mail_storage_set_critical(storage,
-                                       "mkdir_parents(%s) failed: %m", p);
-                       }
-                       return -1;
-               }
-
-               if (directory) {
-                       /* wanted to create only the directory */
-                       return 0;
-               }
-       }
-
-       /* create the mailbox file */
-       fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
-       if (fd != -1) {
-               (void)close(fd);
-               return update == NULL ? 0 : mbox_mailbox_update(box, update);
-       }
-
-       if (errno == EEXIST) {
-               /* mailbox was just created between stat() and open() call.. */
-               mail_storage_set_error(storage, MAIL_ERROR_EXISTS,
-                                      "Mailbox already exists");
-       } else if (!mail_storage_set_error_from_errno(storage)) {
-               mail_storage_set_critical(storage,
-                       "Can't create mailbox %s: %m", box->name);
-       }
-       return -1;
+       (void)close(fd);
+       return update == NULL ? 0 : mbox_mailbox_update(box, update);
 }
 
 static void mbox_mailbox_close(struct mailbox *box)
index 571b13f9162b403bd5052552f003271f86905e12..b87ba6230d26db4d11e412e9a15e29aead6ec7b7 100644 (file)
@@ -95,7 +95,9 @@ shared_list_get_path(struct mailbox_list *list, const char *name,
            shared_storage_get_namespace(&ns, &name) < 0) {
                switch (type) {
                case MAILBOX_LIST_PATH_TYPE_DIR:
+               case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
                case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+               case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
                case MAILBOX_LIST_PATH_TYPE_CONTROL:
                        break;
                case MAILBOX_LIST_PATH_TYPE_INDEX:
@@ -230,6 +232,21 @@ static int shared_list_set_subscribed(struct mailbox_list *list,
        return ret;
 }
 
+static int
+shared_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+                              bool directory)
+{
+       struct mail_namespace *ns = list->ns;
+       int ret;
+
+       if (shared_storage_get_namespace(&ns, &name) < 0)
+               return -1;
+       ret = ns->list->v.create_mailbox_dir(ns->list, name, directory);
+       if (ret < 0)
+               shared_list_copy_error(list, ns);
+       return ret;
+}
+
 static int
 shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
@@ -337,6 +354,7 @@ struct mailbox_list shared_mailbox_list = {
                shared_list_iter_deinit,
                NULL,
                shared_list_set_subscribed,
+               shared_list_create_mailbox_dir,
                shared_list_delete_mailbox,
                shared_list_delete_dir,
                shared_list_rename_mailbox,
index 6f862435bfcd6483bfc9ae41ebbc3d89f609bbb7..f344df28b7ccbc236609c7c105e84f46bf7f0bca 100644 (file)
@@ -142,17 +142,23 @@ fs_list_get_path(struct mailbox_list *_list, const char *name,
                 enum mailbox_list_path_type type)
 {
        const struct mailbox_list_settings *set = &_list->set;
-       const char *path;
+       const char *path, *root_dir;
 
        if (name == NULL) {
                /* return root directories */
                switch (type) {
                case MAILBOX_LIST_PATH_TYPE_DIR:
                        return set->root_dir;
+               case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+                       return _list->set.alt_dir;
                case MAILBOX_LIST_PATH_TYPE_MAILBOX:
                        path = t_strconcat(set->root_dir, "/",
                                           set->mailbox_dir_name, NULL);
                        return t_strndup(path, strlen(path)-1);
+               case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+                       path = t_strconcat(set->alt_dir, "/",
+                                          set->mailbox_dir_name, NULL);
+                       return t_strndup(path, strlen(path)-1);
                case MAILBOX_LIST_PATH_TYPE_CONTROL:
                        return set->control_dir != NULL ?
                                set->control_dir : set->root_dir;
@@ -168,14 +174,25 @@ fs_list_get_path(struct mailbox_list *_list, const char *name,
        if (mailbox_list_try_get_absolute_path(_list, &name))
                return name;
 
+       root_dir = set->root_dir;
        switch (type) {
        case MAILBOX_LIST_PATH_TYPE_DIR:
                if (*set->maildir_name != '\0')
                        return t_strdup_printf("%s/%s%s", set->root_dir,
                                               set->mailbox_dir_name, name);
                break;
+       case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+               if (*set->maildir_name != '\0')
+                       return t_strdup_printf("%s/%s%s", set->alt_dir,
+                                              set->mailbox_dir_name, name);
+               break;
        case MAILBOX_LIST_PATH_TYPE_MAILBOX:
                break;
+       case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+               if (set->alt_dir == NULL)
+                       return NULL;
+               root_dir = set->alt_dir;
+               break;
        case MAILBOX_LIST_PATH_TYPE_CONTROL:
                if (set->control_dir != NULL)
                        return t_strdup_printf("%s/%s%s", set->control_dir,
@@ -191,19 +208,23 @@ fs_list_get_path(struct mailbox_list *_list, const char *name,
                break;
        }
 
-       /* If INBOX is a file, index and control directories are located
-          in root directory. */
-       if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL &&
-           ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
-            type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
-            type == MAILBOX_LIST_PATH_TYPE_DIR))
-               return set->inbox_path;
+       if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
+           type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
+               /* don't use inbox_path */
+       } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) {
+               /* If INBOX is a file, index and control directories are
+                  located in root directory. */
+               if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
+                   type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
+                   type == MAILBOX_LIST_PATH_TYPE_DIR)
+                       return set->inbox_path;
+       }
 
        if (*set->maildir_name == '\0') {
-               return t_strdup_printf("%s/%s%s", set->root_dir,
+               return t_strdup_printf("%s/%s%s", root_dir,
                                       set->mailbox_dir_name, name);
        } else {
-               return t_strdup_printf("%s/%s%s/%s", set->root_dir,
+               return t_strdup_printf("%s/%s%s/%s", root_dir,
                                       set->mailbox_dir_name, name,
                                       set->maildir_name);
        }
@@ -275,6 +296,61 @@ static int fs_list_set_subscribed(struct mailbox_list *_list,
                                       name, set);
 }
 
+static int
+fs_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+                          bool directory)
+{
+       const char *path, *alt_path, *gid_origin, *p;
+       struct stat st;
+       mode_t mode;
+       gid_t gid;
+       bool create_parent_dir;
+
+       /* make sure the alt path doesn't exist yet. it shouldn't (except with
+          race conditions with RENAME/DELETE), but if something crashed and
+          left it lying around we don't want to start overwriting files in
+          it. */
+       if (!directory) {
+               alt_path = mailbox_list_get_path(list, name,
+                                       MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
+               if (alt_path != NULL && stat(alt_path, &st) == 0) {
+                       mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+                                              "Mailbox already exists");
+                       return -1;
+               }
+       }
+
+       path = mailbox_list_get_path(list, name,
+                                    directory ? MAILBOX_LIST_PATH_TYPE_DIR :
+                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
+       create_parent_dir = !directory &&
+               (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
+       if (create_parent_dir) {
+               /* we only need to make sure that the parent directory exists */
+               p = strrchr(path, '/');
+               if (p == NULL)
+                       return 0;
+               path = t_strdup_until(path, p);
+       }
+
+       mailbox_list_get_dir_permissions(list, NULL, &mode,
+                                        &gid, &gid_origin);
+       if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0)
+               return 0;
+       else if (errno == EEXIST) {
+               if (create_parent_dir)
+                       return 0;
+               mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+                                      "Mailbox already exists");
+       } else if (errno == ENOTDIR) {
+               mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+                       "Mailbox doesn't allow inferior mailboxes");
+       } else if (!mailbox_list_set_error_from_errno(list)) {
+               mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
+       }
+       return -1;
+}
+
 static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
        /* let the backend handle the rest */
@@ -461,6 +537,7 @@ struct mailbox_list fs_mailbox_list = {
                fs_list_iter_deinit,
                NULL,
                fs_list_set_subscribed,
+               fs_list_create_mailbox_dir,
                fs_list_delete_mailbox,
                fs_list_delete_dir,
                fs_list_rename_mailbox,
index 11e85d2aea343a455b2766f190aa2f317d352d20..65674d16ca317e623a6159549f1c3a6d6d96c71a 100644 (file)
@@ -3,12 +3,15 @@
 #include "lib.h"
 #include "array.h"
 #include "hostpid.h"
+#include "eacces-error.h"
+#include "mkdir-parents.h"
 #include "subscription-file.h"
 #include "mailbox-list-maildir.h"
 
 #include <stdio.h>
 #include <sys/stat.h>
 
+#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
 #define MAILDIR_GLOBAL_TEMP_PREFIX "temp."
 #define IMAPDIR_GLOBAL_TEMP_PREFIX ".temp."
 
@@ -168,12 +171,17 @@ static const char *
 maildir_list_get_path(struct mailbox_list *_list, const char *name,
                      enum mailbox_list_path_type type)
 {
+       const char *root_dir;
+
        if (name == NULL) {
                /* return root directories */
                switch (type) {
                case MAILBOX_LIST_PATH_TYPE_DIR:
                case MAILBOX_LIST_PATH_TYPE_MAILBOX:
                        return _list->set.root_dir;
+               case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+               case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+                       return _list->set.alt_dir;
                case MAILBOX_LIST_PATH_TYPE_CONTROL:
                        return _list->set.control_dir != NULL ?
                                _list->set.control_dir : _list->set.root_dir;
@@ -188,10 +196,17 @@ maildir_list_get_path(struct mailbox_list *_list, const char *name,
            (*name == '/' || *name == '~'))
                return maildir_list_get_absolute_path(_list, name);
 
+       root_dir = _list->set.root_dir;
        switch (type) {
        case MAILBOX_LIST_PATH_TYPE_DIR:
        case MAILBOX_LIST_PATH_TYPE_MAILBOX:
                break;
+       case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
+       case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
+               if (_list->set.alt_dir == NULL)
+                       return NULL;
+               root_dir = _list->set.alt_dir;
+               break;
        case MAILBOX_LIST_PATH_TYPE_CONTROL:
                if (_list->set.control_dir != NULL) {
                        return maildir_list_get_dirname_path(_list,
@@ -208,10 +223,13 @@ maildir_list_get_path(struct mailbox_list *_list, const char *name,
                break;
        }
 
-       if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
+       if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
+           type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
+               /* don't use inbox_path */
+       } else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
                return _list->set.inbox_path;
 
-       return maildir_list_get_dirname_path(_list, _list->set.root_dir, name);
+       return maildir_list_get_dirname_path(_list, root_dir, name);
 }
 
 static int
@@ -380,6 +398,92 @@ maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
        return ret;
 }
 
+static int
+maildir_list_create_maildirfolder_file(struct mailbox_list *list,
+                                      const char *dir)
+{
+       const char *path, *gid_origin;
+       mode_t mode, old_mask;
+       gid_t gid;
+       int fd;
+
+       /* Maildir++ spec wants that maildirfolder named file is created for
+          all subfolders. */
+       mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
+
+       path = t_strconcat(dir, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
+       old_mask = umask(0);
+       fd = open(path, O_CREAT | O_WRONLY, mode);
+       umask(old_mask);
+       if (fd != -1) {
+               /* ok */
+       } else if (errno == ENOENT) {
+               mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+                       "Mailbox was deleted while it was being created");
+               return -1;
+       } else {
+               mailbox_list_set_critical(list,
+                       "open(%s, O_CREAT) failed: %m", path);
+               return -1;
+       }
+
+       if (gid != (gid_t)-1) {
+               if (fchown(fd, (uid_t)-1, gid) == 0) {
+                       /* ok */
+               } else if (errno == EPERM) {
+                       mailbox_list_set_critical(list, "%s",
+                               eperm_error_get_chgrp("fchown", path,
+                                                     gid, gid_origin));
+               } else {
+                       mailbox_list_set_critical(list,
+                               "fchown(%s) failed: %m", path);
+               }
+       }
+       (void)close(fd);
+       return 0;
+}
+
+static int
+maildir_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
+                               bool directory ATTR_UNUSED)
+{
+       const char *path, *gid_origin, *p;
+       mode_t mode;
+       gid_t gid;
+       bool create_parent_dir;
+
+       path = mailbox_list_get_path(list, name,
+                                    MAILBOX_LIST_PATH_TYPE_MAILBOX);
+       create_parent_dir = !directory &&
+               (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
+       if (create_parent_dir) {
+               /* we only need to make sure that the parent directory exists */
+               p = strrchr(path, '/');
+               if (p == NULL)
+                       return 0;
+               path = t_strdup_until(path, p);
+       }
+
+       mailbox_list_get_dir_permissions(list, NULL, &mode,
+                                        &gid, &gid_origin);
+       if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) {
+               /* ok */
+       } else if (errno == EEXIST) {
+               if (!create_parent_dir) {
+                       mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
+                                              "Mailbox already exists");
+                       return -1;
+               }
+       } else if (mailbox_list_set_error_from_errno(list)) {
+               return -1;
+       } else {
+               mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
+               return -1;
+       }
+       return create_parent_dir ? 0 :
+               maildir_list_create_maildirfolder_file(list, path);
+}
+
 static int
 maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
 {
@@ -461,7 +565,8 @@ maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
 struct mailbox_list maildir_mailbox_list = {
        .name = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS,
        .hierarchy_sep = '.',
-       .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME,
+       .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+               MAILBOX_LIST_PROP_NO_NOSELECT,
        .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
        {
@@ -480,6 +585,7 @@ struct mailbox_list maildir_mailbox_list = {
                maildir_list_iter_deinit,
                NULL,
                maildir_list_set_subscribed,
+               maildir_list_create_mailbox_dir,
                maildir_list_delete_mailbox,
                maildir_list_delete_dir,
                maildir_list_rename_mailbox,
@@ -490,7 +596,8 @@ struct mailbox_list maildir_mailbox_list = {
 struct mailbox_list imapdir_mailbox_list = {
        .name = MAILBOX_LIST_NAME_IMAPDIR,
        .hierarchy_sep = '.',
-       .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME,
+       .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+               MAILBOX_LIST_PROP_NO_NOSELECT,
        .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
        {
@@ -509,6 +616,7 @@ struct mailbox_list imapdir_mailbox_list = {
                maildir_list_iter_deinit,
                NULL,
                maildir_list_set_subscribed,
+               maildir_list_create_mailbox_dir,
                maildir_list_delete_mailbox,
                maildir_list_delete_dir,
                maildir_list_rename_mailbox,
index bf2cc5993534f5b4ac01c1d0af64d710951f8a60..4e5a8d1cb0260b0b0e476efdaa9a68fe367e0710 100644 (file)
@@ -430,5 +430,10 @@ bool mail_storage_set_error_from_errno(struct mail_storage *storage);
 int mail_set_aborted(struct mail *mail);
 void mail_set_expunged(struct mail *mail);
 void mailbox_set_deleted(struct mailbox *box);
+void mailbox_refresh_permissions(struct mailbox *box);
+
+/* Returns -1 if error, 0 if failed with EEXIST, 1 if ok */
+int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
+                     int *fd_r);
 
 #endif
index 61c2180e9cae1e53d5d08d9d6199eda9abcddaec..283bd7b1eb4b7dfacbca838f5698b3cdb6a73489 100644 (file)
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "array.h"
 #include "llist.h"
+#include "eacces-error.h"
 #include "mkdir-parents.h"
 #include "var-expand.h"
 #include "mail-index-private.h"
@@ -520,6 +521,17 @@ int mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                return -1;
        }
 
+       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);
+               return -1;
+       }
+       mailbox_refresh_permissions(box);
+
        return box->v.create(box, update, directory);
 }
 
@@ -1079,3 +1091,74 @@ void mailbox_set_deleted(struct mailbox *box)
                               "Mailbox was deleted under us");
        box->mailbox_deleted = TRUE;
 }
+
+void mailbox_refresh_permissions(struct mailbox *box)
+{
+       const char *origin, *dir_origin;
+       gid_t dir_gid;
+
+       if (box->input != NULL) {
+               box->file_create_mode = 0600;
+               box->dir_create_mode = 0700;
+               box->file_create_gid = (gid_t)-1;
+               box->file_create_gid_origin = "defaults";
+               return;
+       }
+
+       mailbox_list_get_permissions(box->list, box->name,
+                                    &box->file_create_mode,
+                                    &box->file_create_gid, &origin);
+
+       box->file_create_gid_origin = p_strdup(box->pool, origin);
+       mailbox_list_get_dir_permissions(box->list, box->name,
+                                        &box->dir_create_mode,
+                                        &dir_gid, &dir_origin);
+}
+
+int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
+                     int *fd_r)
+{
+       mode_t old_mask;
+       int fd;
+
+       i_assert(box->file_create_mode != 0);
+       i_assert((flags & O_CREAT) != 0);
+
+       *fd_r = -1;
+
+       old_mask = umask(0);
+       fd = open(path, flags, box->file_create_mode);
+       umask(old_mask);
+
+       if (fd != -1) {
+               /* ok */
+       } else if (errno == EEXIST) {
+               /* O_EXCL used, caller will handle this error */
+               return 0;
+       } else if (errno == ENOENT) {
+               mailbox_set_deleted(box);
+               return -1;
+       } else if (mail_storage_set_error_from_errno(box->storage)) {
+               return -1;
+       } else {
+               mail_storage_set_critical(box->storage,
+                       "open(%s, O_CREAT) failed: %m", path);
+               return -1;
+       }
+
+       if (box->file_create_gid != (gid_t)-1) {
+               if (fchown(fd, (uid_t)-1, box->file_create_gid) == 0) {
+                       /* ok */
+               } else if (errno == EPERM) {
+                       mail_storage_set_critical(box->storage, "%s",
+                               eperm_error_get_chgrp("fchown", path,
+                                       box->file_create_gid,
+                                       box->file_create_gid_origin));
+               } else {
+                       mail_storage_set_critical(box->storage,
+                               "fchown(%s) failed: %m", path);
+               }
+       }
+       *fd_r = fd;
+       return 1;
+}
index c718de5f25d5f3cb1972d26a929f645936d72350..60fd7682df391dabdc455686c0a8f423fd4dccb1 100644 (file)
@@ -57,6 +57,8 @@ struct mailbox_list_vfuncs {
 
        int (*set_subscribed)(struct mailbox_list *list,
                              const char *name, bool set);
+       int (*create_mailbox_dir)(struct mailbox_list *list, const char *name,
+                                 bool directory);
        int (*delete_mailbox)(struct mailbox_list *list, const char *name);
        int (*delete_dir)(struct mailbox_list *list, const char *name);
        int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname,
index 91090f9c995e88d7a1b87d0e3958509f941d6441..cae3836336f31764e1ebf1fa1fd4467ad6b81529 100644 (file)
@@ -17,7 +17,9 @@ struct mailbox_list_iterate_context;
 
 enum mailbox_list_properties {
        /* maildir_name must always be empty */
-       MAILBOX_LIST_PROP_NO_MAILDIR_NAME       = 0x01
+       MAILBOX_LIST_PROP_NO_MAILDIR_NAME       = 0x01,
+       /* no support for \noselect directories, only mailboxes */
+       MAILBOX_LIST_PROP_NO_NOSELECT           = 0x02
 };
 
 enum mailbox_list_flags {
@@ -76,8 +78,10 @@ enum mailbox_list_iter_flags {
 enum mailbox_list_path_type {
        /* Return directory's path (eg. ~/dbox/INBOX) */
        MAILBOX_LIST_PATH_TYPE_DIR,
+       MAILBOX_LIST_PATH_TYPE_ALT_DIR,
        /* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */
        MAILBOX_LIST_PATH_TYPE_MAILBOX,
+       MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX,
        /* Return control directory */
        MAILBOX_LIST_PATH_TYPE_CONTROL,
        /* Return index file directory */
index 88314eb64e708d3a7ae6ea32e59a7552494d8fa6..8fea75cc0274f13509ad94cc55fc0a87a7180922 100644 (file)
@@ -462,6 +462,30 @@ static int acl_get_mailbox_name_status(struct mailbox_list *list,
        return 0;
 }
 
+static int
+acl_mailbox_list_create_dir(struct mailbox_list *list, const char *name,
+                           bool directory)
+{
+       struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list);
+       int ret;
+
+       /* we're looking up CREATE permission from our parent's rights */
+       ret = acl_mailbox_list_have_right(list, name, TRUE,
+                                         ACL_STORAGE_RIGHT_CREATE, NULL);
+       if (ret <= 0) {
+               if (ret < 0)
+                       return -1;
+               /* Note that if user didn't have LOOKUP permission to parent
+                  mailbox, this may reveal the mailbox's existence to user.
+                  Can't help it. */
+               mailbox_list_set_error(list, MAIL_ERROR_PERM,
+                                      MAIL_ERRSTR_NO_PERMISSION);
+               return -1;
+       }
+       return alist->module_ctx.super.
+               create_mailbox_dir(list, name, directory);
+}
+
 static int
 acl_mailbox_list_delete(struct mailbox_list *list, const char *name)
 {
@@ -600,6 +624,7 @@ static void acl_mailbox_list_init_default(struct mailbox_list *list)
        list->v.iter_next = acl_mailbox_list_iter_next;
        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;
 
index cdbc975e18012b1569f5e16c9f4f331fb3be6fea..53fcf274740056c6a06eb1c1aeb896d190aeb5bf 100644 (file)
@@ -124,26 +124,14 @@ static void acl_mailbox_copy_acls_from_parent(struct mailbox *box)
        acl_object_list_deinit(&iter);
        acl_object_deinit(&parent_aclobj);
 }
+
 static int
 acl_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                   bool directory)
 {
        struct acl_mailbox *abox = ACL_CONTEXT(box);
-       int ret;
 
-       /* we're looking up CREATE permission from our parent's rights */
-       ret = acl_mailbox_list_have_right(box->list, box->name, TRUE,
-                                         ACL_STORAGE_RIGHT_CREATE, NULL);
-       if (ret <= 0) {
-               if (ret < 0)
-                       return -1;
-               /* Note that if user didn't have LOOKUP permission to parent
-                  mailbox, this may reveal the mailbox's existence to user.
-                  Can't help it. */
-               mail_storage_set_error(box->storage, MAIL_ERROR_PERM,
-                                      MAIL_ERRSTR_NO_PERMISSION);
-               return -1;
-       }
+       /* we already checked permissions in list.mailbox_create_dir(). */
        if (abox->module_ctx.super.create(box, update, directory) < 0)
                return -1;