mail-storage.c revision 8e853397a7ce4faaee6b90dac6f4d1615fdbedf5
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "lib.h"
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen#include "ioloop.h"
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen#include "array.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "llist.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "unichar.h"
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen#include "istream.h"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include "eacces-error.h"
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen#include "mkdir-parents.h"
82fb1ead38f4e1197993adb2f9c12e26531e9c92Timo Sirainen#include "time-util.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "var-expand.h"
2f7c73483ff5474a74a83a646f82e1b60f687680Aki Tuomi#include "mail-index-private.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-index-alloc-cache.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mailbox-tree.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mailbox-list-private.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-storage-private.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-storage-settings.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mail-namespace.h"
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen#include "mail-search.h"
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen#include "mail-search-register.h"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include "mailbox-search-result-private.h"
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainen#include "mailbox-guid-cache.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <stdlib.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <ctype.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen#define MAILBOX_DELETE_RETRY_SECS (60*5)
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainen
7fa573e6ea36024f618492e7d3649a69c1b41028Timo Sirainenextern struct mail_search_register *mail_search_register_imap;
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainenextern struct mail_search_register *mail_search_register_human;
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen
d99107ddf4d9bccb710994482daf65276a9d6321Timo Sirainenstruct mail_storage_module_register mail_storage_module_register = { 0 };
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct mail_module_register mail_module_register = { 0 };
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct mail_storage_mail_index_module mail_storage_mail_index_module =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MODULE_CONTEXT_INIT(&mail_index_module_register);
e248fe370c4047cee921a91b48edc37944ab0526Timo SirainenARRAY_TYPE(mail_storage) mail_storage_classes;
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Boschvoid mail_storage_init(void)
a05fec120ecd8c4ed6331c42100cba42adf22893Stephan Bosch{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen mailbox_lists_init();
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen mail_storage_hooks_init();
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_array_init(&mail_storage_classes, 8);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid mail_storage_deinit(void)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (mail_search_register_human != NULL)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch mail_search_register_deinit(&mail_search_register_human);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (mail_search_register_imap != NULL)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch mail_search_register_deinit(&mail_search_register_imap);
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch if (array_is_created(&mail_storage_classes))
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch array_free(&mail_storage_classes);
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch mail_storage_hooks_deinit();
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch mailbox_lists_deinit();
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch}
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschvoid mail_storage_class_register(struct mail_storage *storage_class)
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen i_assert(mail_storage_find_class(storage_class->name) == NULL);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* append it after the list, so the autodetection order is correct */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen array_append(&mail_storage_classes, &storage_class, 1);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid mail_storage_class_unregister(struct mail_storage *storage_class)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen struct mail_storage *const *classes;
529944a3554da75c2e6231a25fe489d815519b22Stephan Bosch unsigned int i, count;
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch
8ac6623677005256bf99ab33a2ed98c69c1d656cStephan Bosch classes = array_get(&mail_storage_classes, &count);
f32d0295c90ed810889504cdfa5e1a25a415f65fStephan Bosch for (i = 0; i < count; i++) {
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch if (classes[i] == storage_class) {
04eb0abcf8f8b0c014499b5c5bae89484553613fStephan Bosch array_delete(&mail_storage_classes, i, 1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Boschstruct mail_storage *mail_storage_find_class(const char *name)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen struct mail_storage *const *classes;
d694a52bce62c52080c3f87a56dcf77030fd2712Timo Sirainen unsigned int i, count;
009217abb57a24a4076092e8e4e165545747839eStephan Bosch
2598b2f36365b52d9754b9348a5be29569293e46Timo Sirainen i_assert(name != NULL);
6303191abcb37164f435ccdc56e9dbddf1288851Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen classes = array_get(&mail_storage_classes, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < count; i++) {
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen if (strcasecmp(classes[i]->name, name) == 0)
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen return classes[i];
fd3d068169c6ec587c9c446f2ee45560a444334aTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return NULL;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenstatic struct mail_storage *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_storage_autodetect(const struct mail_namespace *ns,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mailbox_list_settings *set)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage *const *classes;
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen unsigned int i, count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen classes = array_get(&mail_storage_classes, &count);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen for (i = 0; i < count; i++) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (classes[i]->v.autodetect != NULL) {
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if (classes[i]->v.autodetect(ns, set))
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return classes[i];
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainenstatic void
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenmail_storage_set_autodetection(const char **data, const char **driver)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen{
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen const char *p;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen /* check if data is in driver:data format (eg. mbox:~/mail) */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p = *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (i_isalnum(*p)) p++;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
2b96880f2d789d125aff6a95eaa7b51f558a6a1cTimo Sirainen if (*p == ':' && p != *data) {
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen /* no autodetection if the storage driver is given. */
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen *driver = t_strdup_until(*data, p);
edfdc577ffe7408fd6463eb9dba11260d380ab53Timo Sirainen *data = p + 1;
b84eff65e25ae86dfd6f798386577209b94838c6Timo Sirainen }
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen}
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainenstatic struct mail_storage *
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainenmail_storage_get_class(struct mail_namespace *ns, const char *driver,
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen struct mailbox_list_settings *list_set,
b045b66988bfbaa2795791e42ee724fae6f0db1cAki Tuomi enum mail_storage_flags flags, const char **error_r)
b045b66988bfbaa2795791e42ee724fae6f0db1cAki Tuomi{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage *storage_class = NULL;
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen const char *home;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (driver == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* no mail_location, autodetect */
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen } else if (strcmp(driver, "auto") == 0) {
64e244defe74f513ce94f33d000a048ddbe2ea23Timo Sirainen /* explicit autodetection with "auto" driver. */
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen if (list_set->root_dir != NULL &&
325d17cdbb7a338f7c413788f5e8e42d2e80a7f8Timo Sirainen *list_set->root_dir == '\0') {
009217abb57a24a4076092e8e4e165545747839eStephan Bosch /* handle the same as with driver=NULL */
009217abb57a24a4076092e8e4e165545747839eStephan Bosch list_set->root_dir = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
97db4761382024093f441e4bc78ba8b6a056504dTimo Sirainen } else {
f29756821a4c6b12b73e4a2a3e1c230117a43773Timo Sirainen storage_class = mail_storage_find_class(driver);
8ce3071e80b9973230048ecadfcb073fb82cc69fTimo Sirainen if (storage_class == NULL) {
fe791e96fdf796f7d8997ee0515b163dc5eddd72Aki Tuomi *error_r = t_strdup_printf(
b6fbc235f981b10333403e2fd6d333fd351c7a3cAki Tuomi "Unknown mail storage driver %s", driver);
b6fbc235f981b10333403e2fd6d333fd351c7a3cAki Tuomi return NULL;
a8dac1be6a0c3adbbce5887ca395f418194c6c06Aki Tuomi }
53f97800b16ab3a8d263c5331132dec1e8fea9a0Aki Tuomi }
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen if (list_set->root_dir == NULL || *list_set->root_dir == '\0') {
cd94aeaa294f7cc507206b4b2075852f00e14d61Timo Sirainen /* no root directory given. is this allowed? */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mailbox_list *list;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
660b99a7059824676b2b8d6f79b8e15d47df25a2Timo Sirainen list = list_set->layout == NULL ? NULL :
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen mailbox_list_find_class(list_set->layout);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (storage_class == NULL &&
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen (flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0) {
bbba7d0fce1b6ce5baa2d7ef946eb1b63e2ab518Timo Sirainen /* autodetection should take care of this */
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen } else if (storage_class != NULL &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* root not required for this storage */
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen } else if (list != NULL &&
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen (list->props & MAILBOX_LIST_PROP_NO_ROOT) != 0) {
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen /* root not required for this layout */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen *error_r = "Root mail directory not given";
9137c55411aa39d41c1e705ddc34d5bd26c65021Timo Sirainen return NULL;
a75907609d7c410c9e17beedfafbf28b4439fa8aTimo Sirainen }
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen }
aebfda1f6e3a924c31e8f54237b81fabf4b2264cTimo Sirainen
2dfd08e8aa16dfcc975d8a62bc8d20b2ef849d71Timo Sirainen if (storage_class != NULL) {
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen storage_class->v.get_list_settings(ns, list_set);
38cb3d139aefb7c65919cf4aba5ded7b5fd50e6fTimo Sirainen return storage_class;
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen }
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen storage_class = mail_storage_autodetect(ns, list_set);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (storage_class != NULL)
843640f0ca224bb9999acb290bca5f76037ab984Timo Sirainen return storage_class;
36b072d84a9076c3c483bf710444a716e987ccc3Stephan Bosch
087939d3fa9c4056419386c9d6c81f147de534cdTimo Sirainen (void)mail_user_get_home(ns->user, &home);
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainen if (home == NULL || *home == '\0') home = "(not set)";
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen if (ns->set->location == NULL || *ns->set->location == '\0') {
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen *error_r = t_strdup_printf(
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen "Mail storage autodetection failed with home=%s", home);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen } else if (strncmp(ns->set->location, "auto:", 5) == 0) {
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen *error_r = t_strdup_printf(
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen "Autodetection failed for %s (home=%s)",
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen ns->set->location, home);
ce1a6c9b82117d253df9acd77e54ac84dd8a247eTimo Sirainen } else {
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen *error_r = t_strdup_printf(
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen "Ambiguous mail location setting, "
ec9429535e925610513bd6bfde6729e273082ccbTimo Sirainen "don't know what to do with it: %s "
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen "(try prefixing it with mbox: or maildir:)",
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen ns->set->location);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return NULL;
1a1159e589def1e32b7dc25397f15146672ef73eTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenstatic int
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenmail_storage_verify_root(const char *root_dir, bool autocreate,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen const char **error_r)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
56af9dd10e7e6caeaca64395bad3f882b28ecdffTimo Sirainen struct stat st;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (stat(root_dir, &st) == 0) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* exists */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return 1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen } else if (errno == EACCES) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen *error_r = mail_error_eacces_msg("stat", root_dir);
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen } else if (errno != ENOENT && errno != ENOTDIR) {
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen *error_r = t_strdup_printf("stat(%s) failed: %m", root_dir);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen } else if (!autocreate) {
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen *error_r = t_strdup_printf(
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen "Root mail directory doesn't exist: %s", root_dir);
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return -1;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen } else {
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen /* doesn't exist */
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen return 0;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen }
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen}
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainenstatic int
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainenmail_storage_create_root(struct mailbox_list *list,
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen enum mail_storage_flags flags, const char **error_r)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen{
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen const char *root_dir, *error;
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen bool autocreate;
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen int ret;
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
9132f9df4e12ed5293c70957813aa3736444a13cTimo Sirainen &root_dir)) {
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen /* storage doesn't use directories (e.g. shared root) */
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen return 0;
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen }
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen if ((flags & MAIL_STORAGE_FLAG_NO_AUTOVERIFY) != 0) {
66ea9eaaa2d7531b3be8f633937628c94d907031Timo Sirainen if (!list->mail_set->mail_debug)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we don't need to verify, but since debugging is
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen enabled, check and log if the root doesn't exist */
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen if (mail_storage_verify_root(root_dir, FALSE, &error) < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_debug("Namespace %s: Creating storage despite: %s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen list->ns->prefix, error);
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen }
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen return 0;
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen }
c4ec7cb598805b1387dc3aab59ec8f32d8cc24e1Timo Sirainen
c4ec7cb598805b1387dc3aab59ec8f32d8cc24e1Timo Sirainen autocreate = (flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) == 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = mail_storage_verify_root(root_dir, autocreate, error_r);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = mailbox_list_try_mkdir_root(list, root_dir,
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen error_r);
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen }
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return ret < 0 ? -1 : 0;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen}
6a9e034441607c0c5a61858ff559af4615ac31caTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenstatic bool
b55f914c0ade77252cfd798ea8eb9a84bda56315Timo Sirainenmail_storage_match_class(struct mail_storage *storage,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage *storage_class,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mailbox_list_settings *set)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strcmp(storage->name, storage_class->name) != 0)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return FALSE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 &&
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen strcmp(storage->unique_root_dir, set->root_dir) != 0)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen return FALSE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strcmp(storage->name, "shared") == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* allow multiple independent shared namespaces */
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen return FALSE;
ae797f3368ebb86e7786ca25d7c9c703f672b9f5Timo Sirainen }
b84eff65e25ae86dfd6f798386577209b94838c6Timo Sirainen return TRUE;
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
46ec792dd4ccf6c34706c4774228301fafde6aa9Timo Sirainenstatic struct mail_storage *
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenmail_storage_find(struct mail_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage *storage_class,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mailbox_list_settings *set)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
decb23442f9e6cd5c4845a9cb162029b8c6d5f0fTimo Sirainen struct mail_storage *storage = user->storages;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (; storage != NULL; storage = storage->next) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_storage_match_class(storage, storage_class, set))
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return storage;
2b96880f2d789d125aff6a95eaa7b51f558a6a1cTimo Sirainen }
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen return NULL;
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen}
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainenint mail_storage_create(struct mail_namespace *ns, const char *driver,
717bb0dbaf4bd3f745669570647845e6d493bfe0Timo Sirainen enum mail_storage_flags flags, const char **error_r)
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen{
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen struct mail_storage *storage_class, *storage = NULL;
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen struct mailbox_list *list;
c5e62353a11087958ea4e619660e084a613e1a37Timo Sirainen struct mailbox_list_settings list_set;
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen enum mailbox_list_flags list_flags = 0;
const char *data = ns->set->location;
const char *p;
if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
ns->mail_set->pop3_uidl_format != NULL) {
/* if pop3_uidl_format contains %m, we want to keep the
header MD5 sums stored even if we're not running POP3
right now. */
p = ns->mail_set->pop3_uidl_format;
while ((p = strchr(p, '%')) != NULL) {
if (p[1] == '%')
p += 2;
else if (var_get_key(++p) == 'm') {
flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
break;
}
}
}
memset(&list_set, 0, sizeof(list_set));
list_set.mailbox_dir_name = "";
list_set.maildir_name = "";
if (data == NULL) {
/* autodetect */
} else if (driver != NULL && strcmp(driver, "shared") == 0) {
/* internal shared namespace */
list_set.root_dir = ns->user->set->base_dir;
} else {
if (driver == NULL)
mail_storage_set_autodetection(&data, &driver);
if (mailbox_list_settings_parse(ns->user, data, &list_set,
error_r) < 0)
return -1;
}
storage_class = mail_storage_get_class(ns, driver, &list_set, flags,
error_r);
if (storage_class == NULL)
return -1;
i_assert(list_set.layout != NULL);
if (ns->list == NULL) {
/* first storage for namespace */
if (mail_storage_is_mailbox_file(storage_class))
list_flags |= MAILBOX_LIST_FLAG_MAILBOX_FILES;
if (mailbox_list_create(list_set.layout, ns, &list_set,
list_flags, &list, error_r) < 0) {
*error_r = t_strdup_printf("Mailbox list driver %s: %s",
list_set.layout, *error_r);
return -1;
}
if (mail_storage_create_root(ns->list, flags, error_r) < 0)
return -1;
}
storage = mail_storage_find(ns->user, storage_class, &list_set);
if (storage != NULL) {
/* using an existing storage */
storage->refcount++;
mail_namespace_add_storage(ns, storage);
return 0;
}
storage = storage_class->v.alloc();
storage->refcount = 1;
storage->storage_class = storage_class;
storage->user = ns->user;
storage->set = ns->mail_set;
storage->flags = flags;
p_array_init(&storage->module_contexts, storage->pool, 5);
if (storage->v.create != NULL &&
storage->v.create(storage, ns, error_r) < 0) {
*error_r = t_strdup_printf("%s: %s", storage->name, *error_r);
pool_unref(&storage->pool);
return -1;
}
T_BEGIN {
hook_mail_storage_created(storage);
} T_END;
DLLIST_PREPEND(&ns->user->storages, storage);
mail_namespace_add_storage(ns, storage);
return 0;
}
void mail_storage_unref(struct mail_storage **_storage)
{
struct mail_storage *storage = *_storage;
i_assert(storage->refcount > 0);
/* set *_storage=NULL only after calling destroy() callback.
for example mdbox wants to access ns->storage */
if (--storage->refcount > 0) {
*_storage = NULL;
return;
}
if (storage->mailboxes != NULL) {
i_panic("Trying to deinit storage without freeing mailbox %s",
storage->mailboxes->vname);
}
if (storage->obj_refcount != 0)
i_panic("Trying to deinit storage before freeing its objects");
DLLIST_REMOVE(&storage->user->storages, storage);
if (storage->v.destroy != NULL)
storage->v.destroy(storage);
i_free(storage->error_string);
*_storage = NULL;
pool_unref(&storage->pool);
mail_index_alloc_cache_destroy_unrefed();
}
void mail_storage_obj_ref(struct mail_storage *storage)
{
i_assert(storage->refcount > 0);
if (storage->obj_refcount++ == 0)
mail_user_ref(storage->user);
}
void mail_storage_obj_unref(struct mail_storage *storage)
{
i_assert(storage->refcount > 0);
i_assert(storage->obj_refcount > 0);
if (--storage->obj_refcount == 0) {
struct mail_user *user = storage->user;
mail_user_unref(&user);
}
}
void mail_storage_clear_error(struct mail_storage *storage)
{
i_free_and_null(storage->error_string);
storage->error = MAIL_ERROR_NONE;
}
void mail_storage_set_error(struct mail_storage *storage,
enum mail_error error, const char *string)
{
i_free(storage->error_string);
storage->error_string = i_strdup(string);
storage->error = error;
}
void mail_storage_set_internal_error(struct mail_storage *storage)
{
const char *str;
str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time);
i_free(storage->error_string);
storage->error_string = i_strdup(str);
storage->error = MAIL_ERROR_TEMP;
}
void mail_storage_set_critical(struct mail_storage *storage,
const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
i_error("%s", t_strdup_vprintf(fmt, va));
va_end(va);
/* critical errors may contain sensitive data, so let user
see only "Internal error" with a timestamp to make it
easier to look from log files the actual error message. */
mail_storage_set_internal_error(storage);
}
void mail_storage_copy_error(struct mail_storage *dest,
struct mail_storage *src)
{
const char *str;
enum mail_error error;
str = mail_storage_get_last_error(src, &error);
mail_storage_set_error(dest, error, str);
}
void mail_storage_copy_list_error(struct mail_storage *storage,
struct mailbox_list *list)
{
const char *str;
enum mail_error error;
str = mailbox_list_get_last_error(list, &error);
mail_storage_set_error(storage, error, str);
}
void mail_storage_set_index_error(struct mailbox *box)
{
if (mail_index_is_deleted(box->index))
mailbox_set_deleted(box);
else
mail_storage_set_internal_error(box->storage);
mail_index_reset_error(box->index);
}
const struct mail_storage_settings *
mail_storage_get_settings(struct mail_storage *storage)
{
return storage->set;
}
struct mail_user *mail_storage_get_user(struct mail_storage *storage)
{
return storage->user;
}
void mail_storage_set_callbacks(struct mail_storage *storage,
struct mail_storage_callbacks *callbacks,
void *context)
{
storage->callbacks = *callbacks;
storage->callback_context = context;
}
int mail_storage_purge(struct mail_storage *storage)
{
return storage->v.purge == NULL ? 0 :
storage->v.purge(storage);
}
const char *mail_storage_get_last_error(struct mail_storage *storage,
enum mail_error *error_r)
{
/* We get here only in error situations, so we have to return some
error. If storage->error is NONE, it means we forgot to set it at
some point.. */
if (storage->error == MAIL_ERROR_NONE) {
if (error_r != NULL)
*error_r = MAIL_ERROR_TEMP;
return storage->error_string != NULL ? storage->error_string :
"BUG: Unknown internal error";
}
if (storage->error_string == NULL) {
/* This shouldn't happen.. */
storage->error_string =
i_strdup_printf("BUG: Unknown 0x%x error",
storage->error);
}
if (error_r != NULL)
*error_r = storage->error;
return storage->error_string;
}
const char *mailbox_get_last_error(struct mailbox *box,
enum mail_error *error_r)
{
return mail_storage_get_last_error(box->storage, error_r);
}
enum mail_error mailbox_get_last_mail_error(struct mailbox *box)
{
enum mail_error error;
mail_storage_get_last_error(box->storage, &error);
return error;
}
bool mail_storage_is_mailbox_file(struct mail_storage *storage)
{
return (storage->class_flags &
MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE) != 0;
}
bool mail_storage_set_error_from_errno(struct mail_storage *storage)
{
const char *error_string;
enum mail_error error;
if (!mail_error_from_errno(&error, &error_string))
return FALSE;
if (storage->set->mail_debug && error != MAIL_ERROR_NOTFOUND) {
/* debugging is enabled - admin may be debugging a
(permission) problem, so return FALSE to get the caller to
log the full error message. */
return FALSE;
}
mail_storage_set_error(storage, error, error_string);
return TRUE;
}
const struct mailbox_settings *
mailbox_settings_find(struct mail_user *user, const char *vname)
{
struct mailbox_settings *const *box_set;
struct mail_namespace *ns;
ns = mail_namespace_find(user->namespaces, vname);
if (ns == NULL)
return NULL;
if (!array_is_created(&ns->set->mailboxes))
return NULL;
if (ns->prefix_len > 0 &&
strncmp(ns->prefix, vname, ns->prefix_len-1) == 0) {
if (vname[ns->prefix_len-1] == mail_namespace_get_sep(ns))
vname += ns->prefix_len;
else if (vname[ns->prefix_len-1] == '\0') {
/* namespace prefix itself */
vname = "";
}
}
array_foreach(&ns->set->mailboxes, box_set) {
if (strcmp((*box_set)->name, vname) == 0)
return *box_set;
}
return NULL;
}
struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
enum mailbox_flags flags)
{
struct mailbox_list *new_list = list;
struct mail_storage *storage;
struct mailbox *box;
i_assert(uni_utf8_str_is_valid(vname));
if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
strncasecmp(vname, "INBOX", 5) == 0 &&
strncmp(vname, "INBOX", 5) != 0) {
/* make sure INBOX shows up in uppercase everywhere */
if (vname[5] == '\0')
vname = "INBOX";
else if (vname[5] == mail_namespace_get_sep(list->ns))
vname = t_strconcat("INBOX", vname + 5, NULL);
}
if (mailbox_list_get_storage(&new_list, vname, &storage) < 0) {
/* just use the first storage. FIXME: does this break? */
storage = list->ns->storage;
}
T_BEGIN {
box = storage->v.mailbox_alloc(storage, new_list, vname, flags);
box->set = mailbox_settings_find(storage->user, vname);
hook_mailbox_allocated(box);
} T_END;
DLLIST_PREPEND(&box->storage->mailboxes, box);
mail_storage_obj_ref(box->storage);
return box;
}
struct mailbox *mailbox_alloc_guid(struct mailbox_list *list,
const guid_128_t guid,
enum mailbox_flags flags)
{
struct mailbox *box = NULL;
struct mailbox_metadata metadata;
enum mail_error open_error = MAIL_ERROR_TEMP;
const char *vname;
if (mailbox_guid_cache_find(list, guid, &vname) < 0) {
vname = NULL;
} else if (vname != NULL) {
box = mailbox_alloc(list, vname, flags);
if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
&metadata) < 0) {
} else if (memcmp(metadata.guid, guid,
sizeof(metadata.guid)) != 0) {
/* GUID mismatch, refresh cache and try again */
mailbox_free(&box);
mailbox_guid_cache_refresh(list);
return mailbox_alloc_guid(list, guid, flags);
} else {
/* successfully opened the correct mailbox */
return box;
}
i_error("mailbox_alloc_guid(%s): "
"Couldn't verify mailbox GUID: %s",
guid_128_to_string(guid),
mailbox_get_last_error(box, NULL));
vname = NULL;
mailbox_free(&box);
} else {
vname = t_strdup_printf("(nonexistent mailbox with GUID=%s)",
guid_128_to_string(guid));
open_error = MAIL_ERROR_NOTFOUND;
}
if (vname == NULL) {
vname = t_strdup_printf("(error in mailbox with GUID=%s)",
guid_128_to_string(guid));
}
box = mailbox_alloc(list, vname, flags);
box->open_error = open_error;
return box;
}
static bool mailbox_is_autocreated(struct mailbox *box)
{
if (box->inbox_user)
return TRUE;
return box->set != NULL &&
strcmp(box->set->autocreate, MAILBOX_SET_AUTO_NO) != 0;
}
static int mailbox_autocreate(struct mailbox *box)
{
const char *errstr;
enum mail_error error;
if (mailbox_create(box, NULL, FALSE) < 0) {
errstr = mailbox_get_last_error(box, &error);
if (error != MAIL_ERROR_NOTFOUND) {
mail_storage_set_critical(box->storage,
"Failed to autocreate mailbox %s: %s",
box->vname, errstr);
return -1;
}
} else if (box->set != NULL &&
strcmp(box->set->autocreate,
MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
if (mailbox_set_subscribed(box, TRUE) < 0) {
mail_storage_set_critical(box->storage,
"Failed to autosubscribe to mailbox %s: %s",
box->vname, mailbox_get_last_error(box, NULL));
return -1;
}
}
return 0;
}
static int mailbox_autocreate_and_reopen(struct mailbox *box)
{
int ret;
if (mailbox_autocreate(box) < 0)
return -1;
mailbox_close(box);
ret = box->v.open(box);
if (ret < 0 && box->inbox_user &&
!box->storage->user->inbox_open_error_logged) {
box->storage->user->inbox_open_error_logged = TRUE;
mail_storage_set_critical(box->storage,
"Opening INBOX failed: %s",
mailbox_get_last_error(box, NULL));
}
return ret;
}
static bool
mailbox_name_verify_separators(const char *vname, char sep,
const char **error_r)
{
unsigned int i;
bool prev_sep = FALSE;
/* Make sure the vname is correct: non-empty, doesn't begin or end
with separator and no adjacent separators */
for (i = 0; vname[i] != '\0'; i++) {
if (vname[i] == sep) {
if (prev_sep) {
*error_r = "Has adjacent hierarchy separators";
return FALSE;
}
prev_sep = TRUE;
} else {
prev_sep = FALSE;
}
}
if (prev_sep && i > 0) {
*error_r = "Ends with hierarchy separator";
return FALSE;
}
return TRUE;
}
static int mailbox_verify_name(struct mailbox *box)
{
struct mail_namespace *ns = box->list->ns;
const char *error, *vname = box->vname;
char list_sep, ns_sep;
if (box->inbox_user) {
/* this is INBOX - don't bother with further checks */
return 0;
}
list_sep = mailbox_list_get_hierarchy_sep(box->list);
ns_sep = mail_namespace_get_sep(ns);
if (ns->prefix_len > 0) {
/* vname is either "namespace/box" or "namespace" */
i_assert(strncmp(vname, ns->prefix, ns->prefix_len-1) == 0);
vname += ns->prefix_len - 1;
if (vname[0] != '\0') {
i_assert(vname[0] == ns->prefix[ns->prefix_len-1]);
vname++;
if (vname[0] == '\0') {
/* "namespace/" isn't a valid mailbox name. */
mail_storage_set_error(box->storage,
MAIL_ERROR_PARAMS,
"Invalid mailbox name");
return -1;
}
}
}
if (ns_sep != list_sep && box->list->set.escape_char == '\0' &&
strchr(vname, list_sep) != NULL) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, t_strdup_printf(
"Character not allowed in mailbox name: '%c'", list_sep));
return -1;
}
if (vname[0] == ns_sep &&
!box->storage->set->mail_full_filesystem_access) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Invalid mailbox name: Begins with hierarchy separator");
return -1;
}
if (!mailbox_name_verify_separators(vname, ns_sep, &error) ||
!mailbox_list_is_valid_name(box->list, box->name, &error)) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
t_strdup_printf("Invalid mailbox name: %s", error));
return -1;
}
return 0;
}
static int mailbox_verify_existing_name(struct mailbox *box)
{
const char *path;
if (box->opened)
return 0;
if (mailbox_verify_name(box) < 0)
return -1;
/* Make sure box->_path is set, so mailbox_get_path() works from
now on. Note that this may also fail with some backends if the
mailbox doesn't exist. */
if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) < 0) {
if (box->storage->error != MAIL_ERROR_NOTFOUND ||
!mailbox_is_autocreated(box))
return -1;
/* if this is an autocreated mailbox, create it now */
if (mailbox_autocreate(box) < 0)
return -1;
mailbox_close(box);
if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&path) < 0)
return -1;
}
return 0;
}
static bool mailbox_name_has_control_chars(const char *name)
{
const char *p;
for (p = name; *p != '\0'; p++) {
if ((unsigned char)*p < ' ')
return TRUE;
}
return FALSE;
}
int mailbox_verify_create_name(struct mailbox *box)
{
char sep = mail_namespace_get_sep(box->list->ns);
/* mailbox_alloc() already checks that vname is valid UTF8,
so we don't need to verify that.
check vname instead of storage name, because vname is what is
visible to users, while storage name may be a fixed length GUID. */
if (mailbox_verify_name(box) < 0)
return -1;
if (mailbox_name_has_control_chars(box->vname)) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Control characters not allowed in new mailbox names");
return -1;
}
if (mailbox_list_name_is_too_large(box->vname, sep)) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Mailbox name too long");
return -1;
}
return 0;
}
static bool have_listable_namespace_prefix(struct mail_namespace *ns,
const char *name)
{
unsigned int name_len = strlen(name);
for (; ns != NULL; ns = ns->next) {
if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
NAMESPACE_FLAG_LIST_CHILDREN)) == 0)
continue;
if (ns->prefix_len <= name_len)
continue;
/* if prefix has multiple hierarchies, match
any of the hierarchies */
if (strncmp(ns->prefix, name, name_len) == 0 &&
ns->prefix[name_len] == mail_namespace_get_sep(ns))
return TRUE;
}
return FALSE;
}
int mailbox_exists(struct mailbox *box, bool auto_boxes,
enum mailbox_existence *existence_r)
{
switch (box->open_error) {
case 0:
break;
case MAIL_ERROR_NOTFOUND:
*existence_r = MAILBOX_EXISTENCE_NONE;
return 0;
default:
/* unsure if this exists or not */
return -1;
}
if (mailbox_verify_name(box) < 0) {
/* the mailbox name is invalid. we don't know if it currently
exists or not, but since it can never be accessed in any way
report it as if it didn't exist. */
*existence_r = MAILBOX_EXISTENCE_NONE;
return 0;
}
if (auto_boxes && box->set != NULL && mailbox_is_autocreated(box)) {
*existence_r = MAILBOX_EXISTENCE_SELECT;
return 0;
}
if (box->v.exists(box, auto_boxes, existence_r) < 0)
return -1;
if (!box->inbox_user && *existence_r == MAILBOX_EXISTENCE_NOSELECT &&
have_listable_namespace_prefix(box->storage->user->namespaces,
box->vname)) {
/* listable namespace prefix always exists. */
*existence_r = MAILBOX_EXISTENCE_NOSELECT;
return 0;
}
/* if this is a shared namespace with only INBOX and
mail_shared_explicit_inbox=no, we'll need to mark the namespace as
usable here since nothing else will. */
box->list->ns->flags |= NAMESPACE_FLAG_USABLE;
return 0;
}
static int ATTR_NULL(2)
mailbox_open_full(struct mailbox *box, struct istream *input)
{
int ret;
if (box->opened)
return 0;
switch (box->open_error) {
case 0:
break;
case MAIL_ERROR_NOTFOUND:
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
return -1;
default:
mail_storage_set_internal_error(box->storage);
box->storage->error = box->open_error;
return -1;
}
if (mailbox_verify_existing_name(box) < 0)
return -1;
if (input != NULL) {
if ((box->storage->class_flags &
MAIL_STORAGE_CLASS_FLAG_OPEN_STREAMS) == 0) {
mail_storage_set_critical(box->storage,
"Storage doesn't support streamed mailboxes");
return -1;
}
box->input = input;
box->flags |= MAILBOX_FLAG_READONLY;
i_stream_ref(box->input);
}
T_BEGIN {
ret = box->v.open(box);
} T_END;
if (ret < 0 && box->storage->error == MAIL_ERROR_NOTFOUND &&
box->input == NULL && mailbox_is_autocreated(box)) T_BEGIN {
ret = mailbox_autocreate_and_reopen(box);
} T_END;
if (ret < 0) {
if (box->input != NULL)
i_stream_unref(&box->input);
return -1;
}
box->list->ns->flags |= NAMESPACE_FLAG_USABLE;
return 0;
}
static bool mailbox_try_undelete(struct mailbox *box)
{
time_t mtime;
if (mail_index_get_modification_time(box->index, &mtime) < 0)
return FALSE;
if (mtime + MAILBOX_DELETE_RETRY_SECS > time(NULL))
return FALSE;
if (mailbox_mark_index_deleted(box, FALSE) < 0)
return FALSE;
box->mailbox_deleted = FALSE;
return TRUE;
}
int mailbox_open(struct mailbox *box)
{
if (mailbox_open_full(box, NULL) < 0) {
if (!box->mailbox_deleted)
return -1;
/* mailbox has been marked as deleted. if this deletion
started (and crashed) a long time ago, it can be confusing
to user that the mailbox can't be opened. so we'll just
undelete it and reopen. */
if(!mailbox_try_undelete(box))
return -1;
if (mailbox_open_full(box, NULL) < 0)
return -1;
}
return 0;
}
int mailbox_open_stream(struct mailbox *box, struct istream *input)
{
return mailbox_open_full(box, input);
}
int mailbox_enable(struct mailbox *box, enum mailbox_feature features)
{
if (mailbox_verify_name(box) < 0)
return -1;
return box->v.enable(box, features);
}
enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box)
{
return box->enabled_features;
}
void mail_storage_free_binary_cache(struct mail_storage *storage)
{
if (storage->binary_cache.box == NULL)
return;
timeout_remove(&storage->binary_cache.to);
i_stream_destroy(&storage->binary_cache.input);
memset(&storage->binary_cache, 0, sizeof(storage->binary_cache));
}
void mailbox_close(struct mailbox *box)
{
if (!box->opened)
return;
if (box->transaction_count != 0) {
i_panic("Trying to close mailbox %s with open transactions",
box->name);
}
box->v.close(box);
if (box->storage->binary_cache.box == box)
mail_storage_free_binary_cache(box->storage);
box->opened = FALSE;
box->mailbox_deleted = FALSE;
array_clear(&box->search_results);
}
void mailbox_free(struct mailbox **_box)
{
struct mailbox *box = *_box;
*_box = NULL;
mailbox_close(box);
box->v.free(box);
DLLIST_REMOVE(&box->storage->mailboxes, box);
mail_storage_obj_unref(box->storage);
if (box->metadata_pool != NULL)
pool_unref(&box->metadata_pool);
pool_unref(&box->pool);
}
bool mailbox_equals(const struct mailbox *box1,
const struct mail_namespace *ns2, const char *vname2)
{
struct mail_namespace *ns1 = mailbox_get_namespace(box1);
const char *name1;
if (ns1 != ns2)
return FALSE;
name1 = mailbox_get_vname(box1);
if (strcmp(name1, vname2) == 0)
return TRUE;
return strcasecmp(name1, "INBOX") == 0 &&
strcasecmp(vname2, "INBOX") == 0;
}
int mailbox_create(struct mailbox *box, const struct mailbox_update *update,
bool directory)
{
int ret;
if (mailbox_verify_create_name(box) < 0)
return -1;
box->creating = TRUE;
ret = box->v.create_box(box, update, directory);
box->creating = FALSE;
return ret;
}
int mailbox_update(struct mailbox *box, const struct mailbox_update *update)
{
i_assert(update->min_next_uid == 0 ||
update->min_first_recent_uid == 0 ||
update->min_first_recent_uid <= update->min_next_uid);
if (mailbox_verify_existing_name(box) < 0)
return -1;
return box->v.update_box(box, update);
}
int mailbox_mark_index_deleted(struct mailbox *box, bool del)
{
struct mail_index_transaction *trans;
enum mail_index_transaction_flags trans_flags = 0;
enum mailbox_flags old_flag;
int ret;
if (box->marked_deleted && del) {
/* we already marked it deleted. this allows plugins to
"lock" the deletion earlier. */
return 0;
}
old_flag = box->flags & MAILBOX_FLAG_OPEN_DELETED;
box->flags |= MAILBOX_FLAG_OPEN_DELETED;
ret = mailbox_open(box);
box->flags = (box->flags & ~MAILBOX_FLAG_OPEN_DELETED) | old_flag;
if (ret < 0)
return -1;
trans_flags = del ? 0 : MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL;
trans = mail_index_transaction_begin(box->view, trans_flags);
if (del)
mail_index_set_deleted(trans);
else
mail_index_set_undeleted(trans);
if (mail_index_transaction_commit(&trans) < 0) {
mail_storage_set_index_error(box);
return -1;
}
/* sync the mailbox. this finishes the index deletion and it can
succeed only for a single session. we do it here, so the rest of
the deletion code doesn't have to worry about race conditions. */
if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0)
return -1;
box->marked_deleted = del;
return 0;
}
int mailbox_delete(struct mailbox *box)
{
int ret;
if (*box->name == '\0') {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Storage root can't be deleted");
return -1;
}
if (box->inbox_any) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"INBOX can't be deleted.");
return -1;
}
box->deleting = TRUE;
if (mailbox_open(box) < 0) {
if (mailbox_get_last_mail_error(box) != MAIL_ERROR_NOTFOUND)
return -1;
/* \noselect mailbox */
}
ret = box->v.delete_box(box);
if (ret < 0 && box->marked_deleted) {
/* deletion failed. revert the mark so it can maybe be
tried again later. */
if (mailbox_mark_index_deleted(box, FALSE) < 0)
return -1;
}
box->deleting = FALSE;
mailbox_close(box);
return ret;
}
int mailbox_delete_empty(struct mailbox *box)
{
int ret;
/* FIXME: should be a parameter to delete(), but since it changes API
don't do it for now */
box->deleting_must_be_empty = TRUE;
ret = mailbox_delete(box);
box->deleting_must_be_empty = FALSE;
return ret;
}
static bool
mail_storages_rename_compatible(struct mail_storage *storage1,
struct mail_storage *storage2,
const char **error_r)
{
if (storage1 == storage2)
return TRUE;
if (strcmp(storage1->name, storage2->name) != 0) {
*error_r = t_strdup_printf("storage %s != %s",
storage1->name, storage2->name);
return FALSE;
}
if ((storage1->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0) {
/* e.g. mdbox where all mails are in storage/ directory and
they can't be easily moved from there. */
*error_r = t_strdup_printf("storage %s uses unique root",
storage1->name);
return FALSE;
}
return TRUE;
}
static bool nullequals(const void *p1, const void *p2)
{
return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL);
}
static bool
mailbox_lists_rename_compatible(struct mailbox_list *list1,
struct mailbox_list *list2,
const char **error_r)
{
if (!nullequals(list1->set.alt_dir, list2->set.alt_dir)) {
*error_r = "one namespace has alt dir and another doesn't";
return FALSE;
}
if (!nullequals(list1->set.index_dir, list2->set.index_dir)) {
*error_r = "one namespace has index dir and another doesn't";
return FALSE;
}
if (!nullequals(list1->set.control_dir, list2->set.control_dir)) {
*error_r = "one namespace has control dir and another doesn't";
return FALSE;
}
return TRUE;
}
int mailbox_rename(struct mailbox *src, struct mailbox *dest)
{
const char *error = NULL;
if (mailbox_verify_existing_name(src) < 0)
return -1;
if (*src->name == '\0') {
mail_storage_set_error(src->storage, MAIL_ERROR_PARAMS,
"Can't rename mailbox root");
return -1;
}
if (mailbox_verify_create_name(dest) < 0) {
mail_storage_copy_error(dest->storage, src->storage);
return -1;
}
if (!mail_storages_rename_compatible(src->storage,
dest->storage, &error) ||
!mailbox_lists_rename_compatible(src->list,
dest->list, &error)) {
if (src->storage->set->mail_debug) {
i_debug("Can't rename '%s' to '%s': %s",
src->vname, dest->vname, error);
}
mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
"Can't rename mailboxes across specified storages.");
return -1;
}
if (src->list != dest->list &&
(src->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE ||
dest->list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE)) {
mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
"Renaming not supported across non-private namespaces.");
return -1;
}
if (src->list == dest->list && strcmp(src->name, dest->name) == 0) {
mail_storage_set_error(src->storage, MAIL_ERROR_EXISTS,
"Can't rename mailbox to itself.");
return -1;
}
return src->v.rename_box(src, dest);
}
int mailbox_set_subscribed(struct mailbox *box, bool set)
{
if (mailbox_verify_name(box) < 0)
return -1;
return box->v.set_subscribed(box, set);
}
bool mailbox_is_subscribed(struct mailbox *box)
{
struct mailbox_node *node;
i_assert(box->list->subscriptions != NULL);
node = mailbox_tree_lookup(box->list->subscriptions, box->vname);
return node != NULL && (node->flags & MAILBOX_SUBSCRIBED) != 0;
}
struct mail_storage *mailbox_get_storage(const struct mailbox *box)
{
return box->storage;
}
struct mail_namespace *
mailbox_get_namespace(const struct mailbox *box)
{
return box->list->ns;
}
const struct mail_storage_settings *mailbox_get_settings(struct mailbox *box)
{
return box->storage->set;
}
const char *mailbox_get_name(const struct mailbox *box)
{
return box->name;
}
const char *mailbox_get_vname(const struct mailbox *box)
{
return box->vname;
}
bool mailbox_is_readonly(struct mailbox *box)
{
i_assert(box->opened);
return box->v.is_readonly(box);
}
bool mailbox_backends_equal(const struct mailbox *box1,
const struct mailbox *box2)
{
struct mail_namespace *ns1 = box1->list->ns, *ns2 = box2->list->ns;
if (strcmp(box1->name, box2->name) != 0)
return FALSE;
while (ns1->alias_for != NULL)
ns1 = ns1->alias_for;
while (ns2->alias_for != NULL)
ns2 = ns2->alias_for;
return ns1 == ns2;
}
int mailbox_get_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
memset(status_r, 0, sizeof(*status_r));
if (mailbox_verify_existing_name(box) < 0)
return -1;
return box->v.get_status(box, items, status_r);
}
void mailbox_get_open_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
i_assert(box->opened);
memset(status_r, 0, sizeof(*status_r));
if (box->v.get_status(box, items, status_r) < 0)
i_unreached();
}
int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
memset(metadata_r, 0, sizeof(*metadata_r));
if (mailbox_verify_existing_name(box) < 0)
return -1;
if (box->metadata_pool != NULL)
p_clear(box->metadata_pool);
if (box->v.get_metadata(box, items, metadata_r) < 0)
return -1;
i_assert((items & MAILBOX_METADATA_GUID) == 0 ||
!guid_128_is_empty(metadata_r->guid));
return 0;
}
enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box)
{
i_assert(box->opened);
if (box->v.get_private_flags_mask != NULL)
return box->v.get_private_flags_mask(box);
else if (box->list->set.index_pvt_dir != NULL)
return MAIL_SEEN; /* FIXME */
else
return 0;
}
struct mailbox_sync_context *
mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
{
struct mailbox_sync_context *ctx;
if (box->transaction_count != 0) {
i_panic("Trying to sync mailbox %s with open transactions",
box->name);
}
T_BEGIN {
ctx = box->v.sync_init(box, flags);
} T_END;
return ctx;
}
bool mailbox_sync_next(struct mailbox_sync_context *ctx,
struct mailbox_sync_rec *sync_rec_r)
{
return ctx->box->v.sync_next(ctx, sync_rec_r);
}
int mailbox_sync_deinit(struct mailbox_sync_context **_ctx,
struct mailbox_sync_status *status_r)
{
struct mailbox_sync_context *ctx = *_ctx;
struct mailbox *box = ctx->box;
const char *errormsg;
enum mail_error error;
int ret;
*_ctx = NULL;
memset(status_r, 0, sizeof(*status_r));
ret = box->v.sync_deinit(ctx, status_r);
if (ret < 0 && box->inbox_user &&
!box->storage->user->inbox_open_error_logged) {
errormsg = mailbox_get_last_error(box, &error);
if (error == MAIL_ERROR_NOTPOSSIBLE) {
box->storage->user->inbox_open_error_logged = TRUE;
i_error("Syncing INBOX failed: %s", errormsg);
}
}
if (ret == 0)
box->synced = TRUE;
return ret;
}
int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags)
{
struct mailbox_sync_context *ctx;
struct mailbox_sync_status status;
if (array_count(&box->search_results) == 0) {
/* we don't care about mailbox's current state, so we might
as well fix inconsistency state */
flags |= MAILBOX_SYNC_FLAG_FIX_INCONSISTENT;
}
ctx = mailbox_sync_init(box, flags);
return mailbox_sync_deinit(&ctx, &status);
}
#undef mailbox_notify_changes
void mailbox_notify_changes(struct mailbox *box,
mailbox_notify_callback_t *callback, void *context)
{
i_assert(box->opened);
box->notify_callback = callback;
box->notify_context = context;
box->v.notify_changes(box);
}
void mailbox_notify_changes_stop(struct mailbox *box)
{
i_assert(box->opened);
box->notify_callback = NULL;
box->notify_context = NULL;
box->v.notify_changes(box);
}
struct mail_search_context *
mailbox_search_init(struct mailbox_transaction_context *t,
struct mail_search_args *args,
const enum mail_sort_type *sort_program,
enum mail_fetch_field wanted_fields,
struct mailbox_header_lookup_ctx *wanted_headers)
{
mail_search_args_ref(args);
if (!args->simplified)
mail_search_args_simplify(args);
return t->box->v.search_init(t, args, sort_program,
wanted_fields, wanted_headers);
}
int mailbox_search_deinit(struct mail_search_context **_ctx)
{
struct mail_search_context *ctx = *_ctx;
struct mail_search_args *args = ctx->args;
int ret;
*_ctx = NULL;
mailbox_search_results_initial_done(ctx);
ret = ctx->transaction->box->v.search_deinit(ctx);
mail_search_args_unref(&args);
return ret;
}
bool mailbox_search_next(struct mail_search_context *ctx, struct mail **mail_r)
{
bool tryagain;
while (!mailbox_search_next_nonblock(ctx, mail_r, &tryagain)) {
if (!tryagain)
return FALSE;
}
return TRUE;
}
bool mailbox_search_next_nonblock(struct mail_search_context *ctx,
struct mail **mail_r, bool *tryagain_r)
{
struct mailbox *box = ctx->transaction->box;
*mail_r = NULL;
if (!box->v.search_next_nonblock(ctx, mail_r, tryagain_r))
return FALSE;
else {
mailbox_search_results_add(ctx, (*mail_r)->uid);
return TRUE;
}
}
bool mailbox_search_seen_lost_data(struct mail_search_context *ctx)
{
return ctx->seen_lost_data;
}
int mailbox_search_result_build(struct mailbox_transaction_context *t,
struct mail_search_args *args,
enum mailbox_search_result_flags flags,
struct mail_search_result **result_r)
{
struct mail_search_context *ctx;
struct mail *mail;
int ret;
ctx = mailbox_search_init(t, args, NULL, 0, NULL);
*result_r = mailbox_search_result_save(ctx, flags);
while (mailbox_search_next(ctx, &mail)) ;
ret = mailbox_search_deinit(&ctx);
if (ret < 0)
mailbox_search_result_free(result_r);
return ret;
}
struct mailbox_transaction_context *
mailbox_transaction_begin(struct mailbox *box,
enum mailbox_transaction_flags flags)
{
struct mailbox_transaction_context *trans;
i_assert(box->opened);
box->transaction_count++;
trans = box->v.transaction_begin(box, flags);
trans->flags = flags;
return trans;
}
int mailbox_transaction_commit(struct mailbox_transaction_context **t)
{
struct mail_transaction_commit_changes changes;
int ret;
/* Store changes temporarily so that plugins overriding
transaction_commit() can look at them. */
changes.pool = NULL;
ret = mailbox_transaction_commit_get_changes(t, &changes);
if (changes.pool != NULL)
pool_unref(&changes.pool);
return ret;
}
int mailbox_transaction_commit_get_changes(
struct mailbox_transaction_context **_t,
struct mail_transaction_commit_changes *changes_r)
{
struct mailbox_transaction_context *t = *_t;
int ret;
t->box->transaction_count--;
*_t = NULL;
T_BEGIN {
ret = t->box->v.transaction_commit(t, changes_r);
} T_END;
return ret;
}
void mailbox_transaction_rollback(struct mailbox_transaction_context **_t)
{
struct mailbox_transaction_context *t = *_t;
t->box->transaction_count--;
*_t = NULL;
t->box->v.transaction_rollback(t);
}
unsigned int mailbox_transaction_get_count(const struct mailbox *box)
{
return box->transaction_count;
}
void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t,
uint64_t max_modseq,
ARRAY_TYPE(seq_range) *seqs)
{
mail_index_transaction_set_max_modseq(t->itrans, max_modseq, seqs);
}
struct mailbox *
mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t)
{
return t->box;
}
struct mail_save_context *
mailbox_save_alloc(struct mailbox_transaction_context *t)
{
struct mail_save_context *ctx;
ctx = t->box->v.save_alloc(t);
ctx->data.received_date = (time_t)-1;
ctx->data.save_date = (time_t)-1;
return ctx;
}
void mailbox_save_set_flags(struct mail_save_context *ctx,
enum mail_flags flags,
struct mail_keywords *keywords)
{
ctx->data.flags = flags;
ctx->data.keywords = keywords;
if (keywords != NULL)
mailbox_keywords_ref(keywords);
}
void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail)
{
const char *const *keywords_list;
keywords_list = mail_get_keywords(mail);
ctx->data.keywords = str_array_length(keywords_list) == 0 ? NULL :
mailbox_keywords_create_valid(ctx->transaction->box,
keywords_list);
ctx->data.flags = mail_get_flags(mail);
}
void mailbox_save_set_min_modseq(struct mail_save_context *ctx,
uint64_t min_modseq)
{
ctx->data.min_modseq = min_modseq;
}
void mailbox_save_set_received_date(struct mail_save_context *ctx,
time_t received_date, int timezone_offset)
{
ctx->data.received_date = received_date;
ctx->data.received_tz_offset = timezone_offset;
}
void mailbox_save_set_save_date(struct mail_save_context *ctx,
time_t save_date)
{
ctx->data.save_date = save_date;
}
void mailbox_save_set_from_envelope(struct mail_save_context *ctx,
const char *envelope)
{
i_free(ctx->data.from_envelope);
ctx->data.from_envelope = i_strdup(envelope);
}
void mailbox_save_set_uid(struct mail_save_context *ctx, uint32_t uid)
{
ctx->data.uid = uid;
}
void mailbox_save_set_guid(struct mail_save_context *ctx, const char *guid)
{
i_assert(guid == NULL || *guid != '\0');
i_free(ctx->data.guid);
ctx->data.guid = i_strdup(guid);
}
void mailbox_save_set_pop3_uidl(struct mail_save_context *ctx, const char *uidl)
{
i_assert(*uidl != '\0');
i_assert(strchr(uidl, '\n') == NULL);
i_free(ctx->data.pop3_uidl);
ctx->data.pop3_uidl = i_strdup(uidl);
}
void mailbox_save_set_pop3_order(struct mail_save_context *ctx,
unsigned int order)
{
i_assert(order > 0);
ctx->data.pop3_order = order;
}
void mailbox_save_set_dest_mail(struct mail_save_context *ctx,
struct mail *mail)
{
ctx->dest_mail = mail;
}
int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input)
{
struct mailbox *box = (*ctx)->transaction->box;
int ret;
if (mail_index_is_deleted(box->index)) {
mailbox_set_deleted(box);
return -1;
}
if (!(*ctx)->copying_via_save)
(*ctx)->saving = TRUE;
if (box->v.save_begin == NULL) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Saving messages not supported");
ret = -1;
} else {
ret = box->v.save_begin(*ctx, input);
}
if (ret < 0) {
mailbox_save_cancel(ctx);
return -1;
}
return 0;
}
int mailbox_save_continue(struct mail_save_context *ctx)
{
return ctx->transaction->box->v.save_continue(ctx);
}
int mailbox_save_finish(struct mail_save_context **_ctx)
{
struct mail_save_context *ctx = *_ctx;
struct mailbox *box = ctx->transaction->box;
struct mail_keywords *keywords = ctx->data.keywords;
int ret;
*_ctx = NULL;
ret = box->v.save_finish(ctx);
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
return ret;
}
void mailbox_save_cancel(struct mail_save_context **_ctx)
{
struct mail_save_context *ctx = *_ctx;
struct mail_keywords *keywords = ctx->data.keywords;
*_ctx = NULL;
ctx->transaction->box->v.save_cancel(ctx);
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
}
struct mailbox_transaction_context *
mailbox_save_get_transaction(struct mail_save_context *ctx)
{
return ctx->transaction;
}
int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
struct mailbox *box = ctx->transaction->box;
struct mail_keywords *keywords = ctx->data.keywords;
int ret;
*_ctx = NULL;
if (mail_index_is_deleted(box->index)) {
mailbox_set_deleted(box);
mailbox_save_cancel(&ctx);
return -1;
}
ret = ctx->transaction->box->v.copy(ctx, mail);
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
return ret;
}
int mailbox_move(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
ctx->moving = TRUE;
if (mailbox_copy(_ctx, mail) < 0)
return -1;
mail_expunge(mail);
return 0;
}
int mailbox_save_using_mail(struct mail_save_context **ctx, struct mail *mail)
{
(*ctx)->saving = TRUE;
return mailbox_copy(ctx, mail);
}
bool mailbox_is_inconsistent(struct mailbox *box)
{
return box->mailbox_deleted || box->v.is_inconsistent(box);
}
void mailbox_set_deleted(struct mailbox *box)
{
mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
"Mailbox was deleted under us");
box->mailbox_deleted = TRUE;
}
int mailbox_get_path_to(struct mailbox *box, enum mailbox_list_path_type type,
const char **path_r)
{
int ret;
if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX && box->_path != NULL) {
if (box->_path[0] == '\0') {
*path_r = NULL;
return 0;
}
*path_r = box->_path;
return 1;
}
ret = mailbox_list_get_path(box->list, box->name, type, path_r);
if (ret < 0) {
mail_storage_copy_list_error(box->storage, box->list);
return -1;
}
if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX && box->_path == NULL)
box->_path = ret == 0 ? "" : p_strdup(box->pool, *path_r);
return ret;
}
const char *mailbox_get_path(struct mailbox *box)
{
i_assert(box->_path != NULL);
i_assert(box->_path[0] != '\0');
return box->_path;
}
static void mailbox_get_permissions_if_not_set(struct mailbox *box)
{
if (box->_perm.file_create_mode != 0)
return;
if (box->input != NULL) {
box->_perm.file_uid = geteuid();
box->_perm.file_create_mode = 0600;
box->_perm.dir_create_mode = 0700;
box->_perm.file_create_gid = (gid_t)-1;
box->_perm.file_create_gid_origin = "defaults";
return;
}
mailbox_list_get_permissions(box->list, box->name, &box->_perm);
box->_perm.file_create_gid_origin =
p_strdup(box->pool, box->_perm.file_create_gid_origin);
}
const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box)
{
mailbox_get_permissions_if_not_set(box);
if (!box->_perm.mail_index_permissions_set && box->index != NULL) {
box->_perm.mail_index_permissions_set = TRUE;
mail_index_set_permissions(box->index,
box->_perm.file_create_mode,
box->_perm.file_create_gid,
box->_perm.file_create_gid_origin);
}
return &box->_perm;
}
void mailbox_refresh_permissions(struct mailbox *box)
{
memset(&box->_perm, 0, sizeof(box->_perm));
(void)mailbox_get_permissions(box);
}
int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
int *fd_r)
{
const struct mailbox_permissions *perm = mailbox_get_permissions(box);
mode_t old_mask;
int fd;
i_assert((flags & O_CREAT) != 0);
*fd_r = -1;
old_mask = umask(0);
fd = open(path, flags, perm->file_create_mode);
umask(old_mask);
if (fd != -1) {
/* ok */
} else if (errno == EEXIST) {
/* O_EXCL used, caller will handle this error */
return 0;
} else if (errno == ENOENT) {
mailbox_set_deleted(box);
return -1;
} else if (errno == ENOTDIR) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox doesn't allow inferior mailboxes");
return -1;
} else if (mail_storage_set_error_from_errno(box->storage)) {
return -1;
} else {
mail_storage_set_critical(box->storage,
"open(%s, O_CREAT) failed: %m", path);
return -1;
}
if (perm->file_create_gid != (gid_t)-1) {
if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) {
/* ok */
} else if (errno == EPERM) {
mail_storage_set_critical(box->storage, "%s",
eperm_error_get_chgrp("fchown", path,
perm->file_create_gid,
perm->file_create_gid_origin));
} else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", path);
}
}
*fd_r = fd;
return 1;
}
int mailbox_mkdir(struct mailbox *box, const char *path,
enum mailbox_list_path_type type)
{
const struct mailbox_permissions *perm = mailbox_get_permissions(box);
const char *root_dir;
if (!perm->gid_origin_is_mailbox_path) {
/* mailbox root directory doesn't exist, create it */
root_dir = mailbox_list_get_root_forced(box->list, type);
if (mailbox_list_mkdir_root(box->list, root_dir, type) < 0) {
mail_storage_copy_list_error(box->storage, box->list);
return -1;
}
}
if (mkdir_parents_chgrp(path, perm->dir_create_mode,
perm->file_create_gid,
perm->file_create_gid_origin) == 0)
return 1;
else if (errno == EEXIST)
return 0;
else if (errno == ENOTDIR) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox doesn't allow inferior mailboxes");
return -1;
} else if (mail_storage_set_error_from_errno(box->storage)) {
return -1;
} else {
mail_storage_set_critical(box->storage,
"mkdir_parents(%s) failed: %m", path);
return -1;
}
}
int mailbox_create_missing_dir(struct mailbox *box,
enum mailbox_list_path_type type)
{
const char *mail_dir, *dir;
struct stat st;
int ret;
if ((ret = mailbox_get_path_to(box, type, &dir)) <= 0)
return ret;
if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&mail_dir) < 0)
return -1;
if (null_strcmp(dir, mail_dir) == 0) {
if ((box->list->props & MAILBOX_LIST_PROP_AUTOCREATE_DIRS) == 0)
return 0;
/* the directory might not have been created yet */
}
/* we call this function even when the directory exists, so first do a
quick check to see if we need to mkdir anything */
if (stat(dir, &st) == 0)
return 0;
return mailbox_mkdir(box, dir, type);
}
unsigned int mail_storage_get_lock_timeout(struct mail_storage *storage,
unsigned int secs)
{
return storage->set->mail_max_lock_timeout == 0 ? secs :
I_MIN(secs, storage->set->mail_max_lock_timeout);
}
enum mail_index_open_flags
mail_storage_settings_to_index_flags(const struct mail_storage_settings *set)
{
enum mail_index_open_flags index_flags = 0;
#ifndef MMAP_CONFLICTS_WRITE
if (set->mmap_disable)
#endif
index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
if (set->dotlock_use_excl)
index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL;
if (set->mail_nfs_index)
index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH;
return index_flags;
}