mailbox-list.c revision c58c12049c883b281c088d47a2a7278c21c390e1
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "lib.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "array.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "ioloop.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "mkdir-parents.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "str.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "sha1.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "home-expand.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "close-keep-errno.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "eacces-error.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "read-full.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "write-full.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "safe-mkstemp.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "unlink-directory.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "settings-parser.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "imap-match.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "imap-utf7.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "mailbox-log.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "mailbox-tree.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "mail-storage-private.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include "mailbox-list-private.h"
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include <time.h>
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include <unistd.h>
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include <dirent.h>
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#include <sys/stat.h>
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen/* 20 * (200+1) < 4096 which is the standard PATH_MAX. Having these settings
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen prevents malicious user from creating eg. "a/a/a/.../a" mailbox name and
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen then start renaming them to larger names from end to beginning, which
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen eventually would start causing the failures when trying to use too
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen long mailbox names. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#define MAILBOX_MAX_HIERARCHY_LEVELS 20
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 200
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstruct ns_list_iterate_context {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list_iterate_context ctx;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list_iterate_context *backend_ctx;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mail_namespace *namespaces;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen pool_t pool;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char **patterns, **patterns_ns_match;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen enum namespace_type type_mask;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen};
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstruct mailbox_list_module_register mailbox_list_module_register = { 0 };
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic ARRAY_DEFINE(mailbox_list_drivers, const struct mailbox_list *);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenvoid mailbox_lists_init(void)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_array_init(&mailbox_list_drivers, 4);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenvoid mailbox_lists_deinit(void)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen array_free(&mailbox_list_drivers);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen const struct mailbox_list *const *drivers;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen unsigned int i, count;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen drivers = array_get(&mailbox_list_drivers, &count);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen for (i = 0; i < count; i++) {
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if (strcasecmp(drivers[i]->name, name) == 0) {
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen *idx_r = i;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return TRUE;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen }
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen }
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return FALSE;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenvoid mailbox_list_register(const struct mailbox_list *list)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen unsigned int idx;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if (mailbox_list_driver_find(list->name, &idx)) {
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen i_fatal("mailbox_list_register(%s): duplicate driver",
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen list->name);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen }
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen array_append(&mailbox_list_drivers, &list, 1);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenvoid mailbox_list_unregister(const struct mailbox_list *list)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen unsigned int idx;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if (!mailbox_list_driver_find(list->name, &idx)) {
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen i_fatal("mailbox_list_unregister(%s): unknown driver",
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen list->name);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen }
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen array_delete(&mailbox_list_drivers, idx, 1);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenconst struct mailbox_list *
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenmailbox_list_find_class(const char *driver)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen const struct mailbox_list *const *class_p;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen unsigned int idx;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if (!mailbox_list_driver_find(driver, &idx))
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return NULL;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return *class_p;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenint mailbox_list_create(const char *driver, struct mail_namespace *ns,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen const struct mailbox_list_settings *set,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen enum mailbox_list_flags flags, const char **error_r)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen const struct mailbox_list *const *class_p;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list *list;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen unsigned int idx;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(ns->list == NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(set->subscription_fname == NULL ||
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *set->subscription_fname != '\0');
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen if (!mailbox_list_driver_find(driver, &idx)) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = "Unknown driver name";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen class_p = array_idx(&mailbox_list_drivers, idx);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen set->maildir_name != NULL && *set->maildir_name != '\0') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = "maildir_name not supported by this driver";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen set->alt_dir != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = "alt_dir not supported by this driver";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen ((*class_p)->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list = (*class_p)->v.alloc();
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->ns = ns;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->mail_set = ns->mail_set;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->flags = flags;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->file_create_mode = (mode_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->dir_create_mode = (mode_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->file_create_gid = (gid_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->changelog_timestamp = (time_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
e869616aca1c2469c436e1e99dad93f7e4b2ae8aTimo Sirainen /* copy settings */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (set->root_dir != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.root_dir = p_strdup(list->pool, set->root_dir);
af5fc8ca268d69dd3ebbe0416ec6270f94121e39Timo Sirainen list->set.index_dir = set->index_dir == NULL ||
af5fc8ca268d69dd3ebbe0416ec6270f94121e39Timo Sirainen strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen p_strdup(list->pool, set->index_dir);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.control_dir = set->control_dir == NULL ||
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen p_strdup(list->pool, set->control_dir);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen }
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.subscription_fname =
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen p_strdup(list->pool, set->subscription_fname);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.maildir_name = set->maildir_name == NULL ? "" :
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen p_strdup(list->pool, set->maildir_name);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.mailbox_dir_name =
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
0482d891a6669537e10a1abc9866b45f2fc55fccTimo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (set->mailbox_dir_name == NULL || *set->mailbox_dir_name == '\0')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.mailbox_dir_name = "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.mailbox_dir_name =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen p_strdup(list->pool, set->mailbox_dir_name);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.mailbox_dir_name =
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
ad9afb64630511d5e25bc5bc11c5304986156928Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (ns->mail_set->mail_debug) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_debug("%s: root=%s, index=%s, control=%s, inbox=%s",
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->name,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.root_dir == NULL ? "" : list->set.root_dir,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->set.index_dir == NULL ? "" : list->set.index_dir,
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.control_dir == NULL ?
aab5704d21173f9f825c016812c8099d16ea5b9fTimo Sirainen "" : list->set.control_dir,
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->set.inbox_path == NULL ?
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen "" : list->set.inbox_path);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen }
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen mail_namespace_finish_list_init(ns, list);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
0a222deffd3f48e43aa61ceb73a4faeeeea5e12cTimo Sirainen hook_mailbox_list_created(list);
aab5704d21173f9f825c016812c8099d16ea5b9fTimo Sirainen return 0;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen}
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainenstatic int fix_path(struct mail_user *user, const char *path,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char **path_r, const char **error_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen size_t len = strlen(path);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (len > 1 && path[len-1] == '/')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen path = t_strndup(path, len-1);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* ~otheruser/dir */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (home_try_expand(&path) < 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = t_strconcat(
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen "No home directory for system user. "
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen "Can't expand ", t_strcut(path, '/'),
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen " for ", NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (mail_user_try_home_expand(user, &path) < 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = "Home directory not set for user. "
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen "Can't expand ~/ for ";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *path_r = path;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenstatic const char *split_next_arg(const char *const **_args)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char *const *args = *_args;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char *str = args[0];
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen args++;
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch while (*args != NULL && **args == '\0') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen args++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (*args == NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* string ends with ":", just ignore it. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen break;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen str = t_strconcat(str, ":", *args, NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen args++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *_args = args;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return str;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenint mailbox_list_settings_parse(struct mail_user *user, const char *data,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list_settings *set_r,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char **error_r)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen const char *const *tmp, *key, *value, **dest, *str, *error;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = NULL;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen memset(set_r, 0, sizeof(*set_r));
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (*data == '\0')
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return 0;
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* <root dir> */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen tmp = t_strsplit(data, ":");
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen str = split_next_arg(&tmp);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (fix_path(user, str, &set_r->root_dir, &error) < 0) {
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen *error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (strncmp(set_r->root_dir, "INBOX=", 6) == 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* probably mbox user trying to avoid root_dir */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = t_strconcat("Mail root directory not given: ",
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen data, NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return -1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen while (*tmp != NULL) {
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody str = split_next_arg(&tmp);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen value = strchr(str, '=');
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (value == NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen key = str;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen value = "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen key = t_strdup_until(str, value);
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen value++;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen }
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen if (strcmp(key, "INBOX") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dest = &set_r->inbox_path;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen else if (strcmp(key, "INDEX") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dest = &set_r->index_dir;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen else if (strcmp(key, "CONTROL") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dest = &set_r->control_dir;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen else if (strcmp(key, "ALT") == 0)
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen dest = &set_r->alt_dir;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen else if (strcmp(key, "LAYOUT") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dest = &set_r->layout;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else if (strcmp(key, "SUBSCRIPTIONS") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dest = &set_r->subscription_fname;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen else if (strcmp(key, "DIRNAME") == 0)
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen dest = &set_r->maildir_name;
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen else if (strcmp(key, "MAILBOXDIR") == 0)
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen dest = &set_r->mailbox_dir_name;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *error_r = t_strdup_printf("Unknown setting: %s", key);
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen return -1;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen }
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen if (fix_path(user, value, dest, &error) < 0) {
075081e25ef07989be10f7c9cf85f833f90be46fTimo Sirainen *error_r = t_strconcat(error, key, " in: ", data, NULL);
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen return -1;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen }
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen }
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen set_r->index_dir = "";
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen return 0;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipekconst char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen enum mailbox_list_path_type type)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const struct mail_storage_settings *mail_set;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char *location = list->ns->unexpanded_set->location;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mail_user *user = list->ns->user;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list_settings set;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen const char *p, *error;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0]) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* set using -o or userdb lookup. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen location++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (*location == '\0') {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen mail_set = mail_user_set_get_driver_settings(user->set_info,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(mail_set != NULL);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen location = mail_set->mail_location;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (*location == SETTING_STRVAR_EXPANDED[0])
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen location++;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* type:settings */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen p = strchr(location, ':');
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (p == NULL)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (mailbox_list_settings_parse(user, p + 1, &set, &error) < 0)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return "";
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen return mailbox_list_get_root_path(&set, type);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen}
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainenvoid mailbox_list_destroy(struct mailbox_list **_list)
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen{
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen struct mailbox_list *list = *_list;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen *_list = NULL;
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek i_free_and_null(list->error_string);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if (list->changelog != NULL)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen mailbox_log_free(&list->changelog);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen list->v.deinit(list);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenconst char *mailbox_list_get_driver_name(const struct mailbox_list *list)
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return list->name;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenenum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return list->flags;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenstruct mail_namespace *
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenmailbox_list_get_namespace(const struct mailbox_list *list)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return list->ns;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen}
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainenstatic mode_t get_dir_mode(mode_t mode)
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen /* add the execute bit if either read or write bit is set */
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if ((mode & 0600) != 0) mode |= 0100;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if ((mode & 0060) != 0) mode |= 0010;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen if ((mode & 0006) != 0) mode |= 0001;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen return mode;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen}
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainenstruct mail_user *
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainenmailbox_list_get_user(const struct mailbox_list *list)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return list->ns->user;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenint mailbox_list_get_storage(struct mailbox_list **list, const char **name,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen struct mail_storage **storage_r)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen if ((*list)->v.get_storage != NULL)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return (*list)->v.get_storage(list, name, storage_r);
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen else {
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen *storage_r = (*list)->ns->storage;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen return 0;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen }
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen}
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenvoid mailbox_list_get_closest_storage(struct mailbox_list *list,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen struct mail_storage **storage)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen *storage = list->ns->storage;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen}
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenstatic void
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainenmailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen mode_t *file_mode_r, mode_t *dir_mode_r,
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen gid_t *gid_r, const char **gid_origin_r)
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen{
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen const char *path;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen struct stat st;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen /* use safe defaults */
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen *file_mode_r = 0600;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen *dir_mode_r = 0700;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen *gid_r = (gid_t)-1;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen *gid_origin_r = "defaults";
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen if (path == NULL) {
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen /* no filesystem support in storage */
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen } else if (stat(path, &st) < 0) {
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen if (!ENOTFOUND(errno)) {
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen mailbox_list_set_critical(list, "stat(%s) failed: %m",
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen path);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen } else if (list->mail_set->mail_debug) {
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen i_debug("Namespace %s: Permission lookup failed from %s",
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->ns->prefix, path);
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen }
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen if (name != NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* return defaults */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen mailbox_list_get_permissions_full(list, NULL,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen file_mode_r,
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen dir_mode_r, gid_r,
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen gid_origin_r);
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen return;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen }
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen } else {
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen *file_mode_r = (st.st_mode & 0666) | 0600;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen *dir_mode_r = (st.st_mode & 0777) | 0700;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen *gid_origin_r = path;
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen if (!S_ISDIR(st.st_mode)) {
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen /* we're getting permissions from a file.
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen apply +x modes as necessary. */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *dir_mode_r = get_dir_mode(*dir_mode_r);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* directory's GID is used automatically for new
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen files */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *gid_r = (gid_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* group has same permissions as world, so don't bother
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen changing it */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *gid_r = (gid_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else if (getegid() == st.st_gid) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen /* using our own gid, no need to change it */
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *gid_r = (gid_t)-1;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen } else {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen *gid_r = st.st_gid;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (name == NULL) {
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen list->file_create_mode = *file_mode_r;
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen list->dir_create_mode = *dir_mode_r;
108340ffacae3b7d09fa51b566d77aa51dd6483bTimo Sirainen list->file_create_gid = *gid_r;
9d0be57bcc352f776a327d7e2a6c6232137f4a04Timo Sirainen list->file_create_gid_origin =
24d6fd2b8e2c327ae2a20a0c4be8af2ef7b3b468Timo Sirainen p_strdup(list->pool, *gid_origin_r);
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen }
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen if (list->mail_set->mail_debug && name == NULL) {
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen i_debug("Namespace %s: Using permissions from %s: "
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen "mode=0%o gid=%ld", list->ns->prefix,
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen path != NULL ? path : "",
8aeae03f9f447c8a792b215c9fb954468053c556Timo Sirainen (int)list->dir_create_mode,
list->file_create_gid == (gid_t)-1 ? -1L :
(long)list->file_create_gid);
}
}
void mailbox_list_get_permissions(struct mailbox_list *list,
const char *name,
mode_t *mode_r, gid_t *gid_r,
const char **gid_origin_r)
{
mode_t dir_mode;
if (list->file_create_mode != (mode_t)-1 && name == NULL) {
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
*gid_origin_r = list->file_create_gid_origin;
return;
}
mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r,
gid_origin_r);
}
void mailbox_list_get_dir_permissions(struct mailbox_list *list,
const char *name,
mode_t *mode_r, gid_t *gid_r,
const char **gid_origin_r)
{
mode_t file_mode;
if (list->dir_create_mode != (mode_t)-1 && name == NULL) {
*mode_r = list->dir_create_mode;
*gid_r = list->file_create_gid;
*gid_origin_r = list->file_create_gid_origin;
return;
}
mailbox_list_get_permissions_full(list, name, &file_mode,
mode_r, gid_r, gid_origin_r);
}
static int
mailbox_list_stat_parent(struct mailbox_list *list, const char *path,
const char **root_dir_r, struct stat *st_r)
{
const char *p;
while (stat(path, st_r) < 0) {
if (errno != ENOENT || strcmp(path, "/") == 0) {
mailbox_list_set_critical(list, "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;
}
int mailbox_list_mkdir(struct mailbox_list *list, const char *path,
enum mailbox_list_path_type type)
{
const char *expanded, *unexpanded, *root_dir, *p, *origin;
struct stat st;
mode_t mode;
gid_t gid;
mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
/* 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 {
expanded = mailbox_list_get_path(list, NULL, type);
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(list, expanded,
&root_dir, &st) < 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) {
mailbox_list_set_critical(list,
"mkdir(%s) failed: %m", expanded);
return -1;
}
}
if (gid == (gid_t)-1 && (mode & S_ISGID) == 0) {
/* change the group for user directories */
gid = getegid();
}
}
/* the rest of the directories exist only for one user. create them
with default directory permissions */
if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 &&
errno != EEXIST) {
mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
return -1;
}
return 0;
}
bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
const char *pattern)
{
bool ret;
T_BEGIN {
ret = list->v.is_valid_pattern(list, pattern);
} T_END;
return ret;
}
bool mailbox_list_is_valid_existing_name(struct mailbox_list *list,
const char *name)
{
bool ret;
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;
}
T_BEGIN {
ret = list->v.is_valid_existing_name(list, name);
} T_END;
return ret;
}
bool mailbox_list_is_valid_create_name(struct mailbox_list *list,
const char *name)
{
const char *p;
int ret;
/* safer to just disallow all control characters */
for (p = name; *p != '\0'; p++) {
if (*p < ' ')
return FALSE;
}
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)
{
return list->v.get_path(list, name, type);
}
const char *
mailbox_list_get_root_path(const struct mailbox_list_settings *set,
enum mailbox_list_path_type type)
{
const char *path;
switch (type) {
case MAILBOX_LIST_PATH_TYPE_DIR:
return set->root_dir;
case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
return set->alt_dir;
case MAILBOX_LIST_PATH_TYPE_MAILBOX:
if (*set->mailbox_dir_name == '\0')
return set->root_dir;
path = t_strconcat(set->root_dir, "/",
set->mailbox_dir_name, NULL);
return t_strndup(path, strlen(path)-1);
case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
if (*set->mailbox_dir_name == '\0')
return set->root_dir;
path = t_strconcat(set->alt_dir, "/",
set->mailbox_dir_name, NULL);
return t_strndup(path, strlen(path)-1);
case MAILBOX_LIST_PATH_TYPE_CONTROL:
return set->control_dir != NULL ?
set->control_dir : set->root_dir;
case MAILBOX_LIST_PATH_TYPE_INDEX:
return set->index_dir != NULL ?
set->index_dir : set->root_dir;
}
i_unreached();
}
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)
{
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);
return list->v.iter_init(list, patterns, flags);
}
static bool
ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
{
if ((ctx->type_mask & ns->type) == 0)
return FALSE;
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) {
if (ns->alias_for != NULL)
return FALSE;
}
return TRUE;
}
static bool
ns_match_inbox(struct mail_namespace *ns, const char *pattern)
{
struct imap_match_glob *glob;
if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
return FALSE;
glob = imap_match_init(pool_datastack_create(), pattern,
TRUE, ns->sep);
return imap_match(glob, "INBOX") == IMAP_MATCH_YES;
}
static bool
ns_match_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns,
const char *pattern)
{
struct imap_match_glob *glob;
enum imap_match_result result;
const char *prefix_without_sep;
unsigned int len;
len = ns->prefix_len;
if (len > 0 && ns->prefix[len-1] == ns->sep)
len--;
if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
NAMESPACE_FLAG_LIST_CHILDREN)) == 0) {
/* non-listable namespace matches only with exact prefix */
if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0)
return FALSE;
}
prefix_without_sep = t_strndup(ns->prefix, len);
if (*prefix_without_sep == '\0')
result = IMAP_MATCH_CHILDREN;
else {
glob = imap_match_init(pool_datastack_create(), pattern,
TRUE, ns->sep);
result = imap_match(glob, prefix_without_sep);
}
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) {
switch (result) {
case IMAP_MATCH_YES:
case IMAP_MATCH_CHILDREN:
return TRUE;
case IMAP_MATCH_NO:
case IMAP_MATCH_PARENT:
break;
}
return FALSE;
}
switch (result) {
case IMAP_MATCH_YES:
/* allow matching prefix only when it's done without
wildcards */
if (strcmp(prefix_without_sep, pattern) == 0)
return TRUE;
break;
case IMAP_MATCH_CHILDREN: {
/* allow this only if there isn't another namespace
with longer prefix that matches this pattern
(namespaces are sorted by prefix length) */
struct mail_namespace *tmp;
T_BEGIN {
for (tmp = ns->next; tmp != NULL; tmp = tmp->next) {
if (ns_match_simple(ctx, tmp) &&
ns_match_next(ctx, tmp, pattern))
break;
}
} T_END;
if (tmp == NULL)
return TRUE;
break;
}
case IMAP_MATCH_NO:
case IMAP_MATCH_PARENT:
break;
}
return FALSE;
}
static bool
ns_match(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
{
unsigned int i;
if (!ns_match_simple(ctx, ns))
return FALSE;
/* filter out namespaces whose prefix doesn't match. this same code
handles both with and without STAR_WITHIN_NS, so the "without" case
is slower than necessary, but this shouldn't matter much */
T_BEGIN {
for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
if (ns_match_inbox(ns, ctx->patterns_ns_match[i]))
break;
if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i]))
break;
}
} T_END;
return ctx->patterns_ns_match[i] != NULL;
}
static struct mail_namespace *
ns_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
{
for (; ns != NULL; ns = ns->next) {
if (ns_match(ctx, ns))
break;
}
return ns;
}
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 = ctx->backend_ctx == NULL ? NULL :
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 = ns_next(ctx, 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 (ctx->backend_ctx != NULL) {
if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
_ctx->failed = TRUE;
}
ret = _ctx->failed ? -1 : 0;
pool_unref(&ctx->pool);
return ret;
}
static const char **
dup_patterns_without_stars(pool_t pool, const char *const *patterns,
unsigned int count)
{
const char **dup;
unsigned int i;
dup = p_new(pool, const char *, count + 1);
for (i = 0; i < count; i++) {
char *p = p_strdup(pool, patterns[i]);
dup[i] = p;
for (; *p != '\0'; p++) {
if (*p == '*')
*p = '%';
}
}
return dup;
}
struct mailbox_list_iterate_context *
mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
const char *const *patterns,
enum namespace_type type_mask,
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", 1024);
ctx = p_new(pool, struct ns_list_iterate_context, 1);
ctx->pool = pool;
ctx->type_mask = type_mask;
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]);
if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
/* create copies of patterns with '*' wildcard changed to '%' */
ctx->patterns_ns_match =
dup_patterns_without_stars(pool, ctx->patterns, count);
} else {
ctx->patterns_ns_match = ctx->patterns;
}
namespaces = ns_next(ctx, namespaces);
ctx->ctx.list->ns = namespaces;
if (namespaces != NULL) {
ctx->backend_ctx =
mailbox_list_iter_init_multiple(namespaces->list,
patterns, flags);
ctx->namespaces = ns_next(ctx, 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_mailbox(struct mailbox_list *list, const char *name,
enum mailbox_info_flags *flags_r)
{
const char *path, *fname, *rootdir, *dir, *inbox;
struct stat st;
unsigned int len;
rootdir = mailbox_list_get_path(list, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
if (rootdir == NULL) {
/* shouldn't happen with anything except shared mailboxes */
return 0;
}
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. */
inbox = mailbox_list_get_path(list, "INBOX",
MAILBOX_LIST_PATH_TYPE_MAILBOX);
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,
&st, flags_r);
}
static bool mailbox_list_init_changelog(struct mailbox_list *list)
{
const char *path;
mode_t mode;
gid_t gid;
const char *gid_origin;
if (list->changelog != NULL)
return TRUE;
/* don't do this in mailbox_list_create(), because _get_path() might be
overridden by storage (mbox). */
path = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX);
if (path == NULL)
return FALSE;
path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
list->changelog = mailbox_log_alloc(path);
mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
mailbox_log_set_permissions(list->changelog, mode, gid, gid_origin);
return TRUE;
}
void mailbox_list_add_change(struct mailbox_list *list,
enum mailbox_log_record_type type,
const uint8_t mailbox_guid[MAIL_GUID_128_SIZE])
{
struct mailbox_log_record rec;
time_t stamp;
if (!mailbox_list_init_changelog(list) ||
mail_guid_128_is_empty(mailbox_guid))
return;
if (!list->index_root_dir_created) {
if (mailbox_list_create_missing_index_dir(list, NULL) < 0)
return;
}
stamp = list->changelog_timestamp != (time_t)-1 ?
list->changelog_timestamp : ioloop_time;
memset(&rec, 0, sizeof(rec));
rec.type = type;
memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid));
mailbox_log_record_set_timestamp(&rec, stamp);
(void)mailbox_log_append(list->changelog, &rec);
}
int mailbox_list_set_subscribed(struct mailbox_list *list,
const char *name, bool set)
{
uint8_t guid[MAIL_GUID_128_SIZE];
int ret;
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_create_dir(struct mailbox_list *list, const char *name)
{
if (!mailbox_list_is_valid_create_name(list, name) || *name == '\0') {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
return list->v.create_mailbox_dir(list, name,
MAILBOX_DIR_CREATE_TYPE_ONLY_NOSELECT);
}
int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
{
if (!mailbox_list_is_valid_existing_name(list, name) || *name == '\0') {
mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
return list->v.delete_dir(list, name);
}
void mailbox_name_get_sha128(const char *name, uint8_t guid[MAIL_GUID_128_SIZE])
{
unsigned char sha[SHA1_RESULTLEN];
sha1_get_digest(name, strlen(name), sha);
memcpy(guid, sha, I_MIN(MAIL_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;
}
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;
}
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_path(list, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
len = strlen(root_dir);
if (strncmp(root_dir, *name, len) == 0 && (*name)[len] == '/') {
mailbox_name = *name + len + 1;
path = mailbox_list_get_path(list, mailbox_name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
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;
}
int mailbox_list_create_parent_dir(struct mailbox_list *list,
const char *mailbox, const char *path)
{
const char *p, *dir, *origin;
gid_t gid;
mode_t mode;
p = strrchr(path, '/');
if (p == NULL)
return 0;
dir = t_strdup_until(path, p);
mailbox_list_get_dir_permissions(list, mailbox, &mode, &gid, &origin);
if (mkdir_parents_chgrp(dir, mode, gid, origin) < 0 &&
errno != EEXIST) {
mailbox_list_set_critical(list, "mkdir_parents(%s) failed: %m",
dir);
return -1;
}
return 0;
}
int mailbox_list_create_missing_index_dir(struct mailbox_list *list,
const char *name)
{
const char *root_dir, *index_dir, *parent_dir, *p, *origin;
mode_t mode;
gid_t gid;
unsigned int n = 0;
list->index_root_dir_created = TRUE;
root_dir = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
index_dir = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_INDEX);
if (*index_dir == '\0' || strcmp(index_dir, root_dir) == 0)
return 0;
mailbox_list_get_dir_permissions(list, name, &mode, &gid, &origin);
while (mkdir_chgrp(index_dir, mode, gid, origin) < 0) {
if (errno == EEXIST)
break;
p = strrchr(index_dir, '/');
if (errno != ENOENT || p == NULL || ++n == 2) {
mailbox_list_set_critical(list,
"mkdir(%s) failed: %m", index_dir);
return -1;
}
/* create the parent directory first */
parent_dir = t_strdup_until(index_dir, p);
if (mailbox_list_mkdir(list, parent_dir,
MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
return -1;
}
return 0;
}
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)
{
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;
}