mailbox-list-fs.c revision c0a87e5f3316a57e6f915882fa1951d0fbb74a61
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "lib.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "hostpid.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mkdir-parents.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mailbox-log.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "subscription-file.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mail-storage.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mailbox-list-delete.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mailbox-list-fs.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include <stdio.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include <unistd.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include <sys/stat.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#define GLOBAL_TEMP_PREFIX ".temp."
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterextern struct mailbox_list fs_mailbox_list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic struct mailbox_list *fs_list_alloc(void)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster struct fs_mailbox_list *list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster pool_t pool;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster pool = pool_alloconly_create("fs list", 2048);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster list = p_new(pool, struct fs_mailbox_list, 1);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster list->list = fs_mailbox_list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster list->list.pool = pool;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster list->temp_prefix = p_strconcat(pool, GLOBAL_TEMP_PREFIX,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster my_hostname, ".", my_pid, ".", NULL);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return &list->list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic void fs_list_deinit(struct mailbox_list *_list)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster pool_unref(&list->list.pool);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic bool fs_list_is_valid_common(const char *name, size_t *len_r)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster *len_r = strlen(name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (name[0] == '\0' || name[*len_r-1] == '/')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return TRUE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic bool
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster bool ret, allow_internal_dirs;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* make sure it's not absolute path */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*name == '/' || *name == '~')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* make sure the mailbox name doesn't contain any foolishness:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "../" could give access outside the mailbox directory.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "./" and "//" could fool ACL checks. */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster allow_internal_dirs = list->v.is_internal_name == NULL ||
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster *list->set.maildir_name != '\0';
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster T_BEGIN {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster const char *const *names;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster names = t_strsplit(name, "/");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster for (; *names != NULL; names++) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster const char *n = *names;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*n == '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break; /* // */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*n == '.') {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (n[1] == '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break; /* ./ */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (n[1] == '.' && n[2] == '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break; /* ../ */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*list->set.maildir_name != '\0' &&
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster strcmp(list->set.maildir_name, n) == 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* don't allow maildir_name to be used as part
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster of the mailbox name */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (!allow_internal_dirs &&
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster list->v.is_internal_name(list, n))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = *names == NULL;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } T_END;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return ret;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic bool
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_is_valid_pattern(struct mailbox_list *list, const char *pattern)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (list->mail_set->mail_full_filesystem_access)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return TRUE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return fs_list_is_valid_common_nonfs(list, pattern);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic bool
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_is_valid_existing_name(struct mailbox_list *list, const char *name)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster size_t len;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (!fs_list_is_valid_common(name, &len))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (list->mail_set->mail_full_filesystem_access)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return TRUE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return fs_list_is_valid_common_nonfs(list, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic bool
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_is_valid_create_name(struct mailbox_list *list, const char *name)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster size_t len;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (!fs_list_is_valid_common(name, &len))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (list->mail_set->mail_full_filesystem_access)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return TRUE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (mailbox_list_name_is_too_large(name, '/'))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return FALSE;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return fs_list_is_valid_common_nonfs(list, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic char fs_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return '/';
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic const char *
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_list_get_path(struct mailbox_list *_list, const char *name,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster enum mailbox_list_path_type type)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster const struct mailbox_list_settings *set = &_list->set;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster const char *root_dir;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (name == NULL) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* return root directories */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return mailbox_list_get_root_path(set, type);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(mailbox_list_is_valid_pattern(_list, name));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (mailbox_list_try_get_absolute_path(_list, &name))
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return name;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster root_dir = set->root_dir;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster switch (type) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_DIR:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*set->maildir_name != '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s", set->root_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (set->alt_dir == NULL)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return NULL;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*set->maildir_name != '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s", set->alt_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster root_dir = set->alt_dir;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_MAILBOX:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (set->alt_dir == NULL)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return NULL;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster root_dir = set->alt_dir;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_CONTROL:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (set->control_dir != NULL)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s", set->control_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAILBOX_LIST_PATH_TYPE_INDEX:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (set->index_dir != NULL) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*set->index_dir == '\0')
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return "";
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s", set->index_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR ||
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* don't use inbox_path */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* If INBOX is a file, index and control directories are
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster located in root directory. */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster type == MAILBOX_LIST_PATH_TYPE_DIR)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return set->inbox_path;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*set->maildir_name == '\0') {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s", root_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return t_strdup_printf("%s/%s%s/%s", root_dir,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->mailbox_dir_name, name,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster set->maildir_name);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic const char *
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_list_get_temp_prefix(struct mailbox_list *_list, bool global)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return global ? GLOBAL_TEMP_PREFIX : list->temp_prefix;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic const char *
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterfs_list_join_refpattern(struct mailbox_list *_list ATTR_UNUSED,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster const char *ref, const char *pattern)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (*pattern == '/' || *pattern == '~') {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* pattern overrides reference */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (*ref != '\0') {
/* merge reference and pattern */
pattern = t_strconcat(ref, pattern, NULL);
}
return pattern;
}
static int fs_list_set_subscribed(struct mailbox_list *_list,
const char *name, bool set)
{
struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
const char *path;
path = t_strconcat(_list->set.control_dir != NULL ?
_list->set.control_dir : _list->set.root_dir,
"/", _list->set.subscription_fname, NULL);
return subsfile_set_subscribed(_list, path, list->temp_prefix,
name, set);
}
static int mailbox_is_selectable(struct mailbox_list *list, const char *name)
{
enum mailbox_info_flags flags;
if (mailbox_list_mailbox(list, name, &flags) < 0)
return -1;
return (flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0 ? 1 : 0;
}
static int
fs_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
enum mailbox_dir_create_type type)
{
const char *path, *gid_origin, *p;
mode_t mode;
gid_t gid;
bool directory, create_parent_dir;
int ret;
directory = type != MAILBOX_DIR_CREATE_TYPE_MAILBOX;
path = mailbox_list_get_path(list, name,
directory ? MAILBOX_LIST_PATH_TYPE_DIR :
MAILBOX_LIST_PATH_TYPE_MAILBOX);
create_parent_dir = !directory &&
(list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
if (create_parent_dir) {
/* we only need to make sure that the parent directory exists */
p = strrchr(path, '/');
if (p == NULL)
return 0;
path = t_strdup_until(path, p);
}
mailbox_list_get_root_dir_permissions(list, &mode, &gid, &gid_origin);
if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0)
return 0;
else if (errno == EEXIST) {
if (create_parent_dir)
return 0;
if (!directory && *list->set.mailbox_dir_name == '\0') {
if ((ret = mailbox_is_selectable(list, name)) <= 0)
return ret;
}
mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
"Mailbox already exists");
} else if (errno == ENOTDIR) {
mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox doesn't allow inferior mailboxes");
} else if (!mailbox_list_set_error_from_errno(list)) {
mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
}
return -1;
}
static const char *mailbox_list_fs_get_trash_dir(struct mailbox_list *list)
{
const char *root_dir;
root_dir = mailbox_list_get_path(list, NULL,
MAILBOX_LIST_PATH_TYPE_DIR);
return t_strdup_printf("%s/"MAILBOX_LIST_FS_TRASH_DIR_NAME, root_dir);
}
static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
{
const char *path, *trash_dir;
int ret = 0;
if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
if (mailbox_list_delete_mailbox_file(list, name) < 0)
return -1;
ret = 1;
}
if (*list->set.maildir_name != '\0' &&
*list->set.mailbox_dir_name != '\0' && ret == 0) {
trash_dir = mailbox_list_fs_get_trash_dir(list);
ret = mailbox_list_delete_maildir_via_trash(list, name,
trash_dir);
if (ret < 0)
return -1;
/* try to delete the parent directory */
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_DIR);
if (rmdir(path) < 0 && errno != ENOENT &&
errno != ENOTEMPTY && errno != EEXIST) {
mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
path);
}
}
if (ret == 0) {
bool rmdir_path = *list->set.maildir_name != '\0';
path = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (mailbox_list_delete_mailbox_nonrecursive(list, name, path,
rmdir_path) < 0)
return -1;
}
mailbox_list_delete_finish(list, name);
return 0;
}
static int fs_list_rmdir(struct mailbox_list *list, const char *name,
const char *path)
{
uint8_t dir_sha128[MAIL_GUID_128_SIZE];
if (rmdir(path) < 0)
return -1;
mailbox_name_get_sha128(name, dir_sha128);
mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR,
dir_sha128);
return 0;
}
static int fs_list_delete_dir(struct mailbox_list *list, const char *name)
{
const char *path, *child_name, *child_path, *p;
char sep;
path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
if (fs_list_rmdir(list, name, path) == 0)
return 0;
if (errno == ENOENT || errno == ENOTDIR) {
mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
} else if (errno == ENOTEMPTY || errno == EEXIST) {
/* mbox workaround: if only .imap/ directory is preventing the
deletion, remove it */
sep = mailbox_list_get_hierarchy_sep(list);
child_name = t_strdup_printf("%s%cchild", name, sep);
child_path = mailbox_list_get_path(list, child_name,
MAILBOX_LIST_PATH_TYPE_INDEX);
if (strncmp(path, child_path, strlen(path)) == 0) {
/* drop the "/child" part out. */
p = strrchr(child_path, '/');
if (rmdir(t_strdup_until(child_path, p)) == 0) {
/* try again */
if (fs_list_rmdir(list, name, path) == 0)
return 0;
}
}
mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
"Mailbox has children, delete them first");
} else {
mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
}
return -1;
}
static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
struct mailbox_list *newlist, const char *newname,
enum mailbox_list_path_type type, bool rmdir_parent)
{
const char *oldpath, *newpath, *p;
oldpath = mailbox_list_get_path(oldlist, oldname, type);
newpath = mailbox_list_get_path(newlist, newname, type);
if (strcmp(oldpath, newpath) == 0)
return 0;
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
if (rmdir_parent && (p = strrchr(oldpath, '/')) != NULL) {
oldpath = t_strdup_until(oldpath, p);
if (rmdir(oldpath) < 0 && errno != ENOENT &&
errno != ENOTEMPTY && errno != EEXIST) {
mailbox_list_set_critical(oldlist,
"rmdir(%s) failed: %m", oldpath);
}
}
/* avoid leaving empty directories lying around */
mailbox_list_delete_until_root(oldlist, oldpath, type);
return 0;
}
static int fs_list_rename_mailbox(struct mailbox_list *oldlist,
const char *oldname,
struct mailbox_list *newlist,
const char *newname, bool rename_children)
{
struct mail_storage *oldstorage;
const char *oldvname, *oldpath, *newpath, *alt_newpath, *root_path;
const char *p, *origin;
enum mailbox_list_path_type path_type, alt_path_type;
struct stat st;
mode_t mode;
gid_t gid;
bool rmdir_parent = FALSE;
oldvname = mailbox_list_get_vname(oldlist, oldname);
if (mailbox_list_get_storage(&oldlist, oldvname, &oldstorage) < 0)
return -1;
if (rename_children) {
path_type = MAILBOX_LIST_PATH_TYPE_DIR;
alt_path_type = MAILBOX_LIST_PATH_TYPE_ALT_DIR;
} else if (mail_storage_is_mailbox_file(oldstorage) ||
*oldlist->set.maildir_name != '\0') {
path_type = MAILBOX_LIST_PATH_TYPE_MAILBOX;
alt_path_type = MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX;
} else {
/* we can't do this, our children would get renamed with us */
mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
"Can't rename mailbox without its children.");
return -1;
}
oldpath = mailbox_list_get_path(oldlist, oldname, path_type);
newpath = mailbox_list_get_path(newlist, newname, path_type);
alt_newpath = mailbox_list_get_path(newlist, newname, alt_path_type);
root_path = mailbox_list_get_path(oldlist, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(oldpath, root_path) == 0) {
/* most likely INBOX */
mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
t_strdup_printf("Renaming %s isn't supported.",
oldname));
return -1;
}
/* create the hierarchy */
p = strrchr(newpath, '/');
if (p != NULL) {
mailbox_list_get_root_dir_permissions(newlist, &mode,
&gid, &origin);
p = t_strdup_until(newpath, p);
if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
errno != EEXIST) {
if (mailbox_list_set_error_from_errno(oldlist))
return -1;
mailbox_list_set_critical(oldlist,
"mkdir_parents(%s) failed: %m", p);
return -1;
}
}
/* first check that the destination mailbox doesn't exist.
this is racy, but we need to be atomic and there's hardly any
possibility that someone actually tries to rename two mailboxes
to same new one */
if (lstat(newpath, &st) == 0) {
mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
return -1;
} else if (errno == ENOTDIR) {
mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
"Target mailbox doesn't allow inferior mailboxes");
return -1;
} else if (errno != ENOENT && errno != EACCES) {
mailbox_list_set_critical(oldlist, "lstat(%s) failed: %m",
newpath);
return -1;
}
if (alt_newpath != NULL) {
if (stat(alt_newpath, &st) == 0) {
/* race condition or a directory left there lying around?
safest to just report error. */
mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
"Target mailbox already exists");
return -1;
} else if (errno != ENOENT) {
mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
alt_newpath);
return -1;
}
}
if (rename(oldpath, newpath) < 0) {
if (ENOTFOUND(errno)) {
mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname));
} else if (!mailbox_list_set_error_from_errno(oldlist)) {
mailbox_list_set_critical(oldlist,
"rename(%s, %s) failed: %m", oldpath, newpath);
}
return -1;
}
if (!rename_children) {
/* if there are no child mailboxes, get rid of the mailbox
directory entirely. */
oldpath = mailbox_list_get_path(oldlist, oldname,
MAILBOX_LIST_PATH_TYPE_DIR);
if (rmdir(oldpath) == 0)
rmdir_parent = TRUE;
else if (errno != ENOENT &&
errno != ENOTEMPTY && errno != EEXIST) {
mailbox_list_set_critical(oldlist,
"rmdir(%s) failed: %m", oldpath);
}
}
if (alt_newpath != NULL) {
(void)rename_dir(oldlist, oldname, newlist, newname,
alt_path_type, rmdir_parent);
}
(void)rename_dir(oldlist, oldname, newlist, newname,
MAILBOX_LIST_PATH_TYPE_CONTROL, rmdir_parent);
(void)rename_dir(oldlist, oldname, newlist, newname,
MAILBOX_LIST_PATH_TYPE_INDEX, rmdir_parent);
return 0;
}
struct mailbox_list fs_mailbox_list = {
.name = MAILBOX_LIST_NAME_FS,
.props = 0,
.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
{
fs_list_alloc,
fs_list_deinit,
NULL,
fs_is_valid_pattern,
fs_is_valid_existing_name,
fs_is_valid_create_name,
fs_list_get_hierarchy_sep,
mailbox_list_default_get_vname,
mailbox_list_default_get_storage_name,
fs_list_get_path,
fs_list_get_temp_prefix,
fs_list_join_refpattern,
fs_list_iter_init,
fs_list_iter_next,
fs_list_iter_deinit,
fs_list_get_mailbox_flags,
NULL,
fs_list_set_subscribed,
fs_list_create_mailbox_dir,
fs_list_delete_mailbox,
fs_list_delete_dir,
fs_list_rename_mailbox
}
};