maildir-storage.c revision b28816eae22d99ba3abf0440f5368d87b69414be
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen/* Copyright (C) 2002-2006 Timo Sirainen */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ioloop.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "array.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "hostpid.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "home-expand.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mkdir-parents.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "unlink-directory.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "maildir-storage.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "maildir-uidlist.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "maildir-keywords.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "maildir-sync.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "index-mail.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <stdio.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <stdlib.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <unistd.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <sys/stat.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define CREATE_MODE 0777 /* umask() should limit it more */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define MAILDIR_PLUSPLUS_DRIVER_NAME "maildir++"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct rename_context {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen bool found;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t oldnamelen;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *newname;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenextern struct mail_storage maildir_storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenextern struct mailbox maildir_mailbox;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic const char *maildirs[] = { "cur", "new", "tmp", NULL };
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int verify_inbox(struct mail_storage *storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic const char *strip_tail_slash(const char *path)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen size_t len = strlen(path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (len > 0 && path[len-1] == '/')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return t_strndup(path, len-1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstatic const char *strip_tail_slash_and_cut(const char *path)
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return strip_tail_slash(t_strcut(path, ':'));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmaildir_get_list_settings(struct mailbox_list_settings *list_set,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *data, enum mail_storage_flags flags)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *home, *path, *p;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen memset(list_set, 0, sizeof(*list_set));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->maildir_name = "";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
ff3c528e094e75e3c6bc11af75ef84484d963ae9Timo Sirainen if (data == NULL || *data == '\0') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("maildir: root directory not given");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we'll need to figure out the maildir location ourself.
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen It's $HOME/Maildir unless we are chrooted. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((home = getenv("HOME")) != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = t_strconcat(home, "/Maildir", NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (access(path, R_OK|W_OK|X_OK) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: root exists (%s)",
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->root_dir = path;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: access(%s, rwx): "
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "failed: %m", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: HOME not set");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (access("/cur", R_OK|W_OK|X_OK) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: /cur exists, assuming chroot");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->root_dir = "/";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: data=%s", data);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p = strchr(data, ':');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (p == NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->root_dir = data;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->root_dir = t_strdup_until(data, p);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen do {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strncmp(p, "INBOX=", 6) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->inbox_path =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen strip_tail_slash_and_cut(p+6);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (strncmp(p, "INDEX=", 6) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->index_dir =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen strip_tail_slash_and_cut(p+6);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (strncmp(p, "CONTROL=", 8) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->control_dir =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen strip_tail_slash_and_cut(p+8);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p = strchr(p, ':');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } while (p != NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (list_set->root_dir == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir: couldn't find root dir");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set->root_dir = strip_tail_slash(list_set->root_dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (list_set->index_dir != NULL &&
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen strcmp(list_set->index_dir, "MEMORY") == 0)
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen list_set->index_dir = "";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic struct mail_storage *
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmaildir_create(const char *data, const char *user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum mail_storage_flags flags,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum file_lock_method lock_method)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct maildir_storage *storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct index_storage *istorage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mailbox_list_settings list_set;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mailbox_list *list;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *error;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct stat st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_t pool;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (maildir_get_list_settings(&list_set, data, flags) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set.mail_storage_flags = &flags;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set.lock_method = &lock_method;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* normally the maildir is created in verify_inbox() */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (stat(list_set.root_dir, &st) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("stat(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen list_set.root_dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool = pool_alloconly_create("storage", 512);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen storage = p_new(pool, struct maildir_storage, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mailbox_list_init("maildir++", &list_set,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_get_list_flags(flags),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mailbox_storage_list_is_mailbox, storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen &list, &error) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("maildir++: %s", error);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_unref(pool);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen storage->copy_with_hardlinks =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen storage->stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen storage->temp_prefix = mailbox_list_get_temp_prefix(list);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (list_set.control_dir == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* put the temp files into tmp/ directory preferrably */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen storage->temp_prefix =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p_strconcat(pool, "tmp/", storage->temp_prefix, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen istorage = INDEX_STORAGE(storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen istorage->storage = maildir_storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen istorage->storage.pool = pool;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen istorage->user = p_strdup(pool, user);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen istorage->callbacks = p_new(pool, struct mail_storage_callbacks, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen index_storage_init(istorage, list, flags, lock_method);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (void)verify_inbox(STORAGE(storage));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return STORAGE(storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void maildir_free(struct mail_storage *_storage)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct index_storage *storage = (struct index_storage *) _storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen index_storage_deinit(storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_unref(storage->storage.pool);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool maildir_autodetect(const char *data, enum mail_storage_flags flags)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct stat st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen data = t_strcut(data, ':');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = t_strconcat(data, "/cur", NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (stat(path, &st) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir autodetect: stat(%s) failed: %m", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!S_ISDIR(st.st_mode)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_info("maildir autodetect: %s not a directory", path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic const char *
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmaildir_get_unlink_dest(struct mail_storage *storage, const char *name)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen const char *root_dir;
ff3c528e094e75e3c6bc11af75ef84484d963ae9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (*name == '/' || *name == '~'))
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strcmp(mailbox_list_get_driver_name(storage->list),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILDIR_PLUSPLUS_DRIVER_NAME) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* Not maildir++ driver. Don't use this trick. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen root_dir = mailbox_list_get_path(storage->list, NULL,
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen MAILBOX_LIST_PATH_TYPE_DIR);
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return t_strdup_printf("%s/%c"MAILDIR_UNLINK_DIRNAME, root_dir,
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen mailbox_list_get_hierarchy_sep(storage->list));
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen}
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainenstatic int mkdir_verify(struct mail_storage *storage,
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen const char *dir, bool verify)
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen{
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen struct stat st;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (verify) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (lstat(dir, &st) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "lstat(%s) failed: %m", dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (errno != EEXIST || !verify)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != EEXIST && (!verify || errno != ENOENT)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "mkdir(%s) failed: %m", dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen/* create or fix maildir, ignore if it already exists */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int create_maildir(struct mail_storage *storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *dir, bool verify)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **tmp, *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!verify && mkdir_verify(storage, dir, verify) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (tmp = maildirs; *tmp != NULL; tmp++) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = t_strconcat(dir, "/", *tmp, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mkdir_verify(storage, path, verify) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!verify || errno != ENOENT)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* small optimization. if we're verifying, we don't
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen check that the root dir actually exists unless we
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen fail here. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mkdir_verify(storage, dir, verify) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mkdir_verify(storage, path, verify) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int create_index_dir(struct mail_storage *storage, const char *name)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *index_dir, *root_dir, *dir;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen index_dir = mailbox_list_get_path(storage->list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_INDEX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*index_dir == '\0')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen root_dir = mailbox_list_get_path(storage->list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strcmp(index_dir, root_dir) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dir = t_strdup_printf("%s/%c%s", index_dir,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mailbox_list_get_hierarchy_sep(storage->list),
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen name);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(storage,
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen "mkdir(%s) failed: %m", dir);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen return -1;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen }
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen return 0;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen}
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstatic int create_control_dir(struct mail_storage *storage, const char *name)
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen{
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen const char *control_dir, *root_dir, *dir;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen control_dir = mailbox_list_get_path(storage->list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen root_dir = mailbox_list_get_path(storage->list, name,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (strcmp(control_dir, root_dir) == 0)
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return 0;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dir = t_strdup_printf("%s/%c%s", control_dir,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen mailbox_list_get_hierarchy_sep(storage->list),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen name);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "mkdir(%s) failed: %m", dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int verify_inbox(struct mail_storage *storage)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = mailbox_list_get_path(storage->list, "INBOX",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (create_maildir(storage, path, TRUE) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (create_index_dir(storage, "INBOX") < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (create_control_dir(storage, "INBOX") < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool maildir_is_recent(struct index_mailbox *ibox, uint32_t uid)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)ibox;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return maildir_uidlist_is_recent(mbox->uidlist, uid);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic struct mailbox *
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmaildir_open(struct maildir_storage *storage, const char *name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum mailbox_open_flags flags)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct index_storage *istorage = INDEX_STORAGE(storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct maildir_mailbox *mbox;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_index *index;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path, *index_dir, *control_dir;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct stat st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int shared;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_t pool;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path = mailbox_list_get_path(istorage->storage.list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen index_dir = mailbox_list_get_path(istorage->storage.list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_INDEX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen control_dir = mailbox_list_get_path(istorage->storage.list, name,
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) != 0)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen index_dir = "";
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen index = index_storage_alloc(index_dir, path,
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen MAILDIR_INDEX_PREFIX);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* for shared mailboxes get the create mode from the
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen permissions of dovecot-shared file. */
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen shared = stat(t_strconcat(path, "/dovecot-shared", NULL), &st) == 0;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (shared)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen mail_index_set_permissions(index, st.st_mode & 0666, st.st_gid);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool = pool_alloconly_create("mailbox", 1024);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox = p_new(pool, struct maildir_mailbox, 1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->ibox.box = maildir_mailbox;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->ibox.box.pool = pool;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->ibox.storage = istorage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->ibox.mail_vfuncs = &maildir_mail_vfuncs;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->ibox.is_recent = maildir_is_recent;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen index_storage_mailbox_init(&mbox->ibox, index, name, flags, FALSE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->storage = storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->path = p_strdup(pool, path);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->control_dir = p_strdup(pool, control_dir);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->uidlist = maildir_uidlist_init(mbox);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->keywords = maildir_keywords_init(mbox);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!shared)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->mail_create_mode = 0600;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->mail_create_mode = st.st_mode & 0666;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mbox->private_flags_mask = MAIL_SEEN;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mbox->ibox.keep_locked) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (maildir_uidlist_lock(mbox->uidlist) <= 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mailbox *box = &mbox->ibox.box;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mailbox_close(&box);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return &mbox->ibox.box;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstatic struct mailbox *
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenmaildir_mailbox_open(struct mail_storage *_storage, const char *name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct istream *input, enum mailbox_open_flags flags)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct maildir_storage *storage = (struct maildir_storage *)_storage;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct stat st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_clear_error(_storage);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (input != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(_storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "Maildir doesn't support streamed mailboxes");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strcmp(name, "INBOX") == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (verify_inbox(_storage) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return maildir_open(storage, "INBOX", flags);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_error(_storage, "Invalid mailbox name");
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return NULL;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen path = mailbox_list_get_path(_storage->list, name,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (stat(path, &st) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* exists - make sure the required directories are also there */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (create_maildir(_storage, path, TRUE) < 0 ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen create_control_dir(_storage, name) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (create_index_dir(_storage, name) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen return maildir_open(storage, name, flags);
f1ab91e28a4dac1305b52f77bb5f32269137d051Timo Sirainen } else if (errno == ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_error(_storage,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_storage_set_critical(_storage, "stat(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path);
return NULL;
}
}
static int maildir_create_shared(struct mail_storage *storage,
const char *dir, mode_t mode, gid_t gid)
{
const char *path;
mode_t old_mask;
int fd;
/* add the execute bit if either read or write bit is set */
if ((mode & 0600) != 0) mode |= 0100;
if ((mode & 0060) != 0) mode |= 0010;
if ((mode & 0006) != 0) mode |= 0001;
old_mask = umask(0777 ^ mode);
if (create_maildir(storage, dir, FALSE) < 0) {
if (errno == EEXIST) {
mail_storage_set_error(storage,
"Mailbox already exists");
}
umask(old_mask);
return -1;
}
if (chown(dir, (uid_t)-1, gid) < 0) {
mail_storage_set_critical(storage, "chown(%s) failed: %m", dir);
}
path = t_strconcat(dir, "/dovecot-shared", NULL);
fd = open(path, O_WRONLY | O_CREAT, mode & 0666);
umask(old_mask);
if (fd == -1) {
mail_storage_set_critical(storage, "open(%s) failed: %m", path);
return -1;
}
if (fchown(fd, (uid_t)-1, gid) < 0) {
mail_storage_set_critical(storage,
"fchown(%s) failed: %m", path);
}
(void)close(fd);
return 0;
}
static int maildir_mailbox_create(struct mail_storage *_storage,
const char *name,
bool directory __attr_unused__)
{
struct stat st;
const char *path, *root_dir, *shared_path;
int fd;
mail_storage_clear_error(_storage);
if (!mailbox_list_is_valid_create_name(_storage->list, name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return -1;
}
path = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
root_dir = mailbox_list_get_path(_storage->list, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
/* if dovecot-shared exists in the root dir, create the mailbox using
its permissions and gid, and copy the dovecot-shared inside it. */
shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
if (stat(shared_path, &st) == 0) {
return maildir_create_shared(_storage, path,
st.st_mode & 0666, st.st_gid);
}
if (create_maildir(_storage, path, FALSE) < 0) {
if (errno == EEXIST) {
mail_storage_set_error(_storage,
"Mailbox already exists");
}
return -1;
}
/* Maildir++ spec want that maildirfolder named file is created for
all subfolders. */
path = t_strconcat(path, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
fd = open(path, O_CREAT | O_WRONLY, CREATE_MODE & 0666);
if (fd == -1)
i_error("open(%s, O_CREAT) failed: %m", path);
else
(void)close(fd);
return 0;
}
static int maildir_mailbox_delete(struct mail_storage *_storage,
const char *name)
{
struct stat st;
const char *src, *dest, *index_dir, *control_dir;
int count;
mail_storage_clear_error(_storage);
if (strcmp(name, "INBOX") == 0) {
mail_storage_set_error(_storage, "INBOX can't be deleted.");
return -1;
}
if (!mailbox_list_is_valid_existing_name(_storage->list, name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return -1;
}
/* check if the mailbox actually exists */
src = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (stat(src, &st) != 0 && errno == ENOENT) {
mail_storage_set_error(_storage,
MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name);
return -1;
}
/* Make sure the indexes are closed before trying to delete the
directory that contains them. It can still fail with some NFS
implementations if indexes are opened by another session, but
that can't really be helped. */
index_storage_destroy_unrefed();
/* if there's a separate index directory, delete it before the actual
maildir. this way we never see partially deleted mailboxes. */
index_dir = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_INDEX);
if (strcmp(index_dir, src) != 0) {
i_assert(*name != '/' && *name != '~');
if (unlink_directory(index_dir, TRUE) < 0 &&
errno != ENOTEMPTY) {
mail_storage_set_critical(_storage,
"unlink_directory(%s) failed: %m", index_dir);
return -1;
}
}
control_dir = mailbox_list_get_path(_storage->list, name,
MAILBOX_LIST_PATH_TYPE_CONTROL);
if (strcmp(control_dir, src) != 0 &&
strcmp(control_dir, index_dir) != 0) {
i_assert(*name != '/' && *name != '~');
if (unlink_directory(control_dir, TRUE) < 0 &&
errno != ENOTEMPTY) {
mail_storage_set_critical(_storage,
"unlink_directory(%s) failed: %m", control_dir);
return -1;
}
}
dest = maildir_get_unlink_dest(_storage, name);
if (dest == NULL) {
/* absolute maildir path, delete the directory directly
without any renaming */
dest = src;
} else {
/* rename the .maildir into ..DOVECOT-TRASH which atomically
marks it as being deleted. If we die before deleting the
..DOVECOT-TRASH directory, it gets deleted the next time
mailbox listing sees it. */
count = 0;
while (rename(src, dest) < 0 && count < 2) {
/* EBUSY is given by some NFS implementations */
if (errno != EEXIST && errno != ENOTEMPTY &&
errno != EBUSY) {
mail_storage_set_critical(_storage,
"rename(%s, %s) failed: %m", src, dest);
return -1;
}
/* already existed, delete it and try again */
if (unlink_directory(dest, TRUE) < 0) {
mail_storage_set_critical(_storage,
"unlink_directory(%s) failed: %m",
dest);
return -1;
}
count++;
}
}
if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
mail_storage_set_critical(_storage,
"unlink_directory(%s) failed: %m", dest);
/* it's already renamed to ..dir, which means it's deleted
as far as the client is concerned. Report success. */
}
return 0;
}
static int rename_dir(struct mail_storage *storage,
enum mailbox_list_path_type type,
const char *oldname, const char *newname)
{
const char *oldpath, *newpath;
oldpath = mailbox_list_get_path(storage->list, oldname, type);
newpath = mailbox_list_get_path(storage->list, newname, type);
if (strcmp(oldpath, newpath) == 0)
return 0;
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
return 0;
}
static int rename_subfolders(struct mail_storage *storage,
const char *oldname, const char *newname)
{
struct mailbox_list_iterate_context *iter;
struct mailbox_info *info;
ARRAY_DEFINE(names_arr, const char *);
const char *mask, *oldpath, *newpath, *old_listname, *new_listname;
const char *const *names;
unsigned int i, count;
size_t oldnamelen;
pool_t pool;
int ret;
ret = 0;
oldnamelen = strlen(oldname);
/* first get a list of the subfolders and save them to memory, because
we can't rely on readdir() not skipping files while the directory
is being modified. this doesn't protect against modifications by
other processes though. */
pool = pool_alloconly_create("Maildir subfolders list", 1024);
i_array_init(&names_arr, 64);
mask = t_strdup_printf("%s%c*", oldname,
mailbox_list_get_hierarchy_sep(storage->list));
iter = mailbox_list_iter_init(storage->list, mask,
MAILBOX_LIST_ITER_FAST_FLAGS);
while ((info = mailbox_list_iter_next(iter)) != NULL) {
const char *name;
i_assert(oldnamelen <= strlen(info->name));
name = p_strdup(pool, info->name + oldnamelen);
array_append(&names_arr, &name, 1);
}
if (mailbox_list_iter_deinit(&iter) < 0) {
ret = -1;
names = NULL; count = 0;
} else {
names = array_get(&names_arr, &count);
}
for (i = 0; i < count; i++) {
t_push();
old_listname = t_strconcat(oldname, names[i], NULL);
new_listname = t_strconcat(newname, names[i], NULL);
oldpath = mailbox_list_get_path(storage->list, old_listname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
newpath = mailbox_list_get_path(storage->list, new_listname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
/* FIXME: it's possible to merge two folders if either one of
them doesn't have existing root folder. We could check this
but I'm not sure if it's worth it. It could be even
considered as a feature.
Anyway, the bug with merging is that if both folders have
identically named subfolder they conflict. Just ignore those
and leave them under the old folder. */
if (rename(oldpath, newpath) == 0 ||
errno == EEXIST || errno == ENOTEMPTY)
ret = 1;
else {
mail_storage_set_critical(storage,
"rename(%s, %s) failed: %m", oldpath, newpath);
ret = -1;
t_pop();
break;
}
(void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_CONTROL,
old_listname, new_listname);
(void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_INDEX,
old_listname, new_listname);
t_pop();
}
array_free(&names_arr);
pool_unref(pool);
return ret;
}
static int maildir_mailbox_rename(struct mail_storage *_storage,
const char *oldname, const char *newname)
{
const char *oldpath, *newpath;
int ret;
bool found;
mail_storage_clear_error(_storage);
if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) ||
!mailbox_list_is_valid_create_name(_storage->list, newname)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return -1;
}
if (strcmp(oldname, "INBOX") == 0) {
mail_storage_set_error(_storage,
"Renaming INBOX isn't supported.");
return -1;
}
/* NOTE: it's possible to rename a nonexisting folder which has
subfolders. In that case we should ignore the rename() error. */
oldpath = mailbox_list_get_path(_storage->list, oldname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
newpath = mailbox_list_get_path(_storage->list, newname,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
ret = rename(oldpath, newpath);
if (ret == 0 || errno == ENOENT) {
(void)rename_dir(_storage, MAILBOX_LIST_PATH_TYPE_CONTROL,
oldname, newname);
(void)rename_dir(_storage, MAILBOX_LIST_PATH_TYPE_INDEX,
oldname, newname);
found = ret == 0;
ret = rename_subfolders(_storage, oldname, newname);
if (ret < 0)
return -1;
if (!found && ret == 0) {
mail_storage_set_error(_storage,
MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname);
return -1;
}
return 0;
}
if (errno == EEXIST) {
mail_storage_set_error(_storage,
"Target mailbox already exists");
return -1;
} else {
mail_storage_set_critical(_storage, "rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
}
static int maildir_storage_close(struct mailbox *box)
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
int ret = 0;
/*FIXME:if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
mail_storage_set_index_error(ibox);
ret = -1;
}*/
if (mbox->ibox.keep_locked)
maildir_uidlist_unlock(mbox->uidlist);
maildir_keywords_deinit(mbox->keywords);
maildir_uidlist_deinit(mbox->uidlist);
index_storage_mailbox_free(box);
return ret;
}
static void
maildir_notify_changes(struct mailbox *box, unsigned int min_interval,
mailbox_notify_callback_t *callback, void *context)
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
mbox->ibox.min_notify_interval = min_interval;
mbox->ibox.notify_callback = callback;
mbox->ibox.notify_context = context;
if (callback == NULL) {
index_mailbox_check_remove_all(&mbox->ibox);
return;
}
index_mailbox_check_add(&mbox->ibox,
t_strconcat(mbox->path, "/new", NULL));
index_mailbox_check_add(&mbox->ibox,
t_strconcat(mbox->path, "/cur", NULL));
}
static int
maildir_storage_is_mailbox(struct mail_storage *_storage,
const char *dir, const char *fname,
enum mailbox_list_iter_flags iter_flags
__attr_unused__,
enum mailbox_info_flags *flags,
enum mailbox_list_file_type type)
{
struct maildir_storage *storage = (struct maildir_storage *)_storage;
int ret;
if (fname[1] == mailbox_list_get_hierarchy_sep(_storage->list) &&
strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0 &&
strcmp(mailbox_list_get_driver_name(_storage->list),
MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) {
const char *path;
struct stat st;
/* this directory is in the middle of being deleted,
or the process trying to delete it had died.
delete it ourself if it's been there longer than
one hour. */
t_push();
path = t_strdup_printf("%s/%s", dir, fname);
if (stat(path, &st) == 0 &&
st.st_mtime < ioloop_time - 3600)
(void)unlink_directory(path, TRUE);
t_pop();
*flags = MAILBOX_NONEXISTENT;
return 0;
}
switch (type) {
case MAILBOX_LIST_FILE_TYPE_DIR:
/* all directories are valid maildirs */
return 1;
case MAILBOX_LIST_FILE_TYPE_FILE:
case MAILBOX_LIST_FILE_TYPE_OTHER:
/* non-directories are not */
*flags = MAILBOX_NOSELECT;
return 0;
case MAILBOX_LIST_FILE_TYPE_UNKNOWN:
case MAILBOX_LIST_FILE_TYPE_SYMLINK:
/* need to check with stat() to be sure */
break;
}
/* Check files beginning with .nfs always because they may be
temporary files created by the kernel */
if (storage->stat_dirs || strncmp(fname, ".nfs", 4) == 0) {
const char *path;
struct stat st;
t_push();
path = t_strdup_printf("%s/%s", dir, fname);
ret = (stat(path, &st) < 0 || !S_ISDIR(st.st_mode)) ? 0 : 1;
t_pop();
if (ret == 0)
*flags = MAILBOX_NONEXISTENT;
} else {
ret = 1;
}
return ret;
}
static void maildir_class_init(void)
{
maildir_transaction_class_init();
}
static void maildir_class_deinit(void)
{
maildir_transaction_class_deinit();
}
struct mail_storage maildir_storage = {
MEMBER(name) MAILDIR_STORAGE_NAME,
MEMBER(mailbox_is_file) FALSE,
{
maildir_class_init,
maildir_class_deinit,
maildir_create,
maildir_free,
maildir_autodetect,
index_storage_set_callbacks,
maildir_mailbox_open,
maildir_mailbox_create,
maildir_mailbox_delete,
maildir_mailbox_rename,
maildir_storage_is_mailbox,
index_storage_get_last_error
}
};
struct mailbox maildir_mailbox = {
MEMBER(name) NULL,
MEMBER(storage) NULL,
{
index_storage_is_readonly,
index_storage_allow_new_keywords,
maildir_storage_close,
index_storage_get_status,
maildir_storage_sync_init,
index_mailbox_sync_next,
index_mailbox_sync_deinit,
maildir_notify_changes,
index_transaction_begin,
index_transaction_commit,
index_transaction_rollback,
index_keywords_create,
index_keywords_free,
index_storage_get_uids,
index_mail_alloc,
index_header_lookup_init,
index_header_lookup_deinit,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next,
index_storage_search_next_update_seq,
maildir_save_init,
maildir_save_continue,
maildir_save_finish,
maildir_save_cancel,
maildir_copy,
index_storage_is_inconsistent
}
};