AC_ARG_WITH(storages,
[ --with-storages Build with specified mail storage formats
- (maildir mbox dbox cydir raw)], [
+ (maildir mbox dbox cydir shared raw)], [
if test "$withval" = "yes" -o "$withval" = "no"; then
AC_MSG_ERROR([--with-storages needs storage list as parameter])
fi
mail_storages=`echo "$withval"|sed 's/,/ /g'` ],
- mail_storages="maildir mbox dbox cydir raw")
+ mail_storages="maildir mbox dbox cydir shared raw")
AC_SUBST(mail_storages)
AC_ARG_WITH(sql-drivers,
dbox_libs='$(top_builddir)/src/lib-storage/index/dbox/libstorage_dbox.a'
cydir_libs='$(top_builddir)/src/lib-storage/index/cydir/libstorage_cydir.a'
raw_libs='$(top_builddir)/src/lib-storage/index/raw/libstorage_raw.a'
+shared_libs='$(top_builddir)/src/lib-storage/index/shared/libstorage_shared.a'
CORE_LIBS='$(top_builddir)/src/lib-storage/index/libstorage_index.a $(top_builddir)/src/lib-storage/libstorage.a $(top_builddir)/src/lib-index/libindex.a'
deliver_storage="raw"
src/lib-storage/index/dbox/Makefile
src/lib-storage/index/cydir/Makefile
src/lib-storage/index/raw/Makefile
+src/lib-storage/index/shared/Makefile
src/lib-storage/register/Makefile
src/auth/Makefile
src/deliver/Makefile
-SUBDIRS = maildir mbox dbox cydir raw
+SUBDIRS = maildir mbox dbox cydir raw shared
noinst_LIBRARIES = libstorage_index.a
--- /dev/null
+noinst_LIBRARIES = libstorage_shared.a
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/lib \
+ -I$(top_srcdir)/src/lib-mail \
+ -I$(top_srcdir)/src/lib-imap \
+ -I$(top_srcdir)/src/lib-index \
+ -I$(top_srcdir)/src/lib-storage \
+ -I$(top_srcdir)/src/lib-storage/index
+
+libstorage_shared_a_SOURCES = \
+ shared-list.c \
+ shared-storage.c
+
+headers = \
+ shared-storage.h
+
+if INSTALL_HEADERS
+ pkginc_libdir=$(pkgincludedir)/src/lib-storage/index/shared
+ pkginc_lib_HEADERS = $(headers)
+else
+ noinst_HEADERS = $(headers)
+endif
--- /dev/null
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mailbox-list-private.h"
+#include "shared-storage.h"
+
+struct shared_mailbox_list_iterate_context {
+ struct mailbox_list_iterate_context ctx;
+};
+
+extern struct mailbox_list shared_mailbox_list;
+
+static struct mailbox_list *shared_list_alloc(void)
+{
+ struct mailbox_list *list;
+ pool_t pool;
+
+ pool = pool_alloconly_create("shared list", 256);
+ list = p_new(pool, struct mailbox_list, 1);
+ *list = shared_mailbox_list;
+ list->pool = pool;
+ return list;
+}
+
+static void shared_list_deinit(struct mailbox_list *list)
+{
+ pool_unref(&list->pool);
+}
+
+static void shared_list_copy_error(struct mailbox_list *shared_list,
+ struct mail_namespace *backend_ns)
+{
+ const char *str;
+ enum mail_error error;
+
+ str = mailbox_list_get_last_error(backend_ns->list, &error);
+ mailbox_list_set_error(shared_list, error, str);
+}
+
+static bool
+shared_is_valid_pattern(struct mailbox_list *list, const char *pattern)
+{
+ struct mail_namespace *ns;
+
+ if (shared_storage_get_namespace(list->ns->storage, &pattern, &ns) < 0)
+ return FALSE;
+ return mailbox_list_is_valid_pattern(ns->list, pattern);
+}
+
+static bool
+shared_is_valid_existing_name(struct mailbox_list *list, const char *name)
+{
+ struct mail_namespace *ns;
+
+ if (shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0)
+ return FALSE;
+ return mailbox_list_is_valid_existing_name(ns->list, name);
+}
+
+static bool
+shared_is_valid_create_name(struct mailbox_list *list, const char *name)
+{
+ struct mail_namespace *ns;
+
+ if (shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0)
+ return FALSE;
+ return mailbox_list_is_valid_create_name(ns->list, name);
+}
+
+static const char *
+shared_list_get_path(struct mailbox_list *list, const char *name,
+ enum mailbox_list_path_type type)
+{
+ struct mail_namespace *ns;
+
+ if (list->ns->storage == NULL ||
+ shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0) {
+ switch (type) {
+ case MAILBOX_LIST_PATH_TYPE_DIR:
+ case MAILBOX_LIST_PATH_TYPE_MAILBOX:
+ case MAILBOX_LIST_PATH_TYPE_CONTROL:
+ break;
+ case MAILBOX_LIST_PATH_TYPE_INDEX:
+ /* we can safely say we don't use indexes */
+ return "";
+ }
+ i_panic("shared mailbox list: Can't return path for '%s'",
+ list->ns->prefix);
+ }
+ return mailbox_list_get_path(ns->list, name, type);
+}
+
+static int
+shared_list_get_mailbox_name_status(struct mailbox_list *list, const char *name,
+ enum mailbox_name_status *status_r)
+{
+ struct mail_namespace *ns;
+ int ret;
+
+ if (shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0)
+ return -1;
+ ret = mailbox_list_get_mailbox_name_status(ns->list, name, status_r);
+ if (ret < 0)
+ shared_list_copy_error(list, ns);
+ return ret;
+}
+
+static const char *
+shared_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED)
+{
+ i_panic("shared mailbox list: Can't return a temp prefix for '%s'",
+ list->ns->prefix);
+ return NULL;
+}
+
+static const char *
+shared_list_join_refpattern(struct mailbox_list *list,
+ const char *ref, const char *pattern)
+{
+ struct mail_namespace *ns;
+
+ if (*ref != '\0' &&
+ shared_storage_get_namespace(list->ns->storage, &ref, &ns) == 0)
+ return mailbox_list_join_refpattern(ns->list, ref, pattern);
+
+ if (*ref == '\0' &&
+ shared_storage_get_namespace(list->ns->storage, &pattern, &ns) == 0)
+ return mailbox_list_join_refpattern(ns->list, "", pattern);
+
+ /* fallback to default behavior */
+ if (*ref != '\0')
+ pattern = t_strconcat(ref, pattern, NULL);
+ return pattern;
+}
+
+static struct mailbox_list_iterate_context *
+shared_list_iter_init(struct mailbox_list *list, const char *const *patterns,
+ enum mailbox_list_iter_flags flags)
+{
+ struct shared_mailbox_list_iterate_context *ctx;
+
+ ctx = i_new(struct shared_mailbox_list_iterate_context, 1);
+ ctx->ctx.list = list;
+ ctx->ctx.flags = flags;
+
+ /* FIXME */
+ return &ctx->ctx;
+}
+
+static const struct mailbox_info *
+shared_list_iter_next(struct mailbox_list_iterate_context *_ctx)
+{
+ struct shared_mailbox_list_iterate_context *ctx =
+ (struct shared_mailbox_list_iterate_context *)_ctx;
+
+ return NULL;
+}
+
+static int shared_list_iter_deinit(struct mailbox_list_iterate_context *_ctx)
+{
+ struct shared_mailbox_list_iterate_context *ctx =
+ (struct shared_mailbox_list_iterate_context *)_ctx;
+
+ i_free(ctx);
+ return -1;
+}
+
+static int shared_list_set_subscribed(struct mailbox_list *list,
+ const char *name, bool set)
+{
+ struct mail_namespace *ns;
+ int ret;
+
+ if (shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0)
+ return -1;
+ ret = mailbox_list_set_subscribed(ns->list, name, set);
+ if (ret < 0)
+ shared_list_copy_error(list, ns);
+ return ret;
+}
+
+static int
+shared_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+ struct mail_namespace *ns;
+ int ret;
+
+ if (shared_storage_get_namespace(list->ns->storage, &name, &ns) < 0)
+ return -1;
+ ret = mailbox_list_delete_mailbox(ns->list, name);
+ if (ret < 0)
+ shared_list_copy_error(list, ns);
+ return ret;
+}
+
+static int shared_list_rename_get_ns(struct mailbox_list *list,
+ const char **oldname, const char **newname,
+ struct mail_namespace **ns_r)
+{
+ struct mail_namespace *old_ns, *new_ns;
+
+ if (shared_storage_get_namespace(list->ns->storage,
+ oldname, &old_ns) < 0 ||
+ shared_storage_get_namespace(list->ns->storage,
+ newname, &new_ns) < 0)
+ return -1;
+ if (old_ns != new_ns) {
+ mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+ "Can't rename mailboxes across storages");
+ return -1;
+ }
+ *ns_r = old_ns;
+ return 0;
+}
+
+static int shared_list_rename_mailbox(struct mailbox_list *list,
+ const char *oldname, const char *newname)
+{
+ struct mail_namespace *ns;
+ int ret;
+
+ if (shared_list_rename_get_ns(list, &oldname, &newname, &ns) < 0)
+ return -1;
+ ret = mailbox_list_rename_mailbox(ns->list, oldname, newname);
+ if (ret < 0)
+ shared_list_copy_error(list, ns);
+ return ret;
+}
+
+static int
+shared_list_rename_mailbox_pre(struct mailbox_list *list,
+ const char *oldname, const char *newname)
+{
+ struct mail_namespace *ns;
+ int ret;
+
+ if (shared_list_rename_get_ns(list, &oldname, &newname, &ns) < 0)
+ return -1;
+ ret = ns->list->v.rename_mailbox_pre(ns->list, oldname, newname);
+ if (ret < 0)
+ shared_list_copy_error(list, ns);
+ return ret;
+}
+
+struct mailbox_list shared_mailbox_list = {
+ MEMBER(name) "shared",
+ MEMBER(hierarchy_sep) '/',
+ MEMBER(mailbox_name_max_length) PATH_MAX,
+
+ {
+ shared_list_alloc,
+ shared_list_deinit,
+ shared_is_valid_pattern,
+ shared_is_valid_existing_name,
+ shared_is_valid_create_name,
+ shared_list_get_path,
+ shared_list_get_mailbox_name_status,
+ shared_list_get_temp_prefix,
+ shared_list_join_refpattern,
+ shared_list_iter_init,
+ shared_list_iter_next,
+ shared_list_iter_deinit,
+ NULL,
+ shared_list_set_subscribed,
+ shared_list_delete_mailbox,
+ shared_list_rename_mailbox,
+ shared_list_rename_mailbox_pre
+ }
+};
--- /dev/null
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "var-expand.h"
+#include "shared-storage.h"
+
+#define SHARED_LIST_CONTEXT(obj) \
+ MODULE_CONTEXT(obj, shared_mailbox_list_module)
+
+extern struct mail_storage shared_storage;
+extern struct mailbox shared_mailbox;
+
+static MODULE_CONTEXT_DEFINE_INIT(shared_mailbox_list_module,
+ &mailbox_list_module_register);
+
+static struct mail_storage *shared_alloc(void)
+{
+ struct shared_storage *storage;
+ pool_t pool;
+
+ pool = pool_alloconly_create("shared storage", 256);
+ storage = p_new(pool, struct shared_storage, 1);
+ storage->storage = shared_storage;
+ storage->storage.pool = pool;
+
+ return &storage->storage;
+}
+
+static int shared_create(struct mail_storage *_storage, const char *data,
+ const char **error_r)
+{
+ struct shared_storage *storage = (struct shared_storage *)_storage;
+ struct mailbox_list_settings list_set;
+ const char *driver, *p;
+ bool have_username;
+
+ /* data must begin with the actual mailbox driver */
+ p = strchr(data, ':');
+ if (p == NULL) {
+ *error_r = "Shared mailbox location not prefixed with driver";
+ return -1;
+ }
+ driver = t_strdup_until(data, p);
+ storage->location = p_strdup(_storage->pool, data);
+ storage->storage_class = mail_storage_find_class(driver);
+ if (storage->storage_class == NULL) {
+ *error_r = t_strconcat("Unknown shared storage driver: ",
+ driver, NULL);
+ return -1;
+ }
+ _storage->mailbox_is_file = storage->storage_class->mailbox_is_file;
+
+ p = strchr(_storage->ns->prefix, '%');
+ if (p == NULL) {
+ *error_r = "Shared namespace prefix doesn't contain %";
+ return -1;
+ }
+ storage->ns_prefix_pattern = p_strdup(_storage->pool, p);
+ _storage->ns->prefix = p_strdup_until(_storage->ns->user->pool,
+ _storage->ns->prefix, p);
+
+ have_username = FALSE;
+ for (p = storage->ns_prefix_pattern; *p != '\0'; p++) {
+ if (*p != '%')
+ continue;
+ if (*++p == '\0')
+ break;
+ if (*p == 'u' || *p == 'n')
+ have_username = TRUE;
+ else if (*p != 'd')
+ break;
+ }
+ if (*p != '\0') {
+ *error_r = "Shared namespace prefix contains unknown variables";
+ return -1;
+ }
+ if (!have_username) {
+ *error_r = "Shared namespace prefix doesn't contain %u or %n";
+ return -1;
+ }
+
+ if (mailbox_list_alloc("shared", &_storage->list, error_r) < 0)
+ return -1;
+ MODULE_CONTEXT_SET_FULL(_storage->list, shared_mailbox_list_module,
+ storage, &storage->list_module_ctx);
+
+ memset(&list_set, 0, sizeof(list_set));
+ list_set.mail_storage_flags = &_storage->flags;
+ list_set.lock_method = &_storage->lock_method;
+ mailbox_list_init(_storage->list, _storage->ns, &list_set,
+ mail_storage_get_list_flags(_storage->flags));
+ return 0;
+}
+
+int shared_storage_get_namespace(struct mail_storage *_storage,
+ const char **_name,
+ struct mail_namespace **ns_r)
+{
+ struct shared_storage *storage = (struct shared_storage *)_storage;
+ struct mail_user *user = _storage->ns->user;
+ static struct var_expand_table static_tab[] = {
+ { 'u', NULL },
+ { 'n', NULL },
+ { 'd', NULL },
+ { '\0', NULL }
+ };
+ struct var_expand_table *tab;
+ struct mail_namespace *ns;
+ const char *domain = NULL, *username = NULL, *userdomain = NULL;
+ const char *name, *p, *next, **dest, *error;
+ string_t *prefix, *location;
+
+ p = storage->ns_prefix_pattern;
+ for (name = *_name; *p != '\0';) {
+ if (*p != '%') {
+ if (*p != *name)
+ return -1;
+ 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)
+ return -1;
+
+ *dest = t_strdup_until(name, next);
+ name = next;
+ }
+ /* 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++;
+ }
+ }
+
+ /* 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 = name;
+ *ns_r = ns;
+ return 0;
+ }
+
+ /* create the new namespace */
+ ns = p_new(user->pool, struct mail_namespace, 1);
+ ns->type = NAMESPACE_SHARED;
+ ns->user = user;
+ ns->prefix = p_strdup(user->pool, str_c(prefix));
+ ns->flags = NAMESPACE_FLAG_LIST | NAMESPACE_FLAG_HIDDEN;
+
+ location = t_str_new(256);
+ var_expand(location, storage->location, tab);
+ if (mail_storage_create(ns, NULL, str_c(location), _storage->flags,
+ _storage->lock_method, &error) < 0) {
+ i_error("Namespace '%s': %s", ns->prefix, error);
+ return -1;
+ }
+ /* FIXME: we could remove namespaces here that don't have usable
+ mailboxes. otherwise the memory usage could just keep growing. */
+ mail_user_add_namespace(user, ns);
+
+ *_name = name;
+ *ns_r = 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 struct mailbox *
+shared_mailbox_open(struct mail_storage *storage, const char *name,
+ struct istream *input, enum mailbox_open_flags flags)
+{
+ struct mail_namespace *ns;
+ struct mailbox *box;
+
+ if (input != NULL) {
+ mail_storage_set_critical(storage,
+ "Shared storage doesn't support streamed mailboxes");
+ return NULL;
+ }
+
+ if (shared_storage_get_namespace(storage, &name, &ns) < 0)
+ return NULL;
+
+ box = mailbox_open(ns->storage, name, NULL, flags);
+ if (box == NULL)
+ shared_mailbox_copy_error(storage, ns);
+ return box;
+}
+
+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,
+ shared_alloc,
+ shared_create,
+ index_storage_destroy,
+ NULL,
+ shared_mailbox_open,
+ shared_mailbox_create
+ }
+};
--- /dev/null
+#ifndef SHARED_STORAGE_H
+#define SHARED_STORAGE_H
+
+#include "index-storage.h"
+#include "mailbox-list-private.h"
+
+#define SHARED_STORAGE_NAME "shared"
+
+struct shared_storage {
+ struct mail_storage storage;
+ union mailbox_list_module_context list_module_ctx;
+
+ const char *ns_prefix_pattern;
+ const char *location;
+
+ struct mail_storage *storage_class;
+};
+
+struct mailbox_list *shared_mailbox_list_alloc(void);
+
+int shared_storage_get_namespace(struct mail_storage *storage,
+ const char **name,
+ struct mail_namespace **ns_r);
+
+#endif
enum file_lock_method lock_method)
{
struct mail_namespace *ns;
- const char *sep, *type, *prefix, *error;
+ const char *sep, *type, *prefix, *driver, *error;
ns = p_new(user->pool, struct mail_namespace, 1);
ns->prefix = p_strdup(user->pool, prefix);
ns->user = user;
- if (mail_storage_create(ns, NULL, data, flags, lock_method,
+ if (ns->type == NAMESPACE_SHARED && strchr(ns->prefix, '%') != NULL) {
+ /* dynamic shared namespace */
+ driver = "shared";
+ } else {
+ driver = NULL;
+ }
+
+ if (mail_storage_create(ns, driver, data, flags, lock_method,
&error) < 0) {
i_error("Namespace '%s': %s", ns->prefix, error);
return NULL;
return TRUE;
}
-static struct mail_namespace *
-namespaces_sort(struct mail_namespace *src)
-{
- struct mail_namespace **tmp, *next, *dest = NULL;
-
- for (; src != NULL; src = next) {
- next = src->next;
-
- for (tmp = &dest; *tmp != NULL; tmp = &(*tmp)->next) {
- if (strlen(src->prefix) < strlen((*tmp)->prefix))
- break;
- }
- src->next = *tmp;
- *tmp = src;
- }
- return dest;
-}
-
int mail_namespaces_init(struct mail_user *user)
{
struct mail_namespace *namespaces, *ns, **ns_p;
if (namespaces != NULL) {
if (!namespaces_check(namespaces))
return -1;
- namespaces = namespaces_sort(namespaces);
- user->namespaces = namespaces;
+ mail_user_add_namespace(user, namespaces);
if (hook_mail_namespaces_created != NULL) {
T_BEGIN {
i_fatal("Unknown lock_method: %s", str);
}
-static struct mail_storage *mail_storage_find(const char *name)
+struct mail_storage *mail_storage_find_class(const char *name)
{
struct mail_storage *const *classes;
unsigned int i, count;
classes = &storage_class;
count = 1;
} else {
- storage_class = mail_storage_find(driver);
+ storage_class = mail_storage_find_class(driver);
if (storage_class == NULL) {
*error_r = t_strdup_printf(
"Unknown mail storage driver %s", driver);
are set to default methods */
void mail_storage_class_register(struct mail_storage *storage_class);
void mail_storage_class_unregister(struct mail_storage *storage_class);
+/* Find mail storage class by name */
+struct mail_storage *mail_storage_find_class(const char *name);
/* Returns flags and lock_method based on environment settings. */
void mail_storage_parse_env(enum mail_storage_flags *flags_r,
user->v.deinit(user);
}
+void mail_user_add_namespace(struct mail_user *user, struct mail_namespace *ns)
+{
+ struct mail_namespace **tmp, *next;
+
+ for (; ns != NULL; ns = next) {
+ next = ns->next;
+
+ tmp = &user->namespaces;
+ for (; *tmp != NULL; tmp = &(*tmp)->next) {
+ if (strlen(ns->prefix) < strlen((*tmp)->prefix))
+ break;
+ }
+ ns->next = *tmp;
+ *tmp = ns;
+ }
+}
+
const char *mail_user_home_expand(struct mail_user *user, const char *path)
{
(void)mail_user_try_home_expand(user, &path);
struct mail_user *mail_user_init(const char *username, const char *home);
void mail_user_deinit(struct mail_user **user);
+/* Add a new namespace to user's namespaces. */
+void mail_user_add_namespace(struct mail_user *user, struct mail_namespace *ns);
+
/* Replace ~/ at the beginning of the path with the user's home directory. */
const char *mail_user_home_expand(struct mail_user *user, const char *path);
/* Returns 0 if ok, -1 if home directory isn't set. */
BUILT_SOURCES = mail-storage-register.c
mail_storages = @mail_storages@
-mailbox_list_drivers = maildir imapdir fs
+mailbox_list_drivers = maildir imapdir fs shared
mail-storage-register.c: Makefile
rm -f $@