]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: mailbox_list_iter_next_call() - Handle subscriptions
authorMarco Bettini <marco.bettini@open-xchange.com>
Tue, 8 Jul 2025 09:25:42 +0000 (09:25 +0000)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 28 Jul 2025 18:03:03 +0000 (18:03 +0000)
directly here

src/imap/cmd-list.c
src/lib-storage/list/mailbox-list-iter.c
src/lib-storage/mailbox-list-private.h

index 7930fd0aadd0f28d759652c9b01f52b38d0ede7f..fbaff904fcb1df3c85702c8a3dfc9481cdad79ab 100644 (file)
@@ -40,15 +40,21 @@ mailbox_flags2str(struct cmd_list_context *ctx, string_t *str,
        size_t orig_len = str_len(str);
 
        if ((flags & MAILBOX_NONEXISTENT) != 0 && !ctx->used_listext) {
-               flags |= MAILBOX_NOSELECT;
                flags &= ENUM_NEGATE(MAILBOX_NONEXISTENT);
+               if (!ctx->lsub) {
+                       /* Convert to \Noselect flag in LIST reply. It can't
+                          be converted to \Noselect in LSUB reply though,
+                          because that means a child mailbox is subscribed. */
+                       flags |= MAILBOX_NOSELECT;
+               }
        }
 
        if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0)
                flags &= ENUM_NEGATE(MAILBOX_CHILDREN | MAILBOX_NOCHILDREN);
 
        if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
-           (flags & MAILBOX_SUBSCRIBED) == 0 && !ctx->used_listext) {
+           (flags & MAILBOX_SUBSCRIBED) == 0 && !ctx->used_listext &&
+           ctx->lsub) {
                /* LSUB uses \Noselect for this */
                flags |= MAILBOX_NOSELECT;
        } else if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) == 0)
index dfa1e4501a31d3d16f9b464c44b5557924161a12..a72d7bf805e54885f856a6cedb9ecbb28520b4f8 100644 (file)
@@ -157,6 +157,21 @@ mailbox_list_iter_init_multiple(struct mailbox_list *list,
        }
 
        ctx = list->v.iter_init(list, patterns, flags);
+       if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
+                     MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) {
+               char sep = mail_namespace_get_sep(list->ns);
+               ctx->subscriptions = mailbox_tree_init(sep);
+               mailbox_list_subscriptions_fill(ctx, ctx->subscriptions, FALSE);
+
+               struct mailbox_tree_iterate_context *iter =
+                       mailbox_tree_iterate_init(ctx->subscriptions, NULL, 0);
+               const char *name ATTR_UNUSED;
+               struct mailbox_node *node;
+               while ((node = mailbox_tree_iterate_next(iter, &name)) != NULL)
+                       node->flags |= MAILBOX_NONEXISTENT;
+               mailbox_tree_iterate_deinit(&iter);
+       }
+
        if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) {
                if (mailbox_list_iter_init_autocreate(ctx) < 0) {
                        hash_table_destroy(&ctx->autocreate_ctx->duplicate_vnames);
@@ -999,6 +1014,88 @@ static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
        return FALSE;
 }
 
+static void mailbox_list_mark_node_visited(struct mailbox_node *node)
+{
+       if (node == NULL)
+               return;
+
+       node->flags &= ENUM_NEGATE(MAILBOX_MATCHED|MAILBOX_NONEXISTENT);
+       while (node->parent != NULL) {
+               node = node->parent;
+               node->flags &= ENUM_NEGATE(MAILBOX_MATCHED);
+       }
+}
+
+static bool
+mailbox_list_match_subscriptions(struct mailbox_list_iterate_context *ctx,
+                                const struct mailbox_info **info)
+{
+       if (ctx->subscriptions == NULL)
+               return TRUE;
+
+       struct mailbox_node *node =
+               mailbox_tree_lookup(ctx->subscriptions, (*info)->vname);
+       enum mailbox_info_flags subs_flags = node == NULL ? 0 :
+               node->flags & MAILBOX_SUBSCRIBED;
+       if (subs_flags == 0) {
+               if (HAS_ANY_BITS((*info)->flags, MAILBOX_SUBSCRIBED |
+                                                MAILBOX_CHILD_SUBSCRIBED)) {
+                       /* auto-subscribed mailbox */
+                       mailbox_list_mark_node_visited(node);
+                       return TRUE;
+               }
+               if (node != NULL)
+                       node->flags |= (*info)->flags;
+               if (HAS_NO_BITS(ctx->flags, MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) {
+                       mailbox_list_mark_node_visited(node);
+                       return TRUE;
+               }
+               else
+                       return FALSE;
+       }
+
+       ctx->subscriptions_info = **info;
+       ctx->subscriptions_info.flags &= ENUM_NEGATE(MAILBOX_SUBSCRIBED |
+                                                    MAILBOX_CHILD_SUBSCRIBED);
+       ctx->subscriptions_info.flags |= subs_flags;
+       *info = &ctx->subscriptions_info;
+
+       mailbox_list_mark_node_visited(node);
+       return TRUE;
+}
+
+static const struct mailbox_info *
+mailbox_list_finish_subscriptions(struct mailbox_list_iterate_context *ctx)
+{
+       if (ctx->subscriptions == NULL ||
+           HAS_NO_BITS(ctx->flags, MAILBOX_LIST_ITER_SELECT_SUBSCRIBED))
+               return NULL;
+
+       if (ctx->subscriptions_iter == NULL)
+               ctx->subscriptions_iter = mailbox_tree_iterate_init(
+                       ctx->subscriptions, NULL, MAILBOX_MATCHED|MAILBOX_SUBSCRIBED);
+
+       struct mailbox_node *node;
+       const char *vname;
+       while ((node = mailbox_tree_iterate_next(ctx->subscriptions_iter, &vname)) == NULL) {
+               if (ctx->subscriptions_children)
+                       return NULL;
+
+               ctx->subscriptions_children = TRUE;
+               mailbox_tree_iterate_deinit(&ctx->subscriptions_iter);
+               ctx->subscriptions_iter = mailbox_tree_iterate_init(
+                       ctx->subscriptions, NULL, MAILBOX_MATCHED|MAILBOX_CHILD_SUBSCRIBED);
+       }
+
+       i_zero(&ctx->subscriptions_info);
+       ctx->subscriptions_info.ns = ctx->list->ns;
+       ctx->subscriptions_info.vname = vname;
+       ctx->subscriptions_info.flags = node->flags;
+
+       mailbox_list_mark_node_visited(node);
+       return &ctx->subscriptions_info;
+}
+
 static const struct mailbox_info *
 mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx)
 {
@@ -1006,9 +1103,13 @@ mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx)
        const struct mailbox_settings *set;
        int ret;
 
-       info = ctx->list->v.iter_next(ctx);
+       while ((info = ctx->list->v.iter_next(ctx)) != NULL)
+               if (mailbox_list_match_subscriptions(ctx, &info))
+                       break;
+
        if (info == NULL)
-               return NULL;
+               if ((info = mailbox_list_finish_subscriptions(ctx)) == NULL)
+                       return NULL;
 
        ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
        if ((ctx->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) {
@@ -1112,6 +1213,8 @@ int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
        if (ctx->autocreate_ctx != NULL)
                hash_table_destroy(&ctx->autocreate_ctx->duplicate_vnames);
        i_free(ctx->specialuse_info_flags);
+       mailbox_tree_iterate_deinit(&ctx->subscriptions_iter);
+       mailbox_tree_deinit(&ctx->subscriptions);
        return ctx->list->v.iter_deinit(ctx);
 }
 
index 8316afeca7617dcb615761656d831ce418e32438..2c7fdfef42c0fbbf0415a0070544ca988f8fd9aa 100644 (file)
@@ -170,6 +170,10 @@ struct mailbox_list_iterate_context {
 
        ARRAY(union mailbox_list_iterate_module_context *) module_contexts;
        HASH_TABLE(const char *, void*) found_mailboxes;
+       struct mailbox_tree_context *subscriptions;
+       struct mailbox_tree_iterate_context *subscriptions_iter;
+       struct mailbox_info subscriptions_info;
+       bool subscriptions_children;
 };
 
 struct mailbox_list_iter_update_context {