changeset 8173:6dd0c6755afe HEAD

Added initial support for shared mailboxes. Listing isn't supported yet. Each user gets their own hidden namespace created automatically when they're accessed the first time.
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Sep 2008 22:34:11 +0300
parents c0a80d6b8ef6
children 4ff103f5b64c
files configure.in src/lib-storage/index/Makefile.am src/lib-storage/index/shared/Makefile.am src/lib-storage/index/shared/shared-list.c src/lib-storage/index/shared/shared-storage.c src/lib-storage/index/shared/shared-storage.h src/lib-storage/mail-namespace.c src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mail-user.c src/lib-storage/mail-user.h src/lib-storage/register/Makefile.am
diffstat 12 files changed, 619 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Sun Sep 07 20:48:43 2008 +0300
+++ b/configure.in	Sun Sep 07 22:34:11 2008 +0300
@@ -362,12 +362,12 @@
 
 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,
@@ -2238,6 +2238,7 @@
 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"
@@ -2404,6 +2405,7 @@
 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
--- a/src/lib-storage/index/Makefile.am	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/index/Makefile.am	Sun Sep 07 22:34:11 2008 +0300
@@ -1,4 +1,4 @@
-SUBDIRS = maildir mbox dbox cydir raw
+SUBDIRS = maildir mbox dbox cydir raw shared
 
 noinst_LIBRARIES = libstorage_index.a
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/shared/Makefile.am	Sun Sep 07 22:34:11 2008 +0300
@@ -0,0 +1,23 @@
+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	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/shared/shared-list.c	Sun Sep 07 22:34:11 2008 +0300
@@ -0,0 +1,269 @@
+/* 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	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/shared/shared-storage.c	Sun Sep 07 22:34:11 2008 +0300
@@ -0,0 +1,262 @@
+/* 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	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/shared/shared-storage.h	Sun Sep 07 22:34:11 2008 +0300
@@ -0,0 +1,25 @@
+#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
--- a/src/lib-storage/mail-namespace.c	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/mail-namespace.c	Sun Sep 07 22:34:11 2008 +0300
@@ -33,7 +33,7 @@
 		  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);
 
@@ -79,7 +79,14 @@
 	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;
@@ -160,24 +167,6 @@
 	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;
@@ -212,8 +201,7 @@
 	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 {
--- a/src/lib-storage/mail-storage.c	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/mail-storage.c	Sun Sep 07 22:34:11 2008 +0300
@@ -119,7 +119,7 @@
 		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;
@@ -199,7 +199,7 @@
 		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);
--- a/src/lib-storage/mail-storage.h	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/mail-storage.h	Sun Sep 07 22:34:11 2008 +0300
@@ -238,6 +238,8 @@
    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,
--- a/src/lib-storage/mail-user.c	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/mail-user.c	Sun Sep 07 22:34:11 2008 +0300
@@ -42,6 +42,23 @@
 	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);
--- a/src/lib-storage/mail-user.h	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/mail-user.h	Sun Sep 07 22:34:11 2008 +0300
@@ -36,6 +36,9 @@
 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. */
--- a/src/lib-storage/register/Makefile.am	Sun Sep 07 20:48:43 2008 +0300
+++ b/src/lib-storage/register/Makefile.am	Sun Sep 07 22:34:11 2008 +0300
@@ -3,7 +3,7 @@
 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 $@