maildir-storage.c revision 36122480cc7080b7e3d17faa9b7ced90331bd308
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "lib.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "ioloop.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mkdir-parents.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "eacces-error.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "unlink-old-files.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mailbox-uidvalidity.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mailbox-list-private.h"
b7324e421e2132cbbf753e6fdbe675bbaecdf929Timo Sirainen#include "maildir-storage.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "maildir-uidlist.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "maildir-keywords.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "maildir-sync.h"
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#include "index-mail.h"
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#include <sys/stat.h>
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#define MAILDIR_LIST_CONTEXT(obj) \
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen MODULE_CONTEXT(obj, maildir_mailbox_list_module)
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainenstruct maildir_mailbox_list_context {
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen union mailbox_list_module_context module_ctx;
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen const struct maildir_settings *set;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen};
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenextern struct mail_storage maildir_storage;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenextern struct mailbox maildir_mailbox;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(maildir_mailbox_list_module,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen &mailbox_list_module_register);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic const char *maildir_subdirs[] = { "cur", "new", "tmp" };
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic struct mail_storage *maildir_storage_alloc(void)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen struct maildir_storage *storage;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen pool_t pool;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen pool = pool_alloconly_create("maildir storage", 512+256);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen storage = p_new(pool, struct maildir_storage, 1);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen storage->storage = maildir_storage;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen storage->storage.pool = pool;
92f5ea24e989266539e97c6fe59ede0565aec6fdTimo Sirainen return &storage->storage;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
fb79b36eb34532dbe67caf99eefe3660b8c841e0Timo Sirainen
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainenstatic int
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainenmaildir_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen const char **error_r ATTR_UNUSED)
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen{
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen struct maildir_storage *storage = (struct maildir_storage *)_storage;
92f5ea24e989266539e97c6fe59ede0565aec6fdTimo Sirainen struct mailbox_list *list = ns->list;
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen const char *dir;
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen storage->set = mail_storage_get_driver_settings(_storage);
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen storage->maildir_list_ext_id = (uint32_t)-1;
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen storage->temp_prefix = p_strdup(_storage->pool,
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen mailbox_list_get_temp_prefix(list));
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen if (list->set.control_dir == NULL && list->set.inbox_path == NULL &&
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen /* put the temp files into tmp/ directory preferrably */
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen storage->temp_prefix = p_strconcat(_storage->pool, "tmp/",
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen storage->temp_prefix, NULL);
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen dir = mailbox_list_get_path(list, NULL,
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen MAILBOX_LIST_PATH_TYPE_DIR);
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen } else {
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen /* control dir should also be writable */
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen dir = mailbox_list_get_path(list, NULL,
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL);
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen }
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/",
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen storage->temp_prefix, NULL);
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen return 0;
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen}
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainenstatic void maildir_storage_get_list_settings(const struct mail_namespace *ns,
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen struct mailbox_list_settings *set)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen{
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (set->layout == NULL)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen set->layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS;
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (set->subscription_fname == NULL)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME;
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen if (set->inbox_path == NULL && set->maildir_name == NULL &&
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen (strcmp(set->layout, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 ||
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen strcmp(set->layout, MAILBOX_LIST_NAME_FS) == 0) &&
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen /* Maildir++ INBOX is the Maildir base itself */
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen set->inbox_path = set->root_dir;
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen }
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen}
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenstatic const char *
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainenmaildir_storage_find_root_dir(const struct mail_namespace *ns)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen{
2aee623fcad4b931c27435ceaa495c3d3edd69b6Aki Tuomi bool debug = ns->mail_set->mail_debug;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen const char *home, *path;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen /* we'll need to figure out the maildir location ourself.
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen It's ~/Maildir unless we are chrooted. */
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (mail_user_get_home(ns->user, &home) > 0) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen path = t_strconcat(home, "/Maildir", NULL);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (access(path, R_OK|W_OK|X_OK) == 0) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (debug)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen i_debug("maildir: root exists (%s)", path);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return path;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen }
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (debug)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen i_debug("maildir: access(%s, rwx): failed: %m", path);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen } else {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (debug)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen i_debug("maildir: Home directory not set");
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (access("/cur", R_OK|W_OK|X_OK) == 0) {
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if (debug)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen i_debug("maildir: /cur exists, assuming chroot");
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen return "/";
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen }
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen }
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen return NULL;
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen}
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainenstatic bool maildir_storage_autodetect(const struct mail_namespace *ns,
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen struct mailbox_list_settings *set)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen{
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen bool debug = ns->mail_set->mail_debug;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen struct stat st;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen const char *path, *root_dir;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if (set->root_dir != NULL)
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen root_dir = set->root_dir;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen else {
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen root_dir = maildir_storage_find_root_dir(ns);
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if (root_dir == NULL) {
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if (debug)
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen i_debug("maildir: couldn't find root dir");
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen return FALSE;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen }
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen }
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen path = t_strconcat(root_dir, "/cur", NULL);
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if (stat(path, &st) < 0) {
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen if (debug)
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen i_debug("maildir autodetect: stat(%s) failed: %m", path);
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen return FALSE;
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen }
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen if (!S_ISDIR(st.st_mode)) {
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen if (debug)
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen i_debug("maildir autodetect: %s not a directory", path);
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen return FALSE;
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen }
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen set->root_dir = root_dir;
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen maildir_storage_get_list_settings(ns, set);
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen return TRUE;
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen}
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainenstatic int
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainenmkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
632018810af689442569cbb0139c55868923ccfeTimo Sirainen const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen bool verify)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen{
632018810af689442569cbb0139c55868923ccfeTimo Sirainen struct stat st;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if (verify) {
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if (stat(dir, &st) == 0)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen return 0;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if (errno != ENOENT) {
632018810af689442569cbb0139c55868923ccfeTimo Sirainen mail_storage_set_critical(storage,
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen "stat(%s) failed: %m", dir);
632018810af689442569cbb0139c55868923ccfeTimo Sirainen return -1;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen }
632018810af689442569cbb0139c55868923ccfeTimo Sirainen }
632018810af689442569cbb0139c55868923ccfeTimo Sirainen
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen if (mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0)
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen return 0;
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen if (errno == EEXIST) {
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen if (verify)
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen return 0;
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen mail_storage_set_error(storage, MAIL_ERROR_EXISTS,
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen "Mailbox already exists");
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen } else if (errno == ENOENT) {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen mail_storage_set_error(storage, MAIL_ERROR_NOTFOUND,
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen "Mailbox was deleted while it was being created");
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen } else if (errno == EACCES) {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen if (ns->type == NAMESPACE_SHARED) {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen /* shared namespace, don't log permission errors */
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen mail_storage_set_error(storage, MAIL_ERROR_PERM,
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen MAIL_ERRSTR_NO_PERMISSION);
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen return -1;
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen }
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen mail_storage_set_critical(storage, "%s",
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen mail_error_create_eacces_msg("mkdir", dir));
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen } else {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen mail_storage_set_critical(storage,
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen "mkdir(%s) failed: %m", dir);
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen }
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen return -1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic int maildir_check_tmp(struct mail_storage *storage, const char *dir)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen const char *path;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen struct stat st;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /* if tmp/ directory exists, we need to clean it up once in a while */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen path = t_strconcat(dir, "/tmp", NULL);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (stat(path, &st) < 0) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (errno == ENOENT)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return 0;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (errno == EACCES) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mail_storage_set_critical(storage, "%s",
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mail_error_eacces_msg("stat", path));
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return -1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return -1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (st.st_atime > st.st_ctime + MAILDIR_TMP_DELETE_SECS) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /* the directory should be empty. we won't do anything
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen until ctime changes. */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen } else if (st.st_atime < ioloop_time - MAILDIR_TMP_SCAN_SECS) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /* time to scan */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen (void)unlink_old_files(path, "",
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen ioloop_time - MAILDIR_TMP_DELETE_SECS);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return 1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen/* create or fix maildir, ignore if it already exists */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic int create_maildir(struct mailbox *box, bool verify)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen const char *path;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen unsigned int i;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen enum mail_error error;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen int ret = 0;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen path = t_strconcat(box->path, "/", maildir_subdirs[i], NULL);
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen if (mkdir_verify(box->storage, box->list->ns, path,
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen box->dir_create_mode, box->file_create_gid,
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen box->file_create_gid_origin, verify) < 0) {
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen (void)mail_storage_get_last_error(box->storage, &error);
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen if (error != MAIL_ERROR_EXISTS)
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen return -1;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen /* try to create all of the directories in case one
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen of them doesn't exist */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen ret = -1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return ret;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen}
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic void maildir_lock_touch_timeout(struct maildir_mailbox *mbox)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen (void)maildir_uidlist_lock_touch(mbox->uidlist);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
static struct mailbox *
maildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list,
const char *name, enum mailbox_flags flags)
{
struct maildir_mailbox *mbox;
struct index_mailbox_context *ibox;
pool_t pool;
pool = pool_alloconly_create("maildir mailbox", 1024*3);
mbox = p_new(pool, struct maildir_mailbox, 1);
mbox->box = maildir_mailbox;
mbox->box.pool = pool;
mbox->box.storage = storage;
mbox->box.list = list;
mbox->box.mail_vfuncs = &maildir_mail_vfuncs;
index_storage_mailbox_alloc(&mbox->box, name, flags,
MAILDIR_INDEX_PREFIX);
ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
ibox->save_commit_pre = maildir_transaction_save_commit_pre;
ibox->save_commit_post = maildir_transaction_save_commit_post;
ibox->save_rollback = maildir_transaction_save_rollback;
mbox->storage = (struct maildir_storage *)storage;
mbox->maildir_ext_id =
mail_index_ext_register(mbox->box.index, "maildir",
sizeof(mbox->maildir_hdr), 0, 0);
return &mbox->box;
}
static int maildir_mailbox_open_existing(struct mailbox *box)
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
struct stat st;
const char *shared_path;
mbox->uidlist = maildir_uidlist_init(mbox);
mbox->keywords = maildir_keywords_init(mbox);
shared_path = t_strconcat(box->path, "/dovecot-shared", NULL);
if (stat(shared_path, &st) == 0)
box->private_flags_mask = MAIL_SEEN;
if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
if (maildir_uidlist_lock(mbox->uidlist) <= 0)
return -1;
mbox->keep_lock_to = timeout_add(MAILDIR_LOCK_TOUCH_SECS * 1000,
maildir_lock_touch_timeout,
mbox);
}
if (access(t_strconcat(box->path, "/cur", NULL), W_OK) < 0 &&
errno == EACCES)
mbox->box.backend_readonly = TRUE;
return index_storage_mailbox_open(box, FALSE);
}
static int maildir_mailbox_open(struct mailbox *box)
{
const char *root_dir;
struct stat st;
int ret;
/* begin by checking if tmp/ directory exists and if it should be
cleaned up. */
ret = maildir_check_tmp(box->storage, box->path);
if (ret > 0) {
/* exists */
return maildir_mailbox_open_existing(box);
}
if (ret < 0)
return -1;
/* tmp/ directory doesn't exist. does the maildir? */
root_dir = mailbox_list_get_path(box->list, NULL,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
if (strcmp(box->path, root_dir) == 0) {
/* root directory. either INBOX or some other namespace root */
errno = ENOENT;
} else if (stat(box->path, &st) == 0) {
/* yes, we'll need to create the missing dirs */
if (create_maildir(box, TRUE) < 0)
return -1;
return maildir_mailbox_open_existing(box);
}
if (errno == ENOENT) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
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 char *path;
mode_t old_mask;
int fd;
old_mask = umask(0);
path = t_strconcat(box->path, "/dovecot-shared", NULL);
fd = open(path, O_WRONLY | O_CREAT, box->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, box->file_create_gid) < 0) {
if (errno == EPERM) {
mail_storage_set_critical(box->storage, "%s",
eperm_error_get_chgrp("fchown", path,
box->file_create_gid,
box->file_create_gid_origin));
} else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", path);
}
}
(void)close(fd);
return 0;
}
static int
maildir_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
struct maildir_uidlist *uidlist;
int ret;
if (!box->opened) {
if (mailbox_open(box) < 0)
return -1;
}
uidlist = mbox->uidlist;
if (maildir_uidlist_lock(uidlist) <= 0)
return -1;
if (!mail_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);
maildir_uidlist_unlock(uidlist);
return ret;
}
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 (directory &&
(box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
return 0;
ret = maildir_check_tmp(box->storage, box->path);
if (ret > 0) {
mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
"Mailbox already exists");
return -1;
}
if (ret < 0)
return -1;
if (create_maildir(box, FALSE) < 0)
return -1;
/* if dovecot-shared exists in the root dir, copy it to newly
created mailboxes */
root_dir = mailbox_list_get_path(box->list, NULL,
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)
return -1;
}
return update == NULL ? 0 : maildir_mailbox_update(box, update);
}
static int
maildir_mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
return maildir_uidlist_get_mailbox_guid(mbox->uidlist, guid);
}
static void maildir_mailbox_close(struct mailbox *box)
{
struct maildir_mailbox *mbox = (struct 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 = (struct maildir_mailbox *)box;
if (box->notify_callback == NULL)
index_mailbox_check_remove_all(&mbox->box);
else {
index_mailbox_check_add(&mbox->box,
t_strconcat(mbox->box.path, "/new", NULL));
index_mailbox_check_add(&mbox->box,
t_strconcat(mbox->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 int
maildir_list_get_mailbox_flags(struct mailbox_list *list,
const char *dir, const char *fname,
enum mailbox_list_file_type type,
struct stat *st_r,
enum mailbox_info_flags *flags)
{
struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list);
struct stat st2;
const char *cur_path;
int ret;
ret = mlist->module_ctx.super.
get_mailbox_flags(list, dir, fname, type, st_r, flags);
if (ret <= 0 || MAILBOX_INFO_FLAGS_FINISHED(*flags))
return ret;
/* see if it's a selectable mailbox. after that we can figure out based
on the link count if we have child mailboxes or not. for a
selectable mailbox we have 3 more links (cur/, new/ and tmp/)
than non-selectable. */
cur_path = t_strconcat(dir, "/", fname, "/cur", NULL);
if ((ret = stat(cur_path, &st2)) < 0 || !S_ISDIR(st2.st_mode)) {
if (ret < 0 && errno == ENOENT)
*flags |= MAILBOX_NONEXISTENT;
else
*flags |= MAILBOX_NOSELECT;
if (st_r->st_nlink > 2)
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
} else {
if (st_r->st_nlink > 5)
*flags |= MAILBOX_CHILDREN;
else
*flags |= MAILBOX_NOCHILDREN;
}
return 1;
}
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_storage_get_driver_settings(storage);
list->v.is_internal_name = maildir_is_internal_name;
list->v.get_mailbox_flags = maildir_list_get_mailbox_flags;
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_path(list, NULL,
MAILBOX_LIST_PATH_TYPE_CONTROL);
path = t_strconcat(path, "/"MAILDIR_UIDVALIDITY_FNAME, NULL);
return mailbox_uidvalidity_next(list, path);
}
struct mail_storage maildir_storage = {
.name = MAILDIR_STORAGE_NAME,
.class_flags = 0,
.v = {
maildir_get_setting_parser_info,
maildir_storage_alloc,
maildir_storage_create,
NULL,
maildir_storage_add_list,
maildir_storage_get_list_settings,
maildir_storage_autodetect,
maildir_mailbox_alloc,
NULL
}
};
struct mailbox maildir_mailbox = {
.v = {
index_storage_is_readonly,
index_storage_allow_new_keywords,
index_storage_mailbox_enable,
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_guid,
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,
index_transaction_set_max_modseq,
index_keywords_create,
index_keywords_create_from_indexes,
index_keywords_ref,
index_keywords_unref,
index_keyword_is_valid,
index_storage_get_seq_range,
index_storage_get_uid_range,
index_storage_get_expunges,
NULL,
NULL,
NULL,
index_mail_alloc,
index_header_lookup_init,
index_header_lookup_deinit,
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,
index_storage_is_inconsistent
}
};