From: Timo Sirainen Date: Thu, 11 Sep 2025 19:00:30 +0000 (+0300) Subject: lib-storage, acl: Move hiding non-listable public/shared mailboxes to lib-storage X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=05340c977ddbc9b29ad27c3d3ae458b0ce6c96f3;p=thirdparty%2Fdovecot%2Fcore.git lib-storage, acl: Move hiding non-listable public/shared mailboxes to lib-storage The subscriptions handling is now in lib-storage code, and ACL code doesn't know about it. Change this so that if public/shared namespace has subscriptions=yes (i.e. shared across all users), any nonexistent mailboxes' subscriptions are hidden. This assumes that the user doesn't have enough ACL rights to list the mailboxes, so they also don't want subscriptions for them. --- diff --git a/src/lib-storage/list/mailbox-list-iter.c b/src/lib-storage/list/mailbox-list-iter.c index 9b99b6ccf2..9dd25c2b85 100644 --- a/src/lib-storage/list/mailbox-list-iter.c +++ b/src/lib-storage/list/mailbox-list-iter.c @@ -182,6 +182,40 @@ mailbox_list_iter_init_multiple(struct mailbox_list *list, return ctx; } +static bool node_has_existing_subscription(enum mailbox_info_flags node_flags) +{ + if ((node_flags & MAILBOX_NONEXISTENT) == 0 && + (node_flags & MAILBOX_SUBSCRIBED) != 0) + return TRUE; + if ((node_flags & MAILBOX_CHILD_SUBSCRIBED) != 0 && + (node_flags & MAILBOX_CHILDREN) != 0) { + /* When listing e.g. public/% but only public/foo/bar + is subscribed, we need to list public/foo. However, since + public/foo/bar doesn't match the list glob, the mailbox node + doesn't exist in the tree. We can instead rely on the + MAILBOX_CHILDREN flag to know whether there are visible + children. */ + return TRUE; + } + return FALSE; +} + +static bool +mailbox_list_want_subscription(struct mail_namespace *ns, + enum mailbox_info_flags node_flags) +{ + if (ns->type != MAIL_NAMESPACE_TYPE_PRIVATE && + (ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) { + /* Non-private namespace with subscriptions=yes. This could be + a site-global subscriptions file, so hide subscriptions for + mailboxes the user doesn't have ACLs to see. This actually + hides all mailboxes that are nonexistent, so the assumption + is that the nonexistence is due to ACLs. */ + return node_has_existing_subscription(node_flags); + } + return TRUE; +} + static bool ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns) { @@ -1078,7 +1112,14 @@ mailbox_list_finish_subscriptions(struct mailbox_list_iterate_context *ctx) struct mailbox_node *node; const char *vname; - while ((node = mailbox_tree_iterate_next(ctx->subscriptions_iter, &vname)) == NULL) { + for (;;) { + node = mailbox_tree_iterate_next(ctx->subscriptions_iter, &vname); + if (node != NULL) { + if (!mailbox_list_want_subscription(ctx->list->ns, node->flags)) + continue; + break; + } + if (ctx->subscriptions_children) return NULL; diff --git a/src/plugins/acl/acl-mailbox-list.c b/src/plugins/acl/acl-mailbox-list.c index bfc1b6bc79..cb495b5bb7 100644 --- a/src/plugins/acl/acl-mailbox-list.c +++ b/src/plugins/acl/acl-mailbox-list.c @@ -20,7 +20,6 @@ struct acl_mailbox_list_iterate_context { struct mailbox_info info; char sep; - bool hide_nonlistable_subscriptions:1; bool simple_star_glob:1; bool autocreate_acls_checked:1; }; @@ -172,14 +171,6 @@ acl_mailbox_list_iter_init(struct mailbox_list *list, ctx = p_new(_ctx->pool, struct acl_mailbox_list_iterate_context, 1); - if (list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE && - (list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0) { - /* non-private namespace with subscriptions=yes. this could be - a site-global subscriptions file, so hide subscriptions for - mailboxes the user doesn't see. */ - ctx->hide_nonlistable_subscriptions = TRUE; - } - ctx->sep = mail_namespace_get_sep(list->ns); /* see if all patterns have only a single '*' and it's at the end. we can use it to do some optimizations. */ @@ -270,7 +261,7 @@ iter_is_listing_all_children(struct mailbox_list_iterate_context *_ctx) static bool iter_mailbox_has_visible_children(struct mailbox_list_iterate_context *_ctx, - bool only_nonpatterns, bool subscribed) + bool only_nonpatterns) { struct acl_mailbox_list_iterate_context *ctx = ACL_LIST_ITERATE_CONTEXT(_ctx); @@ -316,8 +307,6 @@ iter_mailbox_has_visible_children(struct mailbox_list_iterate_context *_ctx, } iter = mailbox_list_iter_init(_ctx->list, str_c(pattern), - (!subscribed ? 0 : - MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (only_nonpatterns && @@ -352,8 +341,7 @@ acl_mailbox_list_info_is_visible(struct mailbox_list_iterate_context *_ctx) } if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 && - (_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && - !ctx->hide_nonlistable_subscriptions) { + (_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0) { /* don't waste time doing an ACL check. we're going to list all subscriptions anyway. */ info->flags &= MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED; @@ -369,7 +357,7 @@ acl_mailbox_list_info_is_visible(struct mailbox_list_iterate_context *_ctx) children, but also don't return incorrect flags */ info->flags &= ENUM_NEGATE(MAILBOX_CHILDREN); } else if ((info->flags & MAILBOX_CHILDREN) != 0 && - !iter_mailbox_has_visible_children(_ctx, FALSE, FALSE)) { + !iter_mailbox_has_visible_children(_ctx, FALSE)) { info->flags &= ENUM_NEGATE(MAILBOX_CHILDREN); info->flags |= MAILBOX_NOCHILDREN; } @@ -384,22 +372,11 @@ acl_mailbox_list_info_is_visible(struct mailbox_list_iterate_context *_ctx) i_assert((info->flags & PRESERVE_MAILBOX_FLAGS) != 0); info->flags = MAILBOX_NONEXISTENT | (info->flags & PRESERVE_MAILBOX_FLAGS); - if (ctx->hide_nonlistable_subscriptions) { - /* global subscriptions file. hide this entry if there - are no visible subscribed children or if we're going - to list the subscribed children anyway. */ - if ((info->flags & MAILBOX_CHILD_SUBSCRIBED) == 0) - return 0; - if (iter_is_listing_all_children(_ctx) || - !iter_mailbox_has_visible_children(_ctx, TRUE, TRUE)) - return 0; - /* e.g. LSUB "" % with visible subscribed children */ - } return 1; } if (!iter_is_listing_all_children(_ctx) && - iter_mailbox_has_visible_children(_ctx, TRUE, FALSE)) { + iter_mailbox_has_visible_children(_ctx, TRUE)) { /* no child mailboxes match the list pattern(s), but mailbox has visible children. we'll need to show this as nonexistent. */