mailbox-list.c revision f0d09be40bd0c4423873128ae2f88a4020075dc4
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch/* Copyright (c) 2006-2015 Dovecot authors, see the included COPYING file */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "lib.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "array.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "abspath.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "ioloop.h"
833bed942977673526c72e79bccc09314fc57104Phil Carmody#include "mkdir-parents.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "str.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "sha1.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "hash.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "home-expand.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "time-util.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "unichar.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "settings-parser.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "iostream-ssl.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "fs-api-private.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "imap-utf7.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "mailbox-log.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "mailbox-tree.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "mail-storage-private.h"
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch#include "mail-storage-hooks.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "mailbox-list-private.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <time.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <ctype.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <unistd.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <dirent.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <sys/stat.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch/* 16 * (255+1) = 4096 which is the standard PATH_MAX. Having these settings
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch then start renaming them to larger names from end to beginning, which
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch eventually would start causing the failures when trying to use too
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch long mailbox names. 255 is the standard single directory name length, so
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch allow up to that high. */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#define MAILBOX_MAX_HIERARCHY_LEVELS 16
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#define MAILBOX_LIST_FS_CONTEXT(obj) \
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch MODULE_CONTEXT(obj, mailbox_list_fs_module)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstruct mailbox_list_fs_context {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch union fs_api_module_context module_ctx;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mailbox_list *list;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch};
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Boschstatic MODULE_CONTEXT_DEFINE_INIT(mailbox_list_fs_module,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch &fs_api_module_register);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Boschvoid mailbox_lists_init(void)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_array_init(&mailbox_list_drivers, 4);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid mailbox_lists_deinit(void)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch array_free(&mailbox_list_drivers);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mailbox_list *const *drivers;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int i, count;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch drivers = array_get(&mailbox_list_drivers, &count);
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch for (i = 0; i < count; i++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (strcasecmp(drivers[i]->name, name) == 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *idx_r = i;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return TRUE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return FALSE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen unsigned int idx;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_fatal("mailbox_list_register(%s): duplicate driver",
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->name);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch array_append(&mailbox_list_drivers, &list, 1);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
dc05b1fb4b7a2b4d91248078311458cb4cbad9a1Stephan Boschvoid mailbox_list_unregister(const struct mailbox_list *list)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int idx;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (!mailbox_list_driver_find(list->name, &idx)) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_fatal("mailbox_list_unregister(%s): unknown driver",
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->name);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch array_delete(&mailbox_list_drivers, idx, 1);
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch}
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschconst struct mailbox_list *
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschmailbox_list_find_class(const char *driver)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mailbox_list *const *class_p;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int idx;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (!mailbox_list_driver_find(driver, &idx))
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return NULL;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return *class_p;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschint mailbox_list_create(const char *driver, struct mail_namespace *ns,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mailbox_list_settings *set,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch enum mailbox_list_flags flags,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mailbox_list **list_r, const char **error_r)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mailbox_list *const *class_p;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mailbox_list *list;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int idx;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(ns->list == NULL ||
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(set->subscription_fname == NULL ||
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *set->subscription_fname != '\0');
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (!mailbox_list_driver_find(driver, &idx)) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *error_r = "Unknown driver name";
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch class_p = array_idx(&mailbox_list_drivers, idx);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *set->maildir_name != '\0') {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen *error_r = "maildir_name not supported by this driver";
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen set->alt_dir != NULL) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen *error_r = "alt_dir not supported by this driver";
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list = (*class_p)->v.alloc();
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->ns = ns;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->mail_set = ns->mail_set;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->flags = flags;
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch list->root_permissions.file_create_mode = (mode_t)-1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen list->root_permissions.dir_create_mode = (mode_t)-1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->root_permissions.file_create_gid = (gid_t)-1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->changelog_timestamp = (time_t)-1;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch /* copy settings */
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (set->root_dir != NULL) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.root_dir = p_strdup(list->pool, set->root_dir);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.index_dir = set->index_dir == NULL ||
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch p_strdup(list->pool, set->index_dir);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch p_strdup(list->pool, set->index_pvt_dir);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.control_dir = set->control_dir == NULL ||
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch p_strdup(list->pool, set->control_dir);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.subscription_fname =
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch p_strdup(list->pool, set->subscription_fname);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch list->set.maildir_name =
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch p_strdup(list->pool, set->maildir_name);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch list->set.mailbox_dir_name =
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch p_strdup(list->pool, set->mailbox_dir_name);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch list->set.alt_dir_nocheck = set->alt_dir_nocheck;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch list->set.index_control_use_maildir_name =
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch set->index_control_use_maildir_name;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (*set->mailbox_dir_name == '\0')
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.mailbox_dir_name = "";
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.mailbox_dir_name =
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch p_strdup(list->pool, set->mailbox_dir_name);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch } else {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.mailbox_dir_name =
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.escape_char = set->escape_char;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.broken_char = set->broken_char;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.utf8 = set->utf8;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (list->v.init != NULL) {
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen if (list->v.init(list, error_r) < 0) {
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch list->v.deinit(list);
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
9ec9b6f85c8fbe67bfac523a5e3d33d34f72dddcStephan Bosch }
9ec9b6f85c8fbe67bfac523a5e3d33d34f72dddcStephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (ns->mail_set->mail_debug) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->name,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.root_dir == NULL ? "" : list->set.root_dir,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.index_dir == NULL ? "" : list->set.index_dir,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch list->set.control_dir == NULL ?
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch "" : list->set.control_dir,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.inbox_path == NULL ?
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch "" : list->set.inbox_path,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch list->set.alt_dir == NULL ? "" : list->set.alt_dir);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch }
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch mail_namespace_finish_list_init(ns, list);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch *list_r = list;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch hook_mailbox_list_created(list);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch return 0;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch}
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic int fix_path(struct mail_user *user, const char *path, bool expand_home,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch const char **path_r, const char **error_r)
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t len = strlen(path);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (len > 1 && path[len-1] == '/')
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch path = t_strndup(path, len-1);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (!expand_home) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch /* no ~ expansion */
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch } else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* ~otheruser/dir */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (home_try_expand(&path) < 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *error_r = t_strconcat(
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch "No home directory for system user. "
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch "Can't expand ", t_strcut(path, '/'),
e1a4ea6ad3e799ef8df7395e765c0ae9218e6c5dStephan Bosch " for ", NULL);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch }
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch } else {
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch if (mail_user_try_home_expand(user, &path) < 0) {
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch *error_r = "Home directory not set for user. "
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch "Can't expand ~/ for ";
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch return -1;
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch *path_r = path;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Boschstatic const char *split_next_arg(const char *const **_args)
ee2633056e67353157bfbce4d9e0d1c3ceaa627aStephan Bosch{
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch const char *const *args = *_args;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch const char *str = args[0];
833bed942977673526c72e79bccc09314fc57104Phil Carmody
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch args++;
while (*args != NULL && **args == '\0') {
args++;
if (*args == NULL) {
/* string ends with ":", just ignore it. */
break;
}
str = t_strconcat(str, ":", *args, NULL);
args++;
}
*_args = args;
return str;
}
static int
mailbox_list_settings_parse_full(struct mail_user *user, const char *data,
bool expand_home,
struct mailbox_list_settings *set_r,
const char **error_r)
{
const char *const *tmp, *key, *value, **dest, *str, *error;
*error_r = NULL;
memset(set_r, 0, sizeof(*set_r));
set_r->maildir_name = "";
set_r->mailbox_dir_name = "";
if (*data == '\0')
return 0;
/* <root dir> */
tmp = t_strsplit(data, ":");
str = split_next_arg(&tmp);
if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
*error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
return -1;
}
if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) {
/* probably mbox user trying to avoid root_dir */
*error_r = t_strconcat("Mail root directory not given: ",
data, NULL);
return -1;
}
while (*tmp != NULL) {
str = split_next_arg(&tmp);
if (strcmp(str, "UTF-8") == 0) {
set_r->utf8 = TRUE;
continue;
}
value = strchr(str, '=');
if (value == NULL) {
key = str;
value = "";
} else {
key = t_strdup_until(str, value);
value++;
}
if (strcmp(key, "INBOX") == 0)
dest = &set_r->inbox_path;
else if (strcmp(key, "INDEX") == 0)
dest = &set_r->index_dir;
else if (strcmp(key, "INDEXPVT") == 0)
dest = &set_r->index_pvt_dir;
else if (strcmp(key, "CONTROL") == 0)
dest = &set_r->control_dir;
else if (strcmp(key, "ALT") == 0)
dest = &set_r->alt_dir;
else if (strcmp(key, "ALTNOCHECK") == 0) {
set_r->alt_dir_nocheck = TRUE;
continue;
} else if (strcmp(key, "LAYOUT") == 0)
dest = &set_r->layout;
else if (strcmp(key, "SUBSCRIPTIONS") == 0)
dest = &set_r->subscription_fname;
else if (strcmp(key, "DIRNAME") == 0)
dest = &set_r->maildir_name;
else if (strcmp(key, "MAILBOXDIR") == 0)
dest = &set_r->mailbox_dir_name;
else if (strcmp(key, "FULLDIRNAME") == 0) {
set_r->index_control_use_maildir_name = TRUE;
dest = &set_r->maildir_name;
} else {
*error_r = t_strdup_printf("Unknown setting: %s", key);
return -1;
}
if (fix_path(user, value, expand_home, dest, &error) < 0) {
*error_r = t_strconcat(error, key, " in: ", data, NULL);
return -1;
}
}
if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
set_r->index_dir = "";
return 0;
}
int mailbox_list_settings_parse(struct mail_user *user, const char *data,
struct mailbox_list_settings *set_r,
const char **error_r)
{
return mailbox_list_settings_parse_full(user, data, TRUE,
set_r, error_r);
}
const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
enum mailbox_list_path_type type)
{
const struct mail_storage_settings *mail_set;
const char *location = list->ns->unexpanded_set->location;
struct mail_user *user = list->ns->user;
struct mailbox_list_settings set;
const char *p, *path, *error;
if (*location == SETTING_STRVAR_EXPANDED[0]) {
/* set using -o or userdb lookup. */
return "";
}
i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
location++;
if (*location == '\0') {
mail_set = mail_user_set_get_driver_settings(user->set_info,
user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
i_assert(mail_set != NULL);
location = mail_set->mail_location;
if (*location == SETTING_STRVAR_EXPANDED[0])
return "";
i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
location++;
}
/* type:settings */
p = strchr(location, ':');
if (p == NULL)
return "";
if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
&set, &error) < 0)
return "";
if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
return "";
return path;
}
static bool need_escape_dirstart(const char *vname, const char *maildir_name)
{
unsigned int len;
if (vname[0] == '.') {
if (vname[1] == '\0' || vname[1] == '/')
return TRUE; /* "." */
if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
return TRUE; /* ".." */
}
if (*maildir_name != '\0') {
len = strlen(maildir_name);
if (strncmp(maildir_name, vname, len) == 0 &&
(vname[len] == '\0' || vname[len] == '/'))
return TRUE; /* e.g. dbox-Mails */
}
return FALSE;
}
static const char *
mailbox_list_escape_name(struct mailbox_list *list, const char *vname)
{
char ns_sep = mail_namespace_get_sep(list->ns);
char list_sep = mailbox_list_get_hierarchy_sep(list);
string_t *escaped_name = t_str_new(64);
char dirstart = TRUE;
/* no escaping of namespace prefix */
if (strncmp(list->ns->prefix, vname, list->ns->prefix_len) == 0) {
str_append_n(escaped_name, vname, list->ns->prefix_len);
vname += list->ns->prefix_len;
}
/* escape the mailbox name */
if (*vname == '~') {
str_printfa(escaped_name, "%c%02x",
list->set.escape_char, *vname);
vname++;
dirstart = FALSE;
}
for (; *vname != '\0'; vname++) {
if (*vname == ns_sep)
str_append_c(escaped_name, list_sep);
else if (*vname == list_sep ||
*vname == list->set.escape_char ||
*vname == '/' ||
(dirstart &&
need_escape_dirstart(vname, list->set.maildir_name))) {
str_printfa(escaped_name, "%c%02x",
list->set.escape_char, *vname);
} else {
str_append_c(escaped_name, *vname);
}
dirstart = *vname == '/';
}
return str_c(escaped_name);
}
static int
mailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
{
char *src, *dest;
unsigned char chr;
if ((src = strchr(name, list->set.broken_char)) == NULL)
return 0;
dest = src;
while (*src != '\0') {
if (*src == list->set.broken_char) {
if (src[1] >= '0' && src[1] <= '9')
chr = (src[1]-'0') * 0x10;
else if (src[1] >= 'a' && src[1] <= 'f')
chr = (src[1]-'a' + 10) * 0x10;
else
return -1;
if (src[2] >= '0' && src[2] <= '9')
chr += src[2]-'0';
else if (src[2] >= 'a' && src[2] <= 'f')
chr += src[2]-'a' + 10;
else
return -1;
*dest++ = chr;
src += 3;
} else {
*dest++ = *src++;
}
}
*dest++ = '\0';
return 0;
}
static char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
{
char *ret, *p;
ret = p_strdup(unsafe_data_stack_pool, storage_name);
for (p = ret; *p != '\0'; p++) {
if (*p == src)
*p = dest;
}
return ret;
}
const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
const char *vname)
{
struct mail_namespace *ns = list->ns;
unsigned int prefix_len = strlen(ns->prefix);
const char *storage_name = vname;
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 && list->set.escape_char == '\0') {
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,
(unsigned 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);
if (list->set.escape_char != '\0') {
vname = mailbox_list_unescape_name(list, vname);
return prefix_len == 0 ? vname :
t_strconcat(list->ns->prefix, vname, NULL);
}
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;
}
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;
}
static int
mailbox_list_get_storage_driver(struct mailbox_list *list, const char *driver,
struct mail_storage **storage_r)
{
struct mail_storage *const *storagep;
const char *error, *data;
array_foreach(&list->ns->all_storages, storagep) {
if (strcmp((*storagep)->name, driver) == 0) {
*storage_r = *storagep;
return 0;
}
}
data = strchr(list->ns->set->location, ':');
if (data == NULL)
data = "";
else
data++;
if (mail_storage_create_full(list->ns, driver, data, 0,
storage_r, &error) < 0) {
mailbox_list_set_critical(list,
"Namespace %s: Failed to create storage '%s': %s",
list->ns->prefix, driver, error);
return -1;
}
return 0;
}
int mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
struct mail_storage **storage_r)
{
const struct mailbox_settings *set;
if ((*list)->v.get_storage != NULL)
return (*list)->v.get_storage(list, vname, storage_r);
set = mailbox_settings_find((*list)->ns, vname);
if (set != NULL && set->driver != NULL && set->driver[0] != '\0') {
return mailbox_list_get_storage_driver(*list, set->driver,
storage_r);
}
*storage_r = mail_namespace_get_default_storage((*list)->ns);
return 0;
}
void mailbox_list_get_default_storage(struct mailbox_list *list,
struct mail_storage **storage)
{
*storage = mail_namespace_get_default_storage(list->ns);
}
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 ||
(list->flags & MAILBOX_LIST_FLAG_NO_MAIL_FILES) != 0) {
/* no filesystem support in storage */
} else if (stat(path, &st) < 0) {
if (errno == EACCES) {
mailbox_list_set_critical(list, "%s",
mail_error_eacces_msg("stat", path));
} else 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 = errno == EACCES ?
mail_error_eacces_msg("stat", root_dir) :
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;
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 (mailbox_list_try_mkdir_root_parent(list, type,
&perm, error_r) < 0)
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') {
if (*list->ns->prefix != '\0') {
/* an ugly way to get to mailbox root (e.g. Maildir/
when it's not the INBOX) */
return TRUE;
}
*error_r = "Name is empty";
return FALSE;
}
/* either the list backend uses '/' as the hierarchy separator or
it doesn't use filesystem at all (PROP_NO_ROOT) */
if ((list->props & MAILBOX_LIST_PROP_NO_ROOT) == 0 &&
mailbox_list_get_hierarchy_sep(list) != '/' &&
strchr(name, '/') != NULL) {
*error_r = "Name must not have '/' characters";
return FALSE;
}
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:
if (set->index_dir != NULL) {
if (set->index_dir[0] == '\0') {
/* in-memory indexes */
return 0;
}
path = set->index_dir;
} else {
path = 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);
if (ret < 0) {
const char *errstr;
enum mail_error error;
/* internal error or with imapc we can get here with
login failures */
errstr = mailbox_get_last_error(box, &error);
mailbox_list_set_error(list, error, errstr);
}
mailbox_free(&box);
if (ret < 0)
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;
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;
}
int mailbox_list_init_fs(struct mailbox_list *list, const char *driver,
const char *args, const char *root_dir,
struct fs **fs_r, const char **error_r)
{
struct fs_settings fs_set;
struct ssl_iostream_settings ssl_set;
struct mailbox_list_fs_context *ctx;
struct fs *parent_fs;
memset(&ssl_set, 0, sizeof(ssl_set));
memset(&fs_set, 0, sizeof(fs_set));
mail_user_init_fs_settings(list->ns->user, &fs_set, &ssl_set);
fs_set.root_path = root_dir;
fs_set.temp_file_prefix = mailbox_list_get_global_temp_prefix(list);
if (fs_init(driver, args, &fs_set, fs_r, error_r) < 0)
return -1;
/* add mailbox_list context to the parent fs, which allows
mailbox_list_fs_get_list() to work */
for (parent_fs = *fs_r; parent_fs->parent != NULL;
parent_fs = parent_fs->parent) ;
ctx = p_new(list->pool, struct mailbox_list_fs_context, 1);
ctx->list = list;
MODULE_CONTEXT_SET(parent_fs, mailbox_list_fs_module, ctx);
/* a bit kludgy notification to the fs that we're now finished setting
up the module context. */
(void)fs_get_properties(*fs_r);
return 0;
}
struct mailbox_list *mailbox_list_fs_get_list(struct fs *fs)
{
struct mailbox_list_fs_context *ctx;
while (fs->parent != NULL)
fs = fs->parent;
ctx = MAILBOX_LIST_FS_CONTEXT(fs);
return ctx == NULL ? NULL : ctx->list;
}