]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added mailbox-alias plugin.
authorTimo Sirainen <tss@iki.fi>
Tue, 18 Sep 2012 15:44:46 +0000 (18:44 +0300)
committerTimo Sirainen <tss@iki.fi>
Tue, 18 Sep 2012 15:44:46 +0000 (18:44 +0300)
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.

configure.in
src/plugins/Makefile.am
src/plugins/mailbox-alias/Makefile.am [new file with mode: 0644]
src/plugins/mailbox-alias/mailbox-alias-plugin.c [new file with mode: 0644]
src/plugins/mailbox-alias/mailbox-alias-plugin.h [new file with mode: 0644]

index 735260b0f4e55454c33800516a5e0cd3f399e0d7..c28f829adac60f7278eb4961eba3a99b1d84bdd7 100644 (file)
@@ -2806,6 +2806,7 @@ src/plugins/fts-squat/Makefile
 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
index 09a9d4c7448f174ba184e4050084f20f8150db5c..3d3e7a78775f0e9d0dbe57af05a199063536f246 100644 (file)
@@ -21,6 +21,7 @@ SUBDIRS = \
        listescape \
        notify \
        mail-log \
+       mailbox-alias \
        quota \
        imap-quota \
        pop3-migration \
diff --git a/src/plugins/mailbox-alias/Makefile.am b/src/plugins/mailbox-alias/Makefile.am
new file mode 100644 (file)
index 0000000..fa5f785
--- /dev/null
@@ -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 --git a/src/plugins/mailbox-alias/mailbox-alias-plugin.c b/src/plugins/mailbox-alias/mailbox-alias-plugin.c
new file mode 100644 (file)
index 0000000..86e60f1
--- /dev/null
@@ -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);
+}
+
+static void mailbox_alias_mail_user_created(struct mail_user *user)
+{
+       struct mail_user_vfuncs *v = user->vlast;
+       struct mailbox_alias_user *auser;
+       struct mailbox_alias *alias;
+       string_t *oldkey, *newkey;
+       const char *old_vname, *new_vname;
+       unsigned int i;
+
+       auser = p_new(user->pool, struct mailbox_alias_user, 1);
+       auser->module_ctx.super = *v;
+       user->vlast = &auser->module_ctx.super;
+
+       p_array_init(&auser->aliases, user->pool, 8);
+
+       oldkey = t_str_new(32);
+       newkey = t_str_new(32);
+       str_append(oldkey, "mailbox_alias_old");
+       str_append(newkey, "mailbox_alias_new");
+       for (i = 2;; i++) {
+               old_vname = mail_user_plugin_getenv(user, str_c(oldkey));
+               new_vname = mail_user_plugin_getenv(user, str_c(newkey));
+               if (old_vname == NULL || new_vname == NULL)
+                       break;
+
+               alias = array_append_space(&auser->aliases);
+               alias->old_vname = old_vname;
+               alias->new_vname = new_vname;
+
+               str_truncate(oldkey, 0);
+               str_truncate(newkey, 0);
+               str_printfa(oldkey, "mailbox_alias_old%u", i);
+               str_printfa(newkey, "mailbox_alias_new%u", i);
+       }
+
+       MODULE_CONTEXT_SET(user, mailbox_alias_user_module, auser);
+}
+
+static void mailbox_alias_mailbox_list_created(struct mailbox_list *list)
+{
+       struct mailbox_list_vfuncs *v = list->vlast;
+       struct mailbox_alias_mailbox_list *alist;
+
+       alist = p_new(list->pool, struct mailbox_alias_mailbox_list, 1);
+       alist->module_ctx.super = *v;
+       list->vlast = &alist->module_ctx.super;
+
+       v->get_storage_name = mailbox_alias_get_storage_name;
+       MODULE_CONTEXT_SET(list, mailbox_alias_mailbox_list_module, alist);
+}
+
+static void mailbox_alias_mailbox_allocated(struct mailbox *box)
+{
+       struct mailbox_vfuncs *v = box->vlast;
+       struct mailbox_alias_mailbox *abox;
+
+       abox = p_new(box->pool, struct mailbox_alias_mailbox, 1);
+       abox->module_ctx.super = *v;
+       box->vlast = &abox->module_ctx.super;
+
+       v->create = mailbox_alias_create;
+       v->delete = mailbox_alias_delete;
+       v->rename = mailbox_alias_rename;
+       MODULE_CONTEXT_SET(box, mailbox_alias_storage_module, abox);
+}
+
+static struct mail_storage_hooks mailbox_alias_mail_storage_hooks = {
+       .mail_user_created = mailbox_alias_mail_user_created,
+       .mailbox_list_created = mailbox_alias_mailbox_list_created,
+       .mailbox_allocated = mailbox_alias_mailbox_allocated
+};
+
+void mailbox_alias_plugin_init(struct module *module)
+{
+       mail_storage_hooks_add(module, &mailbox_alias_mail_storage_hooks);
+}
+
+void mailbox_alias_plugin_deinit(void)
+{
+       mail_storage_hooks_remove(&mailbox_alias_mail_storage_hooks);
+}
diff --git a/src/plugins/mailbox-alias/mailbox-alias-plugin.h b/src/plugins/mailbox-alias/mailbox-alias-plugin.h
new file mode 100644 (file)
index 0000000..bd439b9
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef MAILBOX_ALIAS_PLUGIN_H
+#define MAILBOX_ALIAS_PLUGIN_H
+
+void mailbox_alias_plugin_init(struct module *module);
+void mailbox_alias_plugin_deinit(void);
+
+#endif