]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Moved autocreate plugin functionality into lib-storage.
authorTimo Sirainen <tss@iki.fi>
Fri, 2 Dec 2011 14:22:31 +0000 (16:22 +0200)
committerTimo Sirainen <tss@iki.fi>
Fri, 2 Dec 2011 14:22:31 +0000 (16:22 +0200)
The autocreate plugin is still used for backwards compatibility.

Mailboxes can be configured like:

mailbox Sent {
  auto = subscribe
}
mailbox Spam {
  auto = create
}

src/config/settings-get.pl
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage-settings.c
src/lib-storage/mail-storage-settings.h
src/lib-storage/mail-storage.c
src/lib-storage/mailbox-list-iter.c
src/lib-storage/mailbox-list-private.h
src/plugins/autocreate/autocreate-plugin.c

index edffc7ddfc79152c093b230639d4bd0dfa84590b..bc63cc7c13977fb068e193ea839df5b838a30f74 100755 (executable)
@@ -8,6 +8,7 @@ print '#include "var-expand.h"'."\n";
 print '#include "file-lock.h"'."\n";
 print '#include "fsync-mode.h"'."\n";
 print '#include "hash-format.h"'."\n";
+print '#include "unichar.h"'."\n";
 print '#include "settings-parser.h"'."\n";
 print '#include "all-settings.h"'."\n";
 print '#include <stddef.h>'."\n";
index b13b76ea0a29f30a36a4a16a98757617daf5d251..5149f7d0ae5772cb03cc948785370d68ad853077 100644 (file)
@@ -226,6 +226,8 @@ struct mailbox {
 
        /* default vfuncs for new struct mails. */
        const struct mail_vfuncs *mail_vfuncs;
+       /* Mailbox settings, or NULL if defaults */
+       const struct mailbox_settings *set;
 
        /* If non-zero, fail mailbox_open() with this error. mailbox_alloc()
           can set this to force open to fail. */
index eea5f886bf167ee94beed9c921742223970c47c4..1957ad9d7dbdb0fb60f6eb3cb105beb91305ddcb 100644 (file)
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "hash-format.h"
 #include "var-expand.h"
+#include "unichar.h"
 #include "settings-parser.h"
 #include "mail-index.h"
 #include "mail-user.h"
@@ -15,6 +16,7 @@
 
 static bool mail_storage_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool namespace_settings_check(void *_set, pool_t pool, const char **error_r);
+static bool mailbox_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool mail_user_settings_check(void *_set, pool_t pool, const char **error_r);
 
 #undef DEF
@@ -141,6 +143,37 @@ const struct setting_parser_info mail_namespace_setting_parser_info = {
        .check_func = namespace_settings_check
 };
 
+#undef DEF
+#define DEF(type, name) \
+       { type, #name, offsetof(struct mailbox_settings, name), NULL }
+
+static const struct setting_define mailbox_setting_defines[] = {
+       DEF(SET_STR, name),
+       { SET_ENUM, "auto", offsetof(struct mailbox_settings, autocreate), NULL } ,
+
+       SETTING_DEFINE_LIST_END
+};
+
+const struct mailbox_settings mailbox_default_settings = {
+       .name = "",
+       .autocreate = MAILBOX_SET_AUTO_NO":"
+               MAILBOX_SET_AUTO_CREATE":"
+               MAILBOX_SET_AUTO_SUBSCRIBE
+};
+
+const struct setting_parser_info mailbox_setting_parser_info = {
+       .defines = mailbox_setting_defines,
+       .defaults = &mailbox_default_settings,
+
+       .type_offset = offsetof(struct mailbox_settings, name),
+       .struct_size = sizeof(struct mailbox_settings),
+
+       .parent_offset = (size_t)-1,
+       .parent = &mail_user_setting_parser_info,
+
+       .check_func = mailbox_settings_check
+};
+
 #undef DEF
 #undef DEFLIST_UNIQUE
 #define DEF(type, name) \
@@ -173,6 +206,7 @@ static const struct setting_define mail_user_setting_defines[] = {
        DEF(SET_STR, mail_log_prefix),
 
        DEFLIST_UNIQUE(namespaces, "namespace", &mail_namespace_setting_parser_info),
+       DEFLIST_UNIQUE(mailboxes, "mailbox", &mailbox_setting_parser_info),
        { SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL },
 
        SETTING_DEFINE_LIST_END
@@ -202,6 +236,7 @@ static const struct mail_user_settings mail_user_default_settings = {
        .mail_log_prefix = "%s(%u): ",
 
        .namespaces = ARRAY_INIT,
+       .mailboxes = ARRAY_INIT,
        .plugin_envs = ARRAY_INIT
 };
 
@@ -425,6 +460,19 @@ static bool namespace_settings_check(void *_set, pool_t pool ATTR_UNUSED,
        return TRUE;
 }
 
+static bool mailbox_settings_check(void *_set, pool_t pool ATTR_UNUSED,
+                                  const char **error_r)
+{
+       struct mailbox_settings *set = _set;
+
+       if (!uni_utf8_str_is_valid(set->name)) {
+               *error_r = t_strdup_printf("mailbox %s: name isn't valid UTF-8",
+                                          set->name);
+               return FALSE;
+       }
+       return TRUE;
+}
+
 static bool mail_user_settings_check(void *_set, pool_t pool ATTR_UNUSED,
                                     const char **error_r ATTR_UNUSED)
 {
index 4d27e3678e9f798c112c4149549e002ebc03e046..77c5768ec4ddf39778de027dd7a3eca097bc5e8c 100644 (file)
@@ -55,6 +55,16 @@ struct mail_namespace_settings {
        struct mail_user_settings *user_set;
 };
 
+/* <settings checks> */
+#define MAILBOX_SET_AUTO_NO "no"
+#define MAILBOX_SET_AUTO_CREATE "create"
+#define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe"
+/* </settings checks> */
+struct mailbox_settings {
+       const char *name;
+       const char *autocreate;
+};
+
 struct mail_user_settings {
        const char *base_dir;
        const char *auth_socket_path;
@@ -77,6 +87,7 @@ struct mail_user_settings {
        const char *mail_log_prefix;
 
        ARRAY_DEFINE(namespaces, struct mail_namespace_settings *);
+       ARRAY_DEFINE(mailboxes, struct mailbox_settings *);
        ARRAY_DEFINE(plugin_envs, const char *);
 };
 
@@ -84,6 +95,7 @@ extern const struct setting_parser_info mail_user_setting_parser_info;
 extern const struct setting_parser_info mail_namespace_setting_parser_info;
 extern const struct setting_parser_info mail_storage_setting_parser_info;
 extern const struct mail_namespace_settings mail_namespace_default_settings;
+extern const struct mailbox_settings mailbox_default_settings;
 
 const void *
 mail_user_set_get_driver_settings(const struct setting_parser_info *info,
index 9cee661ccd12459d0a1424750361b7339041da15..380a4227cd969763581824a013749bc75f6386eb 100644 (file)
@@ -602,6 +602,18 @@ bool mail_storage_set_error_from_errno(struct mail_storage *storage)
        return TRUE;
 }
 
+static struct mailbox_settings *
+mailbox_settings_find(struct mail_user *user, const char *vname)
+{
+       struct mailbox_settings *const *box_set;
+
+       array_foreach(&user->set->mailboxes, box_set) {
+               if (strcmp((*box_set)->name, vname) == 0)
+                       return *box_set;
+       }
+       return NULL;
+}
+
 struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
                              enum mailbox_flags flags)
 {
@@ -628,6 +640,7 @@ struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
 
        T_BEGIN {
                box = storage->v.mailbox_alloc(storage, new_list, vname, flags);
+               box->set = mailbox_settings_find(storage->user, vname);
                hook_mailbox_allocated(box);
        } T_END;
 
@@ -704,6 +717,14 @@ static bool have_listable_namespace_prefix(struct mail_namespace *ns,
        return FALSE;
 }
 
+static bool mailbox_is_autocreated(struct mailbox *box)
+{
+       if (box->inbox_user)
+               return TRUE;
+       return box->set != NULL &&
+               strcmp(box->set->autocreate, MAILBOX_SET_AUTO_NO) != 0;
+}
+
 int mailbox_exists(struct mailbox *box, bool auto_boxes,
                   enum mailbox_existence *existence_r)
 {
@@ -732,8 +753,7 @@ int mailbox_exists(struct mailbox *box, bool auto_boxes,
                return 0;
        }
 
-       if (strcmp(box->name, "INBOX") == 0 && box->inbox_user && auto_boxes) {
-               /* INBOX always exists */
+       if (auto_boxes && box->set != NULL && mailbox_is_autocreated(box)) {
                *existence_r = MAILBOX_EXISTENCE_SELECT;
                return 0;
        }
@@ -788,6 +808,47 @@ static int mailbox_check_mismatching_separators(struct mailbox *box)
        return 0;
 }
 
+static void mailbox_autocreate(struct mailbox *box)
+{
+       const char *errstr;
+       enum mail_error error;
+
+       if (mailbox_create(box, NULL, FALSE) < 0) {
+               errstr = mailbox_get_last_error(box, &error);
+               if (error != MAIL_ERROR_NOTFOUND && !box->inbox_user) {
+                       mail_storage_set_critical(box->storage,
+                               "Failed to autocreate mailbox %s: %s",
+                               box->vname, errstr);
+               }
+       } else if (box->set != NULL &&
+                  strcmp(box->set->autocreate,
+                         MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
+               if (mailbox_set_subscribed(box, TRUE) < 0) {
+                       mail_storage_set_critical(box->storage,
+                               "Failed to autosubscribe to mailbox %s: %s",
+                               box->vname, mailbox_get_last_error(box, NULL));
+               }
+       }
+}
+
+static int mailbox_autocreate_and_reopen(struct mailbox *box)
+{
+       int ret;
+
+       mailbox_autocreate(box);
+       mailbox_close(box);
+
+       ret = box->v.open(box);
+       if (ret < 0 && box->inbox_user &&
+           !box->storage->user->inbox_open_error_logged) {
+               box->storage->user->inbox_open_error_logged = TRUE;
+               mail_storage_set_critical(box->storage,
+                       "Opening INBOX failed: %s",
+                       mailbox_get_last_error(box, NULL));
+       }
+       return ret;
+}
+
 static int mailbox_open_full(struct mailbox *box, struct istream *input)
 {
        int ret;
@@ -832,16 +893,8 @@ static int mailbox_open_full(struct mailbox *box, struct istream *input)
        } T_END;
 
        if (ret < 0 && box->storage->error == MAIL_ERROR_NOTFOUND &&
-           box->input == NULL && box->inbox_user) T_BEGIN {
-               /* INBOX should always exist. try to create it and retry. */
-               (void)mailbox_create(box, NULL, FALSE);
-               mailbox_close(box);
-               ret = box->v.open(box);
-               if (ret < 0 && !box->storage->user->inbox_open_error_logged) {
-                       box->storage->user->inbox_open_error_logged = TRUE;
-                       i_error("Opening INBOX failed: %s",
-                               mailbox_get_last_error(box, NULL));
-               }
+           box->input == NULL && mailbox_is_autocreated(box)) T_BEGIN {
+               ret = mailbox_autocreate_and_reopen(box);
        } T_END;
 
        if (ret < 0) {
index 75bb3e8ba0be642b1aad2a14dad788b625c30139..d17f4be3560e1b5d0893c87c344648642f7b1372 100644 (file)
@@ -1,10 +1,35 @@
 /* Copyright (c) 2006-2011 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "imap-match.h"
 #include "mailbox-tree.h"
 #include "mailbox-list-private.h"
 
+enum autocreate_match_result {
+       /* list contains the mailbox */
+       AUTOCREATE_MATCH_RESULT_YES             = 0x01,
+       /* list contains children of the mailbox */
+       AUTOCREATE_MATCH_RESULT_CHILDREN        = 0x02,
+       /* list contains parents of the mailbox */
+       AUTOCREATE_MATCH_RESULT_PARENT          = 0x04
+};
+
+struct autocreate_box {
+       const char *name;
+       enum mailbox_info_flags flags;
+       bool child_listed;
+};
+
+ARRAY_DEFINE_TYPE(mailbox_settings, struct mailbox_settings *);
+struct mailbox_list_autocreate_iterate_context {
+       unsigned int idx;
+       struct mailbox_info new_info;
+       ARRAY_DEFINE(boxes, struct autocreate_box);
+       ARRAY_TYPE(mailbox_settings) box_sets;
+       ARRAY_TYPE(mailbox_settings) all_ns_box_sets;
+};
+
 struct ns_list_iterate_context {
        struct mailbox_list_iterate_context ctx;
        struct mailbox_list_iterate_context *backend_ctx;
@@ -41,6 +66,47 @@ static int mailbox_list_subscriptions_refresh(struct mailbox_list *list)
        return ns->list->v.subscriptions_refresh(ns->list, list);
 }
 
+static void
+mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx)
+{
+       struct mail_user *user = ctx->list->ns->user;
+       struct mailbox_list_autocreate_iterate_context *actx;
+       struct mailbox_settings *const *box_sets;
+       struct mail_namespace *ns;
+       struct autocreate_box *autobox;
+       unsigned int i, count;
+
+       box_sets = array_get(&user->set->mailboxes, &count);
+       if (count == 0)
+               return;
+
+       actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1);
+       ctx->autocreate_ctx = actx;
+
+       /* build the list of mailboxes we need to consider as existing */
+       p_array_init(&actx->boxes, ctx->pool, 16);
+       p_array_init(&actx->box_sets, ctx->pool, 16);
+       p_array_init(&actx->all_ns_box_sets, ctx->pool, 16);
+       for (i = 0; i < count; i++) {
+               if (strcmp(box_sets[i]->autocreate, MAILBOX_SET_AUTO_NO) == 0)
+                       continue;
+
+               ns = mail_namespace_find(user->namespaces, box_sets[i]->name);
+               if (ns != ctx->list->ns)
+                       continue;
+
+               /* autocreate mailbox belongs to listed namespace */
+               array_append(&actx->all_ns_box_sets, &box_sets[i], 1);
+               if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 ||
+                   strcmp(box_sets[i]->autocreate,
+                          MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
+                       array_append(&actx->box_sets, &box_sets[i], 1);
+                       autobox = array_append_space(&actx->boxes);
+                       autobox->name = box_sets[i]->name;
+               }
+       }
+}
+
 struct mailbox_list_iterate_context *
 mailbox_list_iter_init_multiple(struct mailbox_list *list,
                                const char *const *patterns,
@@ -58,6 +124,8 @@ mailbox_list_iter_init_multiple(struct mailbox_list *list,
        ctx = list->v.iter_init(list, patterns, flags);
        if (ret < 0)
                ctx->failed = TRUE;
+       else if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0)
+               mailbox_list_iter_init_autocreate(ctx);
        return ctx;
 }
 
@@ -297,14 +365,210 @@ mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
        return &ctx->ctx;
 }
 
+static enum autocreate_match_result
+autocreate_box_match(const ARRAY_TYPE(mailbox_settings) *boxes,
+                    struct mail_namespace *ns, const char *name,
+                    bool only_subscribed, unsigned int *idx_r)
+{
+       struct mailbox_settings *const *sets;
+       unsigned int i, count, len, name_len = strlen(name);
+       enum autocreate_match_result result = 0;
+       char sep = mail_namespace_get_sep(ns);
+
+       *idx_r = -1U;
+
+       sets = array_get(boxes, &count);
+       for (i = 0; i < count; i++) {
+               if (only_subscribed &&
+                   strcmp(sets[i]->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
+                       continue;
+               len = I_MIN(name_len, strlen(sets[i]->name));
+               if (strncmp(name, sets[i]->name, len) != 0)
+                       continue;
+
+               if (name[len] == '\0' && sets[i]->name[len] == '\0') {
+                       result |= AUTOCREATE_MATCH_RESULT_YES;
+                       *idx_r = i;
+               } else if (name[len] == '\0' && sets[i]->name[len] == sep)
+                       result |= AUTOCREATE_MATCH_RESULT_CHILDREN;
+               else if (name[len] == sep && sets[i]->name[len] == '\0')
+                       result |= AUTOCREATE_MATCH_RESULT_PARENT;
+       }
+       return result;
+}
+
+static const struct mailbox_info *
+autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
+{
+       struct mailbox_list_autocreate_iterate_context *actx =
+               ctx->autocreate_ctx;
+       struct mailbox_info *info = &actx->new_info;
+       enum autocreate_match_result match, match2;
+       unsigned int idx;
+
+       match = autocreate_box_match(&actx->box_sets, ctx->list->ns,
+                                    info->name, FALSE, &idx);
+
+       if ((match & AUTOCREATE_MATCH_RESULT_YES) != 0) {
+               /* we have an exact match in the list.
+                  don't list it at the end. */
+               array_delete(&actx->boxes, idx, 1);
+               array_delete(&actx->box_sets, idx, 1);
+       }
+
+       if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+               if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
+                       info->flags |= MAILBOX_CHILD_SUBSCRIBED;
+               else {
+                       info->flags &= ~MAILBOX_NOCHILDREN;
+                       info->flags |= MAILBOX_CHILDREN;
+               }
+       }
+
+       /* make sure the mailbox existence flags are correct. */
+       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
+               match2 = match;
+       else {
+               info->flags |= MAILBOX_SUBSCRIBED;
+               match2 = autocreate_box_match(&actx->all_ns_box_sets,
+                                             ctx->list->ns, info->name,
+                                             FALSE, &idx);
+       }
+       if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0)
+               info->flags &= ~MAILBOX_NONEXISTENT;
+       if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+               info->flags &= ~MAILBOX_NOCHILDREN;
+               info->flags |= MAILBOX_CHILDREN;
+       }
+
+       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
+           (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
+               /* we're listing all mailboxes and want \Subscribed flag */
+               match2 = autocreate_box_match(&actx->all_ns_box_sets,
+                                             ctx->list->ns, info->name,
+                                             TRUE, &idx);
+               if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) {
+                       /* mailbox is also marked as autosubscribe */
+                       info->flags |= MAILBOX_SUBSCRIBED;
+               }
+               if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
+                       /* mailbox also has a children marked as
+                          autosubscribe */
+                       info->flags |= MAILBOX_CHILD_SUBSCRIBED;
+               }
+       }
+
+       if ((match & AUTOCREATE_MATCH_RESULT_PARENT) != 0) {
+               /* there are autocreate parent boxes.
+                  set their children flag states. */
+               struct autocreate_box *autobox;
+               unsigned int name_len;
+               char sep = mail_namespace_get_sep(ctx->list->ns);
+
+               array_foreach_modifiable(&actx->boxes, autobox) {
+                       name_len = strlen(autobox->name);
+                       if (strncmp(info->name, autobox->name, name_len) != 0 ||
+                           info->name[name_len] != sep)
+                               continue;
+
+                       if ((info->flags & MAILBOX_NONEXISTENT) == 0)
+                               autobox->flags |= MAILBOX_CHILDREN;
+                       if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
+                               autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
+                       autobox->child_listed = TRUE;
+               }
+       }
+       return info;
+}
+
+static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
+                                   const struct autocreate_box *autobox)
+{
+       struct mailbox_list_autocreate_iterate_context *actx =
+               ctx->autocreate_ctx;
+       enum imap_match_result match;
+
+       memset(&actx->new_info, 0, sizeof(actx->new_info));
+       actx->new_info.ns = ctx->list->ns;
+       actx->new_info.name = autobox->name;
+       actx->new_info.flags = autobox->flags;
+
+       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
+               actx->new_info.flags |= MAILBOX_SUBSCRIBED;
+
+       if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
+               actx->new_info.flags |= MAILBOX_NOCHILDREN;
+
+       match = imap_match(ctx->glob, actx->new_info.name);
+       if (match == IMAP_MATCH_YES)
+               return TRUE;
+       if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
+               enum mailbox_info_flags old_flags = actx->new_info.flags;
+               char sep = mail_namespace_get_sep(ctx->list->ns);
+               const char *p;
+
+               /* e.g. autocreate=foo/bar and we're listing % */
+               actx->new_info.flags = MAILBOX_NONEXISTENT |
+                       (old_flags & (MAILBOX_CHILDREN |
+                                     MAILBOX_CHILD_SUBSCRIBED));
+               if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
+                       actx->new_info.flags |= MAILBOX_CHILDREN;
+                       actx->new_info.flags &= ~MAILBOX_NOCHILDREN;
+               }
+               if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
+                       actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
+               do {
+                       p = strrchr(actx->new_info.name, sep);
+                       i_assert(p != NULL);
+                       actx->new_info.name =
+                               t_strdup_until(actx->new_info.name, p);
+                       match = imap_match(ctx->glob, actx->new_info.name);
+               } while (match != IMAP_MATCH_YES);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static const struct mailbox_info *
+autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
+{
+       struct mailbox_list_autocreate_iterate_context *actx =
+               ctx->autocreate_ctx;
+       const struct mailbox_info *info;
+       const struct autocreate_box *autoboxes;
+       unsigned int count;
+
+       if (actx->idx == 0) {
+               info = ctx->list->v.iter_next(ctx);
+               if (info != NULL) {
+                       ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+                       actx->new_info = *info;
+                       return autocreate_iter_existing(ctx);
+               }
+       }
+
+       /* list missing mailboxes */
+       autoboxes = array_get(&actx->boxes, &count);
+       while (actx->idx < count) {
+               if (autocreate_iter_autobox(ctx, &autoboxes[actx->idx++]))
+                       return &actx->new_info;
+       }
+       i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets));
+       return NULL;
+}
+
 const struct mailbox_info *
 mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
 {
        const struct mailbox_info *info;
 
-       info = ctx->list->v.iter_next(ctx);
-       if (info != NULL)
-               ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+       if (ctx->autocreate_ctx != NULL)
+               info = autocreate_iter_next(ctx);
+       else {
+               info = ctx->list->v.iter_next(ctx);
+               if (info != NULL)
+                       ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
+       }
        return info;
 }
 
index 2a8853dd536119f6d28be72c41ea1a593a8b34a9..8aada9faf43ffa0d80752cfaa184b9c7ec7e8ec4 100644 (file)
@@ -146,6 +146,7 @@ struct mailbox_list_iterate_context {
        bool failed;
 
        struct imap_match_glob *glob;
+       struct mailbox_list_autocreate_iterate_context *autocreate_ctx;
 
        ARRAY_DEFINE(module_contexts,
                     union mailbox_list_iterate_module_context *);
index 1c1b108b1d49a8188ba91dc017d50818646c05fb..29982e0b580ff3cf22937bc6439636421f233eb3 100644 (file)
 /* Copyright (c) 2007-2011 Dovecot authors, see the included COPYING file */
 
+/* FIXME: this plugin is only for backwards compatibility. log a warning in
+   v2.2 about this and in later versions remove completely */
+
 #include "lib.h"
+#include "array.h"
 #include "unichar.h"
-#include "imap-match.h"
-#include "mailbox-list-private.h"
-#include "mail-storage-private.h"
+#include "mail-user.h"
 #include "mail-storage-hooks.h"
 #include "autocreate-plugin.h"
 
-#include <stdlib.h>
-
-#define AUTOCREATE_USER_CONTEXT(obj) \
-       MODULE_CONTEXT(obj, autocreate_user_module)
-#define AUTOCREATE_LIST_CONTEXT(obj) \
-       MODULE_CONTEXT(obj, autocreate_list_module)
-#define AUTOCREATE_CONTEXT(obj) \
-       MODULE_CONTEXT(obj, autocreate_storage_module)
-
-enum match_result {
-       /* list contains the mailbox */
-       MATCH_RESULT_YES        = 0x01,
-       /* list contains children of the mailbox */
-       MATCH_RESULT_CHILDREN   = 0x02,
-       /* list contains parents of the mailbox */
-       MATCH_RESULT_PARENT     = 0x04
-};
-
-struct autocreate_box {
-       const char *name;
-       unsigned int name_len;
-       enum mailbox_info_flags flags;
-       bool child_listed;
-
-       struct mail_namespace *ns;
-};
-ARRAY_DEFINE_TYPE(autocreate_box, struct autocreate_box);
-
-struct autocreate_user {
-       union mail_user_module_context module_ctx;
-
-       ARRAY_TYPE(autocreate_box) autocreate_mailboxes;
-       ARRAY_TYPE(autocreate_box) autosubscribe_mailboxes;
-};
-
-struct autocreate_mailbox_list_iterate_context {
-       union mailbox_list_iterate_module_context module_ctx;
-
-       pool_t pool;
-       unsigned int idx;
-       struct mailbox_info new_info;
-       ARRAY_TYPE(autocreate_box) boxes;
-};
-
-struct autocreate_mailbox_list {
-       union mailbox_list_module_context module_ctx;
-};
-
-const char *autocreate_plugin_version = DOVECOT_VERSION;
-
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_user_module,
-                                 &mail_user_module_register);
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_list_module,
-                                 &mailbox_list_module_register);
-static MODULE_CONTEXT_DEFINE_INIT(autocreate_storage_module,
-                                 &mail_storage_module_register);
-
-static enum match_result
-autocreate_box_match(const ARRAY_TYPE(autocreate_box) *boxes, const char *name,
-                    unsigned int *idx_r)
+static struct mailbox_settings *
+mailbox_settings_find(struct mail_user *user, const char *vname)
 {
-       const struct autocreate_box *autoboxes;
-       unsigned int i, count, len, name_len = strlen(name);
-       enum match_result result = 0;
-       char sep;
+       struct mailbox_settings *const *box_set;
 
-       *idx_r = -1U;
-
-       autoboxes = array_get(boxes, &count);
-       for (i = 0; i < count; i++) {
-               len = I_MIN(name_len, autoboxes[i].name_len);
-               if (strncmp(name, autoboxes[i].name, len) != 0)
-                       continue;
-
-               sep = mail_namespace_get_sep(autoboxes[i].ns);
-               if (name[len] == '\0' && autoboxes[i].name[len] == '\0') {
-                       result |= MATCH_RESULT_YES;
-                       *idx_r = i;
-               } else if (name[len] == '\0' && autoboxes[i].name[len] == sep)
-                       result |= MATCH_RESULT_CHILDREN;
-               else if (name[len] == sep && autoboxes[i].name[len] == '\0')
-                       result |= MATCH_RESULT_PARENT;
-       }
-       return result;
-}
-
-static bool
-is_autocreated(struct mail_user *user, const char *name)
-{
-       struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-       unsigned int idx;
-
-       return autocreate_box_match(&auser->autocreate_mailboxes,
-                                   name, &idx) == MATCH_RESULT_YES;
-}
-
-static bool
-is_autosubscribed(struct mail_user *user, const char *name)
-{
-       struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-       unsigned int idx;
-
-       return autocreate_box_match(&auser->autosubscribe_mailboxes,
-                                   name, &idx) == MATCH_RESULT_YES;
-}
-
-static int autocreate_mailbox_open(struct mailbox *box)
-{
-       union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-       int ret;
-
-       if ((ret = abox->super.open(box)) < 0 &&
-           mailbox_get_last_mail_error(box) == MAIL_ERROR_NOTFOUND &&
-           is_autocreated(box->storage->user, box->vname)) {
-               /* autocreate the mailbox */
-               if (mailbox_create(box, NULL, FALSE) < 0) {
-                       i_error("autocreate: Failed to create mailbox %s: %s",
-                               box->vname, mailbox_get_last_error(box, NULL));
-               }
-               mailbox_close(box);
-               ret = box->v.open(box);
-       }
-       return ret;
-}
-
-static int autocreate_mailbox_exists(struct mailbox *box, bool auto_boxes,
-                                    enum mailbox_existence *existence_r)
-{
-       union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-
-       if (auto_boxes && is_autocreated(box->storage->user, box->vname)) {
-               *existence_r = MAILBOX_EXISTENCE_SELECT;
-               return 0;
-       }
-
-       return abox->super.exists(box, auto_boxes, existence_r);
-}
-
-static int
-autocreate_mailbox_create(struct mailbox *box,
-                         const struct mailbox_update *update,
-                         bool directory)
-{
-       union mailbox_module_context *abox = AUTOCREATE_CONTEXT(box);
-
-       if (abox->super.create(box, update, directory) < 0)
-               return -1;
-
-       if (is_autosubscribed(box->storage->user, box->vname)) {
-               if (mailbox_set_subscribed(box, TRUE) < 0) {
-                       i_error("autocreate: Failed to subscribe to mailbox %s: %s",
-                               box->vname, mailbox_get_last_error(box, NULL));
-               }
-       }
-       return 0;
-}
-
-static void autocreate_mailbox_allocated(struct mailbox *box)
-{
-       struct mailbox_vfuncs *v = box->vlast;
-       union mailbox_module_context *abox;
-
-       abox = p_new(box->pool, union mailbox_module_context, 1);
-       abox->super = *v;
-       box->vlast = &abox->super;
-       v->open = autocreate_mailbox_open;
-       v->exists = autocreate_mailbox_exists;
-       v->create = autocreate_mailbox_create;
-
-       MODULE_CONTEXT_SET_SELF(box, autocreate_storage_module, abox);
-}
-
-static struct mailbox_list_iterate_context *
-autocreate_iter_init(struct mailbox_list *list,
-                    const char *const *patterns,
-                    enum mailbox_list_iter_flags flags)
-{
-       union mailbox_list_module_context *alist =
-               AUTOCREATE_LIST_CONTEXT(list);
-       struct mail_user *user = list->ns->user;
-       struct autocreate_user *auser = AUTOCREATE_USER_CONTEXT(user);
-       struct mailbox_list_iterate_context *ctx;
-       struct autocreate_mailbox_list_iterate_context *actx;
-       const ARRAY_TYPE(autocreate_box) *extra_boxes;
-       const struct autocreate_box *autobox;
-       pool_t pool;
-
-       ctx = alist->super.iter_init(list, patterns, flags);
-
-       pool = pool_alloconly_create("autocreate list iter", 1024);
-       actx = p_new(pool, struct autocreate_mailbox_list_iterate_context, 1);
-       actx->pool = pool;
-
-       p_array_init(&actx->boxes, pool, 16);
-       if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) {
-               if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
-                       extra_boxes = &auser->autocreate_mailboxes;
-               else
-                       extra_boxes = &auser->autosubscribe_mailboxes;
-
-               array_foreach(extra_boxes, autobox) {
-                       if (autobox->ns == list->ns)
-                               array_append(&actx->boxes, autobox, 1);
-               }
-       }
-
-       MODULE_CONTEXT_SET(ctx, autocreate_list_module, actx);
-       return ctx;
-}
-
-static int autocreate_iter_deinit(struct mailbox_list_iterate_context *ctx)
-{
-       union mailbox_list_module_context *alist =
-               AUTOCREATE_LIST_CONTEXT(ctx->list);
-       struct autocreate_mailbox_list_iterate_context *actx =
-               AUTOCREATE_LIST_CONTEXT(ctx);
-
-       pool_unref(&actx->pool);
-       return alist->super.iter_deinit(ctx);
-}
-
-static const struct mailbox_info *
-autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
-{
-       struct autocreate_mailbox_list_iterate_context *actx =
-               AUTOCREATE_LIST_CONTEXT(ctx);
-       struct autocreate_user *auser =
-               AUTOCREATE_USER_CONTEXT(ctx->list->ns->user);
-       struct mailbox_info *info = &actx->new_info;
-       enum match_result match, match2;
-       unsigned int idx;
-
-       match = autocreate_box_match(&actx->boxes, info->name, &idx);
-
-       if ((match & MATCH_RESULT_YES) != 0) {
-               /* we have an exact match in the list.
-                  don't list it at the end. */
-               array_delete(&actx->boxes, idx, 1);
-       }
-
-       if ((match & MATCH_RESULT_CHILDREN) != 0) {
-               if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-                       info->flags |= MAILBOX_CHILD_SUBSCRIBED;
-               else {
-                       info->flags &= ~MAILBOX_NOCHILDREN;
-                       info->flags |= MAILBOX_CHILDREN;
-               }
-       }
-
-       /* make sure the mailbox existence flags are correct. */
-       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
-               match2 = match;
-       else {
-               info->flags |= MAILBOX_SUBSCRIBED;
-               match2 = autocreate_box_match(&auser->autocreate_mailboxes,
-                                             info->name, &idx);
-       }
-       if ((match2 & MATCH_RESULT_YES) != 0)
-               info->flags &= ~MAILBOX_NONEXISTENT;
-       if ((match2 & MATCH_RESULT_CHILDREN) != 0) {
-               info->flags &= ~MAILBOX_NOCHILDREN;
-               info->flags |= MAILBOX_CHILDREN;
-       }
-
-       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
-           (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
-               /* we're listing all mailboxes and want \Subscribed flag */
-               match2 = autocreate_box_match(&auser->autosubscribe_mailboxes,
-                                             info->name, &idx);
-               if ((match2 & MATCH_RESULT_YES) != 0) {
-                       /* mailbox is also marked as autosubscribe */
-                       info->flags |= MAILBOX_SUBSCRIBED;
-               }
-               if ((match2 & MATCH_RESULT_CHILDREN) != 0) {
-                       /* mailbox also has a children marked as
-                          autosubscribe */
-                       info->flags |= MAILBOX_CHILD_SUBSCRIBED;
-               }
-       }
-
-       if ((match & MATCH_RESULT_PARENT) != 0) {
-               /* there are autocreate parent boxes.
-                  set their children flag states. */
-               struct autocreate_box *autobox;
-               char sep;
-
-               array_foreach_modifiable(&actx->boxes, autobox) {
-                       sep = mail_namespace_get_sep(autobox->ns);
-
-                       if (strncmp(info->name, autobox->name,
-                                   autobox->name_len) != 0 ||
-                           info->name[autobox->name_len] != sep)
-                               continue;
-
-                       if ((info->flags & MAILBOX_NONEXISTENT) == 0)
-                               autobox->flags |= MAILBOX_CHILDREN;
-                       if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
-                               autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
-                       autobox->child_listed = TRUE;
-               }
-       }
-       return info;
-}
-
-static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
-                                   const struct autocreate_box *autobox)
-{
-       struct autocreate_mailbox_list_iterate_context *actx =
-               AUTOCREATE_LIST_CONTEXT(ctx);
-       enum imap_match_result match;
-
-       memset(&actx->new_info, 0, sizeof(actx->new_info));
-       actx->new_info.ns = ctx->list->ns;
-       actx->new_info.name = autobox->name;
-       actx->new_info.flags = autobox->flags;
-
-       if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-               actx->new_info.flags |= MAILBOX_SUBSCRIBED;
-
-       if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
-               actx->new_info.flags |= MAILBOX_NOCHILDREN;
-
-       match = imap_match(ctx->glob, actx->new_info.name);
-       if (match == IMAP_MATCH_YES)
-               return TRUE;
-       if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
-               enum mailbox_info_flags old_flags = actx->new_info.flags;
-               char sep = mail_namespace_get_sep(ctx->list->ns);
-               const char *p;
-
-               /* e.g. autocreate=foo/bar and we're listing % */
-               actx->new_info.flags = MAILBOX_NONEXISTENT |
-                       (old_flags & (MAILBOX_CHILDREN |
-                                     MAILBOX_CHILD_SUBSCRIBED));
-               if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
-                       actx->new_info.flags |= MAILBOX_CHILDREN;
-                       actx->new_info.flags &= ~MAILBOX_NOCHILDREN;
-               }
-               if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
-                       actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
-               do {
-                       p = strrchr(actx->new_info.name, sep);
-                       i_assert(p != NULL);
-                       actx->new_info.name =
-                               t_strdup_until(actx->new_info.name, p);
-                       match = imap_match(ctx->glob, actx->new_info.name);
-               } while (match != IMAP_MATCH_YES);
-               return TRUE;
-       }
-       return FALSE;
-}
-
-static const struct mailbox_info *
-autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
-{
-       union mailbox_list_module_context *alist =
-               AUTOCREATE_LIST_CONTEXT(ctx->list);
-       struct autocreate_mailbox_list_iterate_context *actx =
-               AUTOCREATE_LIST_CONTEXT(ctx);
-       const struct mailbox_info *info;
-       const struct autocreate_box *autoboxes;
-       unsigned int count;
-
-       if (actx->idx == 0) {
-               info = alist->super.iter_next(ctx);
-               if (info != NULL) {
-                       actx->new_info = *info;
-                       return autocreate_iter_existing(ctx);
-               }
-       }
-
-       /* list missing mailboxes */
-       autoboxes = array_get(&actx->boxes, &count);
-       while (actx->idx < count) {
-               if (autocreate_iter_autobox(ctx, &autoboxes[actx->idx++]))
-                       return &actx->new_info;
+       array_foreach(&user->set->mailboxes, box_set) {
+               if (strcmp((*box_set)->name, vname) == 0)
+                       return *box_set;
        }
        return NULL;
 }
 
-static void autocreate_mailbox_list_created(struct mailbox_list *list)
-{
-       struct mailbox_list_vfuncs *v = list->vlast;
-       union mailbox_list_module_context *alist;
-
-       alist = p_new(list->pool, union mailbox_list_module_context, 1);
-       alist->super = *v;
-       list->vlast = &alist->super;
-       v->iter_init = autocreate_iter_init;
-       v->iter_deinit = autocreate_iter_deinit;
-       v->iter_next = autocreate_iter_next;
-
-       MODULE_CONTEXT_SET_SELF(list, autocreate_list_module, alist);
-}
-
 static void
-add_autobox(struct mail_user *user, ARRAY_TYPE(autocreate_box) *boxes,
-           const char *value)
+add_autobox(struct mail_user *user, const char *vname, bool subscriptions)
 {
-       struct autocreate_box *autobox;
-       struct mail_namespace *ns;
+       struct mailbox_settings *set;
 
-       if (!uni_utf8_str_is_valid(value)) {
+       if (!uni_utf8_str_is_valid(vname)) {
                i_error("autocreate: Mailbox name isn't valid UTF-8: %s",
-                       value);
+                       vname);
                return;
        }
 
-       if ((ns = mail_namespace_find(user->namespaces, value)) == NULL) {
-               if (user->mail_debug) {
-                       i_debug("autocreate: Namespace not found for mailbox: %s",
-                               value);
-               }
-               return;
+       set = mailbox_settings_find(user, vname);
+       if (set == NULL) {
+               set = p_new(user->pool, struct mailbox_settings, 1);
+               set->name = p_strdup(user->pool, vname);
+               set->autocreate = MAILBOX_SET_AUTO_NO;
+               array_append(&user->set->mailboxes, &set, 1);
        }
-
-       autobox = array_append_space(boxes);
-       autobox->name = p_strdup(user->pool, value);
-       autobox->name_len = strlen(value);
-       autobox->ns = ns;
+       if (subscriptions)
+               set->autocreate = MAILBOX_SET_AUTO_SUBSCRIBE;
+       else if (strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
+               set->autocreate = MAILBOX_SET_AUTO_CREATE;
 }
 
-static void read_autobox_settings(struct mail_user *user,
-                                 ARRAY_TYPE(autocreate_box) *boxes,
-                                 const char *env_name_base)
+static void
+read_autobox_settings(struct mail_user *user, const char *env_name_base,
+                     bool subscriptions)
 {
        const char *value;
        char env_name[20];
@@ -440,7 +56,7 @@ static void read_autobox_settings(struct mail_user *user,
 
        value = mail_user_plugin_getenv(user, env_name_base);
        while (value != NULL) {
-               add_autobox(user, boxes, value);
+               add_autobox(user, value, subscriptions);
 
                i_snprintf(env_name, sizeof(env_name), "%s%d",
                           env_name_base, ++i);
@@ -449,27 +65,14 @@ static void read_autobox_settings(struct mail_user *user,
 }
 
 static void
-autocreate_mail_namespaces_created(struct mail_namespace *namespaces)
+autocreate_mail_user_created(struct mail_user *user)
 {
-       struct mail_user *user = namespaces->user;
-       struct autocreate_user *auser;
-
-       auser = p_new(user->pool, struct autocreate_user, 1);
-       p_array_init(&auser->autocreate_mailboxes, user->pool, 8);
-       read_autobox_settings(user, &auser->autocreate_mailboxes, "autocreate");
-
-       p_array_init(&auser->autosubscribe_mailboxes, user->pool, 8);
-       read_autobox_settings(user, &auser->autosubscribe_mailboxes,
-                             "autosubscribe");
-
-       MODULE_CONTEXT_SET(user, autocreate_user_module, auser);
+       read_autobox_settings(user, "autocreate", FALSE);
+       read_autobox_settings(user, "autosubscribe", TRUE);
 }
 
-
 static struct mail_storage_hooks autocreate_mail_storage_hooks = {
-       .mailbox_allocated = autocreate_mailbox_allocated,
-       .mailbox_list_created = autocreate_mailbox_list_created,
-       .mail_namespaces_created = autocreate_mail_namespaces_created
+       .mail_user_created = autocreate_mail_user_created
 };
 
 void autocreate_plugin_init(struct module *module)