mailbox-list.c revision e0f57c552d2f436458163f722a7d0cc9a95d0709
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2015 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* 16 * (255+1) = 4096 which is the standard PATH_MAX. Having these settings
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen then start renaming them to larger names from end to beginning, which
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen eventually would start causing the failures when trying to use too
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen long mailbox names. 255 is the standard single directory name length, so
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen allow up to that high. */
d22301419109ed4a38351715e6760011421dadecTimo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(mailbox_list_fs_module,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen unsigned int idx;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen struct mailbox_list **list_r, const char **error_r)
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen unsigned int idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = "maildir_name not supported by this driver";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen *error_r = "alt_dir not supported by this driver";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->root_permissions.file_create_mode = (mode_t)-1;
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen list->root_permissions.dir_create_mode = (mode_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->root_permissions.file_create_gid = (gid_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* copy settings */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->set.root_dir = p_strdup(list->pool, set->root_dir);
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen list->set.index_dir = set->index_dir == NULL ||
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen p_strdup(list->pool, set->subscription_fname);
888ab4e17f7441b4dcca4a01886d055b57f4586dTimo Sirainen list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen list->set.alt_dir_nocheck = set->alt_dir_nocheck;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen list->set.root_dir == NULL ? "" : list->set.root_dir,
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen list->set.index_dir == NULL ? "" : list->set.index_dir,
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen list->set.alt_dir == NULL ? "" : list->set.alt_dir);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int fix_path(struct mail_user *user, const char *path, bool expand_home,
e22ac7474fb36e3e3dcfeb70ea5f54ea812aa2d0Timo Sirainen /* no ~ expansion */
e22ac7474fb36e3e3dcfeb70ea5f54ea812aa2d0Timo Sirainen } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
e22ac7474fb36e3e3dcfeb70ea5f54ea812aa2d0Timo Sirainen "No home directory for system user. "
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (mail_user_try_home_expand(user, &path) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *error_r = "Home directory not set for user. "
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "Can't expand ~/ for ";
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainenstatic const char *split_next_arg(const char *const **_args)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* string ends with ":", just ignore it. */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenmailbox_list_settings_parse_full(struct mail_user *user, const char *data,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char **error_r)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen const char *const *tmp, *key, *value, **dest, *str, *error;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* <root dir> */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* probably mbox user trying to avoid root_dir */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *error_r = t_strconcat("Mail root directory not given: ",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen if (fix_path(user, value, expand_home, dest, &error) < 0) {
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen *error_r = t_strconcat(error, key, " in: ", data, NULL);
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainenint mailbox_list_settings_parse(struct mail_user *user, const char *data,
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen const char **error_r)
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainen return mailbox_list_settings_parse_full(user, data, TRUE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen const char *location = list->ns->unexpanded_set->location;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen /* set using -o or userdb lookup. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_set = mail_user_set_get_driver_settings(user->set_info,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* type:settings */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool need_escape_dirstart(const char *vname, const char *maildir_name)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen unsigned int len;
return FALSE;
vname++;
(dirstart &&
unsigned char chr;
char *ret, *p;
if (*p == src)
*p = dest;
return ret;
const char *vname)
return storage_name;
return storage_name;
return ret;
const char *vname)
unsigned int num;
(unsigned char)*vname);
const char *storage_name)
return vname;
for (i = 0; i < name_len; i++) {
return vname;
const struct mailbox_list_settings *
struct mail_namespace *
return mode;
struct mail_user *
data++;
const char *name,
&path) < 0)
&path);
path);
if (p == NULL) {
const char *expanded_full)
const char *ret;
slash_count++;
slash2_count++;
if (slash_count == 0)
slash_count--;
if (slash_count != 0)
if (slash2_count == 0)
slash2_count--;
if (slash2_count != 0)
return ret;
const char **error_r)
i_unreached();
} else if (p == NULL) {
if (p == unexpanded)
i_unreached();
if (!home) {
const char **error_r)
const char *root_dir;
i_unreached();
const char *error;
const char **error_r)
return TRUE;
return FALSE;
return FALSE;
T_BEGIN {
const char *const *names;
const char *n = *names;
if (!allow_internal_dirs &&
} T_END;
return ret;
return TRUE;
return FALSE;
return FALSE;
const char **path_r)
int ret;
return ret;
const char **path_r)
int ret;
i_unreached();
if (ret == 0)
return ret > 0;
const char *path;
i_unreached();
return path;
const char **path_r)
switch (type) {
return pattern;
const char *pattern;
int ret;
return ret;
unsigned int len;
*flags_r = 0;
int ret;
if (ret < 0) {
const char *errstr;
if (ret < 0)
switch (existence) {
case MAILBOX_EXISTENCE_NONE:
case MAILBOX_EXISTENCE_SELECT:
const char *vname;
i_unreached();
fname++;
&inbox) <= 0)
i_unreached();
flags_r);
const char *path;
return TRUE;
return FALSE;
return TRUE;
int ret;
&index_dir);
if (ret <= 0)
return ret;
&root_dir);
if (ret <= 0)
return ret;
int ret;
return ret;
const char *error;
const char *error;
return TRUE;
levels++;
level_len = 0;
level_len++;
return TRUE;
return TRUE;
return FALSE;
#ifdef HAVE_DIRENT_D_TYPE
switch (d->d_type) {
case DT_UNKNOWN:
case DT_REG:
case DT_DIR:
case DT_LNK:
return type;
const char *dir_path,
const struct dirent *d)
int ret;
T_BEGIN {
ret = 0;
} T_END;
return ret;
return FALSE;
return FALSE;
return TRUE;
const char **name)
unsigned int len;
return FALSE;
return FALSE;
return FALSE;
&path) <= 0)
return FALSE;
return FALSE;
return TRUE;
const char *str;
const char *error_string;
return FALSE;
return TRUE;