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:04 UTC
[james-project] 07/08: JAMES-3754 SPECIAL-USE needs to be supported as a LIST selection option too
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