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:40:00 UTC

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

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