mailbox-list.c revision 758d8b46f9e8fd87cf58bb4912cddf6bf28918cf
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* 20 * (200+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
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen eventually would start causing the failures when trying to use too
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen long mailbox names. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen unsigned int i, count;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < count; i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainenvoid mailbox_list_unregister(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_unregister(%s): unknown driver",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int idx;
4b2a4c8c762e3eaddf7fd2abfe7d4cca6e5e3fd8Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox_list **list_r, const char **error_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int idx;
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen *error_r = "maildir_name not supported by this driver";
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen *error_r = "alt_dir not supported by this driver";
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
547e916f4e6f01af682f8b6e032c337f2a699364Timo Sirainen ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen list->root_permissions.file_create_mode = (mode_t)-1;
46219292a55094fa49aae33eee681ed075d30e17Timo 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);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->set.index_dir = set->index_dir == NULL ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
fa7c76955c6bc62689fbdf39318194f85905e6e2Timo 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);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo 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);
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo 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,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen list->set.alt_dir == NULL ? "" : list->set.alt_dir);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
7baab0b0b60df7ce9093d0881cd322dff1e79491Timo 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. "
e22ac7474fb36e3e3dcfeb70ea5f54ea812aa2d0Timo Sirainen if (mail_user_try_home_expand(user, &path) < 0) {
e22ac7474fb36e3e3dcfeb70ea5f54ea812aa2d0Timo Sirainen *error_r = "Home directory not set for user. "
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen "Can't expand ~/ for ";
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic const char *split_next_arg(const char *const **_args)
e5fb952c6d49d3b6bff1746551566202e92947daTimo 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);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fix_path(user, value, expand_home, dest, &error) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo 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)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo 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,
2b9e49e4e65e3e2dca38f56971029a3051ccdb99Timo Sirainenconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *location = list->ns->unexpanded_set->location;
e5fb952c6d49d3b6bff1746551566202e92947daTimo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
438f12d7a776da695019114884b48188d94613efTimo Sirainen /* set using -o or userdb lookup. */
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo 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)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int len;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
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)
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;
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) {
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;