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 <btell...@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-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org