dovecot-2.2: Added mailbox-alias plugin.

dovecot at dovecot.org dovecot at dovecot.org
Wed Sep 26 18:01:29 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/f5bb9f6b304d
changeset: 15137:f5bb9f6b304d
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Sep 18 18:44:46 2012 +0300
description:
Added mailbox-alias plugin.
Aliases can be created like:

plugin {
  mailbox_alias_old = Sent
  mailbox_alias_new = Sent Messages
  mailbox_alias_old2 = Sent
  mailbox_alias_new2 = Sent Items
}

When creating an alias, the original mailbox is also created. The alias
itself is a symlink to the original. Deleting an alias deletes the symlink.
The original mailbox can't be deleted or renamed while it has aliases.
Aliases cannot be renamed. Aliases are skipped when recalculating quota.

If a mailbox with the alias's name was already created before the aliasing
was enabled, it's not treated as alias until it's first deleted.

diffstat:

 configure.in                                     |    1 +
 src/plugins/Makefile.am                          |    1 +
 src/plugins/mailbox-alias/Makefile.am            |   18 +
 src/plugins/mailbox-alias/mailbox-alias-plugin.c |  335 +++++++++++++++++++++++
 src/plugins/mailbox-alias/mailbox-alias-plugin.h |    7 +
 5 files changed, 362 insertions(+), 0 deletions(-)

diffs (truncated from 394 to 300 lines):

diff -r 45773a09dcf2 -r f5bb9f6b304d configure.in
--- a/configure.in	Tue Sep 18 18:41:01 2012 +0300
+++ b/configure.in	Tue Sep 18 18:44:46 2012 +0300
@@ -2806,6 +2806,7 @@
 src/plugins/lazy-expunge/Makefile
 src/plugins/listescape/Makefile
 src/plugins/mail-log/Makefile
+src/plugins/mailbox-alias/Makefile
 src/plugins/notify/Makefile
 src/plugins/pop3-migration/Makefile
 src/plugins/quota/Makefile
diff -r 45773a09dcf2 -r f5bb9f6b304d src/plugins/Makefile.am
--- a/src/plugins/Makefile.am	Tue Sep 18 18:41:01 2012 +0300
+++ b/src/plugins/Makefile.am	Tue Sep 18 18:44:46 2012 +0300
@@ -21,6 +21,7 @@
 	listescape \
 	notify \
 	mail-log \
+	mailbox-alias \
 	quota \
 	imap-quota \
 	pop3-migration \
diff -r 45773a09dcf2 -r f5bb9f6b304d src/plugins/mailbox-alias/Makefile.am
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/mailbox-alias/Makefile.am	Tue Sep 18 18:44:46 2012 +0300
@@ -0,0 +1,18 @@
+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
+
+NOPLUGIN_LDFLAGS =
+lib20_mailbox_alias_plugin_la_LDFLAGS = -module -avoid-version
+
+module_LTLIBRARIES = \
+	lib20_mailbox_alias_plugin.la
+
+lib20_mailbox_alias_plugin_la_SOURCES = \
+	mailbox-alias-plugin.c
+
+noinst_HEADERS = \
+	mailbox-alias-plugin.h
diff -r 45773a09dcf2 -r f5bb9f6b304d src/plugins/mailbox-alias/mailbox-alias-plugin.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/mailbox-alias/mailbox-alias-plugin.c	Tue Sep 18 18:44:46 2012 +0300
@@ -0,0 +1,335 @@
+/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "mail-storage-hooks.h"
+#include "mail-storage-private.h"
+#include "mailbox-list-private.h"
+#include "mailbox-alias-plugin.h"
+
+#define MAILBOX_ALIAS_USER_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, mailbox_alias_user_module)
+#define MAILBOX_ALIAS_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, mailbox_alias_storage_module)
+#define MAILBOX_ALIAS_LIST_CONTEXT(obj) \
+	MODULE_CONTEXT(obj, mailbox_alias_mailbox_list_module)
+
+struct mailbox_alias {
+	const char *old_vname, *new_vname;
+};
+
+struct mailbox_alias_user {
+	union mail_user_module_context module_ctx;
+
+	ARRAY_DEFINE(aliases, struct mailbox_alias);
+};
+
+struct mailbox_alias_mailbox_list {
+	union mailbox_list_module_context module_ctx;
+};
+
+struct mailbox_alias_mailbox {
+	union mailbox_module_context module_ctx;
+};
+
+enum mailbox_symlink_existence {
+	MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT,
+	MAILBOX_SYMLINK_EXISTENCE_SYMLINK,
+	MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK
+};
+
+static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_user_module,
+				  &mail_user_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_storage_module,
+				  &mail_storage_module_register);
+static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_mailbox_list_module,
+				  &mailbox_list_module_register);
+
+const char *mailbox_alias_plugin_version = DOVECOT_VERSION;
+
+static const char *
+mailbox_alias_find_new(struct mail_user *user, const char *new_vname)
+{
+	struct mailbox_alias_user *auser = MAILBOX_ALIAS_USER_CONTEXT(user);
+	const struct mailbox_alias *alias;
+
+	array_foreach(&auser->aliases, alias) {
+		if (strcmp(alias->new_vname, new_vname) == 0)
+			return alias->old_vname;
+	}
+	return NULL;
+}
+
+static int mailbox_symlink_exists(struct mailbox_list *list, const char *vname,
+				  enum mailbox_symlink_existence *existence_r)
+{
+	struct mailbox_alias_mailbox_list *alist =
+		MAILBOX_ALIAS_LIST_CONTEXT(list);
+	struct stat st;
+	const char *symlink_name, *symlink_path;
+
+	symlink_name = alist->module_ctx.super.get_storage_name(list, vname);
+	symlink_path = mailbox_list_get_path(list, symlink_name,
+					     MAILBOX_LIST_PATH_TYPE_DIR);
+	if (lstat(symlink_path, &st) < 0) {
+		if (errno == ENOENT) {
+			*existence_r = MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT;
+			return 0;
+		}
+		mailbox_list_set_critical(list,
+					  "lstat(%s) failed: %m", symlink_path);
+		return -1;
+	}
+	if (S_ISLNK(st.st_mode))
+		*existence_r = MAILBOX_SYMLINK_EXISTENCE_SYMLINK;
+	else
+		*existence_r = MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK;
+	return 0;
+}
+
+static int mailbox_is_alias_symlink(struct mailbox *box)
+{
+	enum mailbox_symlink_existence existence;
+
+	if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL)
+		return 0;
+	if (mailbox_symlink_exists(box->list, box->vname, &existence) < 0) {
+		mail_storage_copy_list_error(box->storage, box->list);
+		return -1;
+	}
+	return existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK ? 1 : 0;
+}
+
+static int
+mailbox_has_aliases(struct mailbox_list *list, const char *old_vname)
+{
+	struct mailbox_alias_user *auser =
+		MAILBOX_ALIAS_USER_CONTEXT(list->ns->user);
+	const struct mailbox_alias *alias;
+	enum mailbox_symlink_existence existence;
+	int ret = 0;
+
+	array_foreach(&auser->aliases, alias) {
+		if (strcmp(alias->old_vname, old_vname) == 0) {
+			if (mailbox_symlink_exists(list, alias->new_vname,
+						   &existence) < 0)
+				ret = -1;
+			if (existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK)
+				return 1;
+		}
+	}
+	return ret;
+}
+
+static int
+mailbox_alias_create_symlink(struct mailbox *box,
+			     const char *old_name, const char *new_name)
+{
+	const char *old_path, *new_path, *fname;
+
+	old_path = mailbox_list_get_path(box->list, old_name,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	new_path = mailbox_list_get_path(box->list, new_name,
+					 MAILBOX_LIST_PATH_TYPE_DIR);
+	fname = strrchr(old_path, '/');
+	i_assert(fname != NULL);
+	fname++;
+	i_assert(strncmp(new_path, old_path, fname-old_path) == 0);
+
+	if (symlink(fname, new_path) < 0) {
+		if (errno == EEXIST) {
+			mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+					       "Mailbox already exists");
+			return -1;
+		}
+		mail_storage_set_critical(box->storage,
+			"symlink(%s, %s) failed: %m", fname, new_path);
+		return -1;
+	}
+	return 0;
+}
+
+static const char *
+mailbox_alias_get_storage_name(struct mailbox_list *list, const char *vname)
+{
+	struct mailbox_alias_mailbox_list *alist =
+		MAILBOX_ALIAS_LIST_CONTEXT(list);
+	const char *old_vname;
+	enum mailbox_symlink_existence existence;
+
+	/* access the old mailbox so that e.g. full text search won't
+	   index the mailbox twice. this also means that deletion must be
+	   careful to delete the symlink, box->name. */
+	old_vname = mailbox_alias_find_new(list->ns->user, vname);
+	if (old_vname != NULL &&
+	    mailbox_symlink_exists(list, vname, &existence) == 0 &&
+	    existence != MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK)
+		vname = old_vname;
+
+	return alist->module_ctx.super.get_storage_name(list, vname);
+}
+
+static int
+mailbox_alias_create(struct mailbox *box, const struct mailbox_update *update,
+		     bool directory)
+{
+	struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box);
+	struct mailbox_alias_mailbox_list *alist =
+		MAILBOX_ALIAS_LIST_CONTEXT(box->list);
+	const char *symlink_name;
+	int ret;
+
+	ret = abox->module_ctx.super.create(box, update, directory);
+	if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL)
+		return ret;
+	if (ret < 0 && mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS)
+		return ret;
+
+	/* all the code so far has actually only created the original
+	   mailbox. now we'll create the symlink if it's missing. */
+	symlink_name = alist->module_ctx.super.
+		get_storage_name(box->list, box->vname);
+	return mailbox_alias_create_symlink(box, box->name, symlink_name);
+}
+
+static int mailbox_alias_delete(struct mailbox *box)
+{
+	struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box);
+	struct mailbox_alias_mailbox_list *alist =
+		MAILBOX_ALIAS_LIST_CONTEXT(box->list);
+	const char *symlink_name;
+	int ret;
+
+	ret = mailbox_has_aliases(box->list, box->vname);
+	if (ret < 0)
+		return -1;
+	if (ret > 0) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+			"Can't delete mailbox while it has aliases");
+		return -1;
+	}
+
+	if (mailbox_is_alias_symlink(box)) {
+		/* we're deleting an alias mailbox. we'll need to handle this
+		   explicitly since box->name points to the original mailbox */
+		symlink_name = alist->module_ctx.super.
+			get_storage_name(box->list, box->vname);
+		if (mailbox_list_delete_symlink(box->list, symlink_name) < 0) {
+			mail_storage_copy_list_error(box->storage, box->list);
+			return -1;
+		}
+		return 0;
+	}
+
+	return abox->module_ctx.super.delete(box);
+}
+
+static int mailbox_alias_rename(struct mailbox *src, struct mailbox *dest,
+				bool rename_children)
+{
+	struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(src);
+	int ret;
+
+	if (mailbox_is_alias_symlink(src)) {
+		mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
+				       "Can't rename alias mailboxes");
+		return -1;
+	}
+	if (mailbox_is_alias_symlink(dest)) {
+		mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
+				       "Can't rename to mailbox alias");
+		return -1;
+	}
+	ret = mailbox_has_aliases(src->list, src->vname);
+	if (ret < 0)
+		return -1;
+	if (ret > 0) {
+		mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
+			"Can't rename mailbox while it has aliases");
+		return -1;
+	}
+
+	return abox->module_ctx.super.rename(src, dest, rename_children);


More information about the dovecot-cvs mailing list