The autocreate plugin is still used for backwards compatibility.
Mailboxes can be configured like:
mailbox Sent {
auto = subscribe
}
mailbox Spam {
auto = create
}
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";
/* 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. */
#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"
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
.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) \
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
.mail_log_prefix = "%s(%u): ",
.namespaces = ARRAY_INIT,
+ .mailboxes = ARRAY_INIT,
.plugin_envs = ARRAY_INIT
};
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)
{
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;
const char *mail_log_prefix;
ARRAY_DEFINE(namespaces, struct mail_namespace_settings *);
+ ARRAY_DEFINE(mailboxes, struct mailbox_settings *);
ARRAY_DEFINE(plugin_envs, const char *);
};
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,
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)
{
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;
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)
{
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;
}
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;
} 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) {
/* 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;
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,
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;
}
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;
}
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 *);
/* 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];
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);
}
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)