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:01 UTC
[james-project] 04/08: JAMES-3754 Implement RFC-6154 IMAP LIST Extension for Special-Use Mailboxes
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