mailbox-list.c revision 2cb0a0438b625cd399a4aaa0615d69f4b54d349a
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2006-2012 Dovecot authors, see the included COPYING file */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "lib.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "array.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "abspath.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "ioloop.h"
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen#include "mkdir-parents.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "str.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "sha1.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "hash.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "home-expand.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "time-util.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "unichar.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "settings-parser.h"
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen#include "imap-utf7.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "mailbox-log.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "mailbox-tree.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "mail-storage.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "mail-storage-hooks.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "mailbox-list-private.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <time.h>
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen#include <ctype.h>
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen#include <unistd.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <dirent.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <sys/stat.h>
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen then start renaming them to larger names from end to beginning, which
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen eventually would start causing the failures when trying to use too
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen long mailbox names. */
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen#define MAILBOX_MAX_HIERARCHY_LEVELS 20
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenstatic ARRAY(const struct mailbox_list *) mailbox_list_drivers;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenvoid mailbox_lists_init(void)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen i_array_init(&mailbox_list_drivers, 4);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenvoid mailbox_lists_deinit(void)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen array_free(&mailbox_list_drivers);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
942302b0247403645394d848b3c620ead262a2a5Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen const struct mailbox_list *const *drivers;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen unsigned int i, count;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
11352dc3e4b29f3d2763c82f8ea4f99e8daf4fa3Timo Sirainen for (i = 0; i < count; i++) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen *idx_r = i;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen return TRUE;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen }
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return FALSE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
cd8507179823de33d6e8242e10dbc15d136245b5Timo Sirainen unsigned int idx;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->name);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen array_append(&mailbox_list_drivers, &list, 1);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen}
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen unsigned int idx;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen list->name);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen array_delete(&mailbox_list_drivers, idx, 1);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen}
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenconst struct mailbox_list *
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenmailbox_list_find_class(const char *driver)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen const struct mailbox_list *const *class_p;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen unsigned int idx;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!mailbox_list_driver_find(driver, &idx))
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return NULL;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return *class_p;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct mailbox_list_settings *set,
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen enum mailbox_list_flags flags,
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen struct mailbox_list **list_r, const char **error_r)
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen{
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen const struct mailbox_list *const *class_p;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen struct mailbox_list *list;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen unsigned int idx;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen i_assert(ns->list == NULL ||
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen i_assert(set->subscription_fname == NULL ||
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen *set->subscription_fname != '\0');
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *error_r = "Unknown driver name";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *set->maildir_name != '\0') {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *error_r = "maildir_name not supported by this driver";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen set->alt_dir != NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *error_r = "alt_dir not supported by this driver";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list = (*class_p)->v.alloc();
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->ns = ns;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->mail_set = ns->mail_set;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->flags = flags;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->root_permissions.file_create_mode = (mode_t)-1;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->root_permissions.dir_create_mode = (mode_t)-1;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->root_permissions.file_create_gid = (gid_t)-1;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen list->changelog_timestamp = (time_t)-1;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen /* copy settings */
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (set->root_dir != NULL) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen list->set.root_dir = p_strdup(list->pool, set->root_dir);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen list->set.index_dir = set->index_dir == NULL ||
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen p_strdup(list->pool, set->index_dir);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen p_strdup(list->pool, set->index_pvt_dir);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen p_strdup(list->pool, set->control_dir);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen list->set.subscription_fname =
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen p_strdup(list->pool, set->subscription_fname);
8305127d1074cf9a1e25dec9be2735276462079dTimo Sirainen list->set.maildir_name =
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen p_strdup(list->pool, set->maildir_name);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen list->set.mailbox_dir_name =
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen list->set.alt_dir_nocheck = set->alt_dir_nocheck;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (*set->mailbox_dir_name == '\0')
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen list->set.mailbox_dir_name = "";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen list->set.mailbox_dir_name =
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else {
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen list->set.mailbox_dir_name =
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
15b5076a239682277b44880e33ea23b55fff7e71Timo Sirainen }
15b5076a239682277b44880e33ea23b55fff7e71Timo Sirainen list->set.utf8 = set->utf8;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen if (list->v.init != NULL) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (list->v.init(list, error_r) < 0) {
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen list->v.deinit(list);
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen return -1;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen if (ns->mail_set->mail_debug) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen i_debug("%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->name,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.root_dir == NULL ? "" : list->set.root_dir,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.index_dir == NULL ? "" : list->set.index_dir,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.control_dir == NULL ?
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen "" : list->set.control_dir,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.inbox_path == NULL ?
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen "" : list->set.inbox_path,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list->set.alt_dir == NULL ? "" : list->set.alt_dir);
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen }
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
074055dadbca01626437cc4724853a374acab6a8Timo Sirainen mail_namespace_finish_list_init(ns, list);
074055dadbca01626437cc4724853a374acab6a8Timo Sirainen
074055dadbca01626437cc4724853a374acab6a8Timo Sirainen *list_r = list;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen hook_mailbox_list_created(list);
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen return 0;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen}
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainenstatic int fix_path(struct mail_user *user, const char *path,
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen const char **path_r, const char **error_r)
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen{
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen size_t len = strlen(path);
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (len > 1 && path[len-1] == '/')
7f3b826a89bcb7a72759912e99f574b28309fe1bTimo Sirainen path = t_strndup(path, len-1);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen /* ~otheruser/dir */
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen if (home_try_expand(&path) < 0) {
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen *error_r = t_strconcat(
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen "No home directory for system user. "
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen "Can't expand ", t_strcut(path, '/'),
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen " for ", NULL);
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen } else {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (mail_user_try_home_expand(user, &path) < 0) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *error_r = "Home directory not set for user. "
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen "Can't expand ~/ for ";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *path_r = path;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return 0;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic const char *split_next_arg(const char *const **_args)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char *const *args = *_args;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen const char *str = args[0];
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
18ccd19c244f49665fe03cda785efa066d2c38dfTimo Sirainen while (*args != NULL && **args == '\0') {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen args++;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (*args == NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* string ends with ":", just ignore it. */
c529313e1cbc22244d4528e80aa3e485f8806cd3Timo Sirainen break;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen str = t_strconcat(str, ":", *args, NULL);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen args++;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen *_args = args;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return str;
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainenint mailbox_list_settings_parse(struct mail_user *user, const char *data,
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct mailbox_list_settings *set_r,
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char **error_r)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen const char *const *tmp, *key, *value, **dest, *str, *error;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen *error_r = NULL;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen memset(set_r, 0, sizeof(*set_r));
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen set_r->maildir_name = "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen set_r->mailbox_dir_name = "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*data == '\0')
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return 0;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen /* <root dir> */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen tmp = t_strsplit(data, ":");
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen str = split_next_arg(&tmp);
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen if (fix_path(user, str, &set_r->root_dir, &error) < 0) {
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen return -1;
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen }
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) {
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen /* probably mbox user trying to avoid root_dir */
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen *error_r = t_strconcat("Mail root directory not given: ",
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen data, NULL);
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen return -1;
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen }
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen while (*tmp != NULL) {
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen str = split_next_arg(&tmp);
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen if (strcmp(str, "UTF-8") == 0) {
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen set_r->utf8 = TRUE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen continue;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen value = strchr(str, '=');
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (value == NULL) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen key = str;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen value = "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen } else {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen key = t_strdup_until(str, value);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen value++;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (strcmp(key, "INBOX") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->inbox_path;
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen else if (strcmp(key, "INDEX") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->index_dir;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "INDEXPVT") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->index_pvt_dir;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "CONTROL") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->control_dir;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "ALT") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->alt_dir;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "ALTNOCHECK") == 0) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen set_r->alt_dir_nocheck = TRUE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen continue;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen } else if (strcmp(key, "LAYOUT") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->layout;
ce930f99c6a78f2c74b00df1ad2337095978a9dbTimo Sirainen else if (strcmp(key, "SUBSCRIPTIONS") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->subscription_fname;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "DIRNAME") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->maildir_name;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else if (strcmp(key, "MAILBOXDIR") == 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen dest = &set_r->mailbox_dir_name;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen else {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return -1;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen if (fix_path(user, value, dest, &error) < 0) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen *error_r = t_strconcat(error, key, " in: ", data, NULL);
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen return -1;
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen }
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen }
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen set_r->index_dir = "";
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen return 0;
9cb0fe28ae6f59b9f075e1edfc30f417e846c4a2Timo Sirainen}
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen
9cb0fe28ae6f59b9f075e1edfc30f417e846c4a2Timo Sirainenconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen enum mailbox_list_path_type type)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen const struct mail_storage_settings *mail_set;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen const char *location = list->ns->unexpanded_set->location;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen struct mail_user *user = list->ns->user;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen struct mailbox_list_settings set;
9cb0fe28ae6f59b9f075e1edfc30f417e846c4a2Timo Sirainen const char *p, *path, *error;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen /* set using -o or userdb lookup. */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen location++;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*location == '\0') {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen mail_set = mail_user_set_get_driver_settings(user->set_info,
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen i_assert(mail_set != NULL);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen location = mail_set->mail_location;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0])
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen location++;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen /* type:settings */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen p = strchr(location, ':');
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (p == NULL)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return "";
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (mailbox_list_settings_parse(user, p + 1, &set, &error) < 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (mailbox_list_set_get_root_path(&set, type, &path) <= 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return "";
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return path;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen}
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenstatic bool need_escape_dirstart(const char *vname, const char *maildir_name)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen unsigned int len;
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen if (vname[0] == '.') {
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen if (vname[1] == '\0' || vname[1] == '/')
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen return TRUE; /* "." */
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen return TRUE; /* ".." */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*maildir_name != '\0') {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen len = strlen(maildir_name);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (strncmp(maildir_name, vname, len) == 0 &&
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen (vname[len] == '\0' || vname[len] == '/'))
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return TRUE; /* e.g. dbox-Mails */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return FALSE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen}
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenstatic const char *
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenmailbox_list_escape_name(struct mailbox_list *list, const char *vname)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen char ns_sep = mail_namespace_get_sep(list->ns);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen char list_sep = mailbox_list_get_hierarchy_sep(list);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen string_t *escaped_name = t_str_new(64);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen char dirstart = TRUE;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen /* no escaping of namespace prefix */
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (strncmp(list->ns->prefix, vname, list->ns->prefix_len) == 0) {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen str_append_n(escaped_name, vname, list->ns->prefix_len);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen vname += list->ns->prefix_len;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen /* escape the mailbox name */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*vname == '~') {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen str_printfa(escaped_name, "%c%02x",
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen list->set.escape_char, *vname);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen vname++;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen dirstart = FALSE;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen for (; *vname != '\0'; vname++) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (*vname == ns_sep)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen str_append_c(escaped_name, *vname);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen else if (*vname == list_sep ||
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen *vname == list->set.escape_char ||
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen *vname == '/' ||
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen (dirstart &&
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen need_escape_dirstart(vname, list->set.maildir_name))) {
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen str_printfa(escaped_name, "%c%02x",
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen list->set.escape_char, *vname);
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen } else {
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen str_append_c(escaped_name, *vname);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen dirstart = *vname == '/';
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen }
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen return str_c(escaped_name);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenstatic int
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenmailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen char *src, *dest;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen unsigned char chr;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if ((src = strchr(name, list->set.broken_char)) == NULL)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen return 0;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen dest = src;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen while (*src != '\0') {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (*src == list->set.broken_char) {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (src[1] >= '0' && src[1] <= '9')
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen chr = (src[1]-'0') * 0x10;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen else if (src[1] >= 'a' && src[1] <= 'f')
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen chr = (src[1]-'a' + 10) * 0x10;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen else
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen return -1;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (src[2] >= '0' && src[2] <= '9')
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen chr += src[2]-'0';
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen else if (src[2] >= 'a' && src[2] <= 'f')
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen chr += src[2]-'a' + 10;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen else
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return -1;
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen *dest++ = chr;
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen src += 3;
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen } else {
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen *dest++ = *src++;
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen }
d85a1a9d9af4a36ded4d30cb277905c807de2ec5Timo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen *dest++ = '\0';
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return 0;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen char *ret, *p;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen ret = p_strdup(unsafe_data_stack_pool, storage_name);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen for (p = ret; *p != '\0'; p++) {
18ccd19c244f49665fe03cda785efa066d2c38dfTimo Sirainen if (*p == src)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen *p = dest;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen return ret;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen}
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainenconst char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char *vname)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen struct mail_namespace *ns = list->ns;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen unsigned int prefix_len = strlen(ns->prefix);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen const char *storage_name = vname;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen string_t *str;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen char list_sep, ns_sep, *ret;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (strcasecmp(storage_name, "INBOX") == 0 &&
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen storage_name = "INBOX";
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen else if (list->set.escape_char != '\0')
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen storage_name = mailbox_list_escape_name(list, vname);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (prefix_len > 0 && (strcmp(storage_name, "INBOX") != 0 ||
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)) {
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen /* skip namespace prefix, except if this is INBOX */
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (strncmp(ns->prefix, storage_name, prefix_len) == 0)
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen storage_name += prefix_len;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen else if (strncmp(ns->prefix, storage_name, prefix_len-1) == 0 &&
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen ns->prefix[prefix_len-1] == mail_namespace_get_sep(ns)) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen /* trying to access the namespace prefix itself */
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen storage_name = "";
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen } else {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen /* we're converting a nonexistent mailbox name,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen such as a LIST pattern. */
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen if (!list->set.utf8) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen /* UTF-8 -> mUTF-7 conversion */
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen str = t_str_new(strlen(storage_name)*2);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen if (imap_utf8_to_utf7(storage_name, str) < 0)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen i_panic("Mailbox name not UTF-8: %s", vname);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen storage_name = str_c(str);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen list_sep = mailbox_list_get_hierarchy_sep(list);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen ns_sep = mail_namespace_get_sep(ns);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen if (*storage_name == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen !list->mail_set->mail_shared_explicit_inbox) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen /* opening shared/$user. it's the same as INBOX. */
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen storage_name = "INBOX";
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (list_sep != ns_sep) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen /* shared namespace root. the backend storage's
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen hierarchy separator isn't known yet, so do
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen nothing. */
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen return storage_name;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen } else if (list->set.broken_char == '\0' ||
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen strchr(storage_name, list->set.broken_char) == NULL) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen /* no need to convert broken chars */
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen return storage_name;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen } else {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ret = p_strdup(unsafe_data_stack_pool, storage_name);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (list->set.broken_char != '\0') {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (mailbox_list_unescape_broken_chars(list, ret) < 0) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ret = mailbox_list_convert_sep(storage_name,
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen ns_sep, list_sep);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen return ret;
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen}
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainenconst char *mailbox_list_get_storage_name(struct mailbox_list *list,
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen const char *vname)
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainen{
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen return list->v.get_storage_name(list, vname);
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic const char *
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenmailbox_list_unescape_name(struct mailbox_list *list, const char *src)
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen{
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen char ns_sep = mail_namespace_get_sep(list->ns);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen char list_sep = mailbox_list_get_hierarchy_sep(list);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen string_t *dest = t_str_new(strlen(src));
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen unsigned int num;
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen if (strncmp(src, list->ns->prefix, list->ns->prefix_len) == 0) {
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen str_append_n(dest, src, list->ns->prefix_len);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen src += list->ns->prefix_len;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen }
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen for (; *src != '\0'; src++) {
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen if (*src == list->set.escape_char &&
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen i_isxdigit(src[1]) && i_isxdigit(src[2])) {
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen if (src[1] >= '0' && src[1] <= '9')
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen num = src[1] - '0';
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen else
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen num = i_toupper(src[1]) - 'A' + 10;
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen num *= 16;
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen if (src[2] >= '0' && src[2] <= '9')
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen num += src[2] - '0';
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen else
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen num += i_toupper(src[2]) - 'A' + 10;
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen str_append_c(dest, num);
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen src += 2;
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen } else if (*src == list_sep)
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen str_append_c(dest, ns_sep);
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen else
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen str_append_c(dest, *src);
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen }
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen return str_c(dest);
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen}
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainenstatic void
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainenmailbox_list_escape_broken_chars(struct mailbox_list *list, string_t *str)
d979c1179d55ad86e40f869e48ef3e4db9c817b5Timo Sirainen{
d979c1179d55ad86e40f869e48ef3e4db9c817b5Timo Sirainen unsigned int i;
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen char buf[3];
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen if (strchr(str_c(str), list->set.broken_char) == NULL)
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen return;
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen
a385399497bdb50d4dfce729ffc852b75ed46a36Timo Sirainen for (i = 0; i < str_len(str); i++) {
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen if (str_c(str)[i] == list->set.broken_char) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_snprintf(buf, sizeof(buf), "%02x",
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen list->set.broken_char);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen str_insert(str, i+1, buf);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i += 2;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenstatic void
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenmailbox_list_escape_broken_name(struct mailbox_list *list,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const char *vname, string_t *str)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen str_truncate(str, 0);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen for (; *vname != '\0'; vname++) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (*vname == '&' || (unsigned char)*vname >= 0x80) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen str_printfa(str, "%c%02x", list->set.broken_char,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen *vname);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen } else {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen str_append_c(str, *vname);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenconst char *mailbox_list_default_get_vname(struct mailbox_list *list,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const char *storage_name)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen unsigned int i, prefix_len, name_len;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const char *vname = storage_name;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen char list_sep, ns_sep, *ret;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
cc7e2c3903f8fadb7474f09665b2280463850bebTimo Sirainen if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen strcmp(vname, "INBOX") == 0 &&
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen list->ns->user == list->ns->owner) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* user's INBOX - use as-is. NOTE: don't do case-insensitive
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen comparison, otherwise we can't differentiate between INBOX
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen and <ns prefix>/inBox. */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return vname;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (strcmp(vname, "INBOX") == 0 &&
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen !list->mail_set->mail_shared_explicit_inbox) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* convert to shared/$user, we don't really care about the
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen INBOX suffix here. */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen vname = "";
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (*vname == '\0') {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* return namespace prefix without the separator */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (list->ns->prefix_len == 0)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return list->ns->prefix;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen else {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return t_strndup(list->ns->prefix,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen list->ns->prefix_len - 1);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen } else if (!list->set.utf8) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* mUTF-7 -> UTF-8 conversion */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen string_t *str = t_str_new(strlen(vname));
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (imap_utf7_to_utf8(vname, str) == 0) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (list->set.broken_char != '\0')
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen mailbox_list_escape_broken_chars(list, str);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen vname = str_c(str);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen } else if (list->set.broken_char != '\0') {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen mailbox_list_escape_broken_name(list, vname, str);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen vname = str_c(str);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen prefix_len = strlen(list->ns->prefix);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen list_sep = mailbox_list_get_hierarchy_sep(list);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ns_sep = mail_namespace_get_sep(list->ns);
cc7e2c3903f8fadb7474f09665b2280463850bebTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (list_sep != ns_sep || prefix_len > 0) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* @UNSAFE */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen name_len = strlen(vname);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ret = t_malloc(prefix_len + name_len + 1);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen memcpy(ret, list->ns->prefix, prefix_len);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen for (i = 0; i < name_len; i++) {
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen ret[i + prefix_len] =
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen vname[i] == list_sep ? ns_sep : vname[i];
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen }
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen ret[i + prefix_len] = '\0';
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen vname = ret;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen }
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (list->set.escape_char != '\0')
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen vname = mailbox_list_unescape_name(list, vname);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen return vname;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen}
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainenconst char *mailbox_list_get_vname(struct mailbox_list *list, const char *name)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen{
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen return list->v.get_vname(list, name);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen}
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainenvoid mailbox_list_destroy(struct mailbox_list **_list)
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen{
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen struct mailbox_list *list = *_list;
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen *_list = NULL;
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen i_free_and_null(list->error_string);
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen if (hash_table_is_created(list->guid_cache)) {
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen hash_table_destroy(&list->guid_cache);
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen pool_unref(&list->guid_cache_pool);
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen }
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen if (list->subscriptions != NULL)
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen mailbox_tree_deinit(&list->subscriptions);
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen if (list->changelog != NULL)
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen mailbox_log_free(&list->changelog);
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen list->v.deinit(list);
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen}
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainenconst char *mailbox_list_get_driver_name(const struct mailbox_list *list)
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen{
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen return list->name;
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen}
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainenenum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen{
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen return list->flags;
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstruct mail_namespace *
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainenmailbox_list_get_namespace(const struct mailbox_list *list)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return list->ns;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenstatic mode_t get_dir_mode(mode_t mode)
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen{
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen /* add the execute bit if either read or write bit is set */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if ((mode & 0600) != 0) mode |= 0100;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if ((mode & 0060) != 0) mode |= 0010;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if ((mode & 0006) != 0) mode |= 0001;
34f4c7610b846a945779b6be78d1ef575c7d0ca8Timo Sirainen return mode;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct mail_user *
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenmailbox_list_get_user(const struct mailbox_list *list)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return list->ns->user;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen}
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenint mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct mail_storage **storage_r)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen{
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if ((*list)->v.get_storage != NULL)
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen return (*list)->v.get_storage(list, vname, storage_r);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen else {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen *storage_r = (*list)->ns->storage;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen return 0;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen }
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen}
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainenvoid mailbox_list_get_closest_storage(struct mailbox_list *list,
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen struct mail_storage **storage)
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen{
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen *storage = list->ns->storage;
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen}
013c0431b58809e16fd0afea0429d0b7bb1ef8a1Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenchar mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return list->v.get_hierarchy_sep(list);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainenstatic void ATTR_NULL(2)
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainenmailbox_list_get_permissions_internal(struct mailbox_list *list,
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen const char *name,
8a13b020f90e080570658b18c042e7e352c8b14fTimo Sirainen struct mailbox_permissions *permissions_r)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen const char *path, *parent_name, *p;
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen struct stat st;
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen memset(permissions_r, 0, sizeof(*permissions_r));
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen /* use safe defaults */
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen permissions_r->file_uid = (uid_t)-1;
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen permissions_r->file_gid = (gid_t)-1;
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen permissions_r->file_create_mode = 0600;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen permissions_r->dir_create_mode = 0700;
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen permissions_r->file_create_gid = (gid_t)-1;
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen permissions_r->file_create_gid_origin = "defaults";
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen if (name != NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen &path) < 0)
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen name = NULL;
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (name == NULL) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen &path);
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen }
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 (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=%ld", list->ns->prefix,
path != NULL ? path : "",
(int)permissions_r->dir_create_mode,
permissions_r->file_create_gid == (gid_t)-1 ? -1L :
(long)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 int
mailbox_list_stat_parent(const char *path, const char **root_dir_r,
struct stat *st_r, const char **error_r)
{
const char *p;
while (stat(path, st_r) < 0) {
if (errno != ENOENT || strcmp(path, "/") == 0) {
*error_r = t_strdup_printf("stat(%s) failed: %m", path);
return -1;
}
p = strrchr(path, '/');
if (p == NULL)
path = "/";
else
path = t_strdup_until(path, p);
}
*root_dir_r = path;
return 0;
}
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;
/* 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 == NULL)
expanded = "";
else {
while (p != unexpanded && *p != '/') p--;
if (p == unexpanded)
expanded = "";
else {
if (!mailbox_list_get_root_path(list, type, &expanded))
i_unreached();
expanded = get_expanded_path(unexpanded, p, expanded);
}
}
if (*expanded != '\0') {
/* up to this directory get the permissions from the first
parent directory that exists, if it has setgid bit
enabled. */
if (mailbox_list_stat_parent(expanded, &root_dir, &st,
error_r) < 0)
return -1;
if ((st.st_mode & S_ISGID) != 0 && root_dir != expanded) {
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;
}
}
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;
}
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. */
allow_internal_dirs = list->v.is_internal_name == NULL ||
*list->set.maildir_name != '\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.dir_create_mode,
perm.file_create_gid,
perm.file_create_gid_origin);
return TRUE;
}
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;
const char *root_dir, *index_dir;
time_t stamp;
if (!mailbox_list_init_changelog(list) ||
guid_128_is_empty(mailbox_guid))
return;
if (!list->index_root_dir_created) {
/* if index root dir hasn't been created yet, do it now */
if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
&index_dir) <= 0)
return;
if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&root_dir) <= 0 ||
strcmp(root_dir, index_dir) != 0) {
if (mailbox_list_mkdir_root(list, index_dir,
MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
return;
}
list->index_root_dir_created = TRUE;
}
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)
{
guid_128_t guid;
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;
/* subscriptions are about names, not about mailboxes. it's possible
to have a subscription to nonexistent mailbox. renames also don't
change subscriptions. so instead of using actual GUIDs, we'll use
hash of the name. */
mailbox_name_get_sha128(name, guid);
mailbox_list_add_change(list, set ? MAILBOX_LOG_RECORD_SUBSCRIBE :
MAILBOX_LOG_RECORD_UNSUBSCRIBE, guid);
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;
}