Mercurial > dovecot > core-2.2
view src/lib-storage/mailbox-list-iter.c @ 14133:ba770cba5598
Updated copyright notices to include year 2012.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 12 Feb 2012 18:55:28 +0200 |
parents | f5353573d3a0 |
children | bbe6b6c2ee99 |
line wrap: on
line source
/* Copyright (c) 2006-2012 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "imap-match.h" #include "mail-storage.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; const struct mailbox_settings *set; 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; struct mail_namespace *namespaces; pool_t pool; const char **patterns, **patterns_ns_match; enum namespace_type type_mask; struct mail_namespace *iter_namespaces; struct mailbox_info ns_info; }; static bool ns_match_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *pattern); struct mailbox_list_iterate_context * mailbox_list_iter_init(struct mailbox_list *list, const char *pattern, enum mailbox_list_iter_flags flags) { const char *patterns[2]; patterns[0] = pattern; patterns[1] = NULL; return mailbox_list_iter_init_multiple(list, patterns, flags); } static int mailbox_list_subscriptions_refresh(struct mailbox_list *list) { struct mail_namespace *ns = list->ns; if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) { /* no subscriptions in this namespace. find where they are. */ ns = mail_namespace_find_subscribable(ns->user->namespaces, ns->prefix); if (ns == NULL) { /* no subscriptions. avoid crashes by initializing a subscriptions tree. */ if (list->subscriptions == NULL) { char sep = mail_namespace_get_sep(list->ns); list->subscriptions = mailbox_tree_init(sep); } return 0; } } return ns->list->v.subscriptions_refresh(ns->list, list); } static struct mailbox_settings * mailbox_settings_add_ns_prefix(pool_t pool, struct mail_namespace *ns, struct mailbox_settings *in_set) { struct mailbox_settings *out_set; if (ns->prefix_len == 0 || strcasecmp(in_set->name, "INBOX") == 0) return in_set; out_set = p_new(pool, struct mailbox_settings, 1); *out_set = *in_set; if (*in_set->name == '\0') { /* namespace prefix itself */ out_set->name = p_strndup(pool, ns->prefix, ns->prefix_len-1); } else { out_set->name = p_strconcat(pool, ns->prefix, in_set->name, NULL); } return out_set; } static void mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->list->ns; struct mailbox_list_autocreate_iterate_context *actx; struct mailbox_settings *const *box_sets, *set; struct autocreate_box *autobox; unsigned int i, count; if (!array_is_created(&ns->set->mailboxes)) return; box_sets = array_get(&ns->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; set = mailbox_settings_add_ns_prefix(ctx->pool, ns, box_sets[i]); /* autocreate mailbox belongs to listed namespace */ array_append(&actx->all_ns_box_sets, &set, 1); if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0) { array_append(&actx->box_sets, &set, 1); autobox = array_append_space(&actx->boxes); autobox->name = set->name; autobox->set = set; } } } struct mailbox_list_iterate_context * mailbox_list_iter_init_multiple(struct mailbox_list *list, const char *const *patterns, enum mailbox_list_iter_flags flags) { struct mailbox_list_iterate_context *ctx; int ret = 0; i_assert(*patterns != NULL); if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) ret = mailbox_list_subscriptions_refresh(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; } 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_inbox(struct mail_namespace *ns, const char *pattern) { struct imap_match_glob *glob; if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) return FALSE; glob = imap_match_init(pool_datastack_create(), pattern, TRUE, mail_namespace_get_sep(ns)); return imap_match(glob, "INBOX") == IMAP_MATCH_YES; } static bool ns_is_match_within_ns(struct ns_list_iterate_context *ctx, struct mail_namespace *ns, const char *prefix_without_sep, const char *pattern, enum imap_match_result result) { 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_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] == mail_namespace_get_sep(ns)) 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, mail_namespace_get_sep(ns)); result = imap_match(glob, prefix_without_sep); } if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) { return ns_is_match_within_ns(ctx, ns, prefix_without_sep, pattern, result); } else { switch (result) { case IMAP_MATCH_YES: case IMAP_MATCH_CHILDREN: return TRUE; 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; /* 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_inbox(ns, ctx->patterns_ns_match[i])) break; if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i])) break; } } T_END; return ctx->patterns_ns_match[i] != NULL; } static struct mail_namespace * ns_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { for (; ns != NULL; ns = ns->next) { if (ns_match(ctx, ns)) break; } return ns; } static bool iter_next_try_prefix_pattern(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; i_assert(ns->prefix_len > 0); 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, ns->prefix_len-1); glob = imap_match_init(pool_datastack_create(), pattern, TRUE, mail_namespace_get_sep(ns)); result = imap_match(glob, prefix_without_sep); return result == IMAP_MATCH_YES && ns_is_match_within_ns(ctx, ns, prefix_without_sep, pattern, result); } static bool iter_next_try_prefix(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { unsigned int i; bool ret = FALSE; for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) { T_BEGIN { ret = iter_next_try_prefix_pattern(ctx, ns, ctx->patterns_ns_match[i]); } T_END; if (ret) break; } return ret; } static const struct mailbox_info * mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx) { struct ns_list_iterate_context *ctx = (struct ns_list_iterate_context *)_ctx; const struct mailbox_info *info; while (ctx->iter_namespaces != NULL) { struct mail_namespace *ns = ctx->iter_namespaces; ctx->iter_namespaces = ns->next; if (ns->prefix_len > 0 && iter_next_try_prefix(ctx, ns)) { ctx->ns_info.ns = ns; ctx->ns_info.name = p_strndup(ctx->pool, ns->prefix, ns->prefix_len-1); return &ctx->ns_info; } } 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) _ctx->failed = TRUE; ctx->ctx.list->ns = ctx->namespaces; ctx->backend_ctx = mailbox_list_iter_init_multiple(ctx->namespaces->list, ctx->patterns, _ctx->flags); ctx->namespaces = ns_next(ctx, ctx->namespaces->next); return mailbox_list_ns_iter_next(_ctx); } return info; } static int mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx) { struct ns_list_iterate_context *ctx = (struct ns_list_iterate_context *)_ctx; int ret; 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; unsigned int i, count; pool_t pool; i_assert(namespaces != NULL); 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; ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit; ctx->iter_namespaces = namespaces; count = str_array_length(patterns); ctx->patterns = p_new(pool, const char *, count + 1); 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; 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; } 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) { actx->new_info.special_use = *autobox->set->special_use == '\0' ? NULL : autobox->set->special_use; 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 * mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx) { const struct mailbox_info *info; const struct mailbox_settings *set; info = ctx->list->v.iter_next(ctx); if (info == NULL) return NULL; ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE; if ((ctx->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) { set = mailbox_settings_find(ctx->list->ns->user, info->name); if (set != NULL && *set->special_use != '\0') { ctx->specialuse_info = *info; ctx->specialuse_info.special_use = *set->special_use == '\0' ? NULL : set->special_use; info = &ctx->specialuse_info; } } return info; } 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, *autobox; unsigned int count; if (actx->idx == 0) { info = mailbox_list_iter_next_call(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) { autobox = &autoboxes[actx->idx++]; if (autocreate_iter_autobox(ctx, autobox)) return &actx->new_info; } i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets)); return NULL; } static bool special_use_selection(struct mailbox_list_iterate_context *ctx, const struct mailbox_info *info) { return (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) == 0 || info->special_use != NULL; } const struct mailbox_info * mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx) { const struct mailbox_info *info; do { if (ctx->autocreate_ctx != NULL) info = autocreate_iter_next(ctx); else info = mailbox_list_iter_next_call(ctx); } while (info != NULL && !special_use_selection(ctx, info)); return info; } int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx) { struct mailbox_list_iterate_context *ctx = *_ctx; *_ctx = NULL; return ctx->list->v.iter_deinit(ctx); } static void node_fix_parents(struct mailbox_node *node) { /* If we happened to create any of the parents, we need to mark them nonexistent. */ node = node->parent; for (; node != NULL; node = node->parent) { if ((node->flags & MAILBOX_MATCHED) == 0) node->flags |= MAILBOX_NONEXISTENT; } } static void mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx, const char *name) { struct mail_namespace *ns = ctx->iter_ctx->list->ns; struct mailbox_node *node; enum mailbox_info_flags create_flags, always_flags; enum imap_match_result match; const char *p; bool created, add_matched; create_flags = MAILBOX_NOCHILDREN; always_flags = ctx->leaf_flags; add_matched = TRUE; for (;;) { created = FALSE; match = imap_match(ctx->glob, name); if (match == IMAP_MATCH_YES) { node = ctx->update_only ? mailbox_tree_lookup(ctx->tree_ctx, name) : mailbox_tree_get(ctx->tree_ctx, name, &created); if (created) { node->flags = create_flags; if (create_flags != 0) node_fix_parents(node); } if (node != NULL) { if (!ctx->update_only && add_matched) node->flags |= MAILBOX_MATCHED; node->flags |= always_flags; } /* We don't want to show the parent mailboxes unless something else matches them, but if they are matched we want to show them having child subscriptions */ add_matched = FALSE; } else { if ((match & IMAP_MATCH_PARENT) == 0) break; /* We've a (possibly) non-subscribed parent mailbox which has a subscribed child mailbox. Make sure we return the parent mailbox. */ } if (!ctx->match_parents) break; /* see if parent matches */ p = strrchr(name, mail_namespace_get_sep(ns)); if (p == NULL) break; name = t_strdup_until(name, p); create_flags |= MAILBOX_NONEXISTENT; create_flags &= ~MAILBOX_NOCHILDREN; always_flags = MAILBOX_CHILDREN | ctx->parent_flags; } } void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx, const char *name) { T_BEGIN { mailbox_list_iter_update_real(ctx, name); } T_END; }