From: Timo Sirainen Date: Fri, 2 Dec 2011 14:22:31 +0000 (+0200) Subject: Moved autocreate plugin functionality into lib-storage. X-Git-Tag: 2.1.rc2~107 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=eb1572d7c44ebc7b0b039d085c3dbab2ef7043dd;p=thirdparty%2Fdovecot%2Fcore.git Moved autocreate plugin functionality into lib-storage. The autocreate plugin is still used for backwards compatibility. Mailboxes can be configured like: mailbox Sent { auto = subscribe } mailbox Spam { auto = create } --- diff --git a/src/config/settings-get.pl b/src/config/settings-get.pl index edffc7ddfc..bc63cc7c13 100755 --- a/src/config/settings-get.pl +++ b/src/config/settings-get.pl @@ -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 '."\n"; diff --git a/src/lib-storage/mail-storage-private.h b/src/lib-storage/mail-storage-private.h index b13b76ea0a..5149f7d0ae 100644 --- a/src/lib-storage/mail-storage-private.h +++ b/src/lib-storage/mail-storage-private.h @@ -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. */ diff --git a/src/lib-storage/mail-storage-settings.c b/src/lib-storage/mail-storage-settings.c index eea5f886bf..1957ad9d7d 100644 --- a/src/lib-storage/mail-storage-settings.c +++ b/src/lib-storage/mail-storage-settings.c @@ -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) { diff --git a/src/lib-storage/mail-storage-settings.h b/src/lib-storage/mail-storage-settings.h index 4d27e3678e..77c5768ec4 100644 --- a/src/lib-storage/mail-storage-settings.h +++ b/src/lib-storage/mail-storage-settings.h @@ -55,6 +55,16 @@ struct mail_namespace_settings { struct mail_user_settings *user_set; }; +/* */ +#define MAILBOX_SET_AUTO_NO "no" +#define MAILBOX_SET_AUTO_CREATE "create" +#define MAILBOX_SET_AUTO_SUBSCRIBE "subscribe" +/* */ +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, diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 9cee661ccd..380a4227cd 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -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) { diff --git a/src/lib-storage/mailbox-list-iter.c b/src/lib-storage/mailbox-list-iter.c index 75bb3e8ba0..d17f4be356 100644 --- a/src/lib-storage/mailbox-list-iter.c +++ b/src/lib-storage/mailbox-list-iter.c @@ -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; } diff --git a/src/lib-storage/mailbox-list-private.h b/src/lib-storage/mailbox-list-private.h index 2a8853dd53..8aada9faf4 100644 --- a/src/lib-storage/mailbox-list-private.h +++ b/src/lib-storage/mailbox-list-private.h @@ -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 *); diff --git a/src/plugins/autocreate/autocreate-plugin.c b/src/plugins/autocreate/autocreate-plugin.c index 1c1b108b1d..29982e0b58 100644 --- a/src/plugins/autocreate/autocreate-plugin.c +++ b/src/plugins/autocreate/autocreate-plugin.c @@ -1,438 +1,54 @@ /* 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 - -#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)