maildir-storage.c revision 6913a451b76b1bdb53be55e2f2f7ad572b2c2b8a
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2002-2003 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "hostpid.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "home-expand.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mkdir-parents.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "unlink-directory.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "subscription-file/subscription-file.h"
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen#include "maildir-storage.h"
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen#include "maildir-uidlist.h"
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen#include <stdio.h>
d7a56951e519c28ac9b1fd5810a455a9ba238327Timo Sirainen#include <stdlib.h>
d7a56951e519c28ac9b1fd5810a455a9ba238327Timo Sirainen#include <unistd.h>
15b9759df8e4f6fb00c115353827a2aebbebfebcTimo Sirainen#include <sys/stat.h>
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#define CREATE_MODE 0770 /* umask() should limit it more */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstruct rename_context {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen int found;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen size_t oldnamelen;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *newname;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen};
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenextern struct mail_storage maildir_storage;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenextern struct mailbox maildir_mailbox;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainenstatic const char *maildirs[] = { "cur", "new", "tmp", NULL };
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainenstatic int verify_inbox(struct index_storage *storage);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct mail_storage *
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenmaildir_create(const char *data, const char *user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *namespace, char hierarchy_sep)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct index_storage *storage;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen const char *root_dir, *inbox_dir, *index_dir, *control_dir;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *home, *path, *p;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t len;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen inbox_dir = root_dir = index_dir = control_dir = NULL;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (data == NULL || *data == '\0') {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* we'll need to figure out the maildir location ourself.
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen it's either root dir if we've already chroot()ed, or
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen $HOME/Maildir otherwise */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (access("/cur", R_OK|W_OK|X_OK) == 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen root_dir = "/";
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen else {
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen home = getenv("HOME");
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (home != NULL) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen path = t_strconcat(home, "/Maildir", NULL);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (access(path, R_OK|W_OK|X_OK) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen root_dir = path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen } else {
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen /* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen p = strchr(data, ':');
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen if (p == NULL)
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen root_dir = data;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen else {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen root_dir = t_strdup_until(data, p);
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen do {
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen p++;
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen if (strncmp(p, "INBOX=", 6) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen inbox_dir = t_strcut(p+6, ':');
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen else if (strncmp(p, "INDEX=", 6) == 0)
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen index_dir = t_strcut(p+6, ':');
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen else if (strncmp(p, "CONTROL=", 8) == 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen control_dir = t_strcut(p+8, ':');
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen p = strchr(p, ':');
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } while (p != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (root_dir == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return NULL;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* strip trailing '/' */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen len = strlen(root_dir);
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen if (root_dir[len-1] == '/')
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen root_dir = t_strndup(root_dir, len-1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (index_dir == NULL)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen index_dir = root_dir;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen else if (strcmp(index_dir, "MEMORY") == 0)
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen index_dir = NULL;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen storage = i_new(struct index_storage, 1);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen storage->storage = maildir_storage;
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (hierarchy_sep != '\0')
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen storage->storage.hierarchy_sep = hierarchy_sep;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen storage->storage.namespace = i_strdup(namespace);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen /* the default ".temp.xxx" prefix would be treated as directory */
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen storage->temp_prefix =
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen i_strconcat("temp.", my_hostname, ".", my_pid, ".", NULL);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen storage->dir = i_strdup(home_expand(root_dir));
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen storage->inbox_path = i_strdup(home_expand(inbox_dir));
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen storage->index_dir = i_strdup(home_expand(index_dir));
4b41116563110d00330896a568eff1078c382827Timo Sirainen storage->control_dir = i_strdup(home_expand(control_dir));
4b41116563110d00330896a568eff1078c382827Timo Sirainen storage->user = i_strdup(user);
4b41116563110d00330896a568eff1078c382827Timo Sirainen storage->callbacks = i_new(struct mail_storage_callbacks, 1);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen index_storage_init(storage);
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen (void)verify_inbox(storage);
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen return &storage->storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic void maildir_free(struct mail_storage *_storage)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen struct index_storage *storage = (struct index_storage *) _storage;
ccec5f82349eae44087900c0e64ed1fd5a1a6fcaTimo Sirainen
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen index_storage_deinit(storage);
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(storage->temp_prefix);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen i_free(storage->dir);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen i_free(storage->inbox_path);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen i_free(storage->index_dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(storage->control_dir);
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen i_free(storage->user);
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen i_free(storage->callbacks);
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen i_free(storage);
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen}
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainenstatic int maildir_autodetect(const char *data)
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainen{
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct stat st;
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen data = t_strcut(data, ':');
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen return stat(t_strconcat(data, "/cur", NULL), &st) == 0 &&
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen S_ISDIR(st.st_mode);
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen}
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainen
923eb3dde28e4d8841c14fd6b4a69635b7070c3eTimo Sirainenstatic int maildir_is_valid_create_name(const char *name)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen{
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen size_t len;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen len = strlen(name);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (len == 0 || name[0] == MAILDIR_FS_SEP ||
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen name[len-1] == MAILDIR_FS_SEP ||
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen strchr(name, '*') != NULL || strchr(name, '%') != NULL)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen return FALSE;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen if (full_filesystem_access)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return FALSE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int maildir_is_valid_existing_name(const char *name)
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen{
f95b3d29bc56f139c18c880aa574a0ca72b0cffbTimo Sirainen if (name[0] == '\0' || name[strlen(name)-1] == '/')
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (full_filesystem_access)
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen return TRUE;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen return FALSE;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen return TRUE;
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenstatic const char *maildir_get_absolute_path(const char *name, int unlink)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen const char *p;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen name = home_expand(name);
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen p = strrchr(name, '/');
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen if (p == NULL)
c13f3df87bc8ec1fb279fc0ffa6e8517f74dc07cTimo Sirainen return name;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return t_strconcat(t_strdup_until(name, p+1),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unlink ? MAILDIR_FS_SEP_S MAILDIR_FS_SEP_S :
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen MAILDIR_FS_SEP_S, p+1, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenconst char *maildir_fix_mailbox_name(struct index_storage *storage,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *name, int remove_namespace)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen char *dup, *p, sep;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen size_t len;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (strncasecmp(name, "INBOX", 5) == 0 &&
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen (name[5] == '\0' || name[5] == storage->storage.hierarchy_sep)) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* use same case with all INBOX folders or we'll get
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen into trouble */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen name = t_strconcat("INBOX", name+5, NULL);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (name[5] == '\0') {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* don't check namespace with INBOX */
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return name;
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen }
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen }
42507d758b053bb483de58fba55c73a9eb5d3fbaTimo Sirainen
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen if (storage->storage.namespace != NULL && remove_namespace) {
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen len = strlen(storage->storage.namespace);
f23ede27743c1aa03eacbfc634d6a10de9110c91Timo Sirainen if (strncmp(storage->storage.namespace, name, len) != 0) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_panic("maildir: expecting namespace '%s' in name "
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen "'%s'", storage->storage.namespace, name);
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen }
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen name += len;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen }
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen return name;
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen sep = storage->storage.hierarchy_sep;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (sep == MAILDIR_FS_SEP)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return name;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen dup = t_strdup_noconst(name);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen for (p = dup; *p != '\0'; p++) {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (*p == sep)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen *p = MAILDIR_FS_SEP;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen return dup;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenconst char *maildir_get_path(struct index_storage *storage, const char *name)
0d86aa0d47f7393c669c084b34c0537b193688adTimo Sirainen{
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return maildir_get_absolute_path(name, FALSE);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (strcmp(name, "INBOX") == 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return storage->inbox_path != NULL ?
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen storage->inbox_path : storage->dir;
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen }
a757f31393b9d6fc7760a9dec8363404ab3ae576Timo Sirainen
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen}
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic const char *
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenmaildir_get_unlink_path(struct index_storage *storage, const char *name)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return maildir_get_absolute_path(name, TRUE);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return maildir_get_path(storage,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen t_strconcat(MAILDIR_FS_SEP_S, name, NULL));
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen}
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic const char *maildir_get_index_path(struct index_storage *storage,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen const char *name)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (storage->index_dir == NULL)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return NULL;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return storage->inbox_path;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return maildir_get_absolute_path(name, FALSE);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen}
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic const char *maildir_get_control_path(struct index_storage *storage,
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen const char *name)
838e367716bbd5e44b4a1691db9cbf72af53e9f0Timo Sirainen{
01404d41657a104c5ea1c12bb87f9c321e9c1ac4Timo Sirainen if (storage->control_dir == NULL)
01404d41657a104c5ea1c12bb87f9c321e9c1ac4Timo Sirainen return maildir_get_path(storage, name);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (full_filesystem_access && (*name == '/' || *name == '~'))
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return maildir_get_absolute_path(name, FALSE);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen name, NULL);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic int mkdir_verify(struct index_storage *storage,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen const char *dir, int verify)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen{
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen struct stat st;
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainen if (verify) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (lstat(dir, &st) == 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (errno != ENOENT) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(&storage->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "lstat(%s) failed: %m", dir);
e4c90f0b88e40a8f92b8f5e1f1a3ea701e5c965cTimo Sirainen return -1;
defb12ecd360df672ffb2f4dbf4d1218a0a9549cTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && (errno != EEXIST || !verify)) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (errno != EEXIST && (!verify || errno != ENOENT)) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mail_storage_set_critical(&storage->storage,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen "mkdir(%s) failed: %m", dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen }
194603b35061fea1ee8d171a7104b6985c610966Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen/* create or fix maildir, ignore if it already exists */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int create_maildir(struct index_storage *storage,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *dir, int verify)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **tmp, *path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen if (!verify && mkdir_verify(storage, dir, verify) < 0)
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen return -1;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen for (tmp = maildirs; *tmp != NULL; tmp++) {
e63bdfedcf61e1a9ee21990140cbd0d0638da7e1Timo Sirainen path = t_strconcat(dir, "/", *tmp, NULL);
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (mkdir_verify(storage, path, verify) < 0) {
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen if (!verify || errno != ENOENT)
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen return -1;
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* small optimization. if we're verifying, we don't
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen check that the root dir actually exists unless we
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen fail here. */
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen if (mkdir_verify(storage, dir, verify) < 0)
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen return -1;
f4616f1875297fb2f583d913c0f01b075bdecd5bTimo Sirainen if (mkdir_verify(storage, path, verify) < 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainen }
4321f6c969e7b8f6b243ff5bb6b8d297921676f6Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic int create_index_dir(struct index_storage *storage, const char *name)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen const char *dir;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (storage->index_dir == NULL)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return 0;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if (strcmp(storage->index_dir, storage->dir) == 0 ||
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL &&
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen strcmp(storage->index_dir, storage->inbox_path) == 0))
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen return 0;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) == -1 && errno != EEXIST) {
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen mail_storage_set_critical(&storage->storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "mkdir(%s) failed: %m", dir);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
cfb86adbda733e4945db44c6be27f8fda142c0a3Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen}
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int create_control_dir(struct index_storage *storage, const char *name)
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen{
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen const char *dir;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (storage->control_dir == NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen name, NULL);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(&storage->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "mkdir(%s) failed: %m", dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainenstatic int verify_inbox(struct index_storage *storage)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen const char *inbox;
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen
4bbd396aa6198c84f3f7763b6e8a63a26e97e141Timo Sirainen if (storage->inbox_path == NULL) {
910fa4e4204a73d3d24c03f3059dd24e727ca057Timo Sirainen /* first make sure the cur/ new/ and tmp/ dirs exist
b42697a5749b85659a24316d97f1c208d469e4e8Timo Sirainen in root dir */
b42697a5749b85659a24316d97f1c208d469e4e8Timo Sirainen if (create_maildir(storage, storage->dir, TRUE) < 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return -1;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
b3b4f3875850099c9292ad74d08bb385c3988f8fTimo Sirainen /* create the .INBOX directory */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen inbox = t_strconcat(storage->index_dir,
"/"MAILDIR_FS_SEP_S"INBOX", NULL);
if (mkdir_verify(storage, inbox, TRUE) < 0)
return -1;
} else {
if (create_maildir(storage, storage->inbox_path, TRUE) < 0)
return -1;
}
/* make sure the index directories exist */
if (create_index_dir(storage, "INBOX") < 0)
return -1;
if (create_control_dir(storage, "INBOX") < 0)
return -1;
return 0;
}
static uint32_t maildir_get_recent_count(struct index_mailbox *ibox)
{
return maildir_uidlist_get_recent_count(ibox->uidlist);
}
static struct mailbox *
maildir_open(struct index_storage *storage, const char *name,
enum mailbox_open_flags flags)
{
struct index_mailbox *ibox;
struct mail_index *index;
const char *path, *index_dir, *control_dir;
struct stat st;
path = maildir_get_path(storage, name);
index_dir = maildir_get_index_path(storage, name);
control_dir = maildir_get_control_path(storage, name);
index = index_storage_alloc(index_dir, path, MAILDIR_INDEX_PREFIX);
ibox = index_storage_mailbox_init(storage, &maildir_mailbox,
index, name, flags);
if (ibox == NULL)
return NULL;
ibox->path = i_strdup(path);
ibox->control_dir = i_strdup(control_dir);
ibox->get_recent_count = maildir_get_recent_count;
ibox->mail_interface = &maildir_mail;
ibox->uidlist = maildir_uidlist_init(ibox);
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file */
if (stat(t_strconcat(path, "/dovecot-shared", NULL), &st) < 0)
ibox->mail_create_mode = 0600;
else {
ibox->mail_create_mode = st.st_mode & 0666;
ibox->private_flags_mask = MAIL_SEEN;
}
return &ibox->box;
}
static struct mailbox *
maildir_mailbox_open(struct mail_storage *_storage,
const char *name, enum mailbox_open_flags flags)
{
struct index_storage *storage = (struct index_storage *)_storage;
const char *path;
struct stat st;
mail_storage_clear_error(_storage);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (strcmp(name, "INBOX") == 0) {
if (verify_inbox(storage) < 0)
return NULL;
return maildir_open(storage, "INBOX", flags);
}
if (!maildir_is_valid_existing_name(name)) {
mail_storage_set_error(_storage, "Invalid mailbox name");
return NULL;
}
path = maildir_get_path(storage, name);
if (stat(path, &st) == 0) {
/* exists - make sure the required directories are also there */
if (create_maildir(storage, path, TRUE) < 0 ||
create_index_dir(storage, name) < 0 ||
create_control_dir(storage, 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_mailbox_create(struct mail_storage *_storage,
const char *name,
int directory __attr_unused__)
{
struct index_storage *storage = (struct index_storage *)_storage;
const char *path;
mail_storage_clear_error(_storage);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (!maildir_is_valid_create_name(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;
}
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);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (strcmp(name, "INBOX") == 0) {
mail_storage_set_error(_storage, "INBOX can't be deleted.");
return -1;
}
if (!maildir_is_valid_existing_name(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, *mask;
size_t oldnamelen;
int ret;
ret = 0;
oldnamelen = strlen(oldname);
mask = t_strdup_printf("%s%s%c*", storage->storage.namespace != NULL ?
storage->storage.namespace : "", oldname,
storage->storage.hierarchy_sep);
ctx = maildir_mailbox_list_init(&storage->storage, mask,
MAILBOX_LIST_FAST_FLAGS);
while ((list = maildir_mailbox_list_next(ctx)) != NULL) {
const char *list_name;
t_push();
list_name = maildir_fix_mailbox_name(storage, list->name, TRUE);
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, found;
mail_storage_clear_error(_storage);
oldname = maildir_fix_mailbox_name(storage, oldname, TRUE);
newname = maildir_fix_mailbox_name(storage, newname, TRUE);
if (!maildir_is_valid_existing_name(oldname) ||
!maildir_is_valid_create_name(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, int set)
{
struct index_storage *storage = (struct index_storage *)_storage;
const char *path;
path = t_strconcat(storage->control_dir != NULL ?
storage->control_dir : storage->dir,
"/" SUBSCRIPTION_FILE_NAME, NULL);
name = maildir_fix_mailbox_name(storage, name, FALSE);
return subsfile_set_subscribed(_storage, path, 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);
name = maildir_fix_mailbox_name(storage, name, TRUE);
if (!maildir_is_valid_existing_name(name)) {
*status = MAILBOX_NAME_INVALID;
return 0;
}
path = maildir_get_path(storage, name);
if (stat(path, &st) == 0) {
*status = MAILBOX_NAME_EXISTS;
return 0;
}
if (!maildir_is_valid_create_name(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 index_mailbox *ibox = (struct index_mailbox *)box;
int ret = 0;
/*FIXME:if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
mail_storage_set_index_error(ibox);
ret = -1;
}*/
maildir_uidlist_deinit(ibox->uidlist);
index_storage_mailbox_free(box);
return ret;
}
static void maildir_storage_auto_sync(struct mailbox *box,
enum mailbox_sync_flags flags,
unsigned int min_newmail_notify_interval)
{
struct index_mailbox *ibox = (struct index_mailbox *)box;
ibox->min_newmail_notify_interval = min_newmail_notify_interval;
if ((ibox->autosync_flags == 0 && flags == 0) ||
(ibox->autosync_flags != 0 && flags != 0)) {
/* flags or interval just changed. or nothing. */
ibox->autosync_flags = flags;
}
ibox->autosync_flags = flags;
if (flags == 0) {
index_mailbox_check_remove_all(ibox);
return;
}
index_mailbox_check_add(ibox,
t_strconcat(ibox->storage->dir, "/new", NULL), TRUE);
index_mailbox_check_add(ibox,
t_strconcat(ibox->storage->dir, "/cur", NULL), TRUE);
}
struct mail_storage maildir_storage = {
"maildir", /* name */
NULL, /* namespace */
'.', /* default hierarchy separator */
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,
NULL,
0
};
struct mailbox maildir_mailbox = {
NULL, /* name */
NULL, /* storage */
index_storage_is_readonly,
index_storage_allow_new_keywords,
maildir_storage_close,
index_storage_get_status,
maildir_storage_sync,
maildir_storage_auto_sync,
maildir_transaction_begin,
maildir_transaction_commit,
maildir_transaction_rollback,
index_storage_fetch,
index_storage_get_uids,
index_storage_search_get_sorting,
index_storage_search_init,
index_storage_search_deinit,
index_storage_search_next,
maildir_save,
maildir_copy,
index_storage_is_inconsistent
};