mailbox-list.c revision 8a0ad174adb1eb5108511b90e97f4e5f9089b0ee
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "lib.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "array.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "ioloop.h"
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen#include "home-expand.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "unlink-directory.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "imap-match.h"
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include "mailbox-tree.h"
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include "mailbox-list-private.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <time.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <unistd.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <dirent.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen#include <sys/stat.h>
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen then start renaming them to larger names from end to beginning, which
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen eventually would start causing the failures when trying to use too
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen long mailbox names. */
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define MAILBOX_MAX_HIERARCHY_LEVELS 20
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen/* Message to show to users when critical error occurs */
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen#define CRITICAL_MSG \
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen "Internal error occurred. Refer to server log for more information."
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
356303df200c991580bd24041996a070ad08c05eTimo Sirainenvoid (*hook_mailbox_list_created)(struct mailbox_list *list);
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainenstatic ARRAY_DEFINE(mailbox_list_drivers, const struct mailbox_list *);
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainenvoid mailbox_lists_init(void)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen i_array_init(&mailbox_list_drivers, 4);
d9b73f3eb3d5f608dfd438cee89ce2b27547173fTimo Sirainen}
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainenvoid mailbox_lists_deinit(void)
00bde9ae9eab9e720462bf6ec9a4dd85e88c3bbfTimo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen array_free(&mailbox_list_drivers);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen}
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const struct mailbox_list *const *drivers;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen unsigned int i, count;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen for (i = 0; i < count; i++) {
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen *idx_r = i;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return TRUE;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return FALSE;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen}
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen{
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen unsigned int idx;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen list->name);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen array_append(&mailbox_list_drivers, &list, 1);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen}
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen{
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen unsigned int idx;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen list->name);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen array_delete(&mailbox_list_drivers, idx, 1);
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen}
22535a9e685e29214082878e37a267157044618eTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainenint mailbox_list_alloc(const char *driver, struct mailbox_list **list_r,
22535a9e685e29214082878e37a267157044618eTimo Sirainen const char **error_r)
22535a9e685e29214082878e37a267157044618eTimo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const struct mailbox_list *const *class_p;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen struct mailbox_list *list;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen unsigned int idx;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
356303df200c991580bd24041996a070ad08c05eTimo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen *error_r = t_strdup_printf("Unknown mailbox list driver: %s",
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen driver);
22535a9e685e29214082878e37a267157044618eTimo Sirainen return -1;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen list = *list_r = (*class_p)->v.alloc();
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return 0;
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen}
e15f1d736c225c7ce6f3d08a37c1b2ae66b57c50Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainenstatic const char *fix_path(const char *path)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
356303df200c991580bd24041996a070ad08c05eTimo Sirainen size_t len = strlen(path);
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (len > 1 && path[len-1] == '/')
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen path = t_strndup(path, len-1);
356303df200c991580bd24041996a070ad08c05eTimo Sirainen return home_expand(path);
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen}
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainenint mailbox_list_settings_parse(const char *data,
22535a9e685e29214082878e37a267157044618eTimo Sirainen struct mailbox_list_settings *set,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const char **layout, const char **alt_dir_r,
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const char **error_r)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen{
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen const char *const *tmp, *key, *value;
22535a9e685e29214082878e37a267157044618eTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen i_assert(*data != '\0');
22535a9e685e29214082878e37a267157044618eTimo Sirainen
22535a9e685e29214082878e37a267157044618eTimo Sirainen *error_r = NULL;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (alt_dir_r != NULL)
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen *alt_dir_r = NULL;
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen /* <root dir> */
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen tmp = t_strsplit(data, ":");
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen set->root_dir = fix_path(*tmp);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen tmp++;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen for (; *tmp != NULL; tmp++) {
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen value = strchr(*tmp, '=');
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen if (value == NULL) {
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen key = *tmp;
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen value = "";
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen } else {
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen key = t_strdup_until(*tmp, value);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen value++;
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen }
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen if (strcmp(key, "INBOX") == 0)
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen set->inbox_path = fix_path(value);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "INDEX") == 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen set->index_dir = fix_path(value);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "CONTROL") == 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen set->control_dir = fix_path(value);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "ALT") == 0 && alt_dir_r != NULL)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen *alt_dir_r = fix_path(value);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "LAYOUT") == 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen *layout = value;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "SUBSCRIPTIONS") == 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen set->subscription_fname = fix_path(value);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else if (strcmp(key, "DIRNAME") == 0)
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen set->maildir_name = value;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen else {
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen return -1;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen }
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen if (set->index_dir != NULL && strcmp(set->index_dir, "MEMORY") == 0)
dd93aba1901a457346990f49c54a738947dc7128Timo Sirainen set->index_dir = "";
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen return 0;
806cb455553b71934314da98f1b4a694a3aa152eTimo Sirainen}
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainenvoid mailbox_list_init(struct mailbox_list *list, struct mail_namespace *ns,
b42f37ae6f65ed986315b6885568d32115e589b1Timo Sirainen const struct mailbox_list_settings *set,
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen enum mailbox_list_flags flags)
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen{
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0');
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen i_assert(set->subscription_fname == NULL ||
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen *set->subscription_fname != '\0');
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen list->ns = ns;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen list->flags = flags;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen list->file_create_mode = (mode_t)-1;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen list->file_create_gid = (gid_t)-1;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen /* copy settings */
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen list->set.root_dir = p_strdup(list->pool, set->root_dir);
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen list->set.index_dir = set->index_dir == NULL ||
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
d5abbb932a0a598f002da39a8b3326643b1b5efcTimo Sirainen p_strdup(list->pool, set->index_dir);
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen 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 = p_strdup(list->pool, set->maildir_name);
list->set.mail_storage_flags = set->mail_storage_flags;
list->set.lock_method = set->lock_method;
if ((flags & MAILBOX_LIST_FLAG_DEBUG) != 0) {
i_info("%s: root=%s, index=%s, control=%s, inbox=%s",
list->name, 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(struct mailbox_list *list)
{
return list->name;
}
char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
{
return list->hierarchy_sep;
}
enum mailbox_list_flags mailbox_list_get_flags(struct mailbox_list *list)
{
return list->flags;
}
struct mail_namespace *mailbox_list_get_namespace(struct mailbox_list *list)
{
return list->ns;
}
void mailbox_list_get_permissions(struct mailbox_list *list,
mode_t *mode_r, gid_t *gid_r)
{
const char *path;
struct stat st;
if (list->file_create_mode != (mode_t)-1) {
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
return;
}
path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
if (stat(path, &st) < 0) {
if (!ENOTFOUND(errno)) {
mailbox_list_set_critical(list, "stat(%s) failed: %m",
path);
}
/* return safe defaults */
*mode_r = 0600;
*gid_r = (gid_t)-1;
return;
}
list->file_create_mode = st.st_mode & 0666;
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 & 0060) == 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;
}
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
}
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)
{
return 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);
}
const struct mailbox_info *
mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
{
return ctx->list->v.iter_next(ctx);
}
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_NOTPOSSIBLE,
"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, index_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_iterate_context *ctx,
struct mailbox_tree_context *tree_ctx,
struct imap_match_glob *glob, bool update_only,
bool match_parents, const char *name)
{
struct mail_namespace *ns = ctx->list->ns;
struct mailbox_node *node;
enum mailbox_info_flags create_flags, always_flags;
enum imap_match_result match;
const char *p;
bool created, add_matched;
create_flags = (update_only ||
(ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) ?
(MAILBOX_NONEXISTENT | MAILBOX_NOCHILDREN) : 0;
always_flags = MAILBOX_SUBSCRIBED;
add_matched = TRUE;
for (;;) {
created = FALSE;
match = imap_match(glob, name);
if (match == IMAP_MATCH_YES) {
node = update_only ?
mailbox_tree_lookup(tree_ctx, name) :
mailbox_tree_get(tree_ctx, name, &created);
if (created) {
node->flags = create_flags;
if (create_flags != 0)
node_fix_parents(node);
}
if (node != NULL) {
if (!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 (!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 | MAILBOX_CHILD_SUBSCRIBED;
}
}
void mailbox_list_iter_update(struct mailbox_list_iterate_context *ctx,
struct mailbox_tree_context *tree_ctx,
struct imap_match_glob *glob, bool update_only,
bool match_parents, const char *name)
{
T_BEGIN {
mailbox_list_iter_update_real(ctx, tree_ctx, glob, update_only,
match_parents, 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;
}
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), CRITICAL_MSG_STAMP, tm) > 0 ?
i_strdup(str) : i_strdup(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;
}