maildir-storage.c revision 20a802016205bbcafc90f164f769ea801f88d014
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2002-2003 Timo Sirainen */
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen#include "subscription-file/subscription-file.h"
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen#define CREATE_MODE 0770 /* umask() should limit it more */
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenstatic const char *maildirs[] = { "cur", "new", "tmp", NULL };
fccd110b494a7e31f23d31d9e3bc3e986c9bb1a8Timo Sirainenstatic int verify_inbox(struct index_storage *storage);
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainenmaildir_create(const char *data, const char *user)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen const char *root_dir, *inbox_dir, *index_dir, *control_dir;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen inbox_dir = root_dir = index_dir = control_dir = NULL;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen /* we'll need to figure out the maildir location ourself.
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen it's either root dir if we've already chroot()ed, or
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen $HOME/Maildir otherwise */
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen /* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen } while (p != NULL);
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen /* strip trailing '/' */
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen /* the default ".temp.xxx" prefix would be treated as directory */
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen i_strconcat("temp.", my_hostname, ".", my_pid, ".", NULL);
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen storage->dir = i_strdup(home_expand(root_dir));
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen storage->inbox_path = i_strdup(home_expand(inbox_dir));
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen storage->index_dir = i_strdup(home_expand(index_dir));
1270cb6b6139001b0a89f595ad0868b1f3a0af45Timo Sirainen storage->control_dir = i_strdup(home_expand(control_dir));
1270cb6b6139001b0a89f595ad0868b1f3a0af45Timo Sirainen storage->callbacks = i_new(struct mail_storage_callbacks, 1);
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainenstatic void maildir_free(struct mail_storage *_storage)
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen struct index_storage *storage = (struct index_storage *) _storage;
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainenstatic int maildir_autodetect(const char *data)
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen return stat(t_strconcat(data, "/cur", NULL), &st) == 0 &&
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainenstatic int maildir_is_valid_create_name(const char *name)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen strchr(name, '*') != NULL || strchr(name, '%') != NULL)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainenstatic int maildir_is_valid_existing_name(const char *name)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (name[0] == '\0' || name[strlen(name)-1] == '/')
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainenstatic const char *maildir_get_absolute_path(const char *name, int unlink)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen const char *p;
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainenconst char *maildir_get_path(struct index_storage *storage, const char *name)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen return maildir_get_absolute_path(name, FALSE);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainenstatic const char *
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenmaildir_get_unlink_path(struct index_storage *storage, const char *name)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenstatic const char *maildir_get_index_path(struct index_storage *storage,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return maildir_get_absolute_path(name, FALSE);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenstatic const char *maildir_get_control_path(struct index_storage *storage,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return maildir_get_absolute_path(name, FALSE);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainenstatic int mkdir_verify(struct index_storage *storage,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && (errno != EEXIST || !verify)) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (errno != EEXIST && (!verify || errno != ENOENT)) {
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen/* create or fix maildir, ignore if it already exists */
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainenstatic int create_maildir(struct index_storage *storage,
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if (!verify && mkdir_verify(storage, dir, verify) < 0)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if (mkdir_verify(storage, path, verify) < 0) {
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen /* small optimization. if we're verifying, we don't
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen check that the root dir actually exists unless we
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen fail here. */
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainenstatic int create_index_dir(struct index_storage *storage, const char *name)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen const char *dir;
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if (strcmp(storage->index_dir, storage->dir) == 0 ||
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL &&
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen strcmp(storage->index_dir, storage->inbox_path) == 0))
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if (mkdir_parents(dir, CREATE_MODE) == -1 && errno != EEXIST) {
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainenstatic int create_control_dir(struct index_storage *storage, const char *name)
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen const char *dir;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainenstatic int verify_inbox(struct index_storage *storage)
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen /* first make sure the cur/ new/ and tmp/ dirs exist
632018810af689442569cbb0139c55868923ccfeTimo Sirainen in root dir */
fccd110b494a7e31f23d31d9e3bc3e986c9bb1a8Timo Sirainen if (create_maildir(storage, storage->dir, TRUE) < 0)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen /* create the .INBOX directory */
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if (create_maildir(storage, storage->inbox_path, TRUE) < 0)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen /* make sure the index directories exist */
632018810af689442569cbb0139c55868923ccfeTimo Sirainenstatic int maildir_is_recent(struct index_mailbox *ibox, uint32_t uid)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen return maildir_uidlist_is_recent(ibox->uidlist, uid);
632018810af689442569cbb0139c55868923ccfeTimo Sirainenstatic struct mailbox *
632018810af689442569cbb0139c55868923ccfeTimo Sirainenmaildir_open(struct index_storage *storage, const char *name,
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen index_dir = maildir_get_index_path(storage, name);
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen control_dir = maildir_get_control_path(storage, name);
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen index = index_storage_alloc(index_dir, path, MAILDIR_INDEX_PREFIX);
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen ibox = index_storage_mailbox_init(storage, &maildir_mailbox,
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen /* for shared mailboxes get the create mode from the
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen permissions of dovecot-shared file */
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen if (stat(t_strconcat(path, "/dovecot-shared", NULL), &st) < 0)
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen mail_index_set_permissions(ibox->index, st.st_mode & 0666,
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainenstatic struct mailbox *
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainenmaildir_mailbox_open(struct mail_storage *_storage,
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen const char *name, enum mailbox_open_flags flags)
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen mail_storage_set_error(_storage, "Invalid mailbox name");
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen /* exists - make sure the required directories are also there */
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen if (create_maildir(storage, path, TRUE) < 0 ||
b8b005887cd7f72520c6dcc325461faeecc5f9e9Timo Sirainen mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen mail_storage_set_critical(_storage, "stat(%s) failed: %m",
fb79b36eb34532dbe67caf99eefe3660b8c841e0Timo Sirainenstatic int maildir_mailbox_create(struct mail_storage *_storage,
632018810af689442569cbb0139c55868923ccfeTimo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen mail_storage_set_error(_storage, "Invalid mailbox name");
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen if (create_maildir(storage, path, FALSE) < 0) {
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen "Mailbox already exists");
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainenstatic int maildir_mailbox_delete(struct mail_storage *_storage,
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen mail_storage_set_error(_storage, "INBOX can't be deleted.");
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen mail_storage_set_error(_storage, "Invalid mailbox name");
632018810af689442569cbb0139c55868923ccfeTimo Sirainen /* rename the .maildir into ..maildir which marks it as being
632018810af689442569cbb0139c55868923ccfeTimo Sirainen deleted. delete indexes before the actual maildir. this way we
632018810af689442569cbb0139c55868923ccfeTimo Sirainen never see partially deleted mailboxes. */
632018810af689442569cbb0139c55868923ccfeTimo Sirainen dest = maildir_get_unlink_path(storage, name);
632018810af689442569cbb0139c55868923ccfeTimo Sirainen mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if (storage->index_dir != NULL && *name != '/' && *name != '~' &&
632018810af689442569cbb0139c55868923ccfeTimo Sirainen strcmp(storage->index_dir, storage->dir) != 0) {
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen /* it can fail with some NFS implementations if indexes are
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen opened by another session.. can't really help it. */
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen "unlink_directory(%s) failed: %m", index_dir);
632018810af689442569cbb0139c55868923ccfeTimo Sirainen /* ..dir already existed? delete it and try again */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen /* it's already renamed to ..dir, which means it's deleted
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen as far as client is concerned. Report success. */
37ce8d49a351f073958624b78c702af75362c1cbTimo Sirainenstatic int rename_indexes(struct index_storage *storage,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen strcmp(storage->index_dir, storage->dir) == 0)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /* Rename it's index. */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen oldpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen newpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen "rename(%s, %s) failed: %m",
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic int rename_subfolders(struct index_storage *storage,
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen ctx = maildir_mailbox_list_init(&storage->storage, oldname, "*",
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen while ((list = maildir_mailbox_list_next(ctx)) != NULL) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen oldpath = maildir_get_path(storage, list->name);
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen newpath = maildir_get_path(storage, new_listname);
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen /* FIXME: it's possible to merge two folders if either one of
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen them doesn't have existing root folder. We could check this
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen but I'm not sure if it's worth it. It could be even
1e167fbb281ccf41178a0b70495193c768f9ff75Timo Sirainen considered as a feature.
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen Anyway, the bug with merging is that if both folders have
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen identically named subfolder they conflict. Just ignore those
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen and leave them under the old folder. */
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen "rename(%s, %s) failed: %m",
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen (void)rename_indexes(storage, list->name, new_listname);
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainenstatic int maildir_mailbox_rename(struct mail_storage *_storage,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (!maildir_is_valid_existing_name(oldname) ||
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen mail_storage_set_error(_storage, "Invalid mailbox name");
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen "Renaming INBOX isn't supported.");
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen /* NOTE: it's possible to rename a nonexisting folder which has
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen subfolders. In that case we should ignore the rename() error. */
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen (void)rename_indexes(storage, oldname, newname);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen ret = rename_subfolders(storage, oldname, newname);
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen "Mailbox doesn't exist");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen "Target mailbox already exists");
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen mail_storage_set_critical(_storage, "rename(%s, %s) failed: %m",
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainenstatic int maildir_set_subscribed(struct mail_storage *_storage,
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen path = t_strconcat(storage->control_dir != NULL ?
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen return subsfile_set_subscribed(_storage, path, storage->temp_prefix,
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainenstatic int maildir_get_mailbox_name_status(struct mail_storage *_storage,
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen struct index_storage *storage = (struct index_storage *)_storage;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mail_storage_set_critical(_storage, "stat(%s) failed: %m",
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic int maildir_storage_close(struct mailbox *box)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen struct index_mailbox *ibox = (struct index_mailbox *)box;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /*FIXME:if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mail_storage_set_index_error(ibox);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenmaildir_notify_changes(struct mailbox *box, unsigned int min_interval,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen mailbox_notify_callback_t *callback, void *context)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen struct index_mailbox *ibox = (struct index_mailbox *)box;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen t_strconcat(ibox->storage->dir, "/new", NULL), TRUE);