mailbox-list-fs.c revision a574952c01611899b8ecf81434dbbb3345f27518
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "lib.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "hostpid.h"
dfa2201c6ac8ddb2d2798dee15662cfe774e644eMartti Rannanjärvi#include "mkdir-parents.h"
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#include "mailbox-log.h"
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include "subscription-file.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "mail-storage.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "mailbox-list-fs.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include <stdio.h>
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include <unistd.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include <sys/stat.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#define GLOBAL_TEMP_PREFIX ".temp."
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenextern struct mailbox_list fs_mailbox_list;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenstatic struct mailbox_list *fs_list_alloc(void)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen{
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen struct fs_mailbox_list *list;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen pool_t pool;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen pool = pool_alloconly_create("fs list", 1024);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen list = p_new(pool, struct fs_mailbox_list, 1);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen list->list = fs_mailbox_list;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen list->list.pool = pool;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen list->temp_prefix = p_strconcat(pool, GLOBAL_TEMP_PREFIX,
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen my_hostname, ".", my_pid, ".", NULL);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return &list->list;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic void fs_list_deinit(struct mailbox_list *_list)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen pool_unref(&list->list.pool);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic bool fs_list_is_valid_common(const char *name, size_t *len_r)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen *len_r = strlen(name);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (name[0] == '\0' || name[*len_r-1] == '/')
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return TRUE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic bool
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenfs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen const char *p;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen bool newdir;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen size_t maildir_len;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen /* make sure it's not absolute path */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (*name == '/' || *name == '~')
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen /* make sure the mailbox name doesn't contain any foolishness:
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen "../" could give access outside the mailbox directory.
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen "./" and "//" could fool ACL checks. */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen newdir = TRUE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen maildir_len = strlen(list->set.maildir_name);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen for (p = name; *p != '\0'; p++) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (newdir) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (p[0] == '/')
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen return FALSE; /* // */
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen if (p[0] == '.') {
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen if (p[1] == '/' || p[1] == '\0')
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE; /* ./ */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (p[1] == '.' &&
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen (p[2] == '/' || p[2] == '\0'))
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE; /* ../ */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen }
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (maildir_len > 0 &&
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen strncmp(p, list->set.maildir_name,
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen maildir_len) == 0 &&
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen (p[maildir_len] == '\0' ||
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen p[maildir_len] == '/')) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen /* don't allow maildir_name to be used as part
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen of the mailbox name */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen }
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen }
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen newdir = p[0] == '/';
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen }
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (name[0] == '.' && (name[1] == '\0' ||
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen (name[1] == '.' && name[2] == '\0'))) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen /* "." and ".." aren't allowed. */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen }
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return TRUE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic bool
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenfs_is_valid_pattern(struct mailbox_list *list, const char *pattern)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (list->mail_set->mail_full_filesystem_access)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return TRUE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return fs_list_is_valid_common_nonfs(list, pattern);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen}
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenstatic bool
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenfs_is_valid_existing_name(struct mailbox_list *list, const char *name)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen size_t len;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (!fs_list_is_valid_common(name, &len))
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen return FALSE;
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen if (list->mail_set->mail_full_filesystem_access)
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen return TRUE;
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return fs_list_is_valid_common_nonfs(list, name);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic bool
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenfs_is_valid_create_name(struct mailbox_list *list, const char *name)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen{
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen size_t len;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (!fs_list_is_valid_common(name, &len))
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen return FALSE;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (list->mail_set->mail_full_filesystem_access)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen return TRUE;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (mailbox_list_name_is_too_large(name, '/'))
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return FALSE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return fs_list_is_valid_common_nonfs(list, name);
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen}
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenstatic const char *
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenfs_list_get_path(struct mailbox_list *_list, const char *name,
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen enum mailbox_list_path_type type)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen{
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen const struct mailbox_list_settings *set = &_list->set;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen const char *path, *root_dir;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (name == NULL) {
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen /* return root directories */
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen switch (type) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen case MAILBOX_LIST_PATH_TYPE_DIR:
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return set->root_dir;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen return _list->set.alt_dir;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen case MAILBOX_LIST_PATH_TYPE_MAILBOX:
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen path = t_strconcat(set->root_dir, "/",
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen set->mailbox_dir_name, NULL);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return t_strndup(path, strlen(path)-1);
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen path = t_strconcat(set->alt_dir, "/",
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen set->mailbox_dir_name, NULL);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return t_strndup(path, strlen(path)-1);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen case MAILBOX_LIST_PATH_TYPE_CONTROL:
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen return set->control_dir != NULL ?
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen set->control_dir : set->root_dir;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen case MAILBOX_LIST_PATH_TYPE_INDEX:
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen return set->index_dir != NULL ?
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen set->index_dir : set->root_dir;
0471a9dde7b4fb55779261460c973f85088b4b0cTimo Sirainen }
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen i_unreached();
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen }
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen i_assert(mailbox_list_is_valid_pattern(_list, name));
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (mailbox_list_try_get_absolute_path(_list, &name))
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen return name;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen root_dir = set->root_dir;
f3ef6fdc790caf67352a07829bedb117bc56fb78Timo Sirainen switch (type) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen case MAILBOX_LIST_PATH_TYPE_DIR:
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (*set->maildir_name != '\0')
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen return t_strdup_printf("%s/%s%s", set->root_dir,
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen set->mailbox_dir_name, name);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen break;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (*set->maildir_name != '\0')
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return t_strdup_printf("%s/%s%s", set->alt_dir,
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen set->mailbox_dir_name, name);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen break;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen case MAILBOX_LIST_PATH_TYPE_MAILBOX:
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen break;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
f3ef6fdc790caf67352a07829bedb117bc56fb78Timo Sirainen if (set->alt_dir == NULL)
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen return NULL;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen root_dir = set->alt_dir;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen break;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen case MAILBOX_LIST_PATH_TYPE_CONTROL:
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (set->control_dir != NULL)
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen return t_strdup_printf("%s/%s%s", set->control_dir,
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen set->mailbox_dir_name, name);
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen break;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen case MAILBOX_LIST_PATH_TYPE_INDEX:
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen if (set->index_dir != NULL) {
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen if (*set->index_dir == '\0')
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen return "";
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen return t_strdup_printf("%s/%s%s", set->index_dir,
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen set->mailbox_dir_name, name);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen break;
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen /* don't use inbox_path */
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen /* If INBOX is a file, index and control directories are
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen located in root directory. */
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen type == MAILBOX_LIST_PATH_TYPE_DIR)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return set->inbox_path;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (*set->maildir_name == '\0') {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return t_strdup_printf("%s/%s%s", root_dir,
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen set->mailbox_dir_name, name);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen } else {
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen return t_strdup_printf("%s/%s%s/%s", root_dir,
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen set->mailbox_dir_name, name,
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen set->maildir_name);
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen }
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen}
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainenstatic int
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainenfs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name,
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen enum mailbox_name_status *status)
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct stat st;
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen const char *path, *dir_path;
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen path = mailbox_list_get_path(_list, name,
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen *status = MAILBOX_NAME_EXISTS_MAILBOX;
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen return 0;
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen }
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen if (errno == ENOENT) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen /* see if the directory exists */
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen dir_path = mailbox_list_get_path(_list, name,
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen MAILBOX_LIST_PATH_TYPE_DIR);
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen if (strcmp(path, dir_path) != 0 && stat(dir_path, &st) == 0) {
80b837ed54521cbd241110c2c6b58c02d187b818Timo Sirainen *status = MAILBOX_NAME_EXISTS_DIR;
80b837ed54521cbd241110c2c6b58c02d187b818Timo Sirainen return 0;
b38d0e738ef8be0c437106ba7967a4bdf8057d32Timo Sirainen }
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen errno = ENOENT;
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen }
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen if (!mailbox_list_is_valid_create_name(_list, name)) {
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen *status = MAILBOX_NAME_INVALID;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen return 0;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen }
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen if (ENOTFOUND(errno) || errno == EACCES) {
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen *status = MAILBOX_NAME_VALID;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen return 0;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen } else if (errno == ENOTDIR) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen *status = MAILBOX_NAME_NOINFERIORS;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return 0;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen } else {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return -1;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen}
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainenstatic const char *
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainenfs_list_get_temp_prefix(struct mailbox_list *_list, bool global)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen{
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return global ? GLOBAL_TEMP_PREFIX : list->temp_prefix;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenstatic const char *
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenfs_list_join_refpattern(struct mailbox_list *_list ATTR_UNUSED,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen const char *ref, const char *pattern)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (*pattern == '/' || *pattern == '~') {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen /* pattern overrides reference */
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen } else if (*ref != '\0') {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen /* merge reference and pattern */
4a3e6e01ea368e9a90dc32abc40aea46fe93f926Timo Sirainen pattern = t_strconcat(ref, pattern, NULL);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen }
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return pattern;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenstatic int fs_list_set_subscribed(struct mailbox_list *_list,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen const char *name, bool set)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen const char *path;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen path = t_strconcat(_list->set.control_dir != NULL ?
0622f54c063d2dca88f6b95daa6822cb386068caTimo Sirainen _list->set.control_dir : _list->set.root_dir,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen "/", _list->set.subscription_fname, NULL);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen return subsfile_set_subscribed(_list, path, list->temp_prefix,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen 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 */
return mailbox_list_delete_index_control(list, name);
}
static int fs_list_delete_dir(struct mailbox_list *list, const char *name)
{
const char *path;
uint8_t dir_sha128[MAIL_GUID_128_SIZE];
path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
if (rmdir(path) == 0) {
mailbox_name_get_sha128(name, dir_sha128);
mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR,
dir_sha128);
return 0;
}
if (errno == ENOENT) {
mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
} else if (errno == ENOTEMPTY) {
mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
"Mailbox exists");
} else {
mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
}
return -1;
}
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, *p;
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(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 *oldlist,
const char *oldname,
struct mailbox_list *newlist,
const char *newname, bool rename_children)
{
struct mail_storage *oldstorage;
const char *oldpath, *newpath, *p, *origin;
enum mailbox_list_path_type path_type;
struct stat st;
mode_t mode;
gid_t gid;
bool rmdir_parent = FALSE;
if (mailbox_list_get_storage(&oldlist, &oldname, &oldstorage) < 0)
return -1;
if (rename_children)
path_type = MAILBOX_LIST_PATH_TYPE_DIR;
else if (mail_storage_is_mailbox_file(oldstorage) ||
*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(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(newlist, NULL, &mode,
&gid, &origin);
p = t_strdup_until(newpath, p);
if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
errno != EEXIST) {
if (mailbox_list_set_error_from_errno(oldlist))
return -1;
mailbox_list_set_critical(oldlist,
"mkdir_parents(%s) failed: %m", p);
return -1;
}
}
/* first check that the destination mailbox doesn't exist.
this is racy, but we need to be atomic and there's hardly any
possibility that someone actually tries to rename two mailboxes
to same new one */
if (lstat(newpath, &st) == 0) {
mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
return -1;
} else if (errno == ENOTDIR) {
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(oldlist, "lstat(%s) failed: %m",
newpath);
return -1;
}
if (oldlist->v.rename_mailbox_pre != NULL) {
if (oldlist->v.rename_mailbox_pre(oldlist, oldname,
newlist, newname) < 0)
return -1;
}
/* NOTE: renaming INBOX works just fine with us, it's simply recreated
the next time it's needed. */
if (rename(oldpath, newpath) < 0) {
if (ENOTFOUND(errno)) {
mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
} else if (!mailbox_list_set_error_from_errno(oldlist)) {
mailbox_list_set_critical(oldlist,
"rename(%s, %s) failed: %m", oldpath, newpath);
}
return -1;
}
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;
}
struct mailbox_list fs_mailbox_list = {
.name = MAILBOX_LIST_NAME_FS,
.hierarchy_sep = '/',
.props = 0,
.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
{
fs_list_alloc,
fs_list_deinit,
NULL,
fs_is_valid_pattern,
fs_is_valid_existing_name,
fs_is_valid_create_name,
fs_list_get_path,
fs_list_get_mailbox_name_status,
fs_list_get_temp_prefix,
fs_list_join_refpattern,
fs_list_iter_init,
fs_list_iter_next,
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,
NULL
}
};