You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/12/07 10:39:57 UTC

[james-project] branch master updated (b943ee6e92 -> f4a708488c)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


    from b943ee6e92 JAMES-3864 Refactor vhost management
     new 8604f845a6 JAMES-3754 RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST
     new ce967cf48e JAMES-3754 Document RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST
     new 3179b3bbc6 JAMES-3754 RFC-8440 IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST
     new 4fc47a15ad JAMES-3754 Implement RFC-6154 IMAP LIST Extension for Special-Use Mailboxes
     new e988ce9a90 JAMES-3754 IMAP Fix SUBSCRIBED LIST return option
     new a399049bcb JAMES-3754 MessageManager::getMailboxPath no longer throws
     new b4cb6a1bd4 JAMES-3754 SPECIAL-USE needs to be supported as a LIST selection option too
     new f4a708488c JAMES-3754 LIST STATUS should not read mailboxes information twice

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/james/mailbox/MailboxManager.java   |   2 +
 .../org/apache/james/mailbox/MessageManager.java   |   2 +-
 .../james/mailbox/model/MailboxMetaData.java       |  12 +-
 .../james/mailbox/store/StoreMailboxManager.java   |  27 +++--
 .../org/apache/james/imap/scripts/ListPlus.test    |   7 ++
 .../apache/james/imap/scripts/ListSubscribed.test  |  10 ++
 .../org/apache/james/imap/scripts/Status.test      |   9 ++
 .../org/apache/james/imap/scripts/XList.test       |  50 ++++++++
 .../imap/api/process/DefaultMailboxTyper.java      |   1 +
 .../apache/james/imap/api/process/MailboxType.java |  25 ++--
 .../imap/decode/parser/ListCommandParser.java      |  58 ++++++++-
 .../imap/decode/parser/StatusCommandParser.java    |  32 ++---
 .../james/imap/encode/ListingEncodingUtils.java    |   6 +-
 .../james/imap/message/request/ListRequest.java    |  10 +-
 .../message/response/AbstractListingResponse.java  |   2 +
 .../james/imap/message/response/LSubResponse.java  |   5 +
 .../james/imap/message/response/ListResponse.java  |  19 ++-
 .../james/imap/message/response/XListResponse.java |   4 +
 .../james/imap/processor/DefaultProcessor.java     |   5 +-
 .../apache/james/imap/processor/ListProcessor.java | 134 ++++++++++++++++-----
 .../james/imap/processor/StatusProcessor.java      |  34 ++++--
 .../james/imap/processor/XListProcessor.java       |   9 +-
 .../james/imap/encode/ListResponseEncoderTest.java |   5 +-
 .../imap/encode/ListingEncodingUtilsTest.java      |  16 +--
 .../pages/architecture/implemented-standards.adoc  |   6 +-
 .../methods/SetMessagesCreationProcessorTest.java  |  14 ---
 .../james/jmap/draft/model/MailboxFactoryTest.java |   9 +-
 .../webadmin/routes/UserMailboxesRoutesTest.java   |   3 +-
 src/site/xdoc/protocols/imap4.xml                  |   4 +
 29 files changed, 384 insertions(+), 136 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 04/08: JAMES-3754 Implement RFC-6154 IMAP LIST Extension for Special-Use Mailboxes

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 4fc47a15ad9a05c2a7f0e0977f0f621983b660b4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Nov 24 23:36:44 2022 +0700

    JAMES-3754 Implement RFC-6154 IMAP LIST Extension for Special-Use Mailboxes
---
 .../org/apache/james/imap/scripts/XList.test       | 17 +++++++++
 .../imap/api/process/DefaultMailboxTyper.java      |  1 +
 .../apache/james/imap/api/process/MailboxType.java | 25 +++++++++-----
 .../imap/decode/parser/ListCommandParser.java      | 17 +++++++++
 .../james/imap/encode/ListingEncodingUtils.java    |  6 ++--
 .../james/imap/message/request/ListRequest.java    |  4 ++-
 .../message/response/AbstractListingResponse.java  |  2 ++
 .../james/imap/message/response/LSubResponse.java  |  5 +++
 .../james/imap/message/response/ListResponse.java  | 19 +++++++---
 .../james/imap/message/response/XListResponse.java |  4 +++
 .../james/imap/processor/DefaultProcessor.java     |  2 +-
 .../apache/james/imap/processor/ListProcessor.java | 40 ++++++++++++++--------
 .../james/imap/processor/XListProcessor.java       |  7 ++--
 .../james/imap/encode/ListResponseEncoderTest.java |  5 ++-
 .../imap/encode/ListingEncodingUtilsTest.java      | 16 ++++-----
 .../pages/architecture/implemented-standards.adoc  |  1 +
 src/site/xdoc/protocols/imap4.xml                  |  1 +
 17 files changed, 126 insertions(+), 46 deletions(-)

diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
index 57ed556f05..ba13b0da8a 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
@@ -35,3 +35,20 @@ S: \* XLIST \(\\HasNoChildren \\Spam\) \"\.\" "Spam"
 S: \* XLIST \(\\HasNoChildren \\Trash\) \"\.\" "Trash"
 }
 S: 10 OK XLIST completed.
+
+C: 13 CREATE Archive
+S: 13 OK \[MAILBOXID \(.+\)\] CREATE completed.
+C: 13 CREATE Other
+S: 13 OK \[MAILBOXID \(.+\)\] CREATE completed.
+
+C: 10 LIST "" * RETURN (SPECIAL-USE)
+SUB {
+S: \* LIST \(\\HasNoChildren\) \"\.\" "INBOX"
+S: \* LIST \(\\HasNoChildren\) \"\.\" "Other"
+S: \* LIST \(\\HasNoChildren \\Drafts\) \"\.\" "Drafts"
+S: \* LIST \(\\HasNoChildren \\Sent\) \"\.\" "Sent"
+S: \* LIST \(\\HasNoChildren \\Junk\) \"\.\" "Spam"
+S: \* LIST \(\\HasNoChildren \\Trash\) \"\.\" "Trash"
+S: \* LIST \(\\HasNoChildren \\Archive\) \"\.\" "Archive"
+}
+S: 10 OK LIST completed.
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java
index fd7d5ba3f4..8611ff5b52 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java
@@ -30,6 +30,7 @@ public class DefaultMailboxTyper implements MailboxTyper {
     private static final ImmutableMap<Role, MailboxType> ROLES_TO_MAILBOX_TYPE = ImmutableMap.of(
         Role.INBOX, MailboxType.INBOX,
         Role.SENT, MailboxType.SENT,
+        Role.ARCHIVE, MailboxType.ARCHIVE,
         Role.SPAM, MailboxType.SPAM,
         Role.DRAFTS, MailboxType.DRAFTS,
         Role.TRASH, MailboxType.TRASH);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/MailboxType.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/MailboxType.java
index 15253042e2..d9db674ab0 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/MailboxType.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/MailboxType.java
@@ -24,22 +24,29 @@ package org.apache.james.imap.api.process;
  */
 public enum MailboxType {
 
-    INBOX("\\Inbox"),
-    DRAFTS("\\Drafts"),
-    TRASH("\\Trash"),
-    SPAM("\\Spam"),
-    SENT("\\Sent"),
-    STARRED("\\Starred"),
-    ALLMAIL("\\AllMail"),
-    OTHER(null);
+    INBOX("\\Inbox", null),
+    DRAFTS("\\Drafts", "\\Drafts"),
+    TRASH("\\Trash", "\\Trash"),
+    SPAM("\\Spam", "\\Junk"),
+    SENT("\\Sent", "\\Sent"),
+    STARRED("\\Starred", "\\Flagged"),
+    ALLMAIL("\\AllMail", "\\All"),
+    ARCHIVE(null, "\\Archive"),
+    OTHER(null, null);
 
     private final String attributeName;
+    private final String rfc6154attributeName;
 
-    MailboxType(String attributeName) {
+    MailboxType(String attributeName, String rfc6154attributeName) {
         this.attributeName = attributeName;
+        this.rfc6154attributeName = rfc6154attributeName;
     }
 
     public String getAttributeName() {
         return attributeName;
     }
+
+    public String getRfc6154attributeName() {
+        return rfc6154attributeName;
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
index c913e12b4c..288f2ed285 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
@@ -134,6 +134,8 @@ public class ListCommandParser extends AbstractUidCommandParser {
         char c = request.nextWordChar();
         if (c == 'T' || c == 't') {
             return readStatus(request);
+        } else if (c == 'P' || c == 'p') {
+            return Pair.of(readSpecialUse(request), Optional.empty());
         } else {
             return Pair.of(readReturnSubscribed(request), Optional.empty());
         }
@@ -149,6 +151,21 @@ public class ListCommandParser extends AbstractUidCommandParser {
         return Pair.of(ListReturnOption.STATUS, Optional.of(StatusCommandParser.statusDataItems(request)));
     }
 
+    private ListReturnOption readSpecialUse(ImapRequestLineReader request) throws DecodingException {
+        // 'S' is already consummed
+        assertChar(request, 'P', 'p');
+        assertChar(request, 'E', 'e');
+        assertChar(request, 'C', 'c');
+        assertChar(request, 'I', 'i');
+        assertChar(request, 'A', 'a');
+        assertChar(request, 'L', 'l');
+        assertChar(request, '-', '-');
+        assertChar(request, 'U', 'u');
+        assertChar(request, 'S', 's');
+        assertChar(request, 'E', 'e');
+        return ListReturnOption.SPECIAL_USE;
+    }
+
     private ListSelectOption readSubscribed(ImapRequestLineReader request) throws DecodingException {
         consumeSubscribed(request);
         return ListSelectOption.SUBSCRIBED;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/ListingEncodingUtils.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/ListingEncodingUtils.java
index f0b22e2f24..122780802e 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/encode/ListingEncodingUtils.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/ListingEncodingUtils.java
@@ -29,7 +29,6 @@ import java.util.EnumSet;
 
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapConstants;
-import org.apache.james.imap.api.process.MailboxType;
 import org.apache.james.imap.message.response.AbstractListingResponse;
 import org.apache.james.imap.message.response.ListResponse;
 import org.apache.james.mailbox.model.MailboxMetaData;
@@ -83,7 +82,7 @@ public class ListingEncodingUtils {
 
         selectabilityAsString(response.getSelectability(), builder);
         childrenAsString(response.getChildren(), builder);
-        mailboxAttributeAsString(response.getType(), builder);
+        mailboxAttributeAsString(response.getTypeAsString(), builder);
 
         if (response instanceof ListResponse) {
             ListResponse listResponse = (ListResponse) response;
@@ -121,8 +120,7 @@ public class ListingEncodingUtils {
         }
     }
 
-    private static ImmutableList.Builder<byte[]> mailboxAttributeAsString(MailboxType type, ImmutableList.Builder<byte[]> builder) {
-        String attributeName = type.getAttributeName();
+    private static ImmutableList.Builder<byte[]> mailboxAttributeAsString(String attributeName, ImmutableList.Builder<byte[]> builder) {
         if (attributeName != null) {
             return builder.add(attributeName.getBytes(StandardCharsets.US_ASCII));
         }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
index 37045a001c..bf3e601ae1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
@@ -44,7 +44,9 @@ public class ListRequest extends AbstractImapRequest {
         // https://www.rfc-editor.org/rfc/rfc5819.html LIST STATUS
         STATUS,
         // https://www.rfc-editor.org/rfc/rfc8440.html
-        MYRIGHTS
+        MYRIGHTS,
+        // https://www.rfc-editor.org/rfc/rfc6154.html
+        SPECIAL_USE
     }
 
     private final String baseReferenceName;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/AbstractListingResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/AbstractListingResponse.java
index c50a882d32..bcf899b8d5 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/AbstractListingResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/AbstractListingResponse.java
@@ -65,6 +65,8 @@ public abstract class AbstractListingResponse {
         return type;
     }
 
+    public abstract String getTypeAsString();
+
     public MailboxMetaData.Children getChildren() {
         return children;
     }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/LSubResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/LSubResponse.java
index a4b1aa4a7f..9622a2dde4 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/LSubResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/LSubResponse.java
@@ -36,4 +36,9 @@ public final class LSubResponse extends AbstractListingResponse implements ImapR
         }
         return MailboxMetaData.Selectability.NONE;
     }
+
+    @Override
+    public String getTypeAsString() {
+        return getType().getRfc6154attributeName();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
index cc0513b76e..ba8c6f785f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ListResponse.java
@@ -48,6 +48,7 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
         private char hierarchyDelimiter;
         private boolean returnSubscribed;
         private boolean returnNonExistent;
+        private MailboxType mailboxType;
         private ImmutableSet.Builder<ChildInfo> childInfos;
 
         public Builder() {
@@ -89,6 +90,11 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
             return this;
         }
 
+        public Builder mailboxType(MailboxType mailboxType) {
+            this.mailboxType = mailboxType;
+            return this;
+        }
+
         public Builder forMetaData(MailboxMetaData mailboxMetaData) {
             return children(mailboxMetaData.inferiors())
                 .selectability(mailboxMetaData.getSelectability())
@@ -102,7 +108,8 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
                 .selectability(MailboxMetaData.Selectability.NONE)
                 .hierarchyDelimiter(MailboxConstants.DEFAULT_DELIMITER)
                 .returnSubscribed(RETURN_SUBSCRIBED)
-                .returnNonExistent(RETURN_NON_EXISTENT);
+                .returnNonExistent(RETURN_NON_EXISTENT)
+                .mailboxType(MailboxType.OTHER);
         }
 
         private EnumSet<ChildInfo> buildChildInfos() {
@@ -117,7 +124,7 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
         public ListResponse build() {
 
             return new ListResponse(children, selectability, name, hierarchyDelimiter, returnSubscribed,
-                returnNonExistent, buildChildInfos());
+                returnNonExistent, buildChildInfos(), mailboxType);
         }
     }
 
@@ -134,8 +141,8 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
 
     public ListResponse(MailboxMetaData.Children children, MailboxMetaData.Selectability selectability,
                         String name, char hierarchyDelimiter, boolean returnSubscribed, boolean returnNonExistent,
-                        EnumSet<ChildInfo> childInfos) {
-        super(children, selectability, name, hierarchyDelimiter, MailboxType.OTHER);
+                        EnumSet<ChildInfo> childInfos, MailboxType mailboxType) {
+        super(children, selectability, name, hierarchyDelimiter, mailboxType);
         this.returnSubscribed = returnSubscribed;
         this.returnNonExistent = returnNonExistent;
         this.childInfos = childInfos;
@@ -153,4 +160,8 @@ public final class ListResponse extends AbstractListingResponse implements ImapR
         return childInfos;
     }
 
+    @Override
+    public String getTypeAsString() {
+        return getType().getRfc6154attributeName();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/XListResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/XListResponse.java
index df4b392aae..268ad0e9a3 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/XListResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/XListResponse.java
@@ -32,4 +32,8 @@ public class XListResponse extends AbstractListingResponse implements ImapRespon
         super(children, selectability, name, hierarchyDelimiter, type);
     }
 
+    @Override
+    public String getTypeAsString() {
+        return getType().getAttributeName();
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
index 40d153c8c0..6913be379a 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
@@ -83,7 +83,7 @@ public class DefaultProcessor implements ImapProcessor {
         builder.add(statusProcessor);
         builder.add(new LSubProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory));
         builder.add(new XListProcessor(mailboxManager, statusResponseFactory, mailboxTyper, metricFactory, subscriptionManager));
-        builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor));
+        builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper));
         builder.add(new SearchProcessor(mailboxManager, statusResponseFactory, metricFactory));
         SelectProcessor selectProcessor = new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory);
         builder.add(selectProcessor);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index 2e69aa0d44..fd44a047ed 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -39,6 +39,7 @@ import org.apache.james.imap.api.message.response.ImapResponseMessage;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.MailboxType;
+import org.apache.james.imap.api.process.MailboxTyper;
 import org.apache.james.imap.main.PathConverter;
 import org.apache.james.imap.message.request.ListRequest;
 import org.apache.james.imap.message.response.ListResponse;
@@ -70,23 +71,29 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     public static final boolean RETURN_SUBSCRIBED = true;
     public static final boolean RETURN_NON_EXISTENT = true;
     private static final Logger LOGGER = LoggerFactory.getLogger(ListProcessor.class);
-    private static final List<Capability> CAPA = ImmutableList.of(Capability.of("LIST-EXTENDED"), Capability.of("LIST-STATUS"), Capability.of("LIST-MYRIGHTS"));
+    private static final List<Capability> CAPA = ImmutableList.of(
+        Capability.of("LIST-EXTENDED"),
+        Capability.of("LIST-STATUS"),
+        Capability.of("LIST-MYRIGHTS"),
+        Capability.of("SPECIAL-USE"));
 
     private final SubscriptionManager subscriptionManager;
     private final StatusProcessor statusProcessor;
+    protected final MailboxTyper mailboxTyper;
 
     public ListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory,
                          MetricFactory metricFactory, SubscriptionManager subscriptionManager,
-                         StatusProcessor statusProcessor) {
-        this((Class<T>) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, statusProcessor);
+                         StatusProcessor statusProcessor, MailboxTyper mailboxTyper) {
+        this((Class<T>) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper);
     }
 
     public ListProcessor(Class<T> clazz, MailboxManager mailboxManager, StatusResponseFactory factory,
                          MetricFactory metricFactory, SubscriptionManager subscriptionManager,
-                         StatusProcessor statusProcessor) {
+                         StatusProcessor statusProcessor, MailboxTyper mailboxTyper) {
         super(clazz, mailboxManager, factory, metricFactory);
         this.subscriptionManager = subscriptionManager;
         this.statusProcessor = statusProcessor;
+        this.mailboxTyper = mailboxTyper;
     }
 
     @Override
@@ -131,7 +138,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     protected ImapResponseMessage createResponse(MailboxMetaData.Children children, MailboxMetaData.Selectability selectability, String name,
                                                  char hierarchyDelimiter, MailboxType type) {
         return new ListResponse(children, selectability, name, hierarchyDelimiter, !RETURN_SUBSCRIBED,
-            !RETURN_NON_EXISTENT, EnumSet.noneOf(ListResponse.ChildInfo.class));
+            !RETURN_NON_EXISTENT, EnumSet.noneOf(ListResponse.ChildInfo.class), type);
     }
 
     private void respondNamespace(String referenceName, Responder responder, MailboxSession mailboxSession) {
@@ -197,7 +204,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                     metaData.getSelectability(),
                     mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
                     metaData.getHierarchyDelimiter(),
-                    getMailboxType(session, metaData.getPath()))))
+                    getMailboxType(request, session, metaData.getPath()))))
             .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData))
             .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
@@ -207,7 +214,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
         return Mono.zip(getMailboxManager().search(mailboxQuery, Minimal, mailboxSession).collectList()
                     .map(searchedResultList -> searchedResultList.stream().collect(Collectors.toMap(MailboxMetaData::getPath, Function.identity()))),
                 Flux.from(Throwing.supplier(() -> subscriptionManager.subscriptionsReactive(mailboxSession)).get()).collectList())
-            .map(tuple -> getListResponseForSelectSubscribed(tuple.getT1(), tuple.getT2(), request, mailboxSession, isRelative, mailboxQuery))
+            .map(tuple -> getListResponseForSelectSubscribed(session, tuple.getT1(), tuple.getT2(), request, mailboxSession, isRelative, mailboxQuery))
             .flatMapIterable(list -> list)
             .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getMiddle()))
             .doOnNext(pathAndResponse -> pathAndResponse.getRight().ifPresent(mailboxMetaData -> respondMyRights(request, responder, mailboxSession, mailboxMetaData)))
@@ -215,10 +222,10 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             .then();
     }
 
-    private List<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> getListResponseForSelectSubscribed(Map<MailboxPath, MailboxMetaData> searchedResultMap, List<MailboxPath> allSubscribedSearch,
+    private List<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> getListResponseForSelectSubscribed(ImapSession session, Map<MailboxPath, MailboxMetaData> searchedResultMap, List<MailboxPath> allSubscribedSearch,
                                                                                      ListRequest listRequest, MailboxSession mailboxSession, boolean relative, MailboxQuery mailboxQuery) {
         ImmutableList.Builder<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> responseBuilders = ImmutableList.builder();
-        List<Pair<MailboxPath, ListResponse>> listRecursiveMatch = listRecursiveMatch(searchedResultMap, allSubscribedSearch, mailboxSession, relative, listRequest);
+        List<Pair<MailboxPath, ListResponse>> listRecursiveMatch = listRecursiveMatch(session, searchedResultMap, allSubscribedSearch, mailboxSession, relative, listRequest);
 
         listRecursiveMatch.forEach(pair -> responseBuilders.add(Triple.of(pair.getLeft(), pair.getRight(), Optional.ofNullable(searchedResultMap.get(pair.getLeft())))));
         Set<MailboxPath> listRecursiveMatchPath = listRecursiveMatch.stream().map(Pair::getKey).collect(Collectors.toUnmodifiableSet());
@@ -226,24 +233,25 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
         allSubscribedSearch.stream()
             .filter(subscribed -> !listRecursiveMatchPath.contains(subscribed))
             .filter(mailboxQuery::isPathMatch)
-            .map(subscribed -> buildListResponse(searchedResultMap, mailboxSession, relative, subscribed))
+            .map(subscribed -> buildListResponse(listRequest, searchedResultMap, session, relative, subscribed))
             .forEach(pair -> responseBuilders.add(Triple.of(pair.getLeft(), pair.getRight(), Optional.ofNullable(searchedResultMap.get(pair.getLeft())))));
 
         return responseBuilders.build();
     }
 
-    private Pair<MailboxPath, ListResponse> buildListResponse(Map<MailboxPath, MailboxMetaData> searchedResultMap, MailboxSession mailboxSession, boolean relative, MailboxPath subscribed) {
+    private Pair<MailboxPath, ListResponse> buildListResponse(ListRequest listRequest, Map<MailboxPath, MailboxMetaData> searchedResultMap, ImapSession session, boolean relative, MailboxPath subscribed) {
         return Pair.of(subscribed, Optional.ofNullable(searchedResultMap.get(subscribed))
             .map(mailboxMetaData -> ListResponse.builder()
                 .returnSubscribed(RETURN_SUBSCRIBED)
                 .forMetaData(mailboxMetaData)
                 .name(mailboxName(relative, subscribed, mailboxMetaData.getHierarchyDelimiter()))
-                .returnNonExistent(!RETURN_NON_EXISTENT))
+                .returnNonExistent(!RETURN_NON_EXISTENT)
+                .mailboxType(getMailboxType(listRequest, session, mailboxMetaData.getPath())))
             .orElseGet(() -> ListResponse.builder().nonExitingSubscribedMailbox(subscribed))
             .build());
     }
 
-    private List<Pair<MailboxPath, ListResponse>> listRecursiveMatch(Map<MailboxPath, MailboxMetaData> searchedResultMap,
+    private List<Pair<MailboxPath, ListResponse>> listRecursiveMatch(ImapSession session, Map<MailboxPath, MailboxMetaData> searchedResultMap,
                                                                      List<MailboxPath> allSubscribedSearch, MailboxSession mailboxSession,
                                                                      boolean relative, ListRequest listRequest) {
         if (!listRequest.getSelectOptions().contains(RECURSIVEMATCH)) {
@@ -263,6 +271,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                     .name(mailboxName(relative, metaData.getPath(), metaData.getHierarchyDelimiter()))
                     .childInfos(ListResponse.ChildInfo.SUBSCRIBED)
                     .returnSubscribed(allSubscribedSearch.contains(pair.getKey()))
+                    .mailboxType(getMailboxType(listRequest, session, metaData.getPath()))
                     .build();
                 return Pair.of(pair.getKey(), listResponse);
             })
@@ -323,7 +332,10 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
      * @param path    mailbox's path
      * @return MailboxType value
      */
-    protected MailboxType getMailboxType(ImapSession session, MailboxPath path) {
+    protected MailboxType getMailboxType(ListRequest listRequest, ImapSession session, MailboxPath path) {
+        if (listRequest.getReturnOptions().contains(ListRequest.ListReturnOption.SPECIAL_USE)) {
+            return mailboxTyper.getMailboxType(session, path);
+        }
         return MailboxType.OTHER;
     }
 
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
index 7bab0ff87e..5ae2066be6 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
@@ -29,6 +29,7 @@ import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.MailboxType;
 import org.apache.james.imap.api.process.MailboxTyper;
+import org.apache.james.imap.message.request.ListRequest;
 import org.apache.james.imap.message.request.XListRequest;
 import org.apache.james.imap.message.response.XListResponse;
 import org.apache.james.mailbox.MailboxManager;
@@ -45,12 +46,10 @@ import com.google.common.collect.ImmutableList;
 public class XListProcessor extends ListProcessor<XListRequest> implements CapabilityImplementingProcessor {
 
     private static final List<Capability> XLIST_CAPS = ImmutableList.of(SUPPORTS_XLIST);
-    private final MailboxTyper mailboxTyper;
 
     public XListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MailboxTyper mailboxTyper,
                           MetricFactory metricFactory, SubscriptionManager subscriptionManager) {
-        super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, null);
-        this.mailboxTyper = mailboxTyper;
+        super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, null, mailboxTyper);
     }
 
     @Override
@@ -70,7 +69,7 @@ public class XListProcessor extends ListProcessor<XListRequest> implements Capab
     }
 
     @Override
-    protected MailboxType getMailboxType(ImapSession session, MailboxPath path) {
+    protected MailboxType getMailboxType(ListRequest request, ImapSession session, MailboxPath path) {
         return mailboxTyper.getMailboxType(session, path);
     }
 }
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/ListResponseEncoderTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/ListResponseEncoderTest.java
index 8cbb86f9d2..d6559d7dda 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/ListResponseEncoderTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/ListResponseEncoderTest.java
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.EnumSet;
 
+import org.apache.james.imap.api.process.MailboxType;
 import org.apache.james.imap.encode.base.ByteImapResponseWriter;
 import org.apache.james.imap.encode.base.ImapResponseComposerImpl;
 import org.apache.james.imap.message.response.ListResponse;
@@ -47,7 +48,9 @@ class ListResponseEncoderTest {
 
     @Test
     void encoderShouldIncludeListCommand() throws Exception {
-        encoder.encode(new ListResponse(MailboxMetaData.Children.HAS_CHILDREN, MailboxMetaData.Selectability.NONE, "name", '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class)), composer);
+        encoder.encode(new ListResponse(MailboxMetaData.Children.HAS_CHILDREN, MailboxMetaData.Selectability.NONE,
+            "name", '.', false,
+            false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER), composer);
         assertThat(writer.getString()).startsWith("* LIST");
     }
 }
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/ListingEncodingUtilsTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/ListingEncodingUtilsTest.java
index 1d3728f430..ab748e79a8 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/ListingEncodingUtilsTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/ListingEncodingUtilsTest.java
@@ -43,7 +43,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldWriteNilDelimiterWhenUnassigned() throws Exception {
-        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, ((char) Character.UNASSIGNED), false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, ((char) Character.UNASSIGNED), false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\HasChildren) NIL \"mailbox\"\r\n");
@@ -51,7 +51,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldWriteAnyDelimiter() throws Exception {
-        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, '#', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, '#', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\HasChildren) \"#\" \"mailbox\"\r\n");
@@ -59,7 +59,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldNotIncludeAttributeWhenNone() throws Exception {
-        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST () \".\" \"mailbox\"\r\n");
@@ -67,7 +67,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldAddHasChildrenToAttributes() throws Exception {
-        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.HAS_CHILDREN, Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\HasChildren) \".\" \"mailbox\"\r\n");
@@ -75,7 +75,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldAddHasNoChildrenToAttributes() throws Exception {
-        ListResponse input = new ListResponse(Children.HAS_NO_CHILDREN, Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.HAS_NO_CHILDREN, Selectability.NONE, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\HasNoChildren) \".\" \"mailbox\"\r\n");
@@ -83,7 +83,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldAddSeveralAttributes() throws Exception {
-        ListResponse input = new ListResponse(Children.NO_INFERIORS, Selectability.NOSELECT, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.NO_INFERIORS, Selectability.NOSELECT, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\Noselect \\Noinferiors) \".\" \"mailbox\"\r\n");
@@ -91,7 +91,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldAddMarkedAttribute() throws Exception {
-        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, Selectability.MARKED, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, Selectability.MARKED, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\Marked) \".\" \"mailbox\"\r\n");
@@ -99,7 +99,7 @@ public class ListingEncodingUtilsTest  {
 
     @Test
     void encodeShouldAddUnmarkedAttribute() throws Exception {
-        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, Selectability.UNMARKED, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class));
+        ListResponse input = new ListResponse(Children.CHILDREN_ALLOWED_BUT_UNKNOWN, Selectability.UNMARKED, nameParameter, '.', false, false, EnumSet.noneOf(ListResponse.ChildInfo.class), MailboxType.OTHER);
 
         ListingEncodingUtils.encodeListingResponse(LIST_COMMAND, composer, input);
         assertThat(writer.getString()).isEqualTo("* LIST (\\Unmarked) \".\" \"mailbox\"\r\n");
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
index 398180a13a..7ed8e6fd5a 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
@@ -71,6 +71,7 @@ The following IMAP specifications are implemented:
  - link:https://www.rfc-editor.org/rfc/rfc5258.html[RFC-5258] IMAP LIST Command Extensions
  - link:https://www.rfc-editor.org/rfc/rfc5819.html[RFC-5819] IMAP4 Extension for Returning STATUS Information in Extended LIST
  - link:https://www.rfc-editor.org/rfc/rfc8440.html[RFC-8440] IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST
+ - link:https://www.rfc-editor.org/rfc/rfc8440.html[RFC-6154] IMAP LIST Extension for Special-Use Mailboxes
 
 Partially implemented specifications:
 
diff --git a/src/site/xdoc/protocols/imap4.xml b/src/site/xdoc/protocols/imap4.xml
index 1af6934fd2..96c1be2cab 100644
--- a/src/site/xdoc/protocols/imap4.xml
+++ b/src/site/xdoc/protocols/imap4.xml
@@ -66,6 +66,7 @@
        <li>IMAP LIST Command Extensions (link:https://www.rfc-editor.org/rfc/rfc5258.html)</li>
        <li>IMAP4 Extension for Returning STATUS Information in Extended LIST (https://www.rfc-editor.org/rfc/rfc5819.html)</li>
        <li>IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST (https://www.rfc-editor.org/rfc/rfc8440.html)</li>
+       <li>IMAP LIST Extension for Special-Use Mailboxes (https://www.rfc-editor.org/rfc/rfc6154.html)</li>
      </ul>
      <p>We follow RFC2683 recommendations for our implementations:</p>
      <ul>


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 08/08: JAMES-3754 LIST STATUS should not read mailboxes information twice

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit f4a708488c9b3c3a3672912ac2d5ce0c24a80a57
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 5 18:44:11 2022 +0700

    JAMES-3754 LIST STATUS should not read mailboxes information twice
---
 .../org/apache/james/mailbox/MailboxManager.java   |  2 ++
 .../james/mailbox/model/MailboxMetaData.java       | 12 +++++++---
 .../james/mailbox/store/StoreMailboxManager.java   | 27 ++++++++++++----------
 .../apache/james/imap/processor/ListProcessor.java | 23 ++++++++++++++++--
 .../james/imap/processor/StatusProcessor.java      | 24 +++++++++++--------
 .../james/jmap/draft/model/MailboxFactoryTest.java |  9 ++++----
 .../webadmin/routes/UserMailboxesRoutesTest.java   |  3 ++-
 7 files changed, 68 insertions(+), 32 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
index 1f1552ca12..8fc1cd8241 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
@@ -148,6 +148,8 @@ public interface MailboxManager extends RequestAware, RightManager, MailboxAnnot
 
     Publisher<MessageManager> getMailboxReactive(MailboxPath mailboxPath, MailboxSession session);
 
+    MessageManager getMailbox(Mailbox mailbox, MailboxSession session) throws MailboxException;
+
     /**
      * Creates a new mailbox. Any intermediary mailboxes missing from the
      * hierarchy should be created.
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxMetaData.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxMetaData.java
index dd78368bbc..beb5bba8ba 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxMetaData.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxMetaData.java
@@ -64,6 +64,7 @@ public class MailboxMetaData implements Comparable<MailboxMetaData> {
         .<MailboxMetaData, Boolean>comparing(metadata -> metadata.getPath().isInbox()).reversed()
         .thenComparing(metadata -> metadata.getPath().getName());
 
+    private final Mailbox mailbox;
     private final MailboxPath path;
     private final char delimiter;
     private final Children inferiors;
@@ -72,9 +73,10 @@ public class MailboxMetaData implements Comparable<MailboxMetaData> {
     private final MailboxACL resolvedAcls;
     private final MailboxCounters counters;
 
-    public MailboxMetaData(MailboxPath path, MailboxId mailboxId, char delimiter, Children inferiors, Selectability selectability, MailboxACL resolvedAcls, MailboxCounters counters) {
-        this.path = path;
-        this.mailboxId = mailboxId;
+    public MailboxMetaData(Mailbox mailbox, char delimiter, Children inferiors, Selectability selectability, MailboxACL resolvedAcls, MailboxCounters counters) {
+        this.mailbox = mailbox;
+        this.mailboxId = mailbox.getMailboxId();
+        this.path = mailbox.generateAssociatedPath();
         this.delimiter = delimiter;
         this.inferiors = inferiors;
         this.selectability = selectability;
@@ -127,6 +129,10 @@ public class MailboxMetaData implements Comparable<MailboxMetaData> {
         return mailboxId;
     }
 
+    public Mailbox getMailbox() {
+        return mailbox;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index 01eec15d01..9577ac410a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -284,22 +284,26 @@ public class StoreMailboxManager implements MailboxManager {
         MailboxMapper mapper = mailboxSessionMapperFactory.getMailboxMapper(session);
 
         return mapper.findMailboxByPath(mailboxPath)
-            .map(Throwing.<Mailbox, MessageManager>function(mailboxRow -> {
-                if (!assertUserHasAccessTo(mailboxRow, session)) {
-                    LOGGER.info("Mailbox '{}' does not belong to user '{}' but to '{}'", mailboxPath, session.getUser(), mailboxRow.getUser());
-                    throw new MailboxNotFoundException(mailboxPath);
-                }
-
-                LOGGER.debug("Loaded mailbox {}", mailboxPath);
-
-                return createMessageManager(mailboxRow, session);
-            }).sneakyThrow())
+            .map(Throwing.<Mailbox, MessageManager>function(mailboxRow -> getMailbox(mailboxRow, session)).sneakyThrow())
             .switchIfEmpty(Mono.fromCallable(() -> {
                 LOGGER.debug("Mailbox '{}' not found.", mailboxPath);
                 throw new MailboxNotFoundException(mailboxPath);
             }));
     }
 
+    @Override
+    public MessageManager getMailbox(Mailbox mailboxRow, MailboxSession session) throws MailboxException {
+        MailboxPath mailboxPath = mailboxRow.generateAssociatedPath();
+        if (!assertUserHasAccessTo(mailboxRow, session)) {
+            LOGGER.info("Mailbox '{}' does not belong to user '{}' but to '{}'", mailboxPath, session.getUser(), mailboxRow.getUser());
+            throw new MailboxNotFoundException(mailboxPath);
+        }
+
+        LOGGER.debug("Loaded mailbox {}", mailboxPath);
+
+        return createMessageManager(mailboxRow, session);
+    }
+
     @Override
     public MessageManager getMailbox(MailboxId mailboxId, MailboxSession session) throws MailboxException {
         return block(getMailboxReactive(mailboxId, session));
@@ -837,8 +841,7 @@ public class StoreMailboxManager implements MailboxManager {
 
     private MailboxMetaData toMailboxMetadata(MailboxSession session, Map<MailboxPath, Boolean> parentMap, Mailbox mailbox, MailboxCounters counters) throws UnsupportedRightException {
         return new MailboxMetaData(
-            mailbox.generateAssociatedPath(),
-            mailbox.getMailboxId(),
+            mailbox,
             getDelimiter(),
             computeChildren(parentMap, mailbox),
             Selectability.NONE,
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index 8791fbcab8..559529b9ba 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -45,9 +45,11 @@ import org.apache.james.imap.api.process.MailboxTyper;
 import org.apache.james.imap.main.PathConverter;
 import org.apache.james.imap.message.request.ListRequest;
 import org.apache.james.imap.message.response.ListResponse;
+import org.apache.james.imap.message.response.MailboxStatusResponse;
 import org.apache.james.imap.message.response.MyRightsResponse;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.SubscriptionManager;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxACL;
@@ -221,10 +223,18 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                 }
             })
             .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData))
-            .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
+            .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(retrieveMessageManager(metaData, mailboxSession), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
     }
 
+    private MessageManager retrieveMessageManager(MailboxMetaData metaData, MailboxSession mailboxSession) {
+        try {
+            return getMailboxManager().getMailbox(metaData.getMailbox(), mailboxSession);
+        } catch (MailboxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     private Mono<Void> processWithSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
         return Mono.zip(getMailboxManager().search(mailboxQuery, Minimal, mailboxSession).collectList()
                     .map(searchedResultList -> searchedResultList.stream().collect(Collectors.toMap(MailboxMetaData::getPath, Function.identity()))),
@@ -233,10 +243,19 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             .flatMapIterable(list -> list)
             .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getMiddle()))
             .doOnNext(pathAndResponse -> pathAndResponse.getRight().ifPresent(mailboxMetaData -> respondMyRights(request, responder, mailboxSession, mailboxMetaData)))
-            .flatMap(pathAndResponse -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(pathAndResponse.getLeft(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
+            .flatMap(pathAndResponse -> sendStatusWhenSubscribed(session, request, responder, mailboxSession, pathAndResponse))
             .then();
     }
 
+    private Mono<MailboxStatusResponse> sendStatusWhenSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession,
+                                                                 Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>> pathAndResponse) {
+        return pathAndResponse.getRight()
+            .map(metaData -> retrieveMessageManager(metaData, mailboxSession))
+            .flatMap(messageManager -> request.getStatusDataItems()
+                .map(statusDataItems -> statusProcessor.sendStatus(messageManager, statusDataItems, responder, session, mailboxSession)))
+            .orElse(Mono.empty());
+    }
+
     private List<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> getListResponseForSelectSubscribed(ImapSession session, Map<MailboxPath, MailboxMetaData> searchedResultMap, List<MailboxPath> allSubscribedSearch,
                                                                                      ListRequest listRequest, MailboxSession mailboxSession, boolean relative, MailboxQuery mailboxQuery) {
         ImmutableList.Builder<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> responseBuilders = ImmutableList.builder();
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
index 541f84f421..1b513bd1a7 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
@@ -107,17 +107,21 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
             .then();
     }
 
-    Mono<MailboxStatusResponse> sendStatus(MailboxPath mailboxPath, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) {
+    private Mono<MailboxStatusResponse> sendStatus(MailboxPath mailboxPath, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) {
         return Mono.from(getMailboxManager().getMailboxReactive(mailboxPath, mailboxSession))
-            .flatMap(mailbox -> retrieveMetadata(mailbox, statusDataItems, mailboxSession)
-                .flatMap(metaData -> computeStatusResponse(mailbox, statusDataItems, metaData, mailboxSession)
-                    .doOnNext(response -> {
-                        // Enable CONDSTORE as this is a CONDSTORE enabling command
-                        if (response.getHighestModSeq() != null) {
-                            condstoreEnablingCommand(session, responder, metaData, false);
-                        }
-                        responder.respond(response);
-                    })));
+            .flatMap(mailbox -> sendStatus(mailbox, statusDataItems, responder, session, mailboxSession));
+    }
+
+    Mono<MailboxStatusResponse> sendStatus(MessageManager mailbox, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) {
+        return retrieveMetadata(mailbox, statusDataItems, mailboxSession)
+            .flatMap(metaData -> computeStatusResponse(mailbox, statusDataItems, metaData, mailboxSession)
+                .doOnNext(response -> {
+                    // Enable CONDSTORE as this is a CONDSTORE enabling command
+                    if (response.getHighestModSeq() != null) {
+                        condstoreEnablingCommand(session, responder, metaData, false);
+                    }
+                    responder.respond(response);
+                }));
     }
 
     private Mono<Void> logInitialRequest(MailboxPath mailboxPath) {
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java
index 41f25eeb47..2d7edee171 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/MailboxFactoryTest.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.quota.QuotaRootResolver;
@@ -179,8 +180,9 @@ public class MailboxFactoryTest {
 
         mailboxManager.createMailbox(mailboxPath, mailboxSession);
 
+        org.apache.james.mailbox.model.Mailbox mailbox = new org.apache.james.mailbox.model.Mailbox(parentMailboxPath, UidValidity.of(34), parentId);
         Optional<MailboxId> id = sut.getParentIdFromMailboxPath(mailboxPath,
-            Optional.of(ImmutableMap.of(parentMailboxPath, new MailboxMetaData(parentMailboxPath, parentId, DELIMITER,
+            Optional.of(ImmutableMap.of(parentMailboxPath, new MailboxMetaData(mailbox, DELIMITER,
                 MailboxMetaData.Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, new MailboxACL(),
                 MailboxCounters.empty(parentId)))),
             mailboxSession).block();
@@ -205,16 +207,15 @@ public class MailboxFactoryTest {
     @Test
     public void buildShouldRelyOnPreloadedMailboxes() throws Exception {
         MailboxPath inbox = MailboxPath.inbox(user);
-        Optional<MailboxId> inboxId = mailboxManager.createMailbox(inbox, mailboxSession);
         Optional<MailboxId> otherId = mailboxManager.createMailbox(inbox.child("child", '.'), mailboxSession);
 
         InMemoryId preLoadedId = InMemoryId.of(45);
+        final org.apache.james.mailbox.model.Mailbox mailbox = new org.apache.james.mailbox.model.Mailbox(inbox, UidValidity.of(34), preLoadedId);
         Mailbox retrievedMailbox = sut.builder()
             .id(otherId.get())
             .session(mailboxSession)
             .usingPreloadedMailboxesMetadata(Optional.of(ImmutableMap.of(inbox, new MailboxMetaData(
-                inbox,
-                preLoadedId,
+                mailbox,
                 DELIMITER,
                 MailboxMetaData.Children.NO_INFERIORS,
                 MailboxMetaData.Selectability.NONE,
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
index 9b157ad7a8..9a818bc828 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/UserMailboxesRoutesTest.java
@@ -86,6 +86,7 @@ import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.ThreadId;
+import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.opensearch.IndexAttachments;
@@ -140,7 +141,7 @@ class UserMailboxesRoutesTest {
     private static final Logger LOGGER = LoggerFactory.getLogger(UserMailboxesRoutesTest.class);
 
     public static MailboxMetaData testMetadata(MailboxPath path, MailboxId mailboxId, char delimiter) {
-        return new MailboxMetaData(path, mailboxId, delimiter, MailboxMetaData.Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, new MailboxACL(),
+        return new MailboxMetaData(new Mailbox(path, UidValidity.of(45), mailboxId), delimiter, MailboxMetaData.Children.CHILDREN_ALLOWED_BUT_UNKNOWN, MailboxMetaData.Selectability.NONE, new MailboxACL(),
             MailboxCounters.empty(mailboxId));
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 03/08: JAMES-3754 RFC-8440 IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 3179b3bbc6eb36b60fb929c7eaf990139ac14fe4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Nov 24 18:11:34 2022 +0700

    JAMES-3754 RFC-8440 IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST
---
 .../org/apache/james/imap/scripts/ListPlus.test    |  7 +++++
 .../imap/decode/parser/ListCommandParser.java      | 15 ++++++++++
 .../james/imap/message/request/ListRequest.java    |  4 ++-
 .../apache/james/imap/processor/ListProcessor.java | 33 ++++++++++++++++++----
 .../pages/architecture/implemented-standards.adoc  |  1 +
 src/site/xdoc/protocols/imap4.xml                  |  1 +
 6 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListPlus.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListPlus.test
index cda476cbf0..2265856c5a 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListPlus.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListPlus.test
@@ -49,6 +49,13 @@ S: \* LIST \(\\HasChildren\) \"\.\" \"funny\"
 }
 S: a1 OK LIST completed.
 
+C: a1 LIST "" "listtest" RETURN (MYRIGHTS)
+SUB {
+S: \* LIST \(\\HasChildren\) \"\.\" "listtest"
+S: \* MYRIGHTS "listtest" "aeiklprstwx"
+}
+S: a1 OK LIST completed.
+
 C: a2 list "" funny.%
 SUB {
 S: \* LIST \(\\HasChildren\) \"\.\" \"funny.name with gaps\"
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
index 95d23e1cd9..c913e12b4c 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
@@ -229,6 +229,9 @@ public class ListCommandParser extends AbstractUidCommandParser {
         if (c == 's' || c == 'S') {
             return readS(request);
         }
+        if (c == 'm' || c == 'M') {
+            return Pair.of(readMyRight(request), Optional.empty());
+        }
         throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS,
             "Unknown return option: '" + request.consumeWord(ImapRequestLineReader.NoopCharValidator.INSTANCE) + "'");
     }
@@ -245,6 +248,18 @@ public class ListCommandParser extends AbstractUidCommandParser {
         return ListReturnOption.CHILDREN;
     }
 
+    private ListReturnOption readMyRight(ImapRequestLineReader request) throws DecodingException {
+        assertChar(request, 'M', 'm');
+        assertChar(request, 'Y', 'y');
+        assertChar(request, 'R', 'r');
+        assertChar(request, 'I', 'i');
+        assertChar(request, 'G', 'g');
+        assertChar(request, 'H', 'h');
+        assertChar(request, 'T', 't');
+        assertChar(request, 'S', 's');
+        return ListReturnOption.MYRIGHTS;
+    }
+
     private ListReturnOption readReturnSubscribed(ImapRequestLineReader request) throws DecodingException {
         consumeSubscribed(request);
         return ListReturnOption.SUBSCRIBED;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
index fb1e18e794..37045a001c 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
@@ -42,7 +42,9 @@ public class ListRequest extends AbstractImapRequest {
         CHILDREN,
         SUBSCRIBED,
         // https://www.rfc-editor.org/rfc/rfc5819.html LIST STATUS
-        STATUS
+        STATUS,
+        // https://www.rfc-editor.org/rfc/rfc8440.html
+        MYRIGHTS
     }
 
     private final String baseReferenceName;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index 9ed9551e89..2e69aa0d44 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -31,6 +31,7 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.display.ModifiedUtf7;
 import org.apache.james.imap.api.message.Capability;
@@ -41,10 +42,12 @@ import org.apache.james.imap.api.process.MailboxType;
 import org.apache.james.imap.main.PathConverter;
 import org.apache.james.imap.message.request.ListRequest;
 import org.apache.james.imap.message.response.ListResponse;
+import org.apache.james.imap.message.response.MyRightsResponse;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.SubscriptionManager;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.MailboxPath;
@@ -67,7 +70,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     public static final boolean RETURN_SUBSCRIBED = true;
     public static final boolean RETURN_NON_EXISTENT = true;
     private static final Logger LOGGER = LoggerFactory.getLogger(ListProcessor.class);
-    private static final List<Capability> CAPA = ImmutableList.of(Capability.of("LIST-EXTENDED"), Capability.of("LIST-STATUS"));
+    private static final List<Capability> CAPA = ImmutableList.of(Capability.of("LIST-EXTENDED"), Capability.of("LIST-STATUS"), Capability.of("LIST-MYRIGHTS"));
 
     private final SubscriptionManager subscriptionManager;
     private final StatusProcessor statusProcessor;
@@ -195,6 +198,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                     mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
                     metaData.getHierarchyDelimiter(),
                     getMailboxType(session, metaData.getPath()))))
+            .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData))
             .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
     }
@@ -205,23 +209,25 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                 Flux.from(Throwing.supplier(() -> subscriptionManager.subscriptionsReactive(mailboxSession)).get()).collectList())
             .map(tuple -> getListResponseForSelectSubscribed(tuple.getT1(), tuple.getT2(), request, mailboxSession, isRelative, mailboxQuery))
             .flatMapIterable(list -> list)
-            .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getRight()))
+            .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getMiddle()))
+            .doOnNext(pathAndResponse -> pathAndResponse.getRight().ifPresent(mailboxMetaData -> respondMyRights(request, responder, mailboxSession, mailboxMetaData)))
             .flatMap(pathAndResponse -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(pathAndResponse.getLeft(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
     }
 
-    private List<Pair<MailboxPath, ListResponse>> getListResponseForSelectSubscribed(Map<MailboxPath, MailboxMetaData> searchedResultMap, List<MailboxPath> allSubscribedSearch,
+    private List<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> getListResponseForSelectSubscribed(Map<MailboxPath, MailboxMetaData> searchedResultMap, List<MailboxPath> allSubscribedSearch,
                                                                                      ListRequest listRequest, MailboxSession mailboxSession, boolean relative, MailboxQuery mailboxQuery) {
-        ImmutableList.Builder<Pair<MailboxPath, ListResponse>> responseBuilders = ImmutableList.builder();
+        ImmutableList.Builder<Triple<MailboxPath, ListResponse, Optional<MailboxMetaData>>> responseBuilders = ImmutableList.builder();
         List<Pair<MailboxPath, ListResponse>> listRecursiveMatch = listRecursiveMatch(searchedResultMap, allSubscribedSearch, mailboxSession, relative, listRequest);
-        responseBuilders.addAll(listRecursiveMatch);
+
+        listRecursiveMatch.forEach(pair -> responseBuilders.add(Triple.of(pair.getLeft(), pair.getRight(), Optional.ofNullable(searchedResultMap.get(pair.getLeft())))));
         Set<MailboxPath> listRecursiveMatchPath = listRecursiveMatch.stream().map(Pair::getKey).collect(Collectors.toUnmodifiableSet());
 
         allSubscribedSearch.stream()
             .filter(subscribed -> !listRecursiveMatchPath.contains(subscribed))
             .filter(mailboxQuery::isPathMatch)
             .map(subscribed -> buildListResponse(searchedResultMap, mailboxSession, relative, subscribed))
-            .forEach(responseBuilders::add);
+            .forEach(pair -> responseBuilders.add(Triple.of(pair.getLeft(), pair.getRight(), Optional.ofNullable(searchedResultMap.get(pair.getLeft())))));
 
         return responseBuilders.build();
     }
@@ -263,6 +269,21 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             .collect(Collectors.toList());
     }
 
+    private void respondMyRights(T request, Responder responder, MailboxSession mailboxSession, MailboxMetaData metaData) {
+        if (request.getReturnOptions().contains(ListRequest.ListReturnOption.MYRIGHTS)) {
+            MyRightsResponse myRightsResponse = new MyRightsResponse(metaData.getPath().getName(), getRfc4314Rights(mailboxSession, metaData));
+            responder.respond(myRightsResponse);
+        }
+    }
+
+    private MailboxACL.Rfc4314Rights getRfc4314Rights(MailboxSession mailboxSession, MailboxMetaData metaData) {
+        if (metaData.getPath().belongsTo(mailboxSession)) {
+            return MailboxACL.FULL_RIGHTS;
+        }
+        MailboxACL.EntryKey entryKey = MailboxACL.EntryKey.createUserEntryKey(mailboxSession.getUser());
+        return metaData.getResolvedAcls().getEntries().get(entryKey);
+    }
+
     private MailboxQuery mailboxQuery(MailboxPath basePath, String mailboxName, MailboxSession mailboxSession) {
         if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE)
             && basePath.getUser().equals(mailboxSession.getUser())
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
index 44fd222004..398180a13a 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
@@ -70,6 +70,7 @@ The following IMAP specifications are implemented:
  - link:https://datatracker.ietf.org/doc/html/rfc8438.html[RFC-8438] IMAP Extension for STATUS=SIZE
  - link:https://www.rfc-editor.org/rfc/rfc5258.html[RFC-5258] IMAP LIST Command Extensions
  - link:https://www.rfc-editor.org/rfc/rfc5819.html[RFC-5819] IMAP4 Extension for Returning STATUS Information in Extended LIST
+ - link:https://www.rfc-editor.org/rfc/rfc8440.html[RFC-8440] IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST
 
 Partially implemented specifications:
 
diff --git a/src/site/xdoc/protocols/imap4.xml b/src/site/xdoc/protocols/imap4.xml
index fdcf0a1b36..1af6934fd2 100644
--- a/src/site/xdoc/protocols/imap4.xml
+++ b/src/site/xdoc/protocols/imap4.xml
@@ -65,6 +65,7 @@
        <li>IMAP ID (https://www.rfc-editor.org/rfc/rfc2971.html)</li>
        <li>IMAP LIST Command Extensions (link:https://www.rfc-editor.org/rfc/rfc5258.html)</li>
        <li>IMAP4 Extension for Returning STATUS Information in Extended LIST (https://www.rfc-editor.org/rfc/rfc5819.html)</li>
+       <li>IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST (https://www.rfc-editor.org/rfc/rfc8440.html)</li>
      </ul>
      <p>We follow RFC2683 recommendations for our implementations:</p>
      <ul>


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 06/08: JAMES-3754 MessageManager::getMailboxPath no longer throws

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit a399049bcbebaffa2a9384134a63ea131e4ef4a9
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Dec 2 23:21:14 2022 +0700

    JAMES-3754 MessageManager::getMailboxPath no longer throws
---
 .../draft/methods/SetMessagesCreationProcessorTest.java    | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
index a8eb038ecc..1db876ea72 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
@@ -373,20 +373,6 @@ public class SetMessagesCreationProcessorTest {
         assertThatThrownBy(() -> sut.assertIsUserOwnerOfMailboxes(ImmutableList.of(mailboxId), session).block());
     }
 
-    @Test
-    public void assertIsUserOwnerOfMailboxesShouldThrowWhenRetrievingMailboxPathFails() throws Exception {
-        InMemoryId mailboxId = InMemoryId.of(6789);
-        MessageManager mailbox = mock(MessageManager.class);
-        when(mockedMailboxManager.getMailbox(mailboxId, session))
-            .thenReturn(mailbox);
-        when(mockedMailboxIdFactory.fromString(mailboxId.serialize()))
-            .thenReturn(mailboxId);
-        when(mailbox.getMailboxPath())
-            .thenThrow(new MailboxException());
-
-        assertThatThrownBy(() -> sut.assertIsUserOwnerOfMailboxes(ImmutableList.of(mailboxId), session).block());
-    }
-
     @Test
     public void assertIsUserOwnerOfMailboxesShouldThrowWhenUserIsNotTheOwnerOfTheMailbox() throws Exception {
         InMemoryId mailboxId = InMemoryId.of(6789);


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/08: JAMES-3754 Document RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit ce967cf48e96da0b663eabd1d38f4047e7691f3b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Nov 24 14:59:07 2022 +0700

    JAMES-3754 Document RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST
---
 .../docs/modules/ROOT/pages/architecture/implemented-standards.adoc   | 4 +++-
 src/site/xdoc/protocols/imap4.xml                                     | 2 ++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
index f2992260a9..44fd222004 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
@@ -66,8 +66,10 @@ The following IMAP specifications are implemented:
  - link:https://datatracker.ietf.org/doc/html/rfc3348.html[RFC-3348] IMAP Child Mailbox Extension
  - link:https://www.rfc-editor.org/rfc/rfc7889.html[RFC-7889] IMAP Extension for APPENDLIMIT
  - link:https://www.rfc-editor.org/rfc/rfc8474.html[RFC-8474] IMAP Extension for Object Identifiers
+- link:https://datatracker.ietf.org/doc/html/rfc2971.html[RFC-2971] IMAP ID Extension
  - link:https://datatracker.ietf.org/doc/html/rfc8438.html[RFC-8438] IMAP Extension for STATUS=SIZE
- - link:https://datatracker.ietf.org/doc/html/rfc2971.html[RFC-2971] IMAP ID Extension
+ - link:https://www.rfc-editor.org/rfc/rfc5258.html[RFC-5258] IMAP LIST Command Extensions
+ - link:https://www.rfc-editor.org/rfc/rfc5819.html[RFC-5819] IMAP4 Extension for Returning STATUS Information in Extended LIST
 
 Partially implemented specifications:
 
diff --git a/src/site/xdoc/protocols/imap4.xml b/src/site/xdoc/protocols/imap4.xml
index c2f747b455..fdcf0a1b36 100644
--- a/src/site/xdoc/protocols/imap4.xml
+++ b/src/site/xdoc/protocols/imap4.xml
@@ -63,6 +63,8 @@
        <li>IMAP Extension for STATUS=SIZE (https://www.rfc-editor.org/rfc/rfc8438.html)</li>
        <li>IMAP QUOTA (https://www.rfc-editor.org/rfc/rfc9208.html)</li>
        <li>IMAP ID (https://www.rfc-editor.org/rfc/rfc2971.html)</li>
+       <li>IMAP LIST Command Extensions (link:https://www.rfc-editor.org/rfc/rfc5258.html)</li>
+       <li>IMAP4 Extension for Returning STATUS Information in Extended LIST (https://www.rfc-editor.org/rfc/rfc5819.html)</li>
      </ul>
      <p>We follow RFC2683 recommendations for our implementations:</p>
      <ul>


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/08: JAMES-3754 RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 8604f845a63bbbeaf3ffce6f2279d1d9a9e07b1c
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Nov 24 14:58:55 2022 +0700

    JAMES-3754 RFC-5819 IMAP4 Extension for Returning STATUS Information in Extended LIST
---
 .../org/apache/james/mailbox/MessageManager.java   |  2 +-
 .../org/apache/james/imap/scripts/Status.test      |  9 ++++++
 .../imap/decode/parser/ListCommandParser.java      |  2 +-
 .../imap/decode/parser/StatusCommandParser.java    | 32 +++++++++++-----------
 .../james/imap/processor/DefaultProcessor.java     |  5 ++--
 .../apache/james/imap/processor/ListProcessor.java | 20 +++++++++-----
 .../james/imap/processor/StatusProcessor.java      | 30 +++++++++++---------
 .../james/imap/processor/XListProcessor.java       |  2 +-
 8 files changed, 61 insertions(+), 41 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
index e2d666df6e..65f3b03843 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
@@ -451,7 +451,7 @@ public interface MessageManager {
     /**
      * Gets the path of the referenced mailbox
      */
-    MailboxPath getMailboxPath() throws MailboxException;
+    MailboxPath getMailboxPath();
 
     Flags getApplicableFlags(MailboxSession session) throws MailboxException;
 
diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
index ea37d3085d..89eb6f8640 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
@@ -126,6 +126,15 @@ C: a014 STATUS statustest (MAILBOXID)
 S: \* STATUS \"statustest\" \(MAILBOXID \(.+\)\)
 S: a014 OK STATUS completed.
 
+C: a015 LIST "" * RETURN (STATUS (UNSEEN SIZE MESSAGES DELETED DELETED-STORAGE MAILBOXID))
+SUB {
+S: \* LIST \(\\HasNoChildren\) "." "INBOX"
+S: \* STATUS \"INBOX\" \(MESSAGES 0 SIZE 0 DELETED 0 DELETED-STORAGE 0 UNSEEN 0 MAILBOXID \(.+\)\)
+S: \* LIST \(\\HasNoChildren\) "." "statustest"
+S: \* STATUS "statustest" \(MESSAGES 4 SIZE 1016 DELETED 2 DELETED-STORAGE 508 UNSEEN 4 MAILBOXID \(.+\)\)
+}
+S: a015 OK LIST completed.
+
 # Cleanup
 C: a1 DELETE statustest
 S: a1 OK DELETE completed.
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
index f147718763..95d23e1cd9 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
@@ -146,7 +146,7 @@ public class ListCommandParser extends AbstractUidCommandParser {
         assertChar(request, 'T', 't');
         assertChar(request, 'U', 'u');
         assertChar(request, 'S', 's');
-        return Pair.of(ListReturnOption.STATUS, Optional.empty());
+        return Pair.of(ListReturnOption.STATUS, Optional.of(StatusCommandParser.statusDataItems(request)));
     }
 
     private ListSelectOption readSubscribed(ImapRequestLineReader request) throws DecodingException {
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
index b5e1800a84..9fca72d3d7 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
@@ -49,11 +49,11 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return new StatusRequest(mailboxName, statusDataItems, tag);
     }
 
-    private StatusDataItems statusDataItems(ImapRequestLineReader request) throws DecodingException {
+    static StatusDataItems statusDataItems(ImapRequestLineReader request) throws DecodingException {
         return new StatusDataItems(splitWords(request));
     }
 
-    private EnumSet<StatusDataItems.StatusItem> splitWords(ImapRequestLineReader request) throws DecodingException {
+    private static EnumSet<StatusDataItems.StatusItem> splitWords(ImapRequestLineReader request) throws DecodingException {
         EnumSet<StatusDataItems.StatusItem> words = EnumSet.noneOf(StatusDataItems.StatusItem.class);
 
         request.nextWordChar();
@@ -68,7 +68,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return words;
     }
 
-    private StatusDataItems.StatusItem parseStatus(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem parseStatus(ImapRequestLineReader request) throws DecodingException {
         // All the matching must be done in a case-insensitive fashion.
         // See rfc3501 9. Formal Syntax and IMAP-282
         char c = request.nextWordChar();
@@ -96,7 +96,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Unknown status item: '" + request.consumeWord(ImapRequestLineReader.NoopCharValidator.INSTANCE) + "'");
     }
 
-    private StatusDataItems.StatusItem readM(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readM(ImapRequestLineReader request) throws DecodingException {
         request.consume();
         char c2 = request.nextChar();
         if (c2 == 'e' || c2 == 'E') {
@@ -106,7 +106,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         }
     }
 
-    private StatusDataItems.StatusItem readU(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readU(ImapRequestLineReader request) throws DecodingException {
         char c;
         assertChar(request, 'u', 'U');
         c = request.nextWordChar();
@@ -123,7 +123,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.UID_VALIDITY;
     }
 
-    private void readValidity(ImapRequestLineReader request) throws DecodingException {
+    private static void readValidity(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'v', 'V');
         assertChar(request, 'a', 'A');
         assertChar(request, 'l', 'L');
@@ -134,7 +134,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         assertChar(request, 'y', 'Y');
     }
 
-    private StatusDataItems.StatusItem readAppendLimit(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readAppendLimit(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'a', 'A');
         assertChar(request, 'p', 'P');
         assertChar(request, 'p', 'P');
@@ -149,7 +149,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.APPENDLIMIT;
     }
 
-    private StatusDataItems.StatusItem readSize(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readSize(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 's', 'S');
         assertChar(request, 'i', 'I');
         assertChar(request, 'z', 'Z');
@@ -157,7 +157,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.SIZE;
     }
 
-    private StatusDataItems.StatusItem readUidNext(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readUidNext(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'n', 'N');
         assertChar(request, 'e', 'E');
         assertChar(request, 'x', 'X');
@@ -165,7 +165,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.UID_NEXT;
     }
 
-    private StatusDataItems.StatusItem readUnseen(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readUnseen(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'n', 'N');
         assertChar(request, 's', 'S');
         assertChar(request, 'e', 'E');
@@ -174,7 +174,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.UNSEEN;
     }
 
-    private StatusDataItems.StatusItem readHighestModseq(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readHighestModseq(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'h', 'H');
         assertChar(request, 'i', 'I');
         assertChar(request, 'g', 'G');
@@ -191,7 +191,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.HIGHEST_MODSEQ;
     }
 
-    private StatusDataItems.StatusItem readRecent(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readRecent(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'r', 'R');
         assertChar(request, 'e', 'E');
         assertChar(request, 'c', 'C');
@@ -201,7 +201,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.RECENT;
     }
 
-    private StatusDataItems.StatusItem readMessages(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readMessages(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'e', 'E');
         assertChar(request, 's', 'S');
         assertChar(request, 's', 'S');
@@ -212,7 +212,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.MESSAGES;
     }
 
-    private StatusDataItems.StatusItem readDeleted(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readDeleted(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'd', 'D');
         assertChar(request, 'e', 'E');
         assertChar(request, 'l', 'L');
@@ -235,7 +235,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.DELETED;
     }
 
-    private StatusDataItems.StatusItem readMailboxId(ImapRequestLineReader request) throws DecodingException {
+    private static StatusDataItems.StatusItem readMailboxId(ImapRequestLineReader request) throws DecodingException {
         assertChar(request, 'a', 'A');
         assertChar(request, 'i', 'I');
         assertChar(request, 'l', 'L');
@@ -247,7 +247,7 @@ public class StatusCommandParser extends AbstractImapCommandParser {
         return StatusDataItems.StatusItem.MAILBOXID;
     }
 
-    private void assertChar(ImapRequestLineReader reader, char low, char up) throws DecodingException {
+    private static void assertChar(ImapRequestLineReader reader, char low, char up) throws DecodingException {
         char c = reader.consume();
         if (c != low && c != up) {
             throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Unexpected token in Status item. Expecting " + up + " got " + c);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
index 356d161990..40d153c8c0 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
@@ -79,10 +79,11 @@ public class DefaultProcessor implements ImapProcessor {
         builder.add(new StoreProcessor(mailboxManager, statusResponseFactory, metricFactory));
         builder.add(new NoopProcessor(mailboxManager, statusResponseFactory, metricFactory));
         builder.add(new IdleProcessor(mailboxManager, statusResponseFactory, metricFactory));
-        builder.add(new StatusProcessor(mailboxManager, statusResponseFactory, metricFactory));
+        StatusProcessor statusProcessor = new StatusProcessor(mailboxManager, statusResponseFactory, metricFactory);
+        builder.add(statusProcessor);
         builder.add(new LSubProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory));
         builder.add(new XListProcessor(mailboxManager, statusResponseFactory, mailboxTyper, metricFactory, subscriptionManager));
-        builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager));
+        builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor));
         builder.add(new SearchProcessor(mailboxManager, statusResponseFactory, metricFactory));
         SelectProcessor selectProcessor = new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory);
         builder.add(selectProcessor);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index c4bb28302f..9ed9551e89 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -70,16 +70,20 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     private static final List<Capability> CAPA = ImmutableList.of(Capability.of("LIST-EXTENDED"), Capability.of("LIST-STATUS"));
 
     private final SubscriptionManager subscriptionManager;
+    private final StatusProcessor statusProcessor;
 
     public ListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory,
-                         MetricFactory metricFactory, SubscriptionManager subscriptionManager) {
-        this((Class<T>) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager);
+                         MetricFactory metricFactory, SubscriptionManager subscriptionManager,
+                         StatusProcessor statusProcessor) {
+        this((Class<T>) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, statusProcessor);
     }
 
     public ListProcessor(Class<T> clazz, MailboxManager mailboxManager, StatusResponseFactory factory,
-                         MetricFactory metricFactory, SubscriptionManager subscriptionManager) {
+                         MetricFactory metricFactory, SubscriptionManager subscriptionManager,
+                         StatusProcessor statusProcessor) {
         super(clazz, mailboxManager, factory, metricFactory);
         this.subscriptionManager = subscriptionManager;
+        this.statusProcessor = statusProcessor;
     }
 
     @Override
@@ -177,13 +181,13 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             request.getMailboxPattern(), mailboxSession);
 
         if (request.selectSubscribed()) {
-            return processWithSubscribed(request, responder, mailboxSession, isRelative, mailboxQuery);
+            return processWithSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery);
         } else {
-            return processWithoutSubscribed(session, responder, mailboxSession, isRelative, mailboxQuery);
+            return processWithoutSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery);
         }
     }
 
-    private Mono<Void> processWithoutSubscribed(ImapSession session, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
+    private Mono<Void> processWithoutSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
         return getMailboxManager().search(mailboxQuery, Minimal, mailboxSession)
             .doOnNext(metaData -> responder.respond(
                 createResponse(metaData.inferiors(),
@@ -191,16 +195,18 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
                     mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
                     metaData.getHierarchyDelimiter(),
                     getMailboxType(session, metaData.getPath()))))
+            .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
     }
 
-    private Mono<Void> processWithSubscribed(T request, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
+    private Mono<Void> processWithSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
         return Mono.zip(getMailboxManager().search(mailboxQuery, Minimal, mailboxSession).collectList()
                     .map(searchedResultList -> searchedResultList.stream().collect(Collectors.toMap(MailboxMetaData::getPath, Function.identity()))),
                 Flux.from(Throwing.supplier(() -> subscriptionManager.subscriptionsReactive(mailboxSession)).get()).collectList())
             .map(tuple -> getListResponseForSelectSubscribed(tuple.getT1(), tuple.getT2(), request, mailboxSession, isRelative, mailboxQuery))
             .flatMapIterable(list -> list)
             .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getRight()))
+            .flatMap(pathAndResponse -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(pathAndResponse.getLeft(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
     }
 
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
index 7feeb4afbc..541f84f421 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
@@ -94,18 +94,10 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
     protected Mono<Void> processRequestReactive(StatusRequest request, ImapSession session, Responder responder) {
         MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName());
         MailboxSession mailboxSession = session.getMailboxSession();
+        StatusDataItems statusDataItems = request.getStatusDataItems();
 
         return logInitialRequest(mailboxPath)
-            .then(Mono.from(getMailboxManager().getMailboxReactive(mailboxPath, mailboxSession)))
-            .flatMap(mailbox -> retrieveMetadata(mailbox, request.getStatusDataItems(), mailboxSession)
-                .flatMap(metaData -> computeStatusResponse(mailbox, request, metaData, mailboxSession)
-                    .doOnNext(response -> {
-                        // Enable CONDSTORE as this is a CONDSTORE enabling command
-                        if (response.getHighestModSeq() != null) {
-                            condstoreEnablingCommand(session, responder, metaData, false);
-                        }
-                        responder.respond(response);
-                    })))
+            .then(sendStatus(mailboxPath, statusDataItems, responder, session, mailboxSession))
             .then(unsolicitedResponses(session, responder, false))
             .then(Mono.fromRunnable(() -> okComplete(request, responder)))
             .onErrorResume(MailboxException.class, e -> {
@@ -115,6 +107,19 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
             .then();
     }
 
+    Mono<MailboxStatusResponse> sendStatus(MailboxPath mailboxPath, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) {
+        return Mono.from(getMailboxManager().getMailboxReactive(mailboxPath, mailboxSession))
+            .flatMap(mailbox -> retrieveMetadata(mailbox, statusDataItems, mailboxSession)
+                .flatMap(metaData -> computeStatusResponse(mailbox, statusDataItems, metaData, mailboxSession)
+                    .doOnNext(response -> {
+                        // Enable CONDSTORE as this is a CONDSTORE enabling command
+                        if (response.getHighestModSeq() != null) {
+                            condstoreEnablingCommand(session, responder, metaData, false);
+                        }
+                        responder.respond(response);
+                    })));
+    }
+
     private Mono<Void> logInitialRequest(MailboxPath mailboxPath) {
         if (LOGGER.isDebugEnabled()) {
             return ReactorUtils.logAsMono(() -> LOGGER.debug("Status called on mailbox named {}", mailboxPath));
@@ -142,10 +147,9 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
     }
 
     private Mono<MailboxStatusResponse> computeStatusResponse(MessageManager mailbox,
-                                                              StatusRequest request,
+                                                              StatusDataItems statusDataItems,
                                                               MessageManager.MailboxMetaData metaData,
                                                               MailboxSession session) {
-        StatusDataItems statusDataItems = request.getStatusDataItems();
         return iterateMailbox(statusDataItems, mailbox, session)
             .map(maybeIterationResult -> {
                 Optional<Long> appendLimit = appendLimit(statusDataItems);
@@ -161,7 +165,7 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
                     maybeIterationResult.flatMap(result -> result.getSize(statusDataItems)).orElse(null),
                     maybeIterationResult.flatMap(result -> result.getDeleted(statusDataItems)).orElse(null),
                     maybeIterationResult.flatMap(result -> result.getDeletedStorage(statusDataItems)).orElse(null),
-                    messages, recent, uidNext, highestModSeq, uidValidity, unseen, request.getMailboxName(), mailboxId);
+                    messages, recent, uidNext, highestModSeq, uidValidity, unseen, mailbox.getMailboxPath().getName(), mailboxId);
             });
     }
 
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
index e164522dc7..7bab0ff87e 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
@@ -49,7 +49,7 @@ public class XListProcessor extends ListProcessor<XListRequest> implements Capab
 
     public XListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MailboxTyper mailboxTyper,
                           MetricFactory metricFactory, SubscriptionManager subscriptionManager) {
-        super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager);
+        super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, null);
         this.mailboxTyper = mailboxTyper;
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 07/08: JAMES-3754 SPECIAL-USE needs to be supported as a LIST selection option too

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit b4cb6a1bd4819092e63325c62dd598d427d6f1b9
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Dec 2 23:38:18 2022 +0700

    JAMES-3754 SPECIAL-USE needs to be supported as a LIST selection option too
---
 .../org/apache/james/imap/scripts/XList.test       | 33 ++++++++++++++++++++++
 .../imap/decode/parser/ListCommandParser.java      | 30 ++++++++++++++------
 .../james/imap/message/request/ListRequest.java    |  4 ++-
 .../apache/james/imap/processor/ListProcessor.java | 23 +++++++++------
 4 files changed, 73 insertions(+), 17 deletions(-)

diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
index ba13b0da8a..62b07aa7a6 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/XList.test
@@ -52,3 +52,36 @@ S: \* LIST \(\\HasNoChildren \\Trash\) \"\.\" "Trash"
 S: \* LIST \(\\HasNoChildren \\Archive\) \"\.\" "Archive"
 }
 S: 10 OK LIST completed.
+
+C: 10 LIST (SPECIAL-USE) "" *
+SUB {
+S: \* LIST \(\\HasNoChildren \\Drafts\) \"\.\" "Drafts"
+S: \* LIST \(\\HasNoChildren \\Sent\) \"\.\" "Sent"
+S: \* LIST \(\\HasNoChildren \\Junk\) \"\.\" "Spam"
+S: \* LIST \(\\HasNoChildren \\Trash\) \"\.\" "Trash"
+S: \* LIST \(\\HasNoChildren \\Archive\) \"\.\" "Archive"
+}
+S: 10 OK LIST completed.
+
+C: A12 SUBSCRIBE Sent
+S: A12 OK SUBSCRIBE completed.
+C: A12 SUBSCRIBE Other
+S: A12 OK SUBSCRIBE completed.
+
+C: 10 LIST (SPECIAL-USE SUBSCRIBED) "" *
+SUB {
+S: \* LIST \(\\HasNoChildren \\Sent \\Subscribed\) \"\.\" "Sent"
+}
+S: 10 OK LIST completed.
+
+C: 10 LIST "" * RETURN (SPECIAL-USE SUBSCRIBED)
+SUB {
+S: \* LIST \(\\HasNoChildren\) \"\.\" "INBOX"
+S: \* LIST \(\\HasNoChildren \\Subscribed\) \"\.\" "Other"
+S: \* LIST \(\\HasNoChildren \\Drafts\) \"\.\" "Drafts"
+S: \* LIST \(\\HasNoChildren \\Sent \\Subscribed\) \"\.\" "Sent"
+S: \* LIST \(\\HasNoChildren \\Junk\) \"\.\" "Spam"
+S: \* LIST \(\\HasNoChildren \\Trash\) \"\.\" "Trash"
+S: \* LIST \(\\HasNoChildren \\Archive\) \"\.\" "Archive"
+}
+S: 10 OK LIST completed.
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
index 288f2ed285..5909b2dbe1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ListCommandParser.java
@@ -93,6 +93,9 @@ public class ListCommandParser extends AbstractUidCommandParser {
         if (listOptions.isEmpty() && listReturnOptions.getLeft().isEmpty()) {
             return createMessage(referenceName, mailboxPattern, tag);
         }
+        if (listOptions.contains(ListSelectOption.SPECIAL_USE)) {
+            listReturnOptions.getLeft().add(ListReturnOption.SPECIAL_USE);
+        }
         return new ListRequest(ImapConstants.LIST_COMMAND, referenceName, mailboxPattern, tag, listOptions, listReturnOptions.getLeft(), listReturnOptions.getRight());
     }
 
@@ -123,12 +126,24 @@ public class ListCommandParser extends AbstractUidCommandParser {
             return readR(request);
         }
         if (c == 's' || c == 'S') {
-            return readSubscribed(request);
+            return readSelectS(request);
         }
         throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS,
             "Unknown select option: '" + request.consumeWord(ImapRequestLineReader.NoopCharValidator.INSTANCE) + "'");
     }
 
+    private ListSelectOption readSelectS(ImapRequestLineReader request) throws DecodingException {
+        request.consume();
+        char c = request.nextChar();
+        if (c == 'u' || c == 'U') {
+            consumeSubscribed(request);
+            return ListSelectOption.SUBSCRIBED;
+        } else {
+            consumeSpecialUse(request);
+            return ListSelectOption.SPECIAL_USE;
+        }
+    }
+
     private Pair<ListReturnOption, Optional<StatusDataItems>> readS(ImapRequestLineReader request) throws DecodingException {
         request.consume();
         char c = request.nextWordChar();
@@ -152,6 +167,11 @@ public class ListCommandParser extends AbstractUidCommandParser {
     }
 
     private ListReturnOption readSpecialUse(ImapRequestLineReader request) throws DecodingException {
+        consumeSpecialUse(request);
+        return ListReturnOption.SPECIAL_USE;
+    }
+
+    private void consumeSpecialUse(ImapRequestLineReader request) throws DecodingException {
         // 'S' is already consummed
         assertChar(request, 'P', 'p');
         assertChar(request, 'E', 'e');
@@ -163,12 +183,6 @@ public class ListCommandParser extends AbstractUidCommandParser {
         assertChar(request, 'U', 'u');
         assertChar(request, 'S', 's');
         assertChar(request, 'E', 'e');
-        return ListReturnOption.SPECIAL_USE;
-    }
-
-    private ListSelectOption readSubscribed(ImapRequestLineReader request) throws DecodingException {
-        consumeSubscribed(request);
-        return ListSelectOption.SUBSCRIBED;
     }
 
     private ListSelectOption readR(ImapRequestLineReader request) throws DecodingException {
@@ -283,7 +297,7 @@ public class ListCommandParser extends AbstractUidCommandParser {
     }
 
     private void consumeSubscribed(ImapRequestLineReader request) throws DecodingException {
-        assertChar(request, 'S', 's');
+        // s is already consumed
         assertChar(request, 'U', 'u');
         assertChar(request, 'B', 'b');
         assertChar(request, 'S', 's');
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
index bf3e601ae1..4729c90678 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ListRequest.java
@@ -34,7 +34,9 @@ public class ListRequest extends AbstractImapRequest {
     public enum ListSelectOption {
         REMOTE,
         RECURSIVEMATCH,
-        SUBSCRIBED
+        SUBSCRIBED,
+        // https://www.rfc-editor.org/rfc/rfc6154.html
+        SPECIAL_USE
     }
 
     // https://www.rfc-editor.org/rfc/rfc5258.html
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index 1fbe137595..8791fbcab8 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -20,6 +20,7 @@
 package org.apache.james.imap.processor;
 
 import static org.apache.james.imap.message.request.ListRequest.ListSelectOption.RECURSIVEMATCH;
+import static org.apache.james.imap.message.request.ListRequest.ListSelectOption.SPECIAL_USE;
 import static org.apache.james.mailbox.MailboxManager.MailboxSearchFetchType.Minimal;
 
 import java.util.EnumSet;
@@ -195,7 +196,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
 
         if (request.selectSubscribed()) {
             return processWithSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery);
-        } if (request.getReturnOptions().contains(ListRequest.ListReturnOption.SUBSCRIBED)) {
+        } else if (request.getReturnOptions().contains(ListRequest.ListReturnOption.SUBSCRIBED)) {
             return Flux.from(Throwing.supplier(() -> subscriptionManager.subscriptionsReactive(mailboxSession)).get())
                 .collect(ImmutableMap.toImmutableMap(path -> path, path -> path))
                 .flatMap(subscribed -> processWithoutSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery, subscribed::containsKey));
@@ -207,13 +208,18 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     private Mono<Void> processWithoutSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession,
                                                 boolean isRelative, MailboxQuery mailboxQuery, Predicate<MailboxPath> isSubscribed) {
         return getMailboxManager().search(mailboxQuery, Minimal, mailboxSession)
-            .doOnNext(metaData -> responder.respond(
-                createResponse(metaData.inferiors(),
-                    metaData.getSelectability(),
-                    mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
-                    metaData.getHierarchyDelimiter(),
-                    getMailboxType(request, session, metaData.getPath()),
-                    isSubscribed.test(metaData.getPath()))))
+            .doOnNext(metaData -> {
+                MailboxType mailboxType = getMailboxType(request, session, metaData.getPath());
+                if (!request.getSelectOptions().contains(SPECIAL_USE) || mailboxType.getRfc6154attributeName() != null) {
+                    responder.respond(
+                        createResponse(metaData.inferiors(),
+                            metaData.getSelectability(),
+                            mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
+                            metaData.getHierarchyDelimiter(),
+                            mailboxType,
+                            isSubscribed.test(metaData.getPath())));
+                }
+            })
             .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData))
             .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
@@ -243,6 +249,7 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             .filter(subscribed -> !listRecursiveMatchPath.contains(subscribed))
             .filter(mailboxQuery::isPathMatch)
             .map(subscribed -> buildListResponse(listRequest, searchedResultMap, session, relative, subscribed))
+            .filter(pair -> !listRequest.getSelectOptions().contains(SPECIAL_USE) || mailboxTyper.getMailboxType(session, pair.getKey()).getRfc6154attributeName() != null)
             .forEach(pair -> responseBuilders.add(Triple.of(pair.getLeft(), pair.getRight(), Optional.ofNullable(searchedResultMap.get(pair.getLeft())))));
 
         return responseBuilders.build();


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 05/08: JAMES-3754 IMAP Fix SUBSCRIBED LIST return option

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e988ce9a909cf48e0d75a8717247158c8cf1ff05
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Dec 2 23:20:54 2022 +0700

    JAMES-3754 IMAP Fix SUBSCRIBED LIST return option
---
 .../apache/james/imap/scripts/ListSubscribed.test   | 10 ++++++++++
 .../apache/james/imap/processor/ListProcessor.java  | 21 +++++++++++++++------
 .../apache/james/imap/processor/XListProcessor.java |  2 +-
 3 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListSubscribed.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListSubscribed.test
index 28b745fc26..6c73e7950d 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListSubscribed.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListSubscribed.test
@@ -62,6 +62,16 @@ S: \* LIST \(\\HasChildren \\Subscribed\) \"\.\" "listtest"
 }
 S: A13 OK LIST completed.
 
+C: A08 LIST "" "*" RETURN (SUBSCRIBED)
+SUB {
+S: \* LIST \(\\HasChildren \\Subscribed\) \"\.\" "listtest"
+S: \* LIST \(\\HasNoChildren\) \"\.\" "INBOX"
+S: \* LIST \(\\HasNoChildren \\Subscribed\) \"\.\" "listtest.subfolder"
+S: \* LIST \(\\HasChildren\) \"\.\" "listtest1"
+S: \* LIST \(\\HasNoChildren\) \"\.\" "listtest1.subfolder1"
+}
+S: A08 OK LIST completed.
+
 C: A14 UNSUBSCRIBE listtest.subfolder
 S: A14 OK UNSUBSCRIBE completed.
 C: A15 UNSUBSCRIBE listtest
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index fd44a047ed..1fbe137595 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -28,6 +28,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.tuple.Pair;
@@ -63,6 +64,7 @@ import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -136,8 +138,8 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
     }
 
     protected ImapResponseMessage createResponse(MailboxMetaData.Children children, MailboxMetaData.Selectability selectability, String name,
-                                                 char hierarchyDelimiter, MailboxType type) {
-        return new ListResponse(children, selectability, name, hierarchyDelimiter, !RETURN_SUBSCRIBED,
+                                                 char hierarchyDelimiter, MailboxType type, boolean isSubscribed) {
+        return new ListResponse(children, selectability, name, hierarchyDelimiter, isSubscribed,
             !RETURN_NON_EXISTENT, EnumSet.noneOf(ListResponse.ChildInfo.class), type);
     }
 
@@ -151,7 +153,8 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
             MailboxMetaData.Selectability.NOSELECT,
             referenceRoot,
             mailboxSession.getPathDelimiter(),
-            MailboxType.OTHER));
+            MailboxType.OTHER,
+            !RETURN_SUBSCRIBED));
     }
 
     private String computeReferenceRoot(String referenceName, MailboxSession mailboxSession) {
@@ -192,19 +195,25 @@ public class ListProcessor<T extends ListRequest> extends AbstractMailboxProcess
 
         if (request.selectSubscribed()) {
             return processWithSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery);
+        } if (request.getReturnOptions().contains(ListRequest.ListReturnOption.SUBSCRIBED)) {
+            return Flux.from(Throwing.supplier(() -> subscriptionManager.subscriptionsReactive(mailboxSession)).get())
+                .collect(ImmutableMap.toImmutableMap(path -> path, path -> path))
+                .flatMap(subscribed -> processWithoutSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery, subscribed::containsKey));
         } else {
-            return processWithoutSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery);
+            return processWithoutSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery, any -> false);
         }
     }
 
-    private Mono<Void> processWithoutSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession, boolean isRelative, MailboxQuery mailboxQuery) {
+    private Mono<Void> processWithoutSubscribed(ImapSession session, T request, Responder responder, MailboxSession mailboxSession,
+                                                boolean isRelative, MailboxQuery mailboxQuery, Predicate<MailboxPath> isSubscribed) {
         return getMailboxManager().search(mailboxQuery, Minimal, mailboxSession)
             .doOnNext(metaData -> responder.respond(
                 createResponse(metaData.inferiors(),
                     metaData.getSelectability(),
                     mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()),
                     metaData.getHierarchyDelimiter(),
-                    getMailboxType(request, session, metaData.getPath()))))
+                    getMailboxType(request, session, metaData.getPath()),
+                    isSubscribed.test(metaData.getPath()))))
             .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData))
             .flatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(metaData.getPath(), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty()))
             .then();
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
index 5ae2066be6..b6cf974c1b 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java
@@ -64,7 +64,7 @@ public class XListProcessor extends ListProcessor<XListRequest> implements Capab
 
     @Override
     protected ImapResponseMessage createResponse(MailboxMetaData.Children children, MailboxMetaData.Selectability selectability,
-                                                 String name, char hierarchyDelimiter, MailboxType type) {
+                                                 String name, char hierarchyDelimiter, MailboxType type, boolean isSubscribed) {
         return new XListResponse(children, selectability, name, hierarchyDelimiter, type);
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org