maildir-storage.c revision f0fa0904765d41e7e6c52606400199b004ae7492
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (C) 2002-2003 Timo Sirainen */
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
46552a931924c2d743f045e95b08c3ce6beda91aTimo Sirainen#include "lib.h"
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen#include "hostpid.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "home-expand.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "mkdir-parents.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "unlink-directory.h"
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen#include "subscription-file/subscription-file.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "maildir-storage.h"
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen#include "maildir-uidlist.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "maildir-keywords.h"
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen#include "index-mail.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen#include <stdio.h>
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen#include <stdlib.h>
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include <unistd.h>
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include <sys/stat.h>
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen#define CREATE_MODE 0770 /* umask() should limit it more */
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen/* Don't allow creating too long mailbox names. They could start causing
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen problems when they reach the limit. */
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen#define MAILDIR_MAX_MAILBOX_NAME_LENGTH (PATH_MAX/2)
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainenstruct rename_context {
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen bool found;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen size_t oldnamelen;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen const char *newname;
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen};
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenextern struct mail_storage maildir_storage;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenextern struct mailbox maildir_mailbox;
9508ac436fff0e1dcea975855c139cd251deb703Timo Sirainen
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainenstatic const char *maildirs[] = { "cur", "new", "tmp", NULL };
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainenstatic int verify_inbox(struct maildir_storage *storage);
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenstatic struct mail_storage *
493123e38ca1f27b07ac30dcbc59663c5fcdcba2Timo Sirainenmaildir_create(const char *data, const char *user,
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen enum mail_storage_flags flags,
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen enum mail_storage_lock_method lock_method)
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen{
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen struct maildir_storage *storage;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen struct index_storage *istorage;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen const char *root_dir, *inbox_dir, *index_dir, *control_dir;
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen const char *home, *path, *p;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen size_t len;
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen pool_t pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen inbox_dir = root_dir = index_dir = control_dir = NULL;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen if (data == NULL || *data == '\0') {
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen /* we'll need to figure out the maildir location ourself.
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen It's $HOME/Maildir unless we are chrooted. */
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if ((home = getenv("HOME")) != NULL) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen path = t_strconcat(home, "/Maildir", NULL);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (access(path, R_OK|W_OK|X_OK) == 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (debug) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen i_info("maildir: root exists (%s)",
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen path);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen root_dir = path;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen } else {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (debug) {
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen i_info("maildir: access(%s, rwx): "
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen "failed: %m", path);
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen }
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen }
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen } else {
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen if (debug)
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen i_info("maildir: HOME not set");
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen }
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen if (access("/cur", R_OK|W_OK|X_OK) == 0) {
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen if (debug)
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen i_info("maildir: /cur exists, assuming chroot");
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen root_dir = "/";
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen }
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen } else {
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen /* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (debug)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_info("maildir: data=%s", data);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen p = strchr(data, ':');
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (p == NULL)
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen root_dir = data;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen else {
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen root_dir = t_strdup_until(data, p);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen do {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p++;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (strncmp(p, "INBOX=", 6) == 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen inbox_dir = t_strcut(p+6, ':');
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen else if (strncmp(p, "INDEX=", 6) == 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen index_dir = t_strcut(p+6, ':');
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen else if (strncmp(p, "CONTROL=", 8) == 0)
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen control_dir = t_strcut(p+8, ':');
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen p = strchr(p, ':');
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen } while (p != NULL);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen if (root_dir == NULL) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (debug)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_info("maildir: couldn't find root dir");
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen /* strip trailing '/' */
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen len = strlen(root_dir);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (root_dir[len-1] == '/')
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen root_dir = t_strndup(root_dir, len-1);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (index_dir == NULL)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen index_dir = root_dir;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen else if (strcmp(index_dir, "MEMORY") == 0)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen index_dir = NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (debug) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen i_info("maildir: root=%s, index=%s, control=%s, inbox=%s",
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen root_dir, index_dir == NULL ? "" : index_dir,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen control_dir == NULL ? "" : control_dir,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen inbox_dir == NULL ? "" : inbox_dir);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen }
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen pool = pool_alloconly_create("storage", 512);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen storage = p_new(pool, struct maildir_storage, 1);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen storage->control_dir = p_strdup(pool, home_expand(control_dir));
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen istorage = INDEX_STORAGE(storage);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->storage = maildir_storage;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->storage.pool = pool;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* the default ".temp.xxx" prefix would be treated as directory */
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->temp_prefix =
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen p_strconcat(pool, "temp.", my_hostname, ".", my_pid, ".", NULL);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->dir = p_strdup(pool, home_expand(root_dir));
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->inbox_path = p_strdup(pool, home_expand(inbox_dir));
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen istorage->index_dir = p_strdup(pool, home_expand(index_dir));
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen istorage->user = p_strdup(pool, user);
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen istorage->callbacks = p_new(pool, struct mail_storage_callbacks, 1);
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen index_storage_init(istorage, flags, lock_method);
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen (void)verify_inbox(storage);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return STORAGE(storage);
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen}
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainen
5324117274df8564eeaebe369cb1eca76edb3165Timo Sirainenstatic void maildir_free(struct mail_storage *_storage)
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen{
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen struct index_storage *storage = (struct index_storage *) _storage;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen index_storage_deinit(storage);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen pool_unref(storage->storage.pool);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenstatic bool maildir_autodetect(const char *data, enum mail_storage_flags flags)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen{
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen struct stat st;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen const char *path;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen data = t_strcut(data, ':');
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen path = t_strconcat(data, "/cur", NULL);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (stat(path, &st) < 0) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen if (debug)
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen i_info("maildir autodetect: stat(%s) failed: %m", path);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen return FALSE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen }
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (!S_ISDIR(st.st_mode)) {
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (debug)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen i_info("maildir autodetect: %s not a directory", path);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return FALSE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen }
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return TRUE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen}
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainenstatic bool maildir_is_valid_create_name(struct mail_storage *storage,
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen const char *name)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen{
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen size_t len;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen len = strlen(name);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (len == 0 || len > MAILDIR_MAX_MAILBOX_NAME_LENGTH ||
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen name[0] == MAILDIR_FS_SEP || name[len-1] == MAILDIR_FS_SEP ||
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen strchr(name, '*') != NULL || strchr(name, '%') != NULL)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return FALSE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return TRUE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return FALSE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return TRUE;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen}
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainenstatic bool maildir_is_valid_existing_name(struct mail_storage *storage,
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen const char *name)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen{
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (name[0] == '\0' || name[strlen(name)-1] == '/')
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return FALSE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return TRUE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainenstatic const char *maildir_get_absolute_path(const char *name, bool unlink)
9bc0204ec8bda657ce2e96e6ae715e4034f1538bTimo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *p;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen name = home_expand(name);
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p = strrchr(name, '/');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (p == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return name;
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen return t_strconcat(t_strdup_until(name, p+1),
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen unlink ? MAILDIR_FS_SEP_S MAILDIR_FS_SEP_S :
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainen MAILDIR_FS_SEP_S, p+1, NULL);
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen}
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenconst char *maildir_get_path(struct index_storage *storage, const char *name)
27d50b3aa143964143e4bef66c0bfe3c72aea233Timo Sirainen{
27d50b3aa143964143e4bef66c0bfe3c72aea233Timo Sirainen if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen (*name == '/' || *name == '~'))
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen return maildir_get_absolute_path(name, FALSE);
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (strcmp(name, "INBOX") == 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return storage->inbox_path != NULL ?
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen storage->inbox_path : storage->dir;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen }
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainenstatic const char *
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainenmaildir_get_unlink_path(struct index_storage *storage, const char *name)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen{
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen (*name == '/' || *name == '~'))
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return maildir_get_absolute_path(name, TRUE);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return maildir_get_path(storage,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen t_strconcat(MAILDIR_FS_SEP_S, name, NULL));
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic const char *maildir_get_index_path(struct index_storage *storage,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen const char *name)
d229d26d263a57a77eec8fe7cba24fbfd9509966Timo Sirainen{
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (storage->index_dir == NULL)
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen return NULL;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (strcmp(name, "INBOX") == 0 &&
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen strcmp(storage->index_dir, storage->dir) == 0)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen return storage->dir;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if ((storage->storage.flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen (*name == '/' || *name == '~'))
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return maildir_get_absolute_path(name, FALSE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainenstatic const char *maildir_get_control_path(struct maildir_storage *storage,
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen const char *name)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen{
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen if (storage->control_dir == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return maildir_get_path(INDEX_STORAGE(storage), name);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if ((STORAGE(storage)->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 &&
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen (*name == '/' || *name == '~'))
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return maildir_get_absolute_path(name, FALSE);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen return t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen name, NULL);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen}
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic int mkdir_verify(struct index_storage *storage,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen const char *dir, bool verify)
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen{
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen struct stat st;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen if (verify) {
8e50329e2c5e3a199674ae9f6d3dfcddab02487bTimo Sirainen if (lstat(dir, &st) == 0)
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen return 0;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (errno != ENOENT) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_storage_set_critical(&storage->storage,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "lstat(%s) failed: %m", dir);
eb188b2a2d5395b2c6125f875ec053b04a10e5fbTimo Sirainen return -1;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen }
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen }
ac45ba9c603b67cc43fa7bceffdef0a19100720bTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (errno != EEXIST || !verify)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (errno != EEXIST && (!verify || errno != ENOENT)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_storage_set_critical(&storage->storage,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "mkdir(%s) failed: %m", dir);
6e8ad595d0603295f57bef576da8a3a00b55c5e2Timo Sirainen }
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen return -1;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen }
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen return 0;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen}
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen/* create or fix maildir, ignore if it already exists */
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainenstatic int create_maildir(struct index_storage *storage,
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const char *dir, bool verify)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const char **tmp, *path;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen if (!verify && mkdir_verify(storage, dir, verify) < 0)
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen for (tmp = maildirs; *tmp != NULL; tmp++) {
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen path = t_strconcat(dir, "/", *tmp, NULL);
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen if (mkdir_verify(storage, path, verify) < 0) {
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen if (!verify || errno != ENOENT)
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen return -1;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen /* small optimization. if we're verifying, we don't
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen check that the root dir actually exists unless we
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen fail here. */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (mkdir_verify(storage, dir, verify) < 0)
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen return -1;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen if (mkdir_verify(storage, path, verify) < 0)
80980955bb1bbcc1bd73623fe0912f334194ddd2Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen
31750e7fddc514c68c4eaf85b4f8c00000c281e0Timo Sirainenstatic int create_index_dir(struct index_storage *storage, const char *name)
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen{
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen const char *dir;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen if (storage->index_dir == NULL)
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen return 0;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen if (strcmp(storage->index_dir, storage->dir) == 0 ||
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL &&
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen strcmp(storage->index_dir, storage->inbox_path) == 0))
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen return 0;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen
31750e7fddc514c68c4eaf85b4f8c00000c281e0Timo Sirainen dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen mail_storage_set_critical(&storage->storage,
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen "mkdir(%s) failed: %m", dir);
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen return -1;
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen }
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen return 0;
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen}
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainenstatic int create_control_dir(struct maildir_storage *storage, const char *name)
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen{
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen const char *dir;
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen if (storage->control_dir == NULL)
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return 0;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen name, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen mail_storage_set_critical(STORAGE(storage),
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "mkdir(%s) failed: %m", dir);
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
3612ee5c737954d5fb88fd1775aad80f7bf1dc4eTimo Sirainen return 0;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstatic int verify_inbox(struct maildir_storage *storage)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct index_storage *istorage = INDEX_STORAGE(storage);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const char *path;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
path = istorage->inbox_path != NULL ?
istorage->inbox_path : istorage->dir;
if (create_maildir(istorage, path, TRUE) < 0)
return -1;
/* make sure the index directories exist */
if (create_index_dir(istorage, "INBOX") < 0)
return -1;
if (create_control_dir(storage, "INBOX") < 0)
return -1;
return 0;
}
static bool maildir_is_recent(struct index_mailbox *ibox, uint32_t uid)
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)ibox;
return maildir_uidlist_is_recent(mbox->uidlist, uid);
}
static struct mailbox *
maildir_open(struct maildir_storage *storage, const char *name,
enum mailbox_open_flags flags)
{
struct index_storage *istorage = INDEX_STORAGE(storage);
struct maildir_mailbox *mbox;
struct mail_index *index;
const char *path, *index_dir, *control_dir;
struct stat st;
int shared;
pool_t pool;
path = maildir_get_path(istorage, name);
index_dir = maildir_get_index_path(istorage, name);
control_dir = maildir_get_control_path(storage, name);
if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) != 0)
index_dir = NULL;
index = index_storage_alloc(index_dir, path,
MAILDIR_INDEX_PREFIX);
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file. */
shared = stat(t_strconcat(path, "/dovecot-shared", NULL), &st) == 0;
if (shared)
mail_index_set_permissions(index, st.st_mode & 0666, st.st_gid);
pool = pool_alloconly_create("mailbox", 1024);
mbox = p_new(pool, struct maildir_mailbox, 1);
mbox->ibox.box = maildir_mailbox;
mbox->ibox.box.pool = pool;
mbox->ibox.storage = istorage;
mbox->ibox.mail_vfuncs = &maildir_mail_vfuncs;
mbox->ibox.is_recent = maildir_is_recent;
if (index_storage_mailbox_init(&mbox->ibox, index, name, flags,
FALSE) < 0) {
/* the memory was already freed */
return NULL;
}
mbox->storage = storage;
mbox->path = p_strdup(pool, path);
mbox->control_dir = p_strdup(pool, control_dir);
mbox->uidlist = maildir_uidlist_init(mbox);
mbox->keywords = maildir_keywords_init(mbox);
if (!shared)
mbox->mail_create_mode = 0600;
else {
mbox->mail_create_mode = st.st_mode & 0666;
mbox->private_flags_mask = MAIL_SEEN;
}
return &mbox->ibox.box;
}
static struct mailbox *
maildir_mailbox_open(struct mail_storage *_storage, const char *name,
struct istream *input, enum mailbox_open_flags flags)
{
struct maildir_storage *storage = (struct maildir_storage *)_storage;
struct index_storage *istorage = INDEX_STORAGE(storage);
const char *path;
struct stat st;
mail_storage_clear_error(_storage);
if (input != NULL) {
mail_storage_set_critical(_storage,
"Maildir doesn't support streamed mailboxes");
return NULL;
}
if (strcmp(name, "INBOX") == 0) {
if (verify_inbox(storage) < 0)
return NULL;
return maildir_open(storage, "INBOX", flags);
}
if (!maildir_is_valid_existing_name(_storage, name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return NULL;
}
path = maildir_get_path(istorage, name);
if (stat(path, &st) == 0) {
/* exists - make sure the required directories are also there */
if (create_maildir(istorage, path, TRUE) < 0 ||
create_control_dir(storage, name) < 0)
return NULL;
if ((flags & MAILBOX_OPEN_NO_INDEX_FILES) == 0) {
if (create_index_dir(istorage, name) < 0)
return NULL;
}
return maildir_open(storage, name, flags);
} else if (errno == ENOENT) {
mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
name);
return NULL;
} else {
mail_storage_set_critical(_storage, "stat(%s) failed: %m",
path);
return NULL;
}
}
static int maildir_create_shared(struct mail_storage *storage,
const char *path, mode_t mode, gid_t gid)
{
mode_t old_mask = umask(0);
int fd;
fd = open(path, O_WRONLY | O_CREAT, mode);
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 index_storage *storage = (struct index_storage *)_storage;
struct stat st;
const char *path, *shared_path;
mail_storage_clear_error(_storage);
if (!maildir_is_valid_create_name(_storage, name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return -1;
}
path = maildir_get_path(storage, name);
if (create_maildir(storage, path, FALSE) < 0) {
if (errno == EEXIST) {
mail_storage_set_error(_storage,
"Mailbox already exists");
}
return -1;
}
/* if dovecot-shared exists in the root dir, copy it to the
created mailbox */
shared_path = t_strconcat(storage->dir, "/dovecot-shared", NULL);
if (stat(shared_path, &st) == 0) {
path = t_strconcat(path, "/dovecot-shared", NULL);
(void)maildir_create_shared(_storage, path,
st.st_mode & 0666, st.st_gid);
}
return 0;
}
static int maildir_mailbox_delete(struct mail_storage *_storage,
const char *name)
{
struct index_storage *storage = (struct index_storage *)_storage;
struct stat st;
const char *src, *dest, *index_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 (!maildir_is_valid_existing_name(_storage, name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return -1;
}
/* rename the .maildir into ..maildir which marks it as being
deleted. delete indexes before the actual maildir. this way we
never see partially deleted mailboxes. */
src = maildir_get_path(storage, name);
dest = maildir_get_unlink_path(storage, name);
if (stat(src, &st) != 0 && errno == ENOENT) {
mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
name);
return -1;
}
if (storage->index_dir != NULL && *name != '/' && *name != '~' &&
strcmp(storage->index_dir, storage->dir) != 0) {
index_dir = t_strconcat(storage->index_dir,
"/"MAILDIR_FS_SEP_S, name, NULL);
index_storage_destroy_unrefed();
/* it can fail with some NFS implementations if indexes are
opened by another session.. can't really help it. */
if (unlink_directory(index_dir, TRUE) < 0 &&
errno != ENOTEMPTY) {
mail_storage_set_critical(_storage,
"unlink_directory(%s) failed: %m", index_dir);
return -1;
}
}
count = 0;
while (rename(src, dest) < 0 && count < 2) {
if (errno != EEXIST && errno != ENOTEMPTY) {
mail_storage_set_critical(_storage,
"rename(%s, %s) failed: %m", src, dest);
return -1;
}
/* ..dir 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 client is concerned. Report success. */
}
return 0;
}
static int rename_indexes(struct index_storage *storage,
const char *oldname, const char *newname)
{
const char *oldpath, *newpath;
if (storage->index_dir == NULL ||
strcmp(storage->index_dir, storage->dir) == 0)
return 0;
/* Rename it's index. */
oldpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
oldname, NULL);
newpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
newname, NULL);
if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
mail_storage_set_critical(&storage->storage,
"rename(%s, %s) failed: %m",
oldpath, newpath);
return -1;
}
return 0;
}
static int rename_subfolders(struct index_storage *storage,
const char *oldname, const char *newname)
{
struct mailbox_list_context *ctx;
struct mailbox_list *list;
const char *oldpath, *newpath, *new_listname;
size_t oldnamelen;
int ret;
ret = 0;
oldnamelen = strlen(oldname);
ctx = maildir_mailbox_list_init(&storage->storage, oldname, "*",
MAILBOX_LIST_FAST_FLAGS);
while ((list = maildir_mailbox_list_next(ctx)) != NULL) {
t_push();
i_assert(oldnamelen <= strlen(list->name));
new_listname = t_strconcat(newname,
list->name + oldnamelen, NULL);
oldpath = maildir_get_path(storage, list->name);
newpath = maildir_get_path(storage, new_listname);
/* 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->storage,
"rename(%s, %s) failed: %m",
oldpath, newpath);
ret = -1;
t_pop();
break;
}
(void)rename_indexes(storage, list->name, new_listname);
t_pop();
}
if (maildir_mailbox_list_deinit(ctx) < 0)
return -1;
return ret;
}
static int maildir_mailbox_rename(struct mail_storage *_storage,
const char *oldname, const char *newname)
{
struct index_storage *storage = (struct index_storage *)_storage;
const char *oldpath, *newpath;
int ret;
bool found;
mail_storage_clear_error(_storage);
if (!maildir_is_valid_existing_name(_storage, oldname) ||
!maildir_is_valid_create_name(_storage, 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 = maildir_get_path(storage, oldname);
newpath = maildir_get_path(storage, newname);
ret = rename(oldpath, newpath);
if (ret == 0 || errno == ENOENT) {
(void)rename_indexes(storage, 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,
"Mailbox doesn't exist");
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_set_subscribed(struct mail_storage *_storage,
const char *name, bool set)
{
struct maildir_storage *storage = (struct maildir_storage *)_storage;
const char *path;
path = t_strconcat(storage->control_dir != NULL ?
storage->control_dir : INDEX_STORAGE(storage)->dir,
"/" SUBSCRIPTION_FILE_NAME, NULL);
return subsfile_set_subscribed(_storage, path,
INDEX_STORAGE(storage)->temp_prefix,
name, set);
}
static int maildir_get_mailbox_name_status(struct mail_storage *_storage,
const char *name,
enum mailbox_name_status *status)
{
struct index_storage *storage = (struct index_storage *)_storage;
struct stat st;
const char *path;
mail_storage_clear_error(_storage);
if (!maildir_is_valid_existing_name(_storage, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
path = maildir_get_path(storage, name);
if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) {
*status = MAILBOX_NAME_EXISTS;
return 0;
}
if (!maildir_is_valid_create_name(_storage, name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
if (errno == ENOENT) {
*status = MAILBOX_NAME_VALID;
return 0;
} else {
mail_storage_set_critical(_storage, "stat(%s) failed: %m",
path);
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;
}*/
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));
}
struct mail_storage maildir_storage = {
MEMBER(name) "maildir",
MEMBER(hierarchy_sep) '.',
{
maildir_create,
maildir_free,
maildir_autodetect,
index_storage_set_callbacks,
maildir_mailbox_open,
maildir_mailbox_create,
maildir_mailbox_delete,
maildir_mailbox_rename,
maildir_mailbox_list_init,
maildir_mailbox_list_next,
maildir_mailbox_list_deinit,
maildir_set_subscribed,
maildir_get_mailbox_name_status,
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,
maildir_transaction_begin,
maildir_transaction_commit,
maildir_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_get_sorting,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next,
maildir_save_init,
maildir_save_continue,
maildir_save_finish,
maildir_save_cancel,
maildir_copy,
index_storage_is_inconsistent
}
};