maildir-storage.c revision 68969d3f5b70bceae90e551686b925c9dd5dc2be
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mkdir-parents.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "eacces-error.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "unlink-old-files.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mailbox-uidvalidity.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mailbox-list-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "maildir-storage.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "maildir-uidlist.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "maildir-keywords.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "maildir-sync.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "index-mail.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <sys/stat.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAILDIR_LIST_CONTEXT(obj) \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MODULE_CONTEXT(obj, maildir_mailbox_list_module)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct maildir_mailbox_list_context {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen union mailbox_list_module_context module_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct maildir_settings *set;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenextern struct mail_storage maildir_storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenextern struct mailbox maildir_mailbox;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(maildir_mailbox_list_module,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &mailbox_list_module_register);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const char *maildir_subdirs[] = { "cur", "new", "tmp" };
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void maildir_mailbox_close(struct mailbox *box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic struct mail_storage *maildir_storage_alloc(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct maildir_storage *storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pool_t pool;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pool = pool_alloconly_create("maildir storage", 512+256);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage = p_new(pool, struct maildir_storage, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->storage = maildir_storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->storage.pool = pool;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return &storage->storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmaildir_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r ATTR_UNUSED)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct maildir_storage *storage = MAILDIR_STORAGE(_storage);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox_list *list = ns->list;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->set = mail_namespace_get_driver_settings(ns, _storage);
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->temp_prefix = p_strdup(_storage->pool,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mailbox_list_get_temp_prefix(list));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (list->set.control_dir == NULL && list->set.inbox_path == NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* put the temp files into tmp/ directory preferably */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->temp_prefix = p_strconcat(_storage->pool, "tmp/",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->temp_prefix, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* control dir should also be writable */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen storage->temp_prefix, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void maildir_storage_get_list_settings(const struct mail_namespace *ns,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox_list_settings *set)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (set->layout == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set->layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (set->subscription_fname == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (set->inbox_path == NULL && *set->maildir_name == '\0' &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (strcmp(set->layout, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strcmp(set->layout, MAILBOX_LIST_NAME_FS) == 0) &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Maildir++ INBOX is the Maildir base itself */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set->inbox_path = set->root_dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const char *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmaildir_storage_find_root_dir(const struct mail_namespace *ns)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool debug = ns->mail_set->mail_debug;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *home, *path;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we'll need to figure out the maildir location ourself.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen It's ~/Maildir unless we are chrooted. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ns->owner != NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_user_get_home(ns->owner, &home) > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = t_strconcat(home, "/Maildir", NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (access(path, R_OK|W_OK|X_OK) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir: root exists (%s)", path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return path;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir: access(%s, rwx): failed: %m", path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir: Home directory not set");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (access("/cur", R_OK|W_OK|X_OK) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir: /cur exists, assuming chroot");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return "/";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool maildir_storage_autodetect(const struct mail_namespace *ns,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mailbox_list_settings *set)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen bool debug = ns->mail_set->mail_debug;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct stat st;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *path, *root_dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (set->root_dir != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen root_dir = set->root_dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen root_dir = maildir_storage_find_root_dir(ns);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (root_dir == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir: couldn't find root dir");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = t_strconcat(root_dir, "/cur", NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (stat(path, &st) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir autodetect: stat(%s) failed: %m", path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!S_ISDIR(st.st_mode)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (debug)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("maildir autodetect: %s not a directory", path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set->root_dir = root_dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen maildir_storage_get_list_settings(ns, set);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmkdir_verify(struct mailbox *box, const char *dir, bool verify)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct mailbox_permissions *perm;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct stat st;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (verify) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (stat(dir, &st) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno != ENOENT) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(box->storage,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "stat(%s) failed: %m", dir);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen perm = mailbox_get_permissions(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mkdir_parents_chgrp(dir, perm->dir_create_mode,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen perm->file_create_gid,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen perm->file_create_gid_origin) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno == EEXIST) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (verify)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Mailbox already exists");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (errno == ENOENT) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Mailbox was deleted while it was being created");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (errno == EACCES) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (box->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* shared namespace, don't log permission errors */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_PERM,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MAIL_ERRSTR_NO_PERMISSION);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(box->storage, "%s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_error_create_eacces_msg("mkdir", dir));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(box->storage,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "mkdir(%s) failed: %m", dir);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int maildir_check_tmp(struct mail_storage *storage, const char *dir)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int interval = storage->set->mail_temp_scan_interval;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *path;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct stat st;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* if tmp/ directory exists, we need to clean it up once in a while */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = t_strconcat(dir, "/tmp", NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (stat(path, &st) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno == ENOENT || errno == ENAMETOOLONG)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (errno == EACCES) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(storage, "%s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_error_eacces_msg("stat", path));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (interval == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* disabled */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (st.st_atime > st.st_ctime + MAILDIR_TMP_DELETE_SECS) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* the directory should be empty. we won't do anything
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen until ctime changes. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (st.st_atime < ioloop_time - (time_t)interval) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* time to scan */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)unlink_old_files(path, "",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ioloop_time - MAILDIR_TMP_DELETE_SECS);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* create or fix maildir, ignore if it already exists */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int create_maildir_subdirs(struct mailbox *box, bool verify)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *path, *box_path;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mail_error error;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen &box_path) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen path = t_strconcat(box_path, "/", maildir_subdirs[i], NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mkdir_verify(box, path, verify) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen error = mailbox_get_last_mail_error(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (error != MAIL_ERROR_EXISTS)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* try to create all of the directories in case one
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen of them doesn't exist */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void maildir_lock_touch_timeout(struct maildir_mailbox *mbox)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (void)maildir_uidlist_lock_touch(mbox->uidlist);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainenstatic struct mailbox *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmaildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *vname, enum mailbox_flags flags)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct maildir_mailbox *mbox;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pool_t pool;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen pool = pool_alloconly_create("maildir mailbox", 1024*3);
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen mbox = p_new(pool, struct maildir_mailbox, 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->box = maildir_mailbox;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->box.pool = pool;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->box.storage = storage;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->box.list = list;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->box.mail_vfuncs = &maildir_mail_vfuncs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->maildir_list_index_ext_id = (uint32_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->storage = MAILDIR_STORAGE(storage);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return &mbox->box;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int maildir_mailbox_open_existing(struct mailbox *box)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->uidlist = maildir_uidlist_init(mbox);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->keywords = maildir_keywords_init(mbox);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (maildir_uidlist_lock(mbox->uidlist) <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen maildir_mailbox_close(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->keep_lock_to = timeout_add(MAILDIR_LOCK_TOUCH_SECS * 1000,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen maildir_lock_touch_timeout,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (index_storage_mailbox_open(box, FALSE) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen maildir_mailbox_close(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mbox->maildir_ext_id =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_index_ext_register(mbox->box.index, "maildir",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sizeof(mbox->maildir_hdr), 0, 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool maildir_storage_is_readonly(struct mailbox *box)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (index_storage_is_readonly(box))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (maildir_is_backend_readonly(mbox)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* return read-only only if there are no private flags
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (that are stored in index files) */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mailbox_get_private_flags_mask(box) == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmaildir_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mailbox_existence *existence_r)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return index_storage_mailbox_exists_full(box, "cur", existence_r);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int maildir_mailbox_open(struct mailbox *box)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *box_path = mailbox_get_path(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *root_dir;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct stat st;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* begin by checking if tmp/ directory exists and if it should be
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen cleaned up. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = maildir_check_tmp(box->storage, box_path);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* exists */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return maildir_mailbox_open_existing(box);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret < 0)
return -1;
/* tmp/ directory doesn't exist. does the maildir? autocreate missing
dirs only with Maildir++ and imapdir layouts. */
if (strcmp(box->list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0 &&
strcmp(box->list->name, MAILBOX_LIST_NAME_IMAPDIR) != 0) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
return -1;
}
root_dir = mailbox_list_get_root_forced(box->list,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(box_path, root_dir) == 0 && !box->inbox_any) {
/* root directory for some namespace. */
errno = ENOENT;
} else if (stat(box_path, &st) == 0) {
/* yes, we'll need to create the missing dirs */
if (create_maildir_subdirs(box, TRUE) < 0)
return -1;
return maildir_mailbox_open_existing(box);
}
if (errno == ENOENT || errno == ENAMETOOLONG) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
return -1;
} else {
mail_storage_set_critical(box->storage,
"stat(%s) failed: %m", box_path);
return -1;
}
}
static int maildir_create_shared(struct mailbox *box)
{
const struct mailbox_permissions *perm = mailbox_get_permissions(box);
const char *path;
mode_t old_mask;
int fd, ret;
ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&path);
if (ret < 0)
return -1;
i_assert(ret > 0);
old_mask = umask(0);
path = t_strconcat(path, "/dovecot-shared", NULL);
fd = open(path, O_WRONLY | O_CREAT, perm->file_create_mode);
umask(old_mask);
if (fd == -1) {
mail_storage_set_critical(box->storage, "open(%s) failed: %m",
path);
return -1;
}
if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
if (errno == EPERM) {
mail_storage_set_critical(box->storage, "%s",
eperm_error_get_chgrp("fchown", path,
perm->file_create_gid,
perm->file_create_gid_origin));
} else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", path);
}
}
i_close_fd(&fd);
return 0;
}
static int
maildir_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
{
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
struct maildir_uidlist *uidlist;
bool locked = FALSE;
int ret = 0;
if (!box->opened) {
if (mailbox_open(box) < 0)
return -1;
}
uidlist = mbox->uidlist;
if (update->uid_validity != 0 || update->min_next_uid != 0 ||
!guid_128_is_empty(update->mailbox_guid)) {
if (maildir_uidlist_lock(uidlist) <= 0)
return -1;
locked = TRUE;
if (!guid_128_is_empty(update->mailbox_guid))
maildir_uidlist_set_mailbox_guid(uidlist, update->mailbox_guid);
if (update->uid_validity != 0)
maildir_uidlist_set_uid_validity(uidlist, update->uid_validity);
if (update->min_next_uid != 0) {
maildir_uidlist_set_next_uid(uidlist, update->min_next_uid,
FALSE);
}
ret = maildir_uidlist_update(uidlist);
}
if (ret == 0)
ret = index_storage_mailbox_update(box, update);
if (locked)
maildir_uidlist_unlock(uidlist);
return ret;
}
static int maildir_create_maildirfolder_file(struct mailbox *box)
{
const struct mailbox_permissions *perm;
const char *path;
mode_t old_mask;
int fd;
/* Maildir++ spec wants that maildirfolder named file is created for
all subfolders. Do this only with Maildir++ layout. */
if (strcmp(box->list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) != 0)
return 0;
perm = mailbox_get_permissions(box);
path = t_strconcat(mailbox_get_path(box),
"/"MAILDIR_SUBFOLDER_FILENAME, NULL);
old_mask = umask(0);
fd = open(path, O_CREAT | O_WRONLY, perm->file_create_mode);
umask(old_mask);
if (fd != -1) {
/* ok */
} else if (errno == ENOENT) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
"Mailbox was deleted while it was being created");
return -1;
} else {
mail_storage_set_critical(box->storage,
"open(%s, O_CREAT) failed: %m", path);
return -1;
}
if (perm->file_create_gid != (gid_t)-1) {
if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) {
/* ok */
} else if (errno == EPERM) {
mail_storage_set_critical(box->storage, "%s",
eperm_error_get_chgrp("fchown", path,
perm->file_create_gid,
perm->file_create_gid_origin));
} else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", path);
}
}
i_close_fd(&fd);
return 0;
}
static int
maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
bool directory)
{
const char *root_dir, *shared_path;
struct stat st;
int ret;
if ((ret = index_storage_mailbox_create(box, directory)) <= 0)
return ret;
ret = 0;
/* the maildir is created now. finish the creation as best as we can */
if (create_maildir_subdirs(box, FALSE) < 0)
ret = -1;
if (maildir_create_maildirfolder_file(box) < 0)
ret = -1;
/* if dovecot-shared exists in the root dir, copy it to newly
created mailboxes */
root_dir = mailbox_list_get_root_forced(box->list,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
if (stat(shared_path, &st) == 0) {
if (maildir_create_shared(box) < 0)
ret = -1;
}
if (update != NULL) {
if (maildir_mailbox_update(box, update) < 0)
ret = -1;
}
return ret;
}
static int
maildir_mailbox_get_metadata(struct mailbox *box,
enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
if (index_mailbox_get_metadata(box, items, metadata_r) < 0)
return -1;
if ((items & MAILBOX_METADATA_GUID) != 0) {
if (maildir_uidlist_get_mailbox_guid(mbox->uidlist,
metadata_r->guid) < 0)
return -1;
}
return 0;
}
static void maildir_mailbox_close(struct mailbox *box)
{
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
if (mbox->keep_lock_to != NULL) {
maildir_uidlist_unlock(mbox->uidlist);
timeout_remove(&mbox->keep_lock_to);
}
if (mbox->flags_view != NULL)
mail_index_view_close(&mbox->flags_view);
if (mbox->keywords != NULL)
maildir_keywords_deinit(&mbox->keywords);
maildir_uidlist_deinit(&mbox->uidlist);
index_storage_mailbox_close(box);
}
static void maildir_notify_changes(struct mailbox *box)
{
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
const char *box_path = mailbox_get_path(box);
if (box->notify_callback == NULL)
mailbox_watch_remove_all(&mbox->box);
else {
mailbox_watch_add(&mbox->box,
t_strconcat(box_path, "/new", NULL));
mailbox_watch_add(&mbox->box,
t_strconcat(box_path, "/cur", NULL));
}
}
static bool
maildir_is_internal_name(struct mailbox_list *list ATTR_UNUSED,
const char *name)
{
return strcmp(name, "cur") == 0 ||
strcmp(name, "new") == 0 ||
strcmp(name, "tmp") == 0;
}
static void maildir_storage_add_list(struct mail_storage *storage,
struct mailbox_list *list)
{
struct maildir_mailbox_list_context *mlist;
mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1);
mlist->module_ctx.super = list->v;
mlist->set = mail_namespace_get_driver_settings(list->ns, storage);
list->v.is_internal_name = maildir_is_internal_name;
MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist);
}
uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list)
{
const char *path;
path = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_CONTROL);
path = t_strconcat(path, "/"MAILDIR_UIDVALIDITY_FNAME, NULL);
return mailbox_uidvalidity_next(list, path);
}
static enum mail_flags maildir_get_private_flags_mask(struct mailbox *box)
{
struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
const char *path, *path2;
struct stat st;
if (mbox->private_flags_mask_set)
return mbox->_private_flags_mask;
mbox->private_flags_mask_set = TRUE;
path = mailbox_list_get_root_forced(box->list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (box->list->set.index_pvt_dir != NULL) {
/* private index directory is set. we'll definitely have
private flags. */
mbox->_private_flags_mask = MAIL_SEEN;
} else if (!mailbox_list_get_root_path(box->list,
MAILBOX_LIST_PATH_TYPE_INDEX,
&path2) ||
strcmp(path, path2) == 0) {
/* no separate index directory. we can't have private flags,
so don't even bother checking if dovecot-shared exists */
} else {
path = t_strconcat(mailbox_get_path(box),
"/dovecot-shared", NULL);
if (stat(path, &st) == 0)
mbox->_private_flags_mask = MAIL_SEEN;
}
return mbox->_private_flags_mask;
}
bool maildir_is_backend_readonly(struct maildir_mailbox *mbox)
{
if (!mbox->backend_readonly_set) {
const char *box_path = mailbox_get_path(&mbox->box);
mbox->backend_readonly_set = TRUE;
if (access(t_strconcat(box_path, "/cur", NULL), W_OK) < 0 &&
errno == EACCES)
mbox->backend_readonly = TRUE;
}
return mbox->backend_readonly;
}
struct mail_storage maildir_storage = {
.name = MAILDIR_STORAGE_NAME,
.class_flags = MAIL_STORAGE_CLASS_FLAG_FILE_PER_MSG |
MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS |
MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS |
MAIL_STORAGE_CLASS_FLAG_BINARY_DATA,
.v = {
maildir_get_setting_parser_info,
maildir_storage_alloc,
maildir_storage_create,
index_storage_destroy,
maildir_storage_add_list,
maildir_storage_get_list_settings,
maildir_storage_autodetect,
maildir_mailbox_alloc,
NULL,
NULL,
}
};
struct mailbox maildir_mailbox = {
.v = {
maildir_storage_is_readonly,
index_storage_mailbox_enable,
maildir_mailbox_exists,
maildir_mailbox_open,
maildir_mailbox_close,
index_storage_mailbox_free,
maildir_mailbox_create,
maildir_mailbox_update,
index_storage_mailbox_delete,
index_storage_mailbox_rename,
index_storage_get_status,
maildir_mailbox_get_metadata,
index_storage_set_subscribed,
index_storage_attribute_set,
index_storage_attribute_get,
index_storage_attribute_iter_init,
index_storage_attribute_iter_next,
index_storage_attribute_iter_deinit,
maildir_list_index_has_changed,
maildir_list_index_update_sync,
maildir_storage_sync_init,
index_mailbox_sync_next,
index_mailbox_sync_deinit,
NULL,
maildir_notify_changes,
index_transaction_begin,
index_transaction_commit,
index_transaction_rollback,
maildir_get_private_flags_mask,
index_mail_alloc,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next_nonblock,
index_storage_search_next_update_seq,
maildir_save_alloc,
maildir_save_begin,
maildir_save_continue,
maildir_save_finish,
maildir_save_cancel,
maildir_copy,
maildir_transaction_save_commit_pre,
maildir_transaction_save_commit_post,
maildir_transaction_save_rollback,
index_storage_is_inconsistent
}
};