From: Timo Sirainen Date: Wed, 28 Apr 2010 19:05:13 +0000 (+0300) Subject: lib-storage: mailbox_list_iter_init_namespaces() has more filtering capabilities... X-Git-Tag: 2.0.beta5~74 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=de4d9f77d6378a416c9963963eac5ac18b75ec0b;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: mailbox_list_iter_init_namespaces() has more filtering capabilities now. - namespace types - MAILBOX_LIST_ITER_STAR_WITHIN_NS flag so that "*" doesn't escape beyond the namespace it started in. --HG-- branch : HEAD --- diff --git a/src/dsync/dsync-worker-local.c b/src/dsync/dsync-worker-local.c index 29104727b5..d53897af0b 100644 --- a/src/dsync/dsync-worker-local.c +++ b/src/dsync/dsync-worker-local.c @@ -395,7 +395,8 @@ local_worker_mailbox_iter_init(struct dsync_worker *_worker) iter->ret_pool = pool_alloconly_create("local mailbox iter", 1024); iter->list_iter = mailbox_list_iter_init_namespaces(worker->user->namespaces, - patterns, list_flags); + patterns, NAMESPACE_PRIVATE, + list_flags); (void)dsync_worker_get_mailbox_log(worker); return &iter->iter; } @@ -580,7 +581,8 @@ local_worker_subs_iter_init(struct dsync_worker *_worker) iter->iter.worker = _worker; iter->list_iter = mailbox_list_iter_init_namespaces(worker->user->namespaces, - patterns, list_flags); + patterns, NAMESPACE_PRIVATE, + list_flags); (void)dsync_worker_get_mailbox_log(worker); return &iter->iter; } diff --git a/src/lib-storage/mail-namespace.h b/src/lib-storage/mail-namespace.h index 1bb420810c..3faa828e12 100644 --- a/src/lib-storage/mail-namespace.h +++ b/src/lib-storage/mail-namespace.h @@ -6,9 +6,9 @@ struct mail_storage_callbacks; enum namespace_type { - NAMESPACE_PRIVATE, - NAMESPACE_SHARED, - NAMESPACE_PUBLIC + NAMESPACE_PRIVATE = 0x01, + NAMESPACE_SHARED = 0x02, + NAMESPACE_PUBLIC = 0x04 }; enum namespace_flags { diff --git a/src/lib-storage/mailbox-list.c b/src/lib-storage/mailbox-list.c index ded1d56edc..97752d1c1f 100644 --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@ -38,7 +38,8 @@ struct ns_list_iterate_context { struct mailbox_list_iterate_context *backend_ctx; struct mail_namespace *namespaces; pool_t pool; - const char **patterns; + const char **patterns, **patterns_ns_match; + enum namespace_type type_mask; }; struct mailbox_list_module_register mailbox_list_module_register = { 0 }; @@ -592,12 +593,124 @@ mailbox_list_iter_init_multiple(struct mailbox_list *list, return list->v.iter_init(list, patterns, flags); } +static bool +ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) +{ + if ((ctx->type_mask & ns->type) == 0) + return FALSE; + + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { + if (ns->alias_for != NULL) + return FALSE; + } + return TRUE; +} + +static bool +ns_match_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, + const char *pattern) +{ + struct imap_match_glob *glob; + enum imap_match_result result; + const char *prefix_without_sep; + unsigned int len; + + len = ns->prefix_len; + if (len > 0 && ns->prefix[len-1] == ns->sep) + len--; + + if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX | + NAMESPACE_FLAG_LIST_CHILDREN)) == 0) { + /* non-listable namespace matches only with exact prefix */ + if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0) + return FALSE; + } + + prefix_without_sep = t_strndup(ns->prefix, len); + if (*prefix_without_sep == '\0') + result = IMAP_MATCH_CHILDREN; + else { + glob = imap_match_init(pool_datastack_create(), pattern, + TRUE, ns->sep); + result = imap_match(glob, prefix_without_sep); + } + + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) { + switch (result) { + case IMAP_MATCH_YES: + case IMAP_MATCH_CHILDREN: + return TRUE; + case IMAP_MATCH_NO: + case IMAP_MATCH_PARENT: + break; + } + return FALSE; + } + + switch (result) { + case IMAP_MATCH_YES: + /* allow matching prefix only when it's done without + wildcards */ + if (strcmp(prefix_without_sep, pattern) == 0) + return TRUE; + break; + case IMAP_MATCH_CHILDREN: { + /* allow this only if there isn't another namespace + with longer prefix that matches this pattern + (namespaces are sorted by prefix length) */ + struct mail_namespace *tmp; + + T_BEGIN { + for (tmp = ns->next; tmp != NULL; tmp = tmp->next) { + if (ns_match_simple(ctx, tmp) && + ns_match_next(ctx, tmp, pattern)) + break; + } + } T_END; + if (tmp == NULL) + return TRUE; + break; + } + case IMAP_MATCH_NO: + case IMAP_MATCH_PARENT: + break; + } + return FALSE; +} + +static bool +ns_match(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) +{ + unsigned int i; + + if (!ns_match_simple(ctx, ns)) + return FALSE; + + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) { + /* filter out namespaces whose prefix doesn't match. + this same code handles both with and without + STAR_WITHIN_NS, so the "without" case is slower than + necessary, but this shouldn't matter much */ + T_BEGIN { + for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) { + if (ns_match_next(ctx, ns, + ctx->patterns_ns_match[i])) + break; + } + } T_END; + + if (ctx->patterns_ns_match[i] == NULL) + return FALSE; + } + return ns; +} + static struct mail_namespace * ns_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { - if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { - while (ns != NULL && ns->alias_for != NULL) - ns = ns->next; + for (; ns != NULL; ns = ns->next) { + if (ns_match(ctx, ns)) + break; } return ns; } @@ -609,7 +722,8 @@ mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx) (struct ns_list_iterate_context *)_ctx; const struct mailbox_info *info; - info = mailbox_list_iter_next(ctx->backend_ctx); + info = ctx->backend_ctx == NULL ? NULL : + mailbox_list_iter_next(ctx->backend_ctx); if (info == NULL && ctx->namespaces != NULL) { /* go to the next namespace */ if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) @@ -632,16 +746,39 @@ mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx) (struct ns_list_iterate_context *)_ctx; int ret; - if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) - _ctx->failed = TRUE; + if (ctx->backend_ctx != NULL) { + if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) + _ctx->failed = TRUE; + } ret = _ctx->failed ? -1 : 0; pool_unref(&ctx->pool); return ret; } +static const char ** +dup_patterns_without_stars(pool_t pool, const char *const *patterns, + unsigned int count) +{ + const char **dup; + unsigned int i; + + dup = p_new(pool, const char *, count + 1); + for (i = 0; i < count; i++) { + char *p = p_strdup(pool, patterns[i]); + dup[i] = p; + + for (; *p != '\0'; p++) { + if (*p == '*') + *p = '%'; + } + } + return dup; +} + struct mailbox_list_iterate_context * mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, const char *const *patterns, + enum namespace_type type_mask, enum mailbox_list_iter_flags flags) { struct ns_list_iterate_context *ctx; @@ -650,9 +787,13 @@ mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, i_assert(namespaces != NULL); + i_assert((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0 || + (flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0); + pool = pool_alloconly_create("mailbox list namespaces", 1024); ctx = p_new(pool, struct ns_list_iterate_context, 1); ctx->pool = pool; + ctx->type_mask = type_mask; ctx->ctx.flags = flags; ctx->ctx.list = p_new(pool, struct mailbox_list, 1); ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next; @@ -663,11 +804,22 @@ mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, for (i = 0; i < count; i++) ctx->patterns[i] = p_strdup(pool, patterns[i]); + if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) { + /* create copies of patterns with '*' wildcard changed to '%' */ + ctx->patterns_ns_match = + dup_patterns_without_stars(pool, ctx->patterns, count); + } else { + ctx->patterns_ns_match = ctx->patterns; + } + namespaces = ns_next(ctx, namespaces); ctx->ctx.list->ns = namespaces; - ctx->backend_ctx = mailbox_list_iter_init_multiple(namespaces->list, - patterns, flags); - ctx->namespaces = ns_next(ctx, namespaces->next); + if (namespaces != NULL) { + ctx->backend_ctx = + mailbox_list_iter_init_multiple(namespaces->list, + patterns, flags); + ctx->namespaces = ns_next(ctx, namespaces->next); + } return &ctx->ctx; } diff --git a/src/lib-storage/mailbox-list.h b/src/lib-storage/mailbox-list.h index 6a9d4b49d9..71dd873265 100644 --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@ -10,6 +10,7 @@ # define MAILBOX_LIST_NAME_MAX_LENGTH 4096 #endif +enum namespace_type; struct mail_namespace; struct mail_storage; struct mailbox_list; @@ -63,17 +64,24 @@ enum mailbox_list_iter_flags { /* Use virtual mailbox names (virtual separators and namespace prefixes) for patterns and for returned mailbox names. */ MAILBOX_LIST_ITER_VIRTUAL_NAMES = 0x000002, + /* Don't list INBOX unless it actually exists */ + MAILBOX_LIST_ITER_NO_AUTO_INBOX = 0x000004, + /* For mailbox_list_iter_init_namespaces(): Skip namespaces that have alias_for set. */ - MAILBOX_LIST_ITER_SKIP_ALIASES = 0x000004, - /* Don't list INBOX unless it actually exists */ - MAILBOX_LIST_ITER_NO_AUTO_INBOX = 0x000008, + MAILBOX_LIST_ITER_SKIP_ALIASES = 0x000008, + /* For mailbox_list_iter_init_namespaces(): '*' in a pattern doesn't + match beyond namespace boundary (e.g. "foo*" or "*o" doesn't match + "foo." namespace's mailboxes, but "*.*" does). also '%' can't match + namespace prefixes, if there exists a parent namespace whose children + it matches. VIRTUAL_NAMES must be set when using this flag. */ + MAILBOX_LIST_ITER_STAR_WITHIN_NS = 0x000010, /* List only subscribed mailboxes */ - MAILBOX_LIST_ITER_SELECT_SUBSCRIBED = 0x000010, + MAILBOX_LIST_ITER_SELECT_SUBSCRIBED = 0x000100, /* Return MAILBOX_CHILD_* if mailbox's children match selection criteria, even if the mailbox itself wouldn't match. */ - MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH = 0x000020, + MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH = 0x000200, /* Don't return any flags unless it can be done without cost */ MAILBOX_LIST_ITER_RETURN_NO_FLAGS = 0x001000, @@ -238,6 +246,7 @@ mailbox_list_iter_init_multiple(struct mailbox_list *list, struct mailbox_list_iterate_context * mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, const char *const *patterns, + enum namespace_type type_mask, enum mailbox_list_iter_flags flags); /* Get next mailbox. Returns the mailbox name */ const struct mailbox_info * diff --git a/src/plugins/virtual/virtual-config.c b/src/plugins/virtual/virtual-config.c index c39d7bab10..a90858f400 100644 --- a/src/plugins/virtual/virtual-config.c +++ b/src/plugins/virtual/virtual-config.c @@ -260,6 +260,11 @@ static bool virtual_config_match(const struct mailbox_info *info, static int virtual_config_expand_wildcards(struct virtual_parse_context *ctx) { + const enum namespace_type iter_ns_types = + NAMESPACE_PRIVATE | NAMESPACE_SHARED | NAMESPACE_PUBLIC; + const enum mailbox_list_iter_flags iter_flags = + MAILBOX_LIST_ITER_VIRTUAL_NAMES | + MAILBOX_LIST_ITER_RETURN_NO_FLAGS; struct mail_user *user = ctx->mbox->storage->storage.user; ARRAY_TYPE(virtual_backend_box) wildcard_boxes, neg_boxes; struct mailbox_list_iterate_context *iter; @@ -283,8 +288,7 @@ static int virtual_config_expand_wildcards(struct virtual_parse_context *ctx) /* match listed mailboxes to wildcards */ iter = mailbox_list_iter_init_namespaces(user->namespaces, patterns, - MAILBOX_LIST_ITER_VIRTUAL_NAMES | - MAILBOX_LIST_ITER_RETURN_NO_FLAGS); + iter_ns_types, iter_flags); while ((info = mailbox_list_iter_next(iter)) != NULL) { /* skip non-selectable mailboxes (especially mbox directories) */