mail-storage.c revision 3857e2945a3b6744d603f0f5a656849ed8436ba3
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "ioloop.h"
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen#include "array.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "llist.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "str.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "str-sanitize.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "unichar.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "istream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "eacces-error.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mkdir-parents.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "time-util.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "var-expand.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "dsasl-client.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "imap-date.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "settings-parser.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mail-index-private.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mail-index-alloc-cache.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mailbox-tree.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mailbox-list-private.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mail-storage-private.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "mail-storage-settings.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mail-namespace.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mail-search.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mail-search-register.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mailbox-search-result-private.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mailbox-guid-cache.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "mail-cache.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <ctype.h>
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#define MAILBOX_DELETE_RETRY_SECS 30
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#define MAILBOX_MAX_HIERARCHY_NAME_LENGTH 255
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenextern struct mail_search_register *mail_search_register_imap;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenextern struct mail_search_register *mail_search_register_human;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstruct mail_storage_module_register mail_storage_module_register = { 0 };
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstruct mail_module_register mail_module_register = { 0 };
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstruct mail_storage_mail_index_module mail_storage_mail_index_module =
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen MODULE_CONTEXT_INIT(&mail_index_module_register);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo SirainenARRAY_TYPE(mail_storage) mail_storage_classes;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid mail_storage_init(void)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen{
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen dsasl_clients_init();
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen mailbox_attributes_init();
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen mailbox_lists_init();
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen mail_storage_hooks_init();
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen i_array_init(&mail_storage_classes, 8);
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen}
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenvoid mail_storage_deinit(void)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen{
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen if (mail_search_register_human != NULL)
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen mail_search_register_deinit(&mail_search_register_human);
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen if (mail_search_register_imap != NULL)
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen mail_search_register_deinit(&mail_search_register_imap);
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen if (array_is_created(&mail_storage_classes))
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen array_free(&mail_storage_classes);
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen mail_storage_hooks_deinit();
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen mailbox_lists_deinit();
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen mailbox_attributes_deinit();
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dsasl_clients_deinit();
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid mail_storage_class_register(struct mail_storage *storage_class)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(mail_storage_find_class(storage_class->name) == NULL);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* append it after the list, so the autodetection order is correct */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen array_append(&mail_storage_classes, &storage_class, 1);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
7db932bd4934cd967eeae643300aef5b91caeaeaTimo Sirainen
7db932bd4934cd967eeae643300aef5b91caeaeaTimo Sirainenvoid mail_storage_class_unregister(struct mail_storage *storage_class)
6556f02c704778bad4240363768cdaf2d3a1a79cTimo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mail_storage *const *classes;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i, count;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen classes = array_get(&mail_storage_classes, &count);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen for (i = 0; i < count; i++) {
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (classes[i] == storage_class) {
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen array_delete(&mail_storage_classes, i, 1);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen break;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainenstruct mail_storage *mail_storage_find_class(const char *name)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen{
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen struct mail_storage *const *classes;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen unsigned int i, count;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen i_assert(name != NULL);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen classes = array_get(&mail_storage_classes, &count);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen for (i = 0; i < count; i++) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (strcasecmp(classes[i]->name, name) == 0)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return classes[i];
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return NULL;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic struct mail_storage *
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenmail_storage_autodetect(const struct mail_namespace *ns,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct mailbox_list_settings *set)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct mail_storage *const *classes;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen unsigned int i, count;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen classes = array_get(&mail_storage_classes, &count);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen for (i = 0; i < count; i++) {
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen if (classes[i]->v.autodetect != NULL) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if (classes[i]->v.autodetect(ns, set))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return classes[i];
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen }
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return NULL;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainenstatic void
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenmail_storage_set_autodetection(const char **data, const char **driver)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen const char *p;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* check if data is in driver:data format (eg. mbox:~/mail) */
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen p = *data;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen while (i_isalnum(*p) || *p == '_') p++;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen if (*p == ':' && p != *data) {
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen /* no autodetection if the storage driver is given. */
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen *driver = t_strdup_until(*data, p);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen *data = p + 1;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen }
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen}
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenstatic struct mail_storage *
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenmail_storage_get_class(struct mail_namespace *ns, const char *driver,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen struct mailbox_list_settings *list_set,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen enum mail_storage_flags flags, const char **error_r)
c24ef531ca58abad996482f5c2e8992be9ae8981Timo Sirainen{
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen struct mail_storage *storage_class = NULL;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen const char *home;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen if (driver == NULL) {
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen /* no mail_location, autodetect */
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen } else if (strcmp(driver, "auto") == 0) {
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* explicit autodetection with "auto" driver. */
2524ef7b34965a1b1895d6140fd8296bf57c78d2Timo Sirainen if (list_set->root_dir != NULL &&
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen *list_set->root_dir == '\0') {
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* handle the same as with driver=NULL */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen list_set->root_dir = NULL;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen } else {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen storage_class = mail_user_get_storage_class(ns->user, driver);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (storage_class == NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf(
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen "Unknown mail storage driver %s", driver);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return NULL;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (list_set->root_dir == NULL || *list_set->root_dir == '\0') {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* no root directory given. is this allowed? */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen const struct mailbox_list *list;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen list = list_set->layout == NULL ? NULL :
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen mailbox_list_find_class(list_set->layout);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (storage_class == NULL &&
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) == 0) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* autodetection should take care of this */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen } else if (storage_class != NULL &&
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* root not required for this storage */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen } else if (list != NULL &&
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (list->props & MAILBOX_LIST_PROP_NO_ROOT) != 0) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* root not required for this layout */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen } else {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen *error_r = "Root mail directory not given";
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return NULL;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (storage_class != NULL) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen storage_class->v.get_list_settings(ns, list_set);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return storage_class;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen storage_class = mail_storage_autodetect(ns, list_set);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (storage_class != NULL)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return storage_class;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen (void)mail_user_get_home(ns->user, &home);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (home == NULL || *home == '\0') home = "(not set)";
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen if (ns->set->location == NULL || *ns->set->location == '\0') {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen *error_r = t_strdup_printf(
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "Mail storage autodetection failed with home=%s", home);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen } else if (strncmp(ns->set->location, "auto:", 5) == 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf(
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "Autodetection failed for %s (home=%s)",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ns->set->location, home);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen } else {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf(
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen "Ambiguous mail location setting, "
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "don't know what to do with it: %s "
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "(try prefixing it with mbox: or maildir:)",
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen ns->set->location);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return NULL;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen}
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenmail_storage_verify_root(const char *root_dir, bool autocreate,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen const char **error_r)
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct stat st;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen if (stat(root_dir, &st) == 0) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* exists */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return 1;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen } else if (errno == EACCES) {
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen *error_r = mail_error_eacces_msg("stat", root_dir);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return -1;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen } else if (errno != ENOENT && errno != ENOTDIR) {
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen *error_r = t_strdup_printf("stat(%s) failed: %m", root_dir);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return -1;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen } else if (!autocreate) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf(
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen "Root mail directory doesn't exist: %s", root_dir);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen } else {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* doesn't exist */
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen return 0;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenmail_storage_create_root(struct mailbox_list *list,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen enum mail_storage_flags flags, const char **error_r)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen const char *root_dir, *error;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen bool autocreate;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen int ret;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen &root_dir)) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen /* storage doesn't use directories (e.g. shared root) */
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen return 0;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen }
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if ((flags & MAIL_STORAGE_FLAG_NO_AUTOVERIFY) != 0) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (!list->mail_set->mail_debug)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* we don't need to verify, but since debugging is
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen enabled, check and log if the root doesn't exist */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (mail_storage_verify_root(root_dir, FALSE, &error) < 0) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen i_debug("Namespace %s: Creating storage despite: %s",
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen list->ns->prefix, error);
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen }
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen return 0;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen autocreate = (flags & MAIL_STORAGE_FLAG_NO_AUTOCREATE) == 0;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen ret = mail_storage_verify_root(root_dir, autocreate, error_r);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (ret == 0) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen ret = mailbox_list_try_mkdir_root(list, root_dir,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen error_r);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen return ret < 0 ? -1 : 0;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen}
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenstatic bool
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainenmail_storage_match_class(struct mail_storage *storage,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen const struct mail_storage *storage_class,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen const struct mailbox_list_settings *set)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen{
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (strcmp(storage->name, storage_class->name) != 0)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if ((storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 &&
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen strcmp(storage->unique_root_dir,
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen (set->root_dir != NULL ? set->root_dir : "")) != 0)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (strcmp(storage->name, "shared") == 0) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* allow multiple independent shared namespaces */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen }
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return TRUE;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen}
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainenstatic struct mail_storage *
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainenmail_storage_find(struct mail_user *user,
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen const struct mail_storage *storage_class,
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen const struct mailbox_list_settings *set)
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen{
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen struct mail_storage *storage = user->storages;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen for (; storage != NULL; storage = storage->next) {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (mail_storage_match_class(storage, storage_class, set))
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen return storage;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen }
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen return NULL;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen}
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainenint mail_storage_create_full(struct mail_namespace *ns, const char *driver,
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen const char *data, enum mail_storage_flags flags,
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen struct mail_storage **storage_r,
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen const char **error_r)
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct mail_storage *storage_class, *storage = NULL;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen struct mailbox_list *list;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen struct mailbox_list_settings list_set;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen enum mailbox_list_flags list_flags = 0;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const char *p;
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen ns->mail_set->pop3_uidl_format != NULL) {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen /* if pop3_uidl_format contains %m, we want to keep the
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen header MD5 sums stored even if we're not running POP3
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen right now. */
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen p = ns->mail_set->pop3_uidl_format;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen while ((p = strchr(p, '%')) != NULL) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (p[1] == '%')
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen p += 2;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen else if (var_get_key(++p) == 'm') {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen break;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen }
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen }
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen }
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen mailbox_list_settings_init_defaults(&list_set);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (data == NULL) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* autodetect */
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen } else if (driver != NULL && strcmp(driver, "shared") == 0) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen /* internal shared namespace */
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen list_set.root_dir = ns->user->set->base_dir;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen } else {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (driver == NULL)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen mail_storage_set_autodetection(&data, &driver);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (mailbox_list_settings_parse(ns->user, data, &list_set,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen error_r) < 0)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen return -1;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen storage_class = mail_storage_get_class(ns, driver, &list_set, flags,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen error_r);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (storage_class == NULL)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen return -1;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen i_assert(list_set.layout != NULL);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (ns->list == NULL) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen /* first storage for namespace */
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (mail_storage_is_mailbox_file(storage_class))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen list_flags |= MAILBOX_LIST_FLAG_MAILBOX_FILES;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) != 0)
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen list_flags |= MAILBOX_LIST_FLAG_NO_MAIL_FILES;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_LIST_DELETES) != 0)
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen list_flags |= MAILBOX_LIST_FLAG_NO_DELETES;
df16c7e87511fed827e6890a2a47d13ca48716deTimo Sirainen if (mailbox_list_create(list_set.layout, ns, &list_set,
df16c7e87511fed827e6890a2a47d13ca48716deTimo Sirainen list_flags, &list, error_r) < 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *error_r = t_strdup_printf("Mailbox list driver %s: %s",
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen list_set.layout, *error_r);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return -1;
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen }
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen if ((storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_NO_ROOT) == 0) {
b7651d283ca261015ef3c445f1f27f340f0864e2Timo Sirainen if (mail_storage_create_root(ns->list, flags, error_r) < 0)
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen return -1;
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen }
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen }
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainen storage = mail_storage_find(ns->user, storage_class, &list_set);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (storage != NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* using an existing storage */
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen storage->refcount++;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen mail_namespace_add_storage(ns, storage);
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen *storage_r = storage;
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen return 0;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen }
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen storage = storage_class->v.alloc();
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen storage->refcount = 1;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen storage->storage_class = storage_class;
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen storage->user = ns->user;
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen storage->set = ns->mail_set;
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen storage->flags = flags;
c0b1543512bc3e0a3a9f526056a3678a07ce32f5Timo Sirainen p_array_init(&storage->module_contexts, storage->pool, 5);
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen if (storage->v.create != NULL &&
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen storage->v.create(storage, ns, error_r) < 0) {
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen *error_r = t_strdup_printf("%s: %s", storage->name, *error_r);
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen pool_unref(&storage->pool);
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen return -1;
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen }
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen T_BEGIN {
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen hook_mail_storage_created(storage);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen } T_END;
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen DLLIST_PREPEND(&ns->user->storages, storage);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen mail_namespace_add_storage(ns, storage);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen *storage_r = storage;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen return 0;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainenint mail_storage_create(struct mail_namespace *ns, const char *driver,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen enum mail_storage_flags flags, const char **error_r)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen struct mail_storage *storage;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return mail_storage_create_full(ns, driver, ns->set->location,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen flags, &storage, error_r);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid mail_storage_unref(struct mail_storage **_storage)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen struct mail_storage *storage = *_storage;
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen i_assert(storage->refcount > 0);
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen /* set *_storage=NULL only after calling destroy() callback.
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen for example mdbox wants to access ns->storage */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen if (--storage->refcount > 0) {
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen *_storage = NULL;
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen return;
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen }
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (storage->mailboxes != NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_panic("Trying to deinit storage without freeing mailbox %s",
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen storage->mailboxes->vname);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (storage->obj_refcount != 0)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen i_panic("Trying to deinit storage before freeing its objects");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen DLLIST_REMOVE(&storage->user->storages, storage);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen storage->v.destroy(storage);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_free(storage->error_string);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (array_is_created(&storage->error_stack)) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(array_count(&storage->error_stack) == 0);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen array_free(&storage->error_stack);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *_storage = NULL;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen pool_unref(&storage->pool);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen mail_index_alloc_cache_destroy_unrefed();
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen}
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainenvoid mail_storage_obj_ref(struct mail_storage *storage)
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(storage->refcount > 0);
f4bbeadda12fbd7c219063db68f3e78646d83c2cTimo Sirainen
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen if (storage->obj_refcount++ == 0)
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen mail_user_ref(storage->user);
abe8754852e70763e92f74caabbcc13d0917714cTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenvoid mail_storage_obj_unref(struct mail_storage *storage)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen{
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen i_assert(storage->refcount > 0);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen i_assert(storage->obj_refcount > 0);
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen if (--storage->obj_refcount == 0) {
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen struct mail_user *user = storage->user;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen mail_user_unref(&user);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen}
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenvoid mail_storage_clear_error(struct mail_storage *storage)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen{
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_free_and_null(storage->error_string);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen storage->error = MAIL_ERROR_NONE;
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen}
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid mail_storage_set_error(struct mail_storage *storage,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen enum mail_error error, const char *string)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen{
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen i_free(storage->error_string);
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen storage->error_string = i_strdup(string);
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen storage->error = error;
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen}
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenvoid mail_storage_set_internal_error(struct mail_storage *storage)
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen{
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen const char *str;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen i_free(storage->error_string);
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen storage->error_string = i_strdup(str);
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen storage->error = MAIL_ERROR_TEMP;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen}
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainenvoid mail_storage_set_critical(struct mail_storage *storage,
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen const char *fmt, ...)
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen{
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen va_list va;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen va_start(va, fmt);
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen i_error("%s", t_strdup_vprintf(fmt, va));
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen va_end(va);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen /* critical errors may contain sensitive data, so let user
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen see only "Internal error" with a timestamp to make it
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen easier to look from log files the actual error message. */
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen mail_storage_set_internal_error(storage);
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen}
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainenvoid mail_storage_copy_error(struct mail_storage *dest,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen struct mail_storage *src)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen{
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen const char *str;
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen enum mail_error error;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (src == dest)
14175321ddb88619015866978c05a27786ca4814Timo Sirainen return;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainen str = mail_storage_get_last_error(src, &error);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen mail_storage_set_error(dest, error, str);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen}
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
14175321ddb88619015866978c05a27786ca4814Timo Sirainenvoid mail_storage_copy_list_error(struct mail_storage *storage,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen struct mailbox_list *list)
14175321ddb88619015866978c05a27786ca4814Timo Sirainen{
14175321ddb88619015866978c05a27786ca4814Timo Sirainen const char *str;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen enum mail_error error;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen str = mailbox_list_get_last_error(list, &error);
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen mail_storage_set_error(storage, error, str);
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen}
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainen
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainenvoid mailbox_set_index_error(struct mailbox *box)
14175321ddb88619015866978c05a27786ca4814Timo Sirainen{
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (mail_index_is_deleted(box->index))
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen mailbox_set_deleted(box);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen else
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen mail_storage_set_internal_error(box->storage);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen mail_index_reset_error(box->index);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen}
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenconst struct mail_storage_settings *
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenmail_storage_get_settings(struct mail_storage *storage)
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen{
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen return storage->set;
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen}
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenstruct mail_user *mail_storage_get_user(struct mail_storage *storage)
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen{
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen return storage->user;
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen}
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainenvoid mail_storage_set_callbacks(struct mail_storage *storage,
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen struct mail_storage_callbacks *callbacks,
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen void *context)
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen{
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen storage->callbacks = *callbacks;
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen storage->callback_context = context;
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen}
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainenint mail_storage_purge(struct mail_storage *storage)
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen{
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen return storage->v.purge == NULL ? 0 :
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen storage->v.purge(storage);
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen}
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainenconst char *mail_storage_get_last_error(struct mail_storage *storage,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen enum mail_error *error_r)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen /* We get here only in error situations, so we have to return some
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen error. If storage->error is NONE, it means we forgot to set it at
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen some point.. */
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (storage->error == MAIL_ERROR_NONE) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (error_r != NULL)
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen *error_r = MAIL_ERROR_TEMP;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen return storage->error_string != NULL ? storage->error_string :
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen "BUG: Unknown internal error";
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen }
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (storage->error_string == NULL) {
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen /* This shouldn't happen.. */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen storage->error_string =
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen i_strdup_printf("BUG: Unknown 0x%x error",
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen storage->error);
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen }
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (error_r != NULL)
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen *error_r = storage->error;
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen return storage->error_string;
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen}
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainenconst char *mailbox_get_last_error(struct mailbox *box,
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen enum mail_error *error_r)
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen{
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen return mail_storage_get_last_error(box->storage, error_r);
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen}
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainenenum mail_error mailbox_get_last_mail_error(struct mailbox *box)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen{
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen enum mail_error error;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen mail_storage_get_last_error(box->storage, &error);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen return error;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen}
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainenvoid mail_storage_last_error_push(struct mail_storage *storage)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen{
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen struct mail_storage_error *err;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen if (!array_is_created(&storage->error_stack))
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen i_array_init(&storage->error_stack, 2);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen err = array_append_space(&storage->error_stack);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen err->error_string = i_strdup(storage->error_string);
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen err->error = storage->error;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen}
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainenvoid mail_storage_last_error_pop(struct mail_storage *storage)
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen{
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen unsigned int count = array_count(&storage->error_stack);
d9ab8a13b51c9d8f4e13e1bf785eeadce6702b3bTimo Sirainen const struct mail_storage_error *err =
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen array_idx(&storage->error_stack, count-1);
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen i_free(storage->error_string);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen storage->error_string = err->error_string;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen storage->error = err->error;
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen array_delete(&storage->error_stack, count-1, 1);
d5b3f66491101aba8667369586c95c615cb26ae6Timo Sirainen}
5df8396a7cbad0b38b83a86667fb3d4c223f6f7cTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenbool mail_storage_is_mailbox_file(struct mail_storage *storage)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen{
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return (storage->class_flags &
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE) != 0;
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen}
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainenbool mail_storage_set_error_from_errno(struct mail_storage *storage)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen{
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen const char *error_string;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen enum mail_error error;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (!mail_error_from_errno(&error, &error_string))
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (storage->set->mail_debug && error != MAIL_ERROR_NOTFOUND) {
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* debugging is enabled - admin may be debugging a
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen (permission) problem, so return FALSE to get the caller to
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen log the full error message. */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return FALSE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen }
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen mail_storage_set_error(storage, error, error_string);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return TRUE;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen}
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainenconst struct mailbox_settings *
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenmailbox_settings_find(struct mail_namespace *ns, const char *vname)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
9511a40d933181045343110c8101b75887062aaeTimo Sirainen struct mailbox_settings *const *box_set;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainen if (!array_is_created(&ns->set->mailboxes))
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return NULL;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
9511a40d933181045343110c8101b75887062aaeTimo Sirainen if (ns->prefix_len > 0 &&
9511a40d933181045343110c8101b75887062aaeTimo Sirainen strncmp(ns->prefix, vname, ns->prefix_len-1) == 0) {
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen if (vname[ns->prefix_len-1] == mail_namespace_get_sep(ns))
9511a40d933181045343110c8101b75887062aaeTimo Sirainen vname += ns->prefix_len;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen else if (vname[ns->prefix_len-1] == '\0') {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* namespace prefix itself */
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen vname = "";
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen }
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen array_foreach(&ns->set->mailboxes, box_set) {
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen if (strcmp((*box_set)->name, vname) == 0)
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen return *box_set;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen }
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return NULL;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen}
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenstruct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen enum mailbox_flags flags)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mailbox_list *new_list = list;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct mail_storage *storage;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen struct mailbox *box;
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen enum mail_error open_error = 0;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen const char *errstr = NULL;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen i_assert(uni_utf8_str_is_valid(vname));
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (strncasecmp(vname, "INBOX", 5) == 0 &&
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen strncmp(vname, "INBOX", 5) != 0) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* make sure INBOX shows up in uppercase everywhere. do this
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen regardless of whether we're in inbox=yes namespace, because
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen clients expect INBOX to be case insensitive regardless of
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen server's internal configuration. */
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen if (vname[5] == '\0')
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen vname = "INBOX";
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen else if (vname[5] != mail_namespace_get_sep(list->ns))
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen /* not INBOX prefix */ ;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen else if (strncasecmp(list->ns->prefix, vname, 6) == 0 &&
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen strncmp(list->ns->prefix, "INBOX", 5) != 0) {
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen mailbox_list_set_critical(list,
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen "Invalid server configuration: "
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen "Namespace prefix=%s must be uppercase INBOX",
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen list->ns->prefix);
d14e62b7b37dc78fcc940aca25042eceb358b156Timo Sirainen open_error = MAIL_ERROR_TEMP;
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen } else {
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen vname = t_strconcat("INBOX", vname + 5, NULL);
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen }
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen }
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen
T_BEGIN {
if (mailbox_list_get_storage(&new_list, vname, &storage) < 0) {
/* do a delayed failure at mailbox_open() */
storage = mail_namespace_get_default_storage(list->ns);
errstr = mailbox_list_get_last_error(new_list, &open_error);
errstr = t_strdup(errstr);
}
box = storage->v.mailbox_alloc(storage, new_list, vname, flags);
box->set = mailbox_settings_find(new_list->ns, vname);
box->open_error = open_error;
if (open_error != 0)
mail_storage_set_error(storage, open_error, errstr);
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_EXISTS) {
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" */
if (strncmp(vname, ns->prefix, ns->prefix_len-1) != 0 ||
(vname[ns->prefix_len-1] != '\0' &&
vname[ns->prefix_len-1] != ns->prefix[ns->prefix_len-1])) {
/* User input shouldn't normally be able to get us in
here. The main reason this isn't an assert is to
allow any input at all to mailbox_verify_*_name()
without crashing. */
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
t_strdup_printf("Invalid mailbox name '%s': "
"Missing namespace prefix '%s'",
str_sanitize(vname, 80), ns->prefix));
return -1;
}
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;
}
void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set)
{
box->skip_create_name_restrictions = set;
}
int mailbox_verify_create_name(struct mailbox *box)
{
/* 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 (box->skip_create_name_restrictions)
return 0;
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 (strlen(box->vname) > MAILBOX_LIST_NAME_MAX_LENGTH) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Mailbox name too long");
return -1;
}
/* check individual component names, too */
const char *old_name = box->name;
const char *name;
const char sep = mailbox_list_get_hierarchy_sep(box->list);
while((name = strchr(old_name, sep)) != NULL) {
if (name - old_name > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) {
mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS,
"Mailbox name too long");
return -1;
}
name++;
old_name = name;
}
if (strlen(old_name) > MAILBOX_MAX_HIERARCHY_NAME_LENGTH) {
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)
{
size_t 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 && 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;
i_assert(!box->mailbox_undeleting);
if ((box->flags & MAILBOX_FLAG_READONLY) != 0) {
/* most importantly we don't do this because we want to avoid
a loop: mdbox storage rebuild -> mailbox_open() ->
mailbox_mark_index_deleted() -> mailbox_sync() ->
mdbox storage rebuild. */
return FALSE;
}
if (mail_index_get_modification_time(box->index, &mtime) < 0)
return FALSE;
if (mtime + MAILBOX_DELETE_RETRY_SECS > time(NULL))
return FALSE;
box->mailbox_undeleting = TRUE;
int ret = mailbox_mark_index_deleted(box, FALSE);
box->mailbox_undeleting = FALSE;
if (ret < 0)
return FALSE;
box->mailbox_deleted = FALSE;
return TRUE;
}
int mailbox_open(struct mailbox *box)
{
/* check that the storage supports stubs if require them */
if (((box->flags & MAILBOX_FLAG_USE_STUBS) != 0) &&
((box->storage->storage_class->class_flags & MAIL_STORAGE_CLASS_FLAG_STUBS) == 0)) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Mailbox does not support mail stubs");
return -1;
}
if (mailbox_open_full(box, NULL) < 0) {
if (!box->mailbox_deleted || box->mailbox_undeleting)
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;
}
static int mailbox_alloc_index_pvt(struct mailbox *box)
{
const char *index_dir;
int ret;
if (box->index_pvt != NULL)
return 1;
ret = mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE,
&index_dir);
if (ret <= 0)
return ret; /* error / no private indexes */
if (mailbox_create_missing_dir(box, MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE) < 0)
return -1;
box->index_pvt = mail_index_alloc_cache_get(NULL, index_dir,
t_strconcat(box->index_prefix, ".pvt", NULL));
mail_index_set_fsync_mode(box->index_pvt,
box->storage->set->parsed_fsync_mode, 0);
mail_index_set_lock_method(box->index_pvt,
box->storage->set->parsed_lock_method,
mail_storage_get_lock_timeout(box->storage, UINT_MAX));
return 1;
}
int mailbox_open_index_pvt(struct mailbox *box)
{
enum mail_index_open_flags index_flags;
int ret;
if (box->view_pvt != NULL)
return 1;
if (mailbox_get_private_flags_mask(box) == 0)
return 0;
if ((ret = mailbox_alloc_index_pvt(box)) <= 0)
return ret;
index_flags = MAIL_INDEX_OPEN_FLAG_CREATE |
mail_storage_settings_to_index_flags(box->storage->set);
if ((box->flags & MAILBOX_FLAG_SAVEONLY) != 0)
index_flags |= MAIL_INDEX_OPEN_FLAG_SAVEONLY;
if (mail_index_open(box->index_pvt, index_flags) < 0)
return -1;
box->view_pvt = mail_index_view_open(box->index_pvt);
return 1;
}
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);
i_zero(&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);
if (array_is_created(&box->recent_flags))
array_free(&box->recent_flags);
box->recent_flags_prev_uid = 0;
box->recent_flags_count = 0;
}
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;
}
bool mailbox_is_any_inbox(struct mailbox *box)
{
return box->inbox_any;
}
static void mailbox_copy_cache_decisions_from_inbox(struct mailbox *box)
{
struct mail_namespace *ns =
mail_namespace_find_inbox(box->storage->user->namespaces);
struct mailbox *inbox =
mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY);
enum mailbox_existence existence;
/* this should be NoSelect but since inbox can never be
NoSelect we use EXISTENCE_NONE to avoid creating inbox by accident */
if (mailbox_exists(inbox, FALSE, &existence) == 0 &&
existence != MAILBOX_EXISTENCE_NONE &&
mailbox_open(inbox) == 0 &&
mailbox_open(box) == 0) {
struct mail_index_transaction *dit =
mail_index_transaction_begin(box->view,
MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
mail_cache_decisions_copy(dit, inbox->cache, box->cache);
/* we can't do much about errors here */
(void)mail_index_transaction_commit(&dit);
}
mailbox_free(&inbox);
}
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;
if (ret == 0) {
box->list->guid_cache_updated = TRUE;
if (!box->inbox_any)
mailbox_copy_cache_decisions_from_inbox(box);
} else if (box->opened) {
/* Creation failed after (partially) opening the mailbox.
It may not be in a valid state, so close it. */
mail_storage_last_error_push(box->storage);
mailbox_close(box);
mail_storage_last_error_pop(box->storage);
}
return ret;
}
int mailbox_update(struct mailbox *box, const struct mailbox_update *update)
{
int ret;
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;
ret = box->v.update_box(box, update);
if (!guid_128_is_empty(update->mailbox_guid))
box->list->guid_cache_invalidated = TRUE;
return ret;
}
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) {
mailbox_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. */
box->delete_sync_check = TRUE;
ret = mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ);
box->delete_sync_check = FALSE;
if (ret < 0)
return -1;
box->marked_deleted = del;
return 0;
}
static void mailbox_close_reset_path(struct mailbox *box)
{
i_zero(&box->_perm);
box->_path = NULL;
box->_index_path = NULL;
}
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;
}
box->deleting = TRUE;
if (mailbox_open(box) < 0) {
if (mailbox_get_last_mail_error(box) != MAIL_ERROR_NOTFOUND &&
!box->mailbox_deleted)
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);
/* if mailbox is reopened, its path may be different with
LAYOUT=index */
mailbox_close_reset_path(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;
}
static
int mailbox_rename_check_children(struct mailbox *src, struct mailbox *dest)
{
int ret = 0;
size_t src_prefix_len = strlen(src->vname)+1; /* include separator */
size_t dest_prefix_len = strlen(dest->vname)+1;
/* this can return folders with * in their name, that are not
actually our children */
const char *pattern = t_strdup_printf("%s%c*", src->vname,
mail_namespace_get_sep(src->list->ns));
struct mailbox_list_iterate_context *iter = mailbox_list_iter_init(src->list, pattern,
MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
const struct mailbox_info *child;
while((child = mailbox_list_iter_next(iter)) != NULL) {
if (strncmp(child->vname, src->vname, src_prefix_len) != 0)
continue; /* not our child */
/* if total length of new name exceeds the limit, fail */
if (strlen(child->vname + src_prefix_len)+dest_prefix_len > MAILBOX_LIST_NAME_MAX_LENGTH) {
mail_storage_set_error(dest->storage, MAIL_ERROR_PARAMS,
"Mailbox or child name too long");
ret = -1;
break;
}
}
/* something went bad */
if (mailbox_list_iter_deinit(&iter) < 0) {
mail_storage_copy_list_error(dest->storage, src->list);
ret = -1;
}
return ret;
}
int mailbox_rename(struct mailbox *src, struct mailbox *dest)
{
const char *error = NULL;
/* Check only name validity, \Noselect don't necessarily exist. */
if (mailbox_verify_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 (mailbox_rename_check_children(src, dest) != 0) {
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;
}
if (src->v.rename_box(src, dest) < 0)
return -1;
src->list->guid_cache_invalidated = TRUE;
dest->list->guid_cache_invalidated = TRUE;
return 0;
}
int mailbox_set_subscribed(struct mailbox *box, bool set)
{
if (mailbox_verify_name(box) < 0)
return -1;
if (mailbox_list_iter_subscriptions_refresh(box->list) < 0) {
mail_storage_copy_list_error(box->storage, box->list);
return -1;
}
if (mailbox_is_subscribed(box) == set)
return 0;
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;
}
static void
mailbox_get_status_set_defaults(struct mailbox *box,
struct mailbox_status *status_r)
{
i_zero(status_r);
if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUIDS) != 0)
status_r->have_guids = TRUE;
if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_SAVE_GUIDS) != 0)
status_r->have_save_guids = TRUE;
if ((box->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_HAVE_MAIL_GUID128) != 0)
status_r->have_only_guid128 = TRUE;
}
int mailbox_get_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
mailbox_get_status_set_defaults(box, status_r);
if (mailbox_verify_existing_name(box) < 0)
return -1;
if (box->v.get_status(box, items, status_r) < 0)
return -1;
i_assert(status_r->have_guids || !status_r->have_save_guids);
return 0;
}
void mailbox_get_open_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
i_assert(box->opened);
i_assert((items & MAILBOX_STATUS_FAILING_ITEMS) == 0);
mailbox_get_status_set_defaults(box, 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)
{
i_zero(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)
{
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;
i_zero(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)
{
i_assert(wanted_headers == NULL || wanted_headers->box == t->box);
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;
*tryagain_r = FALSE;
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((flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0 ||
(box->flags & MAILBOX_FLAG_USE_STUBS) != 0);
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. */
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;
struct mailbox *box = t->box;
unsigned int save_count = t->save_count;
int ret;
changes_r->pool = NULL;
*_t = NULL;
T_BEGIN {
ret = box->v.transaction_commit(t, changes_r);
} T_END;
/* either all the saved messages get UIDs or none, because a) we
failed, b) MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS not set,
c) backend doesn't support it (e.g. virtual plugin) */
i_assert(ret < 0 ||
seq_range_count(&changes_r->saved_uids) == save_count ||
array_count(&changes_r->saved_uids) == 0);
/* decrease the transaction count only after transaction_commit().
that way if it creates and destroys transactions internally, we
don't see transaction_count=0 until the parent transaction is fully
finished */
box->transaction_count--;
if (ret < 0 && changes_r->pool != NULL)
pool_unref(&changes_r->pool);
return ret;
}
void mailbox_transaction_rollback(struct mailbox_transaction_context **_t)
{
struct mailbox_transaction_context *t = *_t;
struct mailbox *box = t->box;
*_t = NULL;
box->v.transaction_rollback(t);
box->transaction_count--;
}
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;
}
static void mailbox_save_dest_mail_close(struct mail_save_context *ctx)
{
struct mail_private *mail = (struct mail_private *)ctx->dest_mail;
mail->v.close(&mail->mail);
}
struct mail_save_context *
mailbox_save_alloc(struct mailbox_transaction_context *t)
{
struct mail_save_context *ctx;
T_BEGIN {
ctx = t->box->v.save_alloc(t);
} T_END;
i_assert(!ctx->unfinished);
ctx->unfinished = TRUE;
ctx->data.received_date = (time_t)-1;
ctx->data.save_date = (time_t)-1;
/* Always have a dest_mail available. A lot of plugins make use
of this. */
if (ctx->dest_mail == NULL)
ctx->dest_mail = mail_alloc(t, 0, NULL);
else {
/* make sure the mail isn't used before mail_set_seq_saving() */
mailbox_save_dest_mail_close(ctx);
}
return ctx;
}
void mailbox_save_context_deinit(struct mail_save_context *ctx)
{
i_assert(ctx->dest_mail != NULL);
if (!ctx->dest_mail_external)
mail_free(&ctx->dest_mail);
else
ctx->dest_mail = NULL;
}
void mailbox_save_set_flags(struct mail_save_context *ctx,
enum mail_flags flags,
struct mail_keywords *keywords)
{
struct mailbox *box = ctx->transaction->box;
if (ctx->data.keywords != NULL)
mailbox_keywords_unref(&ctx->data.keywords);
ctx->data.flags = flags & ~mailbox_get_private_flags_mask(box);
ctx->data.pvt_flags = flags & mailbox_get_private_flags_mask(box);
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;
struct mail_keywords *keywords;
keywords_list = mail_get_keywords(mail);
keywords = str_array_length(keywords_list) == 0 ? NULL :
mailbox_keywords_create_valid(ctx->transaction->box,
keywords_list);
mailbox_save_set_flags(ctx, mail_get_flags(mail), keywords);
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
}
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;
if ((ctx->transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) != 0) {
if (!mail_index_lookup_seq(ctx->transaction->view, uid,
&ctx->data.stub_seq))
i_panic("Trying to fill in stub for nonexistent UID %u", 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)
{
i_assert(mail != NULL);
if (!ctx->dest_mail_external)
mail_free(&ctx->dest_mail);
ctx->dest_mail = mail;
ctx->dest_mail_external = TRUE;
}
struct mail *mailbox_save_get_dest_mail(struct mail_save_context *ctx)
{
return ctx->dest_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);
mailbox_save_cancel(ctx);
return -1;
}
/* if we're filling in a stub, we must have set UID already
(which in turn sets stub_seq) */
i_assert(((*ctx)->transaction->flags & MAILBOX_TRANSACTION_FLAG_FILL_IN_STUB) == 0 ||
(*ctx)->data.stub_seq != 0);
if (!(*ctx)->copying_or_moving) {
/* We're actually saving the mail. We're not being called by
mail_storage_copy() because backend didn't support fast
copying. */
i_assert(!(*ctx)->copying_via_save);
(*ctx)->saving = TRUE;
} else {
i_assert((*ctx)->copying_via_save);
}
if (box->v.save_begin == NULL) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Saving messages not supported");
ret = -1;
} else T_BEGIN {
ret = box->v.save_begin(*ctx, input);
} T_END;
if (ret < 0) {
mailbox_save_cancel(ctx);
return -1;
}
return 0;
}
int mailbox_save_continue(struct mail_save_context *ctx)
{
int ret;
T_BEGIN {
ret = ctx->transaction->box->v.save_continue(ctx);
} T_END;
return ret;
}
static void
mailbox_save_add_pvt_flags(struct mailbox_transaction_context *t,
enum mail_flags pvt_flags)
{
struct mail_save_private_changes *save;
if (!array_is_created(&t->pvt_saves))
i_array_init(&t->pvt_saves, 8);
save = array_append_space(&t->pvt_saves);
save->mailnum = t->save_count;
save->flags = pvt_flags;
}
static void
mailbox_save_context_reset(struct mail_save_context *ctx, bool success)
{
i_assert(!ctx->unfinished);
if (!ctx->copying_or_moving) {
/* we're finishing a save (not copy/move). Note that we could
have come here also from mailbox_save_cancel(), in which
case ctx->saving may be FALSE. */
i_assert(!ctx->copying_via_save);
i_assert(ctx->saving || !success);
ctx->saving = FALSE;
} else {
i_assert(ctx->copying_via_save || !success);
/* We came from mailbox_copy(). saving==TRUE is possible here
if we also came from mailbox_save_using_mail(). Don't set
saving=FALSE yet in that case, because copy() is still
running. */
}
}
int mailbox_save_finish(struct mail_save_context **_ctx)
{
struct mail_save_context *ctx = *_ctx;
struct mailbox_transaction_context *t = ctx->transaction;
struct mail_keywords *keywords = ctx->data.keywords;
enum mail_flags pvt_flags = ctx->data.pvt_flags;
bool copying_via_save = ctx->copying_via_save;
int ret;
/* Do one final continue. The caller may not have done it if the
input stream's offset already matched the number of bytes that
were wanted to be saved. But due to nested istreams some of the
underlying ones may not have seen the EOF yet, and haven't flushed
out the pending data. */
if (mailbox_save_continue(ctx) < 0) {
mailbox_save_cancel(_ctx);
return -1;
}
*_ctx = NULL;
ctx->finishing = TRUE;
T_BEGIN {
ret = t->box->v.save_finish(ctx);
} T_END;
ctx->finishing = FALSE;
if (ret == 0 && !copying_via_save) {
if (pvt_flags != 0)
mailbox_save_add_pvt_flags(t, pvt_flags);
t->save_count++;
}
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
mailbox_save_context_reset(ctx, TRUE);
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;
T_BEGIN {
ctx->transaction->box->v.save_cancel(ctx);
} T_END;
if (keywords != NULL && !ctx->finishing)
mailbox_keywords_unref(&keywords);
/* the dest_mail is no longer valid. if we're still saving
more mails, the mail sequence may get reused. make sure
the mail gets reset in between */
mailbox_save_dest_mail_close(ctx);
mailbox_save_context_reset(ctx, FALSE);
}
struct mailbox_transaction_context *
mailbox_save_get_transaction(struct mail_save_context *ctx)
{
return ctx->transaction;
}
static int mailbox_copy_int(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
struct mailbox_transaction_context *t = ctx->transaction;
struct mail_keywords *keywords = ctx->data.keywords;
enum mail_flags pvt_flags = ctx->data.pvt_flags;
struct mail *backend_mail;
int ret;
*_ctx = NULL;
if (mail_index_is_deleted(t->box->index)) {
mailbox_set_deleted(t->box);
mailbox_save_cancel(&ctx);
return -1;
}
/* bypass virtual storage, so hard linking can be used whenever
possible */
if (mail_get_backend_mail(mail, &backend_mail) < 0) {
mailbox_save_cancel(&ctx);
return -1;
}
i_assert(!ctx->copying_or_moving);
i_assert(ctx->copy_src_mail == NULL);
ctx->copying_or_moving = TRUE;
ctx->copy_src_mail = mail;
ctx->finishing = TRUE;
T_BEGIN {
ret = t->box->v.copy(ctx, backend_mail);
} T_END;
ctx->finishing = FALSE;
if (ret == 0) {
if (pvt_flags != 0)
mailbox_save_add_pvt_flags(t, pvt_flags);
t->save_count++;
}
if (keywords != NULL)
mailbox_keywords_unref(&keywords);
i_assert(!ctx->unfinished);
ctx->copy_src_mail = NULL;
ctx->copying_via_save = FALSE;
ctx->copying_or_moving = FALSE;
ctx->saving = FALSE; /* if we came from mailbox_save_using_mail() */
return ret;
}
int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
i_assert(!ctx->saving);
i_assert(!ctx->moving);
return mailbox_copy_int(_ctx, mail);
}
int mailbox_move(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
int ret;
i_assert(!ctx->saving);
i_assert(!ctx->moving);
ctx->moving = TRUE;
if ((ret = mailbox_copy_int(_ctx, mail)) == 0)
mail_expunge(mail);
ctx->moving = FALSE;
return ret;
}
int mailbox_save_using_mail(struct mail_save_context **_ctx, struct mail *mail)
{
struct mail_save_context *ctx = *_ctx;
i_assert(!ctx->saving);
i_assert(!ctx->moving);
ctx->saving = TRUE;
return mailbox_copy_int(_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;
}
if (type == MAILBOX_LIST_PATH_TYPE_INDEX && box->_index_path != NULL) {
if (box->_index_path[0] == '\0') {
*path_r = NULL;
return 0;
}
*path_r = box->_index_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);
if (type == MAILBOX_LIST_PATH_TYPE_INDEX && box->_index_path == NULL)
box->_index_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;
}
const char *mailbox_get_index_path(struct mailbox *box)
{
i_assert(box->_index_path != NULL);
i_assert(box->_index_path[0] != '\0');
return box->_index_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)
{
i_zero(&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;
}
int mail_parse_human_timestamp(const char *str, time_t *timestamp_r,
bool *utc_r)
{
struct tm tm;
unsigned int secs;
const char *error;
if (i_isdigit(str[0]) && i_isdigit(str[1]) &&
i_isdigit(str[2]) && i_isdigit(str[3]) && str[4] == '-' &&
i_isdigit(str[5]) && i_isdigit(str[6]) && str[7] == '-' &&
i_isdigit(str[8]) && i_isdigit(str[9]) && str[10] == '\0') {
/* yyyy-mm-dd */
i_zero(&tm);
tm.tm_year = (str[0]-'0') * 1000 + (str[1]-'0') * 100 +
(str[2]-'0') * 10 + (str[3]-'0') - 1900;
tm.tm_mon = (str[5]-'0') * 10 + (str[6]-'0') - 1;
tm.tm_mday = (str[8]-'0') * 10 + (str[9]-'0');
*timestamp_r = mktime(&tm);
*utc_r = FALSE;
return 0;
} else if (imap_parse_date(str, timestamp_r)) {
/* imap date */
*utc_r = FALSE;
return 0;
} else if (str_to_time(str, timestamp_r) == 0) {
/* unix timestamp */
*utc_r = TRUE;
return 0;
} else if (settings_get_time(str, &secs, &error) == 0) {
*timestamp_r = ioloop_time - secs;
*utc_r = TRUE;
return 0;
} else {
return -1;
}
}