mailbox-list.c revision 2d8d76a57173de7b7e1ff62f2deabd637ce908ae
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen then start renaming them to larger names from end to beginning, which
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen eventually would start causing the failures when trying to use too
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen long mailbox names. */
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int i, count;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen for (i = 0; i < count; i++) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int idx;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
b66412da78711db8423288847ecfb08469609a03Timo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen unsigned int idx;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen unsigned int idx;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mailbox_list **list_r, const char **error_r)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int idx;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen *error_r = "maildir_name not supported by this driver";
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *error_r = "alt_dir not supported by this driver";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->root_permissions.file_create_mode = (mode_t)-1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->root_permissions.dir_create_mode = (mode_t)-1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->root_permissions.file_create_gid = (gid_t)-1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* copy settings */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.root_dir = p_strdup(list->pool, set->root_dir);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.index_dir = set->index_dir == NULL ||
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
b66412da78711db8423288847ecfb08469609a03Timo Sirainen strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
b66412da78711db8423288847ecfb08469609a03Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen p_strdup(list->pool, set->subscription_fname);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.alt_dir_nocheck = set->alt_dir_nocheck;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.root_dir == NULL ? "" : list->set.root_dir,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.index_dir == NULL ? "" : list->set.index_dir,
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.alt_dir == NULL ? "" : list->set.alt_dir);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
b66412da78711db8423288847ecfb08469609a03Timo Sirainenstatic int fix_path(struct mail_user *user, const char *path, bool expand_home,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* no ~ expansion */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "No home directory for system user. "
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen if (mail_user_try_home_expand(user, &path) < 0) {
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen *error_r = "Home directory not set for user. "
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen "Can't expand ~/ for ";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic const char *split_next_arg(const char *const **_args)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* string ends with ":", just ignore it. */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmailbox_list_settings_parse_full(struct mail_user *user, const char *data,
b66412da78711db8423288847ecfb08469609a03Timo Sirainen const char **error_r)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *const *tmp, *key, *value, **dest, *str, *error;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen /* <root dir> */
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) {
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen /* probably mbox user trying to avoid root_dir */
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen *error_r = t_strconcat("Mail root directory not given: ",
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (fix_path(user, value, expand_home, dest, &error) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *error_r = t_strconcat(error, key, " in: ", data, NULL);
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenint mailbox_list_settings_parse(struct mail_user *user, const char *data,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char **error_r)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return mailbox_list_settings_parse_full(user, data, TRUE,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *location = list->ns->unexpanded_set->location;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* set using -o or userdb lookup. */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_set = mail_user_set_get_driver_settings(user->set_info,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* type:settings */
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenstatic bool need_escape_dirstart(const char *vname, const char *maildir_name)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen unsigned int len;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenstatic const char *
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenmailbox_list_escape_name(struct mailbox_list *list, const char *vname)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen char ns_sep = mail_namespace_get_sep(list->ns);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen char list_sep = mailbox_list_get_hierarchy_sep(list);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen /* no escaping of namespace prefix */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (strncmp(list->ns->prefix, vname, list->ns->prefix_len) == 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen str_append_n(escaped_name, vname, list->ns->prefix_len);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* escape the mailbox name */
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen need_escape_dirstart(vname, list->set.maildir_name))) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned char chr;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if ((src = strchr(name, list->set.broken_char)) == NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen ret = p_strdup(unsafe_data_stack_pool, storage_name);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenconst char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
return storage_name;
return storage_name;
return ret;
const char *vname)
unsigned int num;
*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 *
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;
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;