shared-storage.c revision d927bc5618696157fc55eb1f11b5cab05400ed52
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
18398a5d21c88cbb34c601c6b6c1f9dea502e1caTimo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "var-expand.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "index-storage.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include "shared-storage.h"
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen
cc833a7a4e2258afdc834ace4bfe6579820a1df3Timo Sirainen#include <stdlib.h>
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen#include <ctype.h>
366eb7178f2c90d97134e0c2d1958f93fcdaba12Timo Sirainen
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen#define SHARED_LIST_CONTEXT(obj) \
7e235b3a5f622813121cd18f351e036650aaf8f8Timo Sirainen MODULE_CONTEXT(obj, shared_mailbox_list_module)
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainenextern struct mail_storage shared_storage;
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainenextern struct mailbox shared_mailbox;
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainen
471e447023ab73a73f0f78da2afc0c55905330ddTimo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(shared_mailbox_list_module,
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen &mailbox_list_module_register);
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenstatic struct mail_storage *shared_alloc(void)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen{
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct shared_storage *storage;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen pool_t pool;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen pool = pool_alloconly_create("shared storage", 1024);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen storage = p_new(pool, struct shared_storage, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen storage->storage = shared_storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen storage->storage.pool = pool;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen storage->storage.storage_class = &shared_storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen storage->base_dir = p_strdup(pool, getenv("BASE_DIR"));
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen if (storage->base_dir == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen storage->base_dir = PKG_RUNDIR;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return &storage->storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int shared_create(struct mail_storage *_storage, const char *data,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
55a210942dc7da58b2fd0b11bed8da6b030af5c1Timo Sirainen{
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen struct shared_storage *storage = (struct shared_storage *)_storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mailbox_list_settings list_set;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const char *driver, *p;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char *wildcardp;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool have_username;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* data must begin with the actual mailbox driver */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p = strchr(data, ':');
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (p == NULL) {
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen *error_r = "Shared mailbox location not prefixed with driver";
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen driver = t_strdup_until(data, p);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen storage->location = p_strdup(_storage->pool, data);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen storage->storage_class = mail_storage_find_class(driver);
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen if (storage->storage_class == NULL) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen *error_r = t_strconcat("Unknown shared storage driver: ",
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen driver, NULL);
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen _storage->mailbox_is_file = storage->storage_class->mailbox_is_file;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen wildcardp = strchr(_storage->ns->prefix, '%');
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen if (wildcardp == NULL) {
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen *error_r = "Shared namespace prefix doesn't contain %";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen storage->ns_prefix_pattern = p_strdup(_storage->pool, wildcardp);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen have_username = FALSE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (p = storage->ns_prefix_pattern; *p != '\0'; p++) {
45e62043058738e294f89504c319d852e25943ccTimo Sirainen if (*p != '%')
45e62043058738e294f89504c319d852e25943ccTimo Sirainen continue;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen if (*++p == '\0')
45e62043058738e294f89504c319d852e25943ccTimo Sirainen break;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (*p == 'u' || *p == 'n')
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen have_username = TRUE;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen else if (*p != '%' && *p != 'd')
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen break;
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen }
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if (*p != '\0') {
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen *error_r = "Shared namespace prefix contains unknown variables";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!have_username) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = "Shared namespace prefix doesn't contain %u or %n";
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen return -1;
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* truncate prefix after the above checks are done, so they can log
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen the full prefix in error conditions */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen *wildcardp = '\0';
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (mailbox_list_alloc("shared", &_storage->list, error_r) < 0)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return -1;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen MODULE_CONTEXT_SET_FULL(_storage->list, shared_mailbox_list_module,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen storage, &storage->list_module_ctx);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen memset(&list_set, 0, sizeof(list_set));
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen list_set.mail_storage_flags = &_storage->flags;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen list_set.lock_method = &_storage->lock_method;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return 0;
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainenget_nonexisting_user_location(struct shared_storage *storage,
2e99f3f3bb35715ce5e0a75a2f2a9bac3ab4224bTimo Sirainen const char *username, string_t *location)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* user wasn't found. we'll still need to create the storage
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen to avoid exposing which users exist and which don't. */
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen str_append(location, storage->storage_class->name);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append_c(location, ':');
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* use a reachable but non-existing path as the mail root directory */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen str_append(location, storage->base_dir);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen str_append(location, "/user-not-found/");
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen str_append(location, username);
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen}
b87a4156eca6dcf6b29c504eb0cb9be2fdb11b63Timo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenint shared_storage_get_namespace(struct mail_storage *_storage,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen const char **_name,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct mail_namespace **ns_r)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen{
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct shared_storage *storage = (struct shared_storage *)_storage;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct mail_user *user = _storage->ns->user;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen static struct var_expand_table static_tab[] = {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen { 'u', NULL, "user" },
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen { 'n', NULL, "username" },
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen { 'd', NULL, "domain" },
{ 'h', NULL, "home" },
{ '\0', NULL, NULL }
};
struct var_expand_table *tab;
struct mail_namespace *ns;
struct mail_namespace_settings *ns_set;
struct mail_user *owner;
const char *domain = NULL, *username = NULL, *userdomain = NULL;
const char *name, *p, *next, **dest, *error;
string_t *prefix, *location;
int ret;
*ns_r = NULL;
p = storage->ns_prefix_pattern;
for (name = *_name; *p != '\0';) {
if (*p != '%') {
if (*p != *name)
break;
p++; name++;
continue;
}
switch (*++p) {
case 'd':
dest = &domain;
break;
case 'n':
dest = &username;
break;
case 'u':
dest = &userdomain;
break;
default:
/* we checked this already above */
i_unreached();
}
p++;
next = strchr(name, *p != '\0' ? *p : _storage->ns->sep);
if (next == NULL) {
*dest = name;
name = "";
break;
}
*dest = t_strdup_until(name, next);
name = next;
}
if (*p != '\0') {
if (*name == '\0' ||
(name[1] == '\0' && *name == _storage->ns->sep)) {
/* trying to open <prefix>/<user> mailbox */
name = "INBOX";
} else {
mail_storage_set_critical(_storage,
"Invalid namespace prefix %s vs %s",
storage->ns_prefix_pattern, *_name);
return -1;
}
}
/* successfully matched the name. */
if (userdomain == NULL) {
i_assert(username != NULL);
userdomain = domain == NULL ? username :
t_strconcat(username, "@", domain, NULL);
} else {
domain = strchr(userdomain, '@');
if (domain == NULL)
username = userdomain;
else {
username = t_strdup_until(userdomain, domain);
domain++;
}
}
if (*userdomain == '\0') {
mail_storage_set_error(_storage, MAIL_ERROR_PARAMS,
"Empty username doesn't exist");
return -1;
}
/* expand the namespace prefix and see if it already exists.
this should normally happen only when the mailbox is being opened */
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
tab[0].value = userdomain;
tab[1].value = username;
tab[2].value = domain;
prefix = t_str_new(128);
str_append(prefix, _storage->ns->prefix);
var_expand(prefix, storage->ns_prefix_pattern, tab);
ns = mail_namespace_find_prefix(user->namespaces, str_c(prefix));
if (ns != NULL) {
*_name = mail_namespace_fix_sep(ns, name);
*ns_r = ns;
return 0;
}
owner = mail_user_alloc(userdomain, user->unexpanded_set);
if (!var_has_key(storage->location, 'h', "home"))
ret = 1;
else {
/* we'll need to look up the user's home directory */
if ((ret = mail_user_get_home(owner, &tab[3].value)) < 0) {
mail_storage_set_critical(_storage, "Namespace '%s': "
"Could not lookup home for user %s",
_storage->ns->prefix, userdomain);
mail_user_unref(&owner);
return -1;
}
}
if (mail_user_init(owner, &error) < 0) {
mail_storage_set_critical(_storage,
"Couldn't create namespace '%s' for user %s: %s",
_storage->ns->prefix, userdomain, error);
mail_user_unref(&owner);
return -1;
}
/* create the new namespace */
ns = i_new(struct mail_namespace, 1);
ns->type = NAMESPACE_SHARED;
ns->user = user;
ns->prefix = i_strdup(str_c(prefix));
ns->owner = owner;
ns->flags = NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN |
NAMESPACE_FLAG_AUTOCREATED | NAMESPACE_FLAG_INBOX;
ns->sep = _storage->ns->sep;
ns->mail_set = _storage->set;
location = t_str_new(256);
if (ret > 0)
var_expand(location, storage->location, tab);
else {
get_nonexisting_user_location(storage, userdomain, location);
ns->flags |= NAMESPACE_FLAG_UNUSABLE;
}
ns_set = p_new(user->pool, struct mail_namespace_settings, 1);
ns_set->type = "shared";
ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep);
ns_set->prefix = ns->prefix;
ns_set->location = p_strdup(user->pool, str_c(location));
ns_set->hidden = TRUE;
ns_set->list = "yes";
ns->set = ns_set;
if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) {
mail_storage_set_critical(_storage, "Namespace '%s': %s",
ns->prefix, error);
mail_namespace_destroy(ns);
return -1;
}
_storage->ns->flags |= NAMESPACE_FLAG_USABLE;
*_name = mail_namespace_fix_sep(ns, name);
*ns_r = ns;
mail_user_add_namespace(user, &ns);
return 0;
}
static void shared_mailbox_copy_error(struct mail_storage *shared_storage,
struct mail_namespace *backend_ns)
{
const char *str;
enum mail_error error;
str = mail_storage_get_last_error(backend_ns->storage, &error);
mail_storage_set_error(shared_storage, error, str);
}
static int shared_mailbox_create(struct mail_storage *storage,
const char *name, bool directory)
{
struct mail_namespace *ns;
int ret;
if (shared_storage_get_namespace(storage, &name, &ns) < 0)
return -1;
ret = mail_storage_mailbox_create(ns->storage, name, directory);
if (ret < 0)
shared_mailbox_copy_error(storage, ns);
return ret;
}
struct mail_storage shared_storage = {
MEMBER(name) SHARED_STORAGE_NAME,
MEMBER(mailbox_is_file) FALSE, /* unknown at this point */
{
NULL,
NULL,
NULL,
shared_alloc,
shared_create,
index_storage_destroy,
NULL,
NULL,
shared_mailbox_create,
NULL
}
};