mailbox-list.c revision ff26037994e5d77049aba7434dbe138f6474504c
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "lib.h"
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen#include "array.h"
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen#include "ioloop.h"
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen#include "str.h"
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen#include "home-expand.h"
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen#include "unlink-directory.h"
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen#include "imap-match.h"
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen#include "imap-utf7.h"
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include "mailbox-tree.h"
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include "mailbox-list-private.h"
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include <time.h>
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include <unistd.h>
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include <dirent.h>
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen#include <sys/stat.h>
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen then start renaming them to larger names from end to beginning, which
22535a9e685e29214082878e37a267157044618eTimo Sirainen eventually would start causing the failures when trying to use too
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen long mailbox names. */
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen#define MAILBOX_MAX_HIERARCHY_LEVELS 20
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainenstruct ns_list_iterate_context {
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen struct mailbox_list_iterate_context ctx;
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen struct mailbox_list_iterate_context *backend_ctx;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen struct mail_namespace *namespaces;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen pool_t pool;
3cf67672fdc87583cb23ce088c95bb5dee60e74dTimo Sirainen const char **patterns;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen};
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
e48d89622047bd8bbd0475b881ca9377d592f535Timo Sirainen
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainenvoid (*hook_mailbox_list_created)(struct mailbox_list *list);
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainenstatic ARRAY_DEFINE(mailbox_list_drivers, const struct mailbox_list *);
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainenvoid mailbox_lists_init(void)
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen{
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen i_array_init(&mailbox_list_drivers, 4);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen}
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenvoid mailbox_lists_deinit(void)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen{
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen array_free(&mailbox_list_drivers);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen}
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen{
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen const struct mailbox_list *const *drivers;
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen unsigned int i, count;
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen for (i = 0; i < count; i++) {
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen *idx_r = i;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen return TRUE;
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen }
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen }
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen return FALSE;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen}
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen unsigned int idx;
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen
12aad74464367f7e8be11eafe1af985bf7b1adecTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
e48d89622047bd8bbd0475b881ca9377d592f535Timo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
04052d7cacaa866a3f00afb4e104fa46c04c1dd7Timo Sirainen list->name);
f3d506e525a720f214020ca0f989a1966b30edaeTimo Sirainen }
849969f639a00eab26791db3cb1b66430420c0cdTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen}
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen{
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen unsigned int idx;
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
43a66a0b16299bd4f7615acd85e98bd3832c54d5Timo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen list->name);
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen array_delete(&mailbox_list_drivers, idx, 1);
}
int mailbox_list_alloc(const char *driver, struct mailbox_list **list_r,
const char **error_r)
{
const struct mailbox_list *const *class_p;
struct mailbox_list *list;
unsigned int idx;
if (!mailbox_list_driver_find(driver, &idx)) {
*error_r = t_strdup_printf("Unknown mailbox list driver: %s",
driver);
return -1;
}
class_p = array_idx(&mailbox_list_drivers, idx);
list = *list_r = (*class_p)->v.alloc();
array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
return 0;
}
static int fix_path(struct mail_namespace *ns, const char *path,
const char **path_r)
{
size_t len = strlen(path);
if (len > 1 && path[len-1] == '/')
path = t_strndup(path, len-1);
if (mail_user_try_home_expand(ns->user, &path) < 0)
return -1;
*path_r = path;
return 0;
}
int mailbox_list_settings_parse(const char *data,
struct mailbox_list_settings *set,
struct mail_namespace *ns,
const char **layout, const char **alt_dir_r,
const char **error_r)
{
const char *const *tmp, *key, *value, **dest;
i_assert(*data != '\0');
*error_r = NULL;
if (alt_dir_r != NULL)
*alt_dir_r = NULL;
/* <root dir> */
tmp = t_strsplit(data, ":");
if (fix_path(ns, *tmp, &set->root_dir) < 0) {
*error_r = t_strdup_printf(
"Home directory not set, can't expand ~/ for "
"mail root dir in: %s", data);
return -1;
}
tmp++;
for (; *tmp != NULL; tmp++) {
value = strchr(*tmp, '=');
if (value == NULL) {
key = *tmp;
value = "";
} else {
key = t_strdup_until(*tmp, value);
value++;
}
if (strcmp(key, "INBOX") == 0)
dest = &set->inbox_path;
else if (strcmp(key, "INDEX") == 0)
dest = &set->index_dir;
else if (strcmp(key, "CONTROL") == 0)
dest = &set->control_dir;
else if (strcmp(key, "ALT") == 0 && alt_dir_r != NULL)
dest = alt_dir_r;
else if (strcmp(key, "LAYOUT") == 0)
dest = layout;
else if (strcmp(key, "SUBSCRIPTIONS") == 0)
dest = &set->subscription_fname;
else if (strcmp(key, "DIRNAME") == 0)
dest = &set->maildir_name;
else if (strcmp(key, "MAILBOXDIR") == 0)
dest = &set->mailbox_dir_name;
else {
*error_r = t_strdup_printf("Unknown setting: %s", key);
return -1;
}
if (fix_path(ns, value, dest) < 0) {
*error_r = t_strdup_printf(
"Home directory not set, can't expand ~/ for "
"%s in: %s", key, data);
return -1;
}
}
if (set->index_dir != NULL && strcmp(set->index_dir, "MEMORY") == 0)
set->index_dir = "";
return 0;
}
void mailbox_list_init(struct mailbox_list *list, struct mail_namespace *ns,
const struct mailbox_list_settings *set,
enum mailbox_list_flags flags)
{
i_assert(set->root_dir == NULL || *set->root_dir != '\0');
i_assert(set->subscription_fname == NULL ||
*set->subscription_fname != '\0');
list->ns = ns;
list->mail_set = ns->mail_set;
list->flags = flags;
list->file_create_mode = (mode_t)-1;
list->dir_create_mode = (mode_t)-1;
list->file_create_gid = (gid_t)-1;
/* copy settings */
list->set.root_dir = p_strdup(list->pool, set->root_dir);
list->set.index_dir = set->index_dir == NULL ||
strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
p_strdup(list->pool, set->index_dir);
list->set.control_dir = set->control_dir == NULL ||
strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
p_strdup(list->pool, set->control_dir);
list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
list->set.subscription_fname =
p_strdup(list->pool, set->subscription_fname);
list->set.maildir_name =
(list->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 ? "" :
p_strdup(list->pool, set->maildir_name);
list->set.mailbox_dir_name =
p_strdup(list->pool, set->mailbox_dir_name);
if (set->mailbox_dir_name == NULL || *set->mailbox_dir_name == '\0')
list->set.mailbox_dir_name = "";
else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
list->set.mailbox_dir_name =
p_strdup(list->pool, set->mailbox_dir_name);
} else {
list->set.mailbox_dir_name =
p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
}
list->set.mail_storage_flags = set->mail_storage_flags;
list->set.lock_method = set->lock_method;
if (ns->mail_set->mail_debug) {
i_info("%s: root=%s, index=%s, control=%s, inbox=%s",
list->name,
list->set.root_dir == NULL ? "" : list->set.root_dir,
list->set.index_dir == NULL ? "" : list->set.index_dir,
list->set.control_dir == NULL ?
"" : list->set.control_dir,
list->set.inbox_path == NULL ?
"" : list->set.inbox_path);
}
if (hook_mailbox_list_created != NULL)
hook_mailbox_list_created(list);
list->set.mail_storage_flags = NULL;
list->set.lock_method = NULL;
}
void mailbox_list_deinit(struct mailbox_list *list)
{
i_free_and_null(list->error_string);
list->v.deinit(list);
}
const char *mailbox_list_get_driver_name(const struct mailbox_list *list)
{
return list->name;
}
char mailbox_list_get_hierarchy_sep(const struct mailbox_list *list)
{
return list->hierarchy_sep;
}
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;
}
void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
mode_t *mode_r, gid_t *gid_r)
{
const char *path;
struct stat st;
if (list->file_create_mode != (mode_t)-1 && name == NULL) {
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
return;
}
path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
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_info("Namespace %s: Permission lookup failed from %s",
list->ns->prefix, path);
}
/* return safe defaults */
list->file_create_mode = 0600;
list->dir_create_mode = 0700;
list->file_create_gid = (gid_t)-1;
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
return;
}
list->file_create_mode = st.st_mode & 0666;
list->dir_create_mode = st.st_mode & 0777;
if (!S_ISDIR(st.st_mode)) {
/* we're getting permissions from a file.
apply +x modes as necessary. */
list->dir_create_mode = get_dir_mode(list->dir_create_mode);
}
if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
/* directory's GID is used automatically for new files */
list->file_create_gid = (gid_t)-1;
} else if ((st.st_mode & 0070) == 0) {
/* group doesn't have any permissions, so don't bother
changing it */
list->file_create_gid = (gid_t)-1;
} else if (getegid() == st.st_gid) {
/* using our own gid, no need to change it */
list->file_create_gid = (gid_t)-1;
} else {
list->file_create_gid = st.st_gid;
}
if (list->mail_set->mail_debug && name == NULL) {
i_info("Namespace %s: Using permissions from %s: "
"mode=0%o gid=%ld", list->ns->prefix, path,
(int)list->dir_create_mode,
list->file_create_gid == (gid_t)-1 ? -1L :
(long)list->file_create_gid);
}
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
}
void mailbox_list_get_dir_permissions(struct mailbox_list *list,
const char *name,
mode_t *mode_r, gid_t *gid_r)
{
mode_t mode;
mailbox_list_get_permissions(list, name, &mode, gid_r);
*mode_r = list->dir_create_mode;
}
bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
const char *pattern)
{
return list->v.is_valid_pattern(list, pattern);
}
bool mailbox_list_is_valid_existing_name(struct mailbox_list *list,
const char *name)
{
return list->v.is_valid_existing_name(list, name);
}
bool mailbox_list_is_valid_create_name(struct mailbox_list *list,
const char *name)
{
int ret;
T_BEGIN {
string_t *str = t_str_new(256);
ret = imap_utf7_to_utf8(name, str);
} T_END;
return ret < 0 ? FALSE :
list->v.is_valid_create_name(list, name);
}
const char *mailbox_list_get_path(struct mailbox_list *list, const char *name,
enum mailbox_list_path_type type)
{
mailbox_list_clear_error(list);
return list->v.get_path(list, name, type);
}
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_list_get_mailbox_name_status(struct mailbox_list *list,
const char *name,
enum mailbox_name_status *status)
{
mailbox_list_clear_error(list);
if (!mailbox_list_is_valid_existing_name(list, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
return list->v.get_mailbox_name_status(list, name, status);
}
struct mailbox_list_iterate_context *
mailbox_list_iter_init(struct mailbox_list *list, const char *pattern,
enum mailbox_list_iter_flags flags)
{
const char *patterns[2];
patterns[0] = pattern;
patterns[1] = NULL;
return mailbox_list_iter_init_multiple(list, patterns, flags);
}
struct mailbox_list_iterate_context *
mailbox_list_iter_init_multiple(struct mailbox_list *list,
const char *const *patterns,
enum mailbox_list_iter_flags flags)
{
i_assert(*patterns != NULL);
mailbox_list_clear_error(list);
return list->v.iter_init(list, patterns, flags);
}
static const struct mailbox_info *
mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx)
{
struct ns_list_iterate_context *ctx =
(struct ns_list_iterate_context *)_ctx;
const struct mailbox_info *info;
info = mailbox_list_iter_next(ctx->backend_ctx);
if (info == NULL && ctx->namespaces != NULL) {
/* go to the next namespace */
if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
_ctx->failed = TRUE;
ctx->ctx.list->ns = ctx->namespaces;
ctx->backend_ctx =
mailbox_list_iter_init_multiple(ctx->namespaces->list,
ctx->patterns,
_ctx->flags);
ctx->namespaces = ctx->namespaces->next;
return mailbox_list_ns_iter_next(_ctx);
}
return info;
}
static int
mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx)
{
struct ns_list_iterate_context *ctx =
(struct ns_list_iterate_context *)_ctx;
int ret;
if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
_ctx->failed = TRUE;
ret = _ctx->failed ? -1 : 0;
pool_unref(&ctx->pool);
return ret;
}
struct mailbox_list_iterate_context *
mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
const char *const *patterns,
enum mailbox_list_iter_flags flags)
{
struct ns_list_iterate_context *ctx;
unsigned int i, count;
pool_t pool;
i_assert(namespaces != NULL);
pool = pool_alloconly_create("mailbox list namespaces", 256);
ctx = p_new(pool, struct ns_list_iterate_context, 1);
ctx->pool = pool;
ctx->ctx.flags = flags;
ctx->ctx.list = p_new(pool, struct mailbox_list, 1);
ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next;
ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit;
count = str_array_length(patterns);
ctx->patterns = p_new(pool, const char *, count + 1);
for (i = 0; i < count; i++)
ctx->patterns[i] = p_strdup(pool, patterns[i]);
ctx->ctx.list->ns = namespaces;
ctx->backend_ctx = mailbox_list_iter_init_multiple(namespaces->list,
patterns, flags);
ctx->namespaces = namespaces->next;
return &ctx->ctx;
}
const struct mailbox_info *
mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
{
const struct mailbox_info *info;
info = ctx->list->v.iter_next(ctx);
if (info != NULL)
ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
return info;
}
int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
{
struct mailbox_list_iterate_context *ctx = *_ctx;
*_ctx = NULL;
return ctx->list->v.iter_deinit(ctx);
}
int mailbox_list_set_subscribed(struct mailbox_list *list,
const char *name, bool set)
{
mailbox_list_clear_error(list);
return list->v.set_subscribed(list, name, set);
}
int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
if (!mailbox_list_is_valid_existing_name(list, name)) {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
if (strcmp(name, "INBOX") == 0 &&
(list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
"INBOX can't be deleted.");
return -1;
}
return list->v.delete_mailbox(list, name);
}
int mailbox_list_rename_mailbox(struct mailbox_list *list,
const char *oldname, const char *newname)
{
if (!mailbox_list_is_valid_existing_name(list, oldname) ||
!mailbox_list_is_valid_create_name(list, newname)) {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
return list->v.rename_mailbox(list, oldname, newname);
}
static int mailbox_list_try_delete(struct mailbox_list *list, const char *dir)
{
if (unlink_directory(dir, TRUE) == 0 || errno == ENOENT)
return 0;
if (errno == ENOTEMPTY) {
/* We're most likely using NFS and we can't delete
.nfs* files. */
mailbox_list_set_error(list, MAIL_ERROR_INUSE,
"Mailbox is still open in another session, "
"can't delete it.");
} else {
mailbox_list_set_critical(list,
"unlink_directory(%s) failed: %m", dir);
}
return -1;
}
int mailbox_list_delete_index_control(struct mailbox_list *list,
const char *name)
{
const char *path, *index_dir, *dir;
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
/* delete the index directory first, so that if we crash we don't
leave indexes for deleted mailboxes lying around */
index_dir = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_INDEX);
if (*index_dir != '\0' && strcmp(index_dir, path) != 0) {
if (mailbox_list_try_delete(list, index_dir) < 0)
return -1;
}
/* control directory next */
dir = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL);
if (*dir != '\0' && strcmp(dir, path) != 0 &&
strcmp(dir, index_dir) != 0) {
if (mailbox_list_try_delete(list, dir) < 0)
return -1;
}
return 0;
}
static void node_fix_parents(struct mailbox_node *node)
{
/* If we happened to create any of the parents, we need to mark them
nonexistent. */
node = node->parent;
for (; node != NULL; node = node->parent) {
if ((node->flags & MAILBOX_MATCHED) == 0)
node->flags |= MAILBOX_NONEXISTENT;
}
}
static void
mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx,
const char *name)
{
struct mail_namespace *ns = ctx->iter_ctx->list->ns;
struct mailbox_node *node;
enum mailbox_info_flags create_flags = 0, always_flags;
enum imap_match_result match;
const char *p;
bool created, add_matched;
if (ctx->update_only ||
(ctx->iter_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0)
create_flags = MAILBOX_NONEXISTENT | MAILBOX_NOCHILDREN;
always_flags = ctx->leaf_flags;
add_matched = TRUE;
for (;;) {
created = FALSE;
match = imap_match(ctx->glob, name);
if (match == IMAP_MATCH_YES) {
node = ctx->update_only ?
mailbox_tree_lookup(ctx->tree_ctx, name) :
mailbox_tree_get(ctx->tree_ctx, name, &created);
if (created) {
node->flags = create_flags;
if (create_flags != 0)
node_fix_parents(node);
}
if (node != NULL) {
if (!ctx->update_only && add_matched)
node->flags |= MAILBOX_MATCHED;
node->flags |= always_flags;
}
/* We don't want to show the parent mailboxes unless
something else matches them, but if they are matched
we want to show them having child subscriptions */
add_matched = FALSE;
} else {
if ((match & IMAP_MATCH_PARENT) == 0)
break;
/* We've a (possibly) non-subscribed parent mailbox
which has a subscribed child mailbox. Make sure we
return the parent mailbox. */
}
if (!ctx->match_parents)
break;
/* see if parent matches */
p = strrchr(name, ns->sep);
if (p == NULL)
break;
name = t_strdup_until(name, p);
create_flags &= ~MAILBOX_NOCHILDREN;
always_flags = MAILBOX_CHILDREN | ctx->parent_flags;
}
}
void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx,
const char *name)
{
T_BEGIN {
mailbox_list_iter_update_real(ctx, name);
} T_END;
}
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;
}
bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
const char **name)
{
if (!list->mail_set->mail_full_filesystem_access)
return FALSE;
if (**name == '/')
return TRUE;
if (**name != '~')
return FALSE;
/* try to expand home directory */
if ((*name)[1] == '/') {
/* ~/dir - use the configured home directory */
if (mail_user_try_home_expand(list->ns->user, name) == 0)
return TRUE;
} else {
/* ~otheruser/dir - assume we're using system users */
if (home_try_expand(name) == 0)
return TRUE;
}
/* fallback to using ~dir */
return FALSE;
}
const char *mailbox_list_get_last_error(struct mailbox_list *list,
enum mail_error *error_r)
{
*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)
{
struct tm *tm;
char str[256];
tm = localtime(&ioloop_time);
i_free(list->error_string);
list->error_string =
strftime(str, sizeof(str),
MAIL_ERRSTR_CRITICAL_MSG_STAMP, tm) > 0 ?
i_strdup(str) : i_strdup(MAIL_ERRSTR_CRITICAL_MSG);
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;
}