mailbox-list.c revision 2d8d76a57173de7b7e1ff62f2deabd637ce908ae
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "lib.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "array.h"
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen#include "abspath.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "ioloop.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mkdir-parents.h"
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen#include "str.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "sha1.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "hash.h"
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen#include "home-expand.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "time-util.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "unichar.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "settings-parser.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "imap-utf7.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mailbox-log.h"
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen#include "mailbox-tree.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mail-storage.h"
b66412da78711db8423288847ecfb08469609a03Timo Sirainen#include "mail-storage-hooks.h"
b66412da78711db8423288847ecfb08469609a03Timo Sirainen#include "mailbox-list-private.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include <time.h>
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include <ctype.h>
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include <unistd.h>
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include <dirent.h>
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include <sys/stat.h>
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
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 Sirainen#define MAILBOX_MAX_HIERARCHY_LEVELS 20
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid mailbox_lists_init(void)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen{
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen i_array_init(&mailbox_list_drivers, 4);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen}
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainenvoid mailbox_lists_deinit(void)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen array_free(&mailbox_list_drivers);
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct mailbox_list *const *drivers;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int i, count;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
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) {
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen *idx_r = i;
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen return TRUE;
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return FALSE;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int idx;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->name);
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen }
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen{
b66412da78711db8423288847ecfb08469609a03Timo Sirainen unsigned int idx;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen array_delete(&mailbox_list_drivers, idx, 1);
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenconst struct mailbox_list *
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmailbox_list_find_class(const char *driver)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct mailbox_list *const *class_p;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen unsigned int idx;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (!mailbox_list_driver_find(driver, &idx))
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return NULL;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return *class_p;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct mailbox_list_settings *set,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen enum mailbox_list_flags flags,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mailbox_list **list_r, const char **error_r)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct mailbox_list *const *class_p;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mailbox_list *list;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int idx;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_assert(ns->list == NULL ||
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_assert(set->subscription_fname == NULL ||
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen *set->subscription_fname != '\0');
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen *error_r = "Unknown driver name";
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
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 *set->maildir_name != '\0') {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen *error_r = "maildir_name not supported by this driver";
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set->alt_dir != NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *error_r = "alt_dir not supported by this driver";
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
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
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list = (*class_p)->v.alloc();
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->ns = ns;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->mail_set = ns->mail_set;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->flags = flags;
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 list->changelog_timestamp = (time_t)-1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* copy settings */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (set->root_dir != NULL) {
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 p_strdup(list->pool, set->index_dir);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
b66412da78711db8423288847ecfb08469609a03Timo Sirainen strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen p_strdup(list->pool, set->index_pvt_dir);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen p_strdup(list->pool, set->control_dir);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen list->set.subscription_fname =
b66412da78711db8423288847ecfb08469609a03Timo Sirainen p_strdup(list->pool, set->subscription_fname);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.maildir_name =
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen p_strdup(list->pool, set->maildir_name);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen list->set.mailbox_dir_name =
b66412da78711db8423288847ecfb08469609a03Timo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
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
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (*set->mailbox_dir_name == '\0')
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.mailbox_dir_name = "";
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.mailbox_dir_name =
5bd2cf0f30371cb0374b026322a6f52fdb20755fTimo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.mailbox_dir_name =
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->set.utf8 = set->utf8;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (list->v.init != NULL) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (list->v.init(list, error_r) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->v.deinit(list);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ns->mail_set->mail_debug) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->name,
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.control_dir == NULL ?
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "" : list->set.control_dir,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.inbox_path == NULL ?
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "" : list->set.inbox_path,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.alt_dir == NULL ? "" : list->set.alt_dir);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen mail_namespace_finish_list_init(ns, list);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen *list_r = list;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen hook_mailbox_list_created(list);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainenstatic int fix_path(struct mail_user *user, const char *path, bool expand_home,
b66412da78711db8423288847ecfb08469609a03Timo Sirainen const char **path_r, const char **error_r)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen size_t len = strlen(path);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (len > 1 && path[len-1] == '/')
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen path = t_strndup(path, len-1);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (!expand_home) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* no ~ expansion */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* ~otheruser/dir */
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (home_try_expand(&path) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *error_r = t_strconcat(
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "No home directory for system user. "
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "Can't expand ", t_strcut(path, '/'),
b66412da78711db8423288847ecfb08469609a03Timo Sirainen " for ", NULL);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return -1;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else {
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 ";
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen return -1;
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen *path_r = path;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic const char *split_next_arg(const char *const **_args)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen const char *const *args = *_args;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *str = args[0];
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen args++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen while (*args != NULL && **args == '\0') {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen args++;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (*args == NULL) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen /* string ends with ":", just ignore it. */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen break;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen str = t_strconcat(str, ":", *args, NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen args++;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen *_args = args;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return str;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainenstatic int
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmailbox_list_settings_parse_full(struct mail_user *user, const char *data,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen bool expand_home,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mailbox_list_settings *set_r,
b66412da78711db8423288847ecfb08469609a03Timo Sirainen const char **error_r)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *const *tmp, *key, *value, **dest, *str, *error;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *error_r = NULL;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen memset(set_r, 0, sizeof(*set_r));
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set_r->maildir_name = "";
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen set_r->mailbox_dir_name = "";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen if (*data == '\0')
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return 0;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen /* <root dir> */
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen tmp = t_strsplit(data, ":");
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen str = split_next_arg(&tmp);
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 return -1;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen }
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: ",
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen data, NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen while (*tmp != NULL) {
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen str = split_next_arg(&tmp);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (strcmp(str, "UTF-8") == 0) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen set_r->utf8 = TRUE;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen continue;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen }
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen value = strchr(str, '=');
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen if (value == NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen key = str;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen value = "";
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen } else {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen key = t_strdup_until(str, value);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen value++;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen }
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (strcmp(key, "INBOX") == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dest = &set_r->inbox_path;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (strcmp(key, "INDEX") == 0)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen dest = &set_r->index_dir;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (strcmp(key, "INDEXPVT") == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dest = &set_r->index_pvt_dir;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen else if (strcmp(key, "CONTROL") == 0)
901d0f036826476cf75799a0fdda5777e51301e4Timo Sirainen dest = &set_r->control_dir;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (strcmp(key, "ALT") == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dest = &set_r->alt_dir;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (strcmp(key, "ALTNOCHECK") == 0) {
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen set_r->alt_dir_nocheck = TRUE;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen continue;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else if (strcmp(key, "LAYOUT") == 0)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen dest = &set_r->layout;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (strcmp(key, "SUBSCRIPTIONS") == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dest = &set_r->subscription_fname;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen else if (strcmp(key, "DIRNAME") == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dest = &set_r->maildir_name;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen else if (strcmp(key, "MAILBOXDIR") == 0)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen dest = &set_r->mailbox_dir_name;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else {
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
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 return -1;
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen }
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen }
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen set_r->index_dir = "";
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen return 0;
e2e81d93cde2fbd4d2b22eea617edf2e4af56ff1Timo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenint mailbox_list_settings_parse(struct mail_user *user, const char *data,
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen struct mailbox_list_settings *set_r,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char **error_r)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return mailbox_list_settings_parse_full(user, data, TRUE,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set_r, error_r);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen enum mailbox_list_path_type type)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const struct mail_storage_settings *mail_set;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *location = list->ns->unexpanded_set->location;
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen struct mail_user *user = list->ns->user;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mailbox_list_settings set;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const char *p, *path, *error;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* set using -o or userdb lookup. */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return "";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen location++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (*location == '\0') {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_set = mail_user_set_get_driver_settings(user->set_info,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen i_assert(mail_set != NULL);
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen location = mail_set->mail_location;
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0])
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return "";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen location++;
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* type:settings */
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen p = strchr(location, ':');
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (p == NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return "";
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen &set, &error) < 0)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return "";
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return "";
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return path;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenstatic bool need_escape_dirstart(const char *vname, const char *maildir_name)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen{
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen unsigned int len;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (vname[0] == '.') {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (vname[1] == '\0' || vname[1] == '/')
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return TRUE; /* "." */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return TRUE; /* ".." */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (*maildir_name != '\0') {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen len = strlen(maildir_name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (strncmp(maildir_name, vname, len) == 0 &&
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen (vname[len] == '\0' || vname[len] == '/'))
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return TRUE; /* e.g. dbox-Mails */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return FALSE;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenstatic const char *
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenmailbox_list_escape_name(struct mailbox_list *list, const char *vname)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen{
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen char ns_sep = mail_namespace_get_sep(list->ns);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen char list_sep = mailbox_list_get_hierarchy_sep(list);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen string_t *escaped_name = t_str_new(64);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen char dirstart = TRUE;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
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 vname += list->ns->prefix_len;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* escape the mailbox name */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (*vname == '~') {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen str_printfa(escaped_name, "%c%02x",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->set.escape_char, *vname);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen vname++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dirstart = FALSE;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen }
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen for (; *vname != '\0'; vname++) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen if (*vname == ns_sep)
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen str_append_c(escaped_name, *vname);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen else if (*vname == list_sep ||
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen *vname == list->set.escape_char ||
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen *vname == '/' ||
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen (dirstart &&
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen need_escape_dirstart(vname, list->set.maildir_name))) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen str_printfa(escaped_name, "%c%02x",
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen list->set.escape_char, *vname);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen } else {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen str_append_c(escaped_name, *vname);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen dirstart = *vname == '/';
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen }
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return str_c(escaped_name);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen}
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainenstatic int
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen char *src, *dest;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned char chr;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if ((src = strchr(name, list->set.broken_char)) == NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen dest = src;
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen while (*src != '\0') {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (*src == list->set.broken_char) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (src[1] >= '0' && src[1] <= '9')
b66412da78711db8423288847ecfb08469609a03Timo Sirainen chr = (src[1]-'0') * 0x10;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (src[1] >= 'a' && src[1] <= 'f')
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen chr = (src[1]-'a' + 10) * 0x10;
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen else
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen return -1;
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen if (src[2] >= '0' && src[2] <= '9')
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen chr += src[2]-'0';
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (src[2] >= 'a' && src[2] <= 'f')
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen chr += src[2]-'a' + 10;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen else
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen return -1;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen *dest++ = chr;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen src += 3;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen } else {
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen *dest++ = *src++;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen }
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen }
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen *dest++ = '\0';
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
b66412da78711db8423288847ecfb08469609a03Timo Sirainen char *ret, *p;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen ret = p_strdup(unsafe_data_stack_pool, storage_name);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen for (p = ret; *p != '\0'; p++) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (*p == src)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen *p = dest;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return ret;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenconst char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen const char *vname)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mail_namespace *ns = list->ns;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen unsigned int prefix_len = strlen(ns->prefix);
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen const char *storage_name = vname;
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen string_t *str;
char list_sep, ns_sep, *ret;
if (strcasecmp(storage_name, "INBOX") == 0 &&
(ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
storage_name = "INBOX";
else if (list->set.escape_char != '\0')
storage_name = mailbox_list_escape_name(list, vname);
if (prefix_len > 0 && (strcmp(storage_name, "INBOX") != 0 ||
(ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)) {
/* skip namespace prefix, except if this is INBOX */
if (strncmp(ns->prefix, storage_name, prefix_len) == 0)
storage_name += prefix_len;
else if (strncmp(ns->prefix, storage_name, prefix_len-1) == 0 &&
strlen(storage_name) == prefix_len-1 &&
ns->prefix[prefix_len-1] == mail_namespace_get_sep(ns)) {
/* trying to access the namespace prefix itself */
storage_name = "";
} else {
/* we're converting a nonexistent mailbox name,
such as a LIST pattern. */
}
}
if (!list->set.utf8) {
/* UTF-8 -> mUTF-7 conversion */
str = t_str_new(strlen(storage_name)*2);
if (imap_utf8_to_utf7(storage_name, str) < 0)
i_panic("Mailbox name not UTF-8: %s", vname);
storage_name = str_c(str);
}
list_sep = mailbox_list_get_hierarchy_sep(list);
ns_sep = mail_namespace_get_sep(ns);
if (*storage_name == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
(ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
!list->mail_set->mail_shared_explicit_inbox) {
/* opening shared/$user. it's the same as INBOX. */
storage_name = "INBOX";
}
if (list_sep != ns_sep) {
if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
(ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
/* shared namespace root. the backend storage's
hierarchy separator isn't known yet, so do
nothing. */
return storage_name;
}
ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep);
} else if (list->set.broken_char == '\0' ||
strchr(storage_name, list->set.broken_char) == NULL) {
/* no need to convert broken chars */
return storage_name;
} else {
ret = p_strdup(unsafe_data_stack_pool, storage_name);
}
if (list->set.broken_char != '\0') {
if (mailbox_list_unescape_broken_chars(list, ret) < 0) {
ret = mailbox_list_convert_sep(storage_name,
ns_sep, list_sep);
}
}
return ret;
}
const char *mailbox_list_get_storage_name(struct mailbox_list *list,
const char *vname)
{
return list->v.get_storage_name(list, vname);
}
static const char *
mailbox_list_unescape_name(struct mailbox_list *list, const char *src)
{
char ns_sep = mail_namespace_get_sep(list->ns);
char list_sep = mailbox_list_get_hierarchy_sep(list);
string_t *dest = t_str_new(strlen(src));
unsigned int num;
if (strncmp(src, list->ns->prefix, list->ns->prefix_len) == 0) {
str_append_n(dest, src, list->ns->prefix_len);
src += list->ns->prefix_len;
}
for (; *src != '\0'; src++) {
if (*src == list->set.escape_char &&
i_isxdigit(src[1]) && i_isxdigit(src[2])) {
if (src[1] >= '0' && src[1] <= '9')
num = src[1] - '0';
else
num = i_toupper(src[1]) - 'A' + 10;
num *= 16;
if (src[2] >= '0' && src[2] <= '9')
num += src[2] - '0';
else
num += i_toupper(src[2]) - 'A' + 10;
str_append_c(dest, num);
src += 2;
} else if (*src == list_sep)
str_append_c(dest, ns_sep);
else
str_append_c(dest, *src);
}
return str_c(dest);
}
static void
mailbox_list_escape_broken_chars(struct mailbox_list *list, string_t *str)
{
unsigned int i;
char buf[3];
if (strchr(str_c(str), list->set.broken_char) == NULL)
return;
for (i = 0; i < str_len(str); i++) {
if (str_c(str)[i] == list->set.broken_char) {
i_snprintf(buf, sizeof(buf), "%02x",
list->set.broken_char);
str_insert(str, i+1, buf);
i += 2;
}
}
}
static void
mailbox_list_escape_broken_name(struct mailbox_list *list,
const char *vname, string_t *str)
{
str_truncate(str, 0);
for (; *vname != '\0'; vname++) {
if (*vname == '&' || (unsigned char)*vname >= 0x80) {
str_printfa(str, "%c%02x", list->set.broken_char,
*vname);
} else {
str_append_c(str, *vname);
}
}
}
const char *mailbox_list_default_get_vname(struct mailbox_list *list,
const char *storage_name)
{
unsigned int i, prefix_len, name_len;
const char *vname = storage_name;
char list_sep, ns_sep, *ret;
if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
strcmp(vname, "INBOX") == 0 &&
list->ns->user == list->ns->owner) {
/* user's INBOX - use as-is. NOTE: don't do case-insensitive
comparison, otherwise we can't differentiate between INBOX
and <ns prefix>/inBox. */
return vname;
}
if (strcmp(vname, "INBOX") == 0 &&
list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
(list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
!list->mail_set->mail_shared_explicit_inbox) {
/* convert to shared/$user, we don't really care about the
INBOX suffix here. */
vname = "";
}
if (*vname == '\0') {
/* return namespace prefix without the separator */
if (list->ns->prefix_len == 0)
return list->ns->prefix;
else {
return t_strndup(list->ns->prefix,
list->ns->prefix_len - 1);
}
} else if (!list->set.utf8) {
/* mUTF-7 -> UTF-8 conversion */
string_t *str = t_str_new(strlen(vname));
if (imap_utf7_to_utf8(vname, str) == 0) {
if (list->set.broken_char != '\0')
mailbox_list_escape_broken_chars(list, str);
vname = str_c(str);
} else if (list->set.broken_char != '\0') {
mailbox_list_escape_broken_name(list, vname, str);
vname = str_c(str);
}
}
prefix_len = strlen(list->ns->prefix);
list_sep = mailbox_list_get_hierarchy_sep(list);
ns_sep = mail_namespace_get_sep(list->ns);
if (list_sep != ns_sep || prefix_len > 0) {
/* @UNSAFE */
name_len = strlen(vname);
ret = t_malloc(prefix_len + name_len + 1);
memcpy(ret, list->ns->prefix, prefix_len);
for (i = 0; i < name_len; i++) {
ret[i + prefix_len] =
vname[i] == list_sep ? ns_sep : vname[i];
}
ret[i + prefix_len] = '\0';
vname = ret;
}
if (list->set.escape_char != '\0')
vname = mailbox_list_unescape_name(list, vname);
return vname;
}
const char *mailbox_list_get_vname(struct mailbox_list *list, const char *name)
{
return list->v.get_vname(list, name);
}
void mailbox_list_destroy(struct mailbox_list **_list)
{
struct mailbox_list *list = *_list;
*_list = NULL;
i_free_and_null(list->error_string);
if (hash_table_is_created(list->guid_cache)) {
hash_table_destroy(&list->guid_cache);
pool_unref(&list->guid_cache_pool);
}
if (list->subscriptions != NULL)
mailbox_tree_deinit(&list->subscriptions);
if (list->changelog != NULL)
mailbox_log_free(&list->changelog);
list->v.deinit(list);
}
const char *mailbox_list_get_driver_name(const struct mailbox_list *list)
{
return list->name;
}
const struct mailbox_list_settings *
mailbox_list_get_settings(const struct mailbox_list *list)
{
return &list->set;
}
enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
{
return list->flags;
}
struct mail_namespace *
mailbox_list_get_namespace(const struct mailbox_list *list)
{
return list->ns;
}
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;
}
struct mail_user *
mailbox_list_get_user(const struct mailbox_list *list)
{
return list->ns->user;
}
int mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
struct mail_storage **storage_r)
{
if ((*list)->v.get_storage != NULL)
return (*list)->v.get_storage(list, vname, storage_r);
else {
*storage_r = (*list)->ns->storage;
return 0;
}
}
void mailbox_list_get_closest_storage(struct mailbox_list *list,
struct mail_storage **storage)
{
*storage = list->ns->storage;
}
char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
{
return list->v.get_hierarchy_sep(list);
}
static void ATTR_NULL(2)
mailbox_list_get_permissions_internal(struct mailbox_list *list,
const char *name,
struct mailbox_permissions *permissions_r)
{
const char *path, *parent_name, *parent_path, *p;
struct stat st;
memset(permissions_r, 0, sizeof(*permissions_r));
/* use safe defaults */
permissions_r->file_uid = (uid_t)-1;
permissions_r->file_gid = (gid_t)-1;
permissions_r->file_create_mode = 0600;
permissions_r->dir_create_mode = 0700;
permissions_r->file_create_gid = (gid_t)-1;
permissions_r->file_create_gid_origin = "defaults";
if (name != NULL) {
if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
&path) < 0)
name = NULL;
}
if (name == NULL) {
(void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
&path);
}
if (path == NULL) {
/* no filesystem support in storage */
} else if (stat(path, &st) < 0) {
if (!ENOTFOUND(errno)) {
mailbox_list_set_critical(list, "stat(%s) failed: %m",
path);
} else if (list->mail_set->mail_debug) {
i_debug("Namespace %s: %s doesn't exist yet, "
"using default permissions",
list->ns->prefix, path);
}
if (name != NULL) {
/* return parent mailbox */
p = strrchr(name, mailbox_list_get_hierarchy_sep(list));
if (p == NULL) {
/* return root defaults */
parent_name = NULL;
} else {
parent_name = t_strdup_until(name, p);
}
mailbox_list_get_permissions(list, parent_name,
permissions_r);
return;
}
/* assume current defaults for mailboxes that don't exist or
can't be looked up for some other reason */
permissions_r->file_uid = geteuid();
permissions_r->file_gid = getegid();
} else {
permissions_r->file_uid = st.st_uid;
permissions_r->file_gid = st.st_gid;
permissions_r->file_create_mode = (st.st_mode & 0666) | 0600;
permissions_r->dir_create_mode = (st.st_mode & 0777) | 0700;
permissions_r->file_create_gid_origin = path;
permissions_r->gid_origin_is_mailbox_path = name != NULL;
if (!S_ISDIR(st.st_mode)) {
/* we're getting permissions from a file.
apply +x modes as necessary. */
permissions_r->dir_create_mode =
get_dir_mode(permissions_r->dir_create_mode);
}
if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
/* directory's GID is used automatically for new
files */
permissions_r->file_create_gid = (gid_t)-1;
} else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
/* group has same permissions as world, so don't bother
changing it */
permissions_r->file_create_gid = (gid_t)-1;
} else if (getegid() == st.st_gid) {
/* using our own gid, no need to change it */
permissions_r->file_create_gid = (gid_t)-1;
} else {
permissions_r->file_create_gid = st.st_gid;
}
if (!S_ISDIR(st.st_mode) &&
permissions_r->file_create_gid != (gid_t)-1) {
/* we need to stat() the parent directory to see if
it has setgid-bit set */
p = strrchr(path, '/');
parent_path = p == NULL ? NULL :
t_strdup_until(path, p);
if (parent_path != NULL &&
stat(parent_path, &st) == 0 &&
(st.st_mode & S_ISGID) != 0) {
/* directory's GID is used automatically for
new files */
permissions_r->file_create_gid = (gid_t)-1;
}
}
}
if (name == NULL) {
list->root_permissions = *permissions_r;
list->root_permissions.file_create_gid_origin =
p_strdup(list->pool,
permissions_r->file_create_gid_origin);
}
if (list->mail_set->mail_debug && name == NULL) {
i_debug("Namespace %s: Using permissions from %s: "
"mode=0%o gid=%s", list->ns->prefix,
path != NULL ? path : "",
(int)permissions_r->dir_create_mode,
permissions_r->file_create_gid == (gid_t)-1 ? "default" :
dec2str(permissions_r->file_create_gid));
}
}
void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
struct mailbox_permissions *permissions_r)
{
mailbox_list_get_permissions_internal(list, name, permissions_r);
}
void mailbox_list_get_root_permissions(struct mailbox_list *list,
struct mailbox_permissions *permissions_r)
{
if (list->root_permissions.file_create_mode != (mode_t)-1)
*permissions_r = list->root_permissions;
else {
mailbox_list_get_permissions_internal(list, NULL,
permissions_r);
}
}
static const char *
get_expanded_path(const char *unexpanded_start, const char *unexpanded_stop,
const char *expanded_full)
{
const char *ret;
unsigned int i, slash_count = 0, slash2_count = 0;
/* get the expanded path up to the same amount of '/' characters.
if there isn't the same amount of '/' characters, it means %variable
expansion added more of them and we can't handle this. */
for (i = 0; unexpanded_start+i != unexpanded_stop; i++) {
if (unexpanded_start[i] == '/')
slash_count++;
}
for (; unexpanded_start[i] != '\0'; i++) {
if (unexpanded_start[i] == '/')
slash2_count++;
}
for (i = 0; expanded_full[i] != '\0'; i++) {
if (expanded_full[i] == '/') {
if (slash_count == 0)
break;
slash_count--;
}
}
if (slash_count != 0)
return "";
ret = t_strndup(expanded_full, i);
for (; expanded_full[i] != '\0'; i++) {
if (expanded_full[i] == '/') {
if (slash2_count == 0)
return "";
slash2_count--;
}
}
if (slash2_count != 0)
return "";
return ret;
}
static int
mailbox_list_try_mkdir_root_parent(struct mailbox_list *list,
enum mailbox_list_path_type type,
struct mailbox_permissions *perm,
const char **error_r)
{
const char *expanded, *unexpanded, *root_dir, *p;
struct stat st;
bool home = FALSE;
/* get the directory path up to last %variable. for example
unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
to get expanded="/var/mail/domain/nn" */
unexpanded = mailbox_list_get_unexpanded_path(list, type);
p = strrchr(unexpanded, '%');
if ((p == unexpanded && p[1] == 'h') ||
(p == NULL && unexpanded[0] == '~')) {
/* home directory used */
if (!mailbox_list_get_root_path(list, type, &expanded))
i_unreached();
home = TRUE;
} else if (p == NULL) {
return 0;
} else {
while (p != unexpanded && *p != '/') p--;
if (p == unexpanded)
return 0;
if (!mailbox_list_get_root_path(list, type, &expanded))
i_unreached();
expanded = get_expanded_path(unexpanded, p, expanded);
if (*expanded == '\0')
return 0;
}
/* get the first existing parent directory's permissions */
if (stat_first_parent(expanded, &root_dir, &st) < 0) {
*error_r = t_strdup_printf("stat(%s) failed: %m", root_dir);
return -1;
}
/* if the parent directory doesn't have setgid-bit enabled, we don't
copy any permissions from it. */
if ((st.st_mode & S_ISGID) == 0)
return 0;
if (!home) {
/* assuming we have e.g. /var/vmail/%d/%n directory, here we
want to create up to /var/vmail/%d with permissions from
the parent directory. we never want to create the %n
directory itself. */
if (root_dir == expanded) {
/* this is the %n directory */
} else {
if (mkdir_parents_chgrp(expanded, st.st_mode,
(gid_t)-1, root_dir) < 0 &&
errno != EEXIST) {
*error_r = t_strdup_printf(
"mkdir(%s) failed: %m", expanded);
return -1;
}
}
if (perm->file_create_gid == (gid_t)-1 &&
(perm->dir_create_mode & S_ISGID) == 0) {
/* change the group for user directories */
perm->dir_create_mode |= S_ISGID;
perm->file_create_gid = getegid();
perm->file_create_gid_origin = "egid";
perm->gid_origin_is_mailbox_path = FALSE;
}
} else {
/* when using %h and the parent has setgid-bit,
copy the permissions from it for the home we're creating */
perm->file_create_mode = st.st_mode & 0666;
perm->dir_create_mode = st.st_mode;
perm->file_create_gid = (gid_t)-1;
perm->file_create_gid_origin = "parent";
perm->gid_origin_is_mailbox_path = FALSE;
}
return 0;
}
int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path,
enum mailbox_list_path_type type,
const char **error_r)
{
const char *root_dir, *error;
struct stat st;
struct mailbox_permissions perm;
if (stat(path, &st) == 0) {
/* looks like it already exists, don't bother checking
further. */
return 0;
}
mailbox_list_get_root_permissions(list, &perm);
if (!mailbox_list_get_root_path(list, type, &root_dir))
i_unreached();
i_assert(strncmp(root_dir, path, strlen(root_dir)) == 0);
if (strcmp(root_dir, path) != 0 && stat(root_dir, &st) == 0) {
/* creating a subdirectory under an already existing root dir.
use the root's permissions */
} else if (mail_user_is_path_mounted(list->ns->user, path, &error)) {
if (mailbox_list_try_mkdir_root_parent(list, type,
&perm, error_r) < 0)
return -1;
} else {
*error_r = t_strdup_printf(
"Can't create mailbox root dir %s: %s", path, error);
return -1;
}
/* the rest of the directories exist only for one user. create them
with default directory permissions */
if (mkdir_parents_chgrp(path, perm.dir_create_mode,
perm.file_create_gid,
perm.file_create_gid_origin) < 0 &&
errno != EEXIST) {
if (errno == EACCES)
*error_r = mail_error_create_eacces_msg("mkdir", path);
else
*error_r = t_strdup_printf("mkdir(%s) failed: %m", path);
return -1;
}
return 0;
}
int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path,
enum mailbox_list_path_type type)
{
const char *error;
if (mailbox_list_try_mkdir_root(list, path, type, &error) < 0) {
mailbox_list_set_critical(list, "%s", error);
return -1;
}
if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
list->index_root_dir_created = TRUE;
return 0;
}
static bool
mailbox_list_is_valid_fs_name(struct mailbox_list *list, const char *name,
const char **error_r)
{
bool ret, allow_internal_dirs;
*error_r = NULL;
if (list->mail_set->mail_full_filesystem_access)
return TRUE;
/* make sure it's not absolute path */
if (*name == '/') {
*error_r = "Begins with '/'";
return FALSE;
}
if (*name == '~') {
*error_r = "Begins with '~'";
return FALSE;
}
/* make sure the mailbox name doesn't contain any foolishness:
"../" could give access outside the mailbox directory.
"./" and "//" could fool ACL checks.
some mailbox formats have reserved directory names, such as
Maildir's cur/new/tmp. if any of those would conflict with the
mailbox directory name, it's not valid. maildir++ is kludged here as
a special case because all of its mailbox dirs begin with "." */
allow_internal_dirs = list->v.is_internal_name == NULL ||
*list->set.maildir_name != '\0' ||
strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0;
T_BEGIN {
const char *const *names;
names = t_strsplit(name, "/");
for (; *names != NULL; names++) {
const char *n = *names;
if (*n == '\0') {
*error_r = "Has adjacent '/' chars";
break; /* // */
}
if (*n == '.') {
if (n[1] == '\0') {
*error_r = "Contains '.' part";
break; /* ./ */
}
if (n[1] == '.' && n[2] == '\0') {
*error_r = "Contains '..' part";
break; /* ../ */
}
}
if (*list->set.maildir_name != '\0' &&
strcmp(list->set.maildir_name, n) == 0) {
/* don't allow maildir_name to be used as part
of the mailbox name */
*error_r = "Contains reserved name";
break;
}
if (!allow_internal_dirs &&
list->v.is_internal_name(list, n)) {
*error_r = "Contains reserved name";
break;
}
}
ret = *names == NULL;
} T_END;
return ret;
}
bool mailbox_list_is_valid_name(struct mailbox_list *list,
const char *name, const char **error_r)
{
if (*name == '\0' && *list->ns->prefix != '\0') {
/* an ugly way to get to mailbox root (e.g. Maildir/ when
it's not the INBOX) */
return TRUE;
}
return mailbox_list_is_valid_fs_name(list, name, error_r);
}
int mailbox_list_get_path(struct mailbox_list *list, const char *name,
enum mailbox_list_path_type type,
const char **path_r)
{
int ret;
if ((ret = list->v.get_path(list, name, type, path_r)) <= 0)
*path_r = NULL;
else
i_assert(*path_r != NULL);
return ret;
}
bool mailbox_list_get_root_path(struct mailbox_list *list,
enum mailbox_list_path_type type,
const char **path_r)
{
int ret;
if ((ret = list->v.get_path(list, NULL, type, path_r)) < 0)
i_unreached();
if (ret == 0)
*path_r = NULL;
else
i_assert(*path_r != NULL);
return ret > 0;
}
const char *mailbox_list_get_root_forced(struct mailbox_list *list,
enum mailbox_list_path_type type)
{
const char *path;
if (!mailbox_list_get_root_path(list, type, &path))
i_unreached();
return path;
}
bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set,
enum mailbox_list_path_type type,
const char **path_r)
{
const char *path = NULL;
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
path = set->root_dir;
break;
case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
path = set->alt_dir;
break;
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
if (*set->mailbox_dir_name == '\0')
path = set->root_dir;
else {
path = t_strconcat(set->root_dir, "/",
set->mailbox_dir_name, NULL);
path = t_strndup(path, strlen(path)-1);
}
break;
case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
if (*set->mailbox_dir_name == '\0')
path = set->root_dir;
else if (set->alt_dir != NULL) {
path = t_strconcat(set->alt_dir, "/",
set->mailbox_dir_name, NULL);
path = t_strndup(path, strlen(path)-1);
}
break;
case MAILBOX_LIST_PATH_TYPE_CONTROL:
path = set->control_dir != NULL ?
set->control_dir : set->root_dir;
break;
case MAILBOX_LIST_PATH_TYPE_INDEX:
path = set->index_dir != NULL ?
set->index_dir : set->root_dir;
break;
case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE:
path = set->index_pvt_dir;
break;
}
*path_r = path;
return path != NULL;
}
const char *mailbox_list_get_temp_prefix(struct mailbox_list *list)
{
return list->v.get_temp_prefix(list, FALSE);
}
const char *mailbox_list_get_global_temp_prefix(struct mailbox_list *list)
{
return list->v.get_temp_prefix(list, TRUE);
}
const char *mailbox_list_join_refpattern(struct mailbox_list *list,
const char *ref, const char *pattern)
{
if (list->v.join_refpattern != NULL)
return list->v.join_refpattern(list, ref, pattern);
/* the default implementation: */
if (*ref != '\0') {
/* merge reference and pattern */
pattern = t_strconcat(ref, pattern, NULL);
}
return pattern;
}
int mailbox_has_children(struct mailbox_list *list, const char *name)
{
struct mailbox_list_iterate_context *iter;
const char *pattern;
int ret;
pattern = t_strdup_printf("%s%c%%", name,
mail_namespace_get_sep(list->ns));
iter = mailbox_list_iter_init(list, pattern,
MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
ret = mailbox_list_iter_next(iter) != NULL ? 1 : 0;
if (mailbox_list_iter_deinit(&iter) < 0)
ret = -1;
return ret;
}
int mailbox_list_mailbox(struct mailbox_list *list, const char *name,
enum mailbox_info_flags *flags_r)
{
const char *path, *fname, *rootdir, *dir, *inbox;
unsigned int len;
*flags_r = 0;
if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
strcasecmp(name, "INBOX") == 0) {
/* special handling for INBOX, mainly because with Maildir++
layout it needs to check if the cur/ directory exists,
which the Maildir++ layout backend itself can't do.. */
struct mailbox *box;
enum mailbox_existence existence;
int ret;
/* kludge: with imapc backend we can get here with
list=Maildir++ (for indexes), but list->ns->list=imapc */
box = mailbox_alloc(list->ns->list, "INBOX", 0);
ret = mailbox_exists(box, FALSE, &existence);
mailbox_free(&box);
if (ret < 0) {
/* this can only be an internal error */
mailbox_list_set_internal_error(list);
return -1;
}
switch (existence) {
case MAILBOX_EXISTENCE_NONE:
case MAILBOX_EXISTENCE_NOSELECT:
*flags_r |= MAILBOX_NONEXISTENT;
return 0;
case MAILBOX_EXISTENCE_SELECT:
break;
}
return 1;
}
if (list->v.get_mailbox_flags == NULL) {
/* can't do this optimized. do it the slow way. */
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
const char *vname;
vname = mailbox_list_get_vname(list, name);
iter = mailbox_list_iter_init(list, vname, 0);
info = mailbox_list_iter_next(iter);
if (info == NULL)
*flags_r = MAILBOX_NONEXISTENT;
else
*flags_r = info->flags;
return mailbox_list_iter_deinit(&iter);
}
rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0)
i_unreached();
fname = strrchr(path, '/');
if (fname == NULL) {
fname = path;
dir = "/";
} else {
dir = t_strdup_until(path, fname);
fname++;
}
len = strlen(rootdir);
if (strncmp(path, rootdir, len) == 0 && path[len] == '/') {
/* looking up a regular mailbox under mail root dir */
} else if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
strcasecmp(name, "INBOX") == 0) {
/* looking up INBOX that's elsewhere */
} else {
/* looking up the root dir itself */
dir = path;
fname = "";
}
if (*fname == '\0' && *name == '\0' &&
(list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
/* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
access it also via namespace prefix. */
if (mailbox_list_get_path(list, "INBOX",
MAILBOX_LIST_PATH_TYPE_MAILBOX,
&inbox) <= 0)
i_unreached();
if (strcmp(inbox, dir) == 0) {
*flags_r |= MAILBOX_NONEXISTENT;
return 0;
}
}
return list->v.get_mailbox_flags(list, dir, fname,
MAILBOX_LIST_FILE_TYPE_UNKNOWN,
flags_r);
}
static bool mailbox_list_init_changelog(struct mailbox_list *list)
{
struct mailbox_permissions perm;
const char *path;
if (list->changelog != NULL)
return TRUE;
/* don't do this in mailbox_list_create(), because _get_path() might be
overridden by storage (mbox). */
if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path))
return FALSE;
path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
list->changelog = mailbox_log_alloc(path);
mailbox_list_get_root_permissions(list, &perm);
mailbox_log_set_permissions(list->changelog, perm.file_create_mode,
perm.file_create_gid,
perm.file_create_gid_origin);
return TRUE;
}
int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list)
{
const char *root_dir, *index_dir;
int ret;
if (list->index_root_dir_created)
return 1;
/* if index root dir hasn't been created yet, do it now */
ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
&index_dir);
if (ret <= 0)
return ret;
if (index_dir[0] == '\0')
return 0;
ret = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&root_dir);
if (ret <= 0)
return ret;
if (strcmp(root_dir, index_dir) != 0) {
if (mailbox_list_mkdir_root(list, index_dir,
MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
return -1;
}
list->index_root_dir_created = TRUE;
return 1;
}
void mailbox_list_add_change(struct mailbox_list *list,
enum mailbox_log_record_type type,
const guid_128_t mailbox_guid)
{
struct mailbox_log_record rec;
time_t stamp;
if (!mailbox_list_init_changelog(list) ||
guid_128_is_empty(mailbox_guid))
return;
if (mailbox_list_mkdir_missing_index_root(list) <= 0)
return;
stamp = list->changelog_timestamp != (time_t)-1 ?
list->changelog_timestamp : ioloop_time;
memset(&rec, 0, sizeof(rec));
rec.type = type;
memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid));
mailbox_log_record_set_timestamp(&rec, stamp);
(void)mailbox_log_append(list->changelog, &rec);
}
int mailbox_list_set_subscribed(struct mailbox_list *list,
const char *name, bool set)
{
int ret;
/* make sure we'll refresh the file on next list */
list->subscriptions_mtime = (time_t)-1;
if ((ret = list->v.set_subscribed(list, name, set)) <= 0)
return ret;
return 0;
}
int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
{
const char *error;
if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
return list->v.delete_dir(list, name);
}
int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name)
{
const char *error;
if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
return list->v.delete_symlink(list, name);
}
void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r)
{
unsigned char sha[SHA1_RESULTLEN];
sha1_get_digest(name, strlen(name), sha);
memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha)));
}
struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list)
{
return !mailbox_list_init_changelog(list) ? NULL : list->changelog;
}
void mailbox_list_set_changelog_timestamp(struct mailbox_list *list,
time_t stamp)
{
list->changelog_timestamp = stamp;
}
bool mailbox_list_name_is_too_large(const char *name, char sep)
{
unsigned int levels = 1, level_len = 0;
for (; *name != '\0'; name++) {
if (*name == sep) {
if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
return TRUE;
levels++;
level_len = 0;
} else {
level_len++;
}
}
if (level_len > MAILBOX_MAX_HIERARCHY_NAME_LENGTH)
return TRUE;
if (levels > MAILBOX_MAX_HIERARCHY_LEVELS)
return TRUE;
return FALSE;
}
enum mailbox_list_file_type
mailbox_list_get_file_type(const struct dirent *d ATTR_UNUSED)
{
enum mailbox_list_file_type type;
#ifdef HAVE_DIRENT_D_TYPE
switch (d->d_type) {
case DT_UNKNOWN:
type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
break;
case DT_REG:
type = MAILBOX_LIST_FILE_TYPE_FILE;
break;
case DT_DIR:
type = MAILBOX_LIST_FILE_TYPE_DIR;
break;
case DT_LNK:
type = MAILBOX_LIST_FILE_TYPE_SYMLINK;
break;
default:
type = MAILBOX_LIST_FILE_TYPE_OTHER;
break;
}
#else
type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
#endif
return type;
}
int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list,
const char *dir_path,
const struct dirent *d)
{
struct stat st;
int ret;
if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK)
return 1;
T_BEGIN {
const char *path, *linkpath;
path = t_strconcat(dir_path, "/", d->d_name, NULL);
if (lstat(path, &st) < 0) {
mailbox_list_set_critical(list,
"lstat(%s) failed: %m", path);
ret = -1;
} else if (!S_ISLNK(st.st_mode)) {
ret = 0;
} else if (t_readlink(path, &linkpath) < 0) {
i_error("readlink(%s) failed: %m", path);
ret = -1;
} else {
/* it's an alias only if it points to the same
directory */
ret = strchr(linkpath, '/') == NULL ? 1 : 0;
}
} T_END;
return ret;
}
static bool
mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name)
{
if ((*name)[1] == '/') {
/* ~/dir - use the configured home directory */
if (mail_user_try_home_expand(list->ns->user, name) < 0)
return FALSE;
} else {
/* ~otheruser/dir - assume we're using system users */
if (home_try_expand(name) < 0)
return FALSE;
}
return TRUE;
}
bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
const char **name)
{
const char *root_dir, *path, *mailbox_name;
unsigned int len;
if (!list->mail_set->mail_full_filesystem_access)
return FALSE;
if (**name == '~') {
/* try to expand home directory */
if (!mailbox_list_try_get_home_path(list, name)) {
/* fallback to using actual "~name" mailbox */
return FALSE;
}
} else {
if (**name != '/')
return FALSE;
}
/* okay, we have an absolute path now. but check first if it points to
same directory as one of our regular mailboxes. */
root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
len = strlen(root_dir);
if (strncmp(root_dir, *name, len) == 0 && (*name)[len] == '/') {
mailbox_name = *name + len + 1;
if (mailbox_list_get_path(list, mailbox_name,
MAILBOX_LIST_PATH_TYPE_MAILBOX,
&path) <= 0)
return FALSE;
if (strcmp(path, *name) == 0) {
/* yeah, we can replace the full path with mailbox
name. this way we can use indexes. */
*name = mailbox_name;
return FALSE;
}
}
return TRUE;
}
const char *mailbox_list_get_last_error(struct mailbox_list *list,
enum mail_error *error_r)
{
if (error_r != NULL)
*error_r = list->error;
return list->error_string != NULL ? list->error_string :
"Unknown internal list error";
}
void mailbox_list_clear_error(struct mailbox_list *list)
{
i_free_and_null(list->error_string);
list->error = MAIL_ERROR_NONE;
}
void mailbox_list_set_error(struct mailbox_list *list,
enum mail_error error, const char *string)
{
i_free(list->error_string);
list->error_string = i_strdup(string);
list->error = error;
}
void mailbox_list_set_internal_error(struct mailbox_list *list)
{
const char *str;
str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time);
i_free(list->error_string);
list->error_string = i_strdup(str);
list->error = MAIL_ERROR_TEMP;
}
void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
i_error("%s", t_strdup_vprintf(fmt, va));
va_end(va);
/* critical errors may contain sensitive data, so let user
see only "Internal error" with a timestamp to make it
easier to look from log files the actual error message. */
mailbox_list_set_internal_error(list);
}
bool mailbox_list_set_error_from_errno(struct mailbox_list *list)
{
const char *error_string;
enum mail_error error;
if (!mail_error_from_errno(&error, &error_string))
return FALSE;
mailbox_list_set_error(list, error, error_string);
return TRUE;
}