]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage, acl: Move hiding non-listable public/shared mailboxes to lib-storage
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 11 Sep 2025 19:00:30 +0000 (22:00 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Tue, 30 Sep 2025 05:48:46 +0000 (05:48 +0000)
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.

src/lib-storage/list/mailbox-list-iter.c
src/plugins/acl/acl-mailbox-list.c

index 9b99b6ccf21a2b271ec4dd4c3b40302b1a89f405..9dd25c2b8572a077ff340626fceb08fee0f74953 100644 (file)
@@ -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;
 
index bfc1b6bc794463b507ad2b34c4f94c0c3f15b76b..cb495b5bb74d3cf9091f2a54dd1c666d9839f795 100644 (file)
@@ -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. */