You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2016/08/31 08:06:53 UTC
[04/10] james-project git commit: MAILBOX-270: getmetadata command,
add new definition for command parser and processor.
MAILBOX-270: getmetadata command, add new definition for command parser and processor.
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ced2d52f
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ced2d52f
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ced2d52f
Branch: refs/heads/master
Commit: ced2d52fe2085596e7fdd2f5cd0e8953176faf18
Parents: 709cd70
Author: Quynh Nguyen <qn...@linagora.com>
Authored: Tue Jul 5 13:31:46 2016 +0700
Committer: Quynh Nguyen <qn...@linagora.com>
Committed: Tue Aug 30 09:21:30 2016 +0700
----------------------------------------------------------------------
.../apache/james/imap/api/ImapConstants.java | 4 +
.../parser/GetAnnotationCommandParser.java | 131 +++++++
.../imap/decode/parser/ImapParserFactory.java | 3 +-
.../message/request/GetAnnotationRequest.java | 158 ++++++++
.../imap/processor/DefaultProcessorChain.java | 4 +-
.../imap/processor/GetAnnotationProcessor.java | 175 +++++++++
.../message/response/StatusResponseTest.java | 3 +-
.../parser/GetAnnotationCommandParserTest.java | 390 +++++++++++++++++++
.../processor/GetAnnotationProcessorTest.java | 329 ++++++++++++++++
protocols/src/site/xdoc/imap4.xml | 5 +-
10 files changed, 1196 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
index 70d1b22..0d9ef2f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
@@ -239,6 +239,8 @@ public interface ImapConstants {
String SETANNOTATION_COMMAND_NAME = "SETMETADATA";
+ String GETANNOTATION_COMMAND_NAME = "GETMETADATA";
+
String LIST_RESPONSE_NAME = "LIST";
String XLIST_RESPONSE_NAME = "XLIST";
@@ -257,6 +259,8 @@ public interface ImapConstants {
String MYRIGHTS_RESPONSE_NAME = "MYRIGHTS";
+ String ANNOTATION_RESPONSE_NAME = "METADATA";
+
String NAME_ATTRIBUTE_NOINFERIORS = "\\Noinferiors";
String NAME_ATTRIBUTE_NOSELECT = "\\Noselect";
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java
new file mode 100644
index 0000000..07cf408
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java
@@ -0,0 +1,131 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.decode.parser;
+
+import com.google.common.base.Optional;
+import org.apache.james.imap.message.request.GetAnnotationRequest;
+import org.apache.james.mailbox.model.MailboxAnnotationKey;
+import com.google.common.base.CharMatcher;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapMessage;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.decode.ImapRequestLineReader;
+import org.apache.james.imap.decode.base.AbstractImapCommandParser;
+import org.apache.james.imap.message.request.GetAnnotationRequest.Depth;
+import org.apache.james.protocols.imap.DecodingException;
+
+public class GetAnnotationCommandParser extends AbstractImapCommandParser {
+ private static final CharMatcher ENDOFLINE_PATTERN = CharMatcher.isNot('\n').and(CharMatcher.isNot('\r'));
+ private static final String MAXSIZE = "MAXSIZE";
+ private static final String DEPTH = "DEPTH";
+ private static final boolean STOP_ON_PAREN = true;
+
+ public GetAnnotationCommandParser() {
+ super(ImapCommand.authenticatedStateCommand(ImapConstants.GETANNOTATION_COMMAND_NAME));
+ }
+
+ @Override
+ protected ImapMessage decode(ImapCommand command, ImapRequestLineReader requestReader, String tag, ImapSession session)
+ throws DecodingException {
+ try {
+ return buildAnnotationRequest(command, requestReader, tag);
+ } catch (NullPointerException e) {
+ throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage());
+ } catch (IllegalArgumentException e) {
+ throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage());
+ } catch (IllegalStateException e) {
+ throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage());
+ }
+ }
+
+ private ImapMessage buildAnnotationRequest(ImapCommand command, ImapRequestLineReader requestReader, String tag) throws DecodingException {
+ GetAnnotationRequest.Builder builder = GetAnnotationRequest.builder()
+ .tag(tag)
+ .command(command)
+ .mailboxName(requestReader.mailbox());
+
+ consumeOptionsAndKeys(requestReader, builder);
+
+ if (ENDOFLINE_PATTERN.matches(requestReader.nextNonSpaceChar())) {
+ consumeKey(requestReader, builder);
+ }
+
+ return builder.build();
+ }
+
+ private void consumeOptionsAndKeys(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException {
+ while (requestReader.nextNonSpaceChar() == '(') {
+ requestReader.consumeChar('(');
+ switch (requestReader.nextChar()) {
+ case 'M':
+ consumeMaxsizeOpt(requestReader, builder);
+ break;
+
+ case 'D':
+ consumeDepthOpt(requestReader, builder);
+ break;
+
+ default:
+ consumeKeys(requestReader, builder);
+ break;
+ }
+ }
+ }
+
+ private void consumeDepthOpt(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException {
+ if (!requestReader.atom().equalsIgnoreCase(DEPTH)) {
+ throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Wrong on, it should be DEPTH");
+ }
+
+ builder.depth(Depth.fromString(requestReader.atom()));
+ requestReader.consumeChar(')');
+ }
+
+ private void consumeMaxsizeOpt(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException {
+ if (!requestReader.atom().equalsIgnoreCase(MAXSIZE)) {
+ throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Wrong on, it should be MAXSIZE");
+ }
+
+ builder.maxsize(Optional.of((int)requestReader.number(STOP_ON_PAREN)));
+ requestReader.consumeChar(')');
+ }
+
+ private void consumeKey(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException {
+ builder.keys(ImmutableSet.of(new MailboxAnnotationKey(requestReader.atom())));
+ requestReader.eol();
+ }
+
+ private void consumeKeys(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException {
+ Builder<MailboxAnnotationKey> keys = ImmutableSet.builder();
+
+ do {
+ keys.add(new MailboxAnnotationKey(requestReader.atom()));
+ } while (requestReader.nextWordChar() != ')');
+
+ builder.keys(keys.build());
+
+ requestReader.consumeChar(')');
+ requestReader.eol();
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
index ea57de6..c133f51 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
@@ -108,8 +108,9 @@ public class ImapParserFactory implements ImapCommandParserFactory {
_imapCommands.put(ImapConstants.SETQUOTA_COMMAND_NAME, SetQuotaCommandParser.class);
//RFC5464
- //SETMETADATA
+ //SETMETADATA, GETMETADATA
_imapCommands.put(ImapConstants.SETANNOTATION_COMMAND_NAME, SetAnnotationCommandParser.class);
+ _imapCommands.put(ImapConstants.GETANNOTATION_COMMAND_NAME, GetAnnotationCommandParser.class);
}
/**
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
new file mode 100644
index 0000000..0da09fc
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java
@@ -0,0 +1,158 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.message.request;
+
+import java.util.Set;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import org.apache.james.imap.api.ImapCommand;
+
+import com.google.common.base.Preconditions;
+import org.apache.james.mailbox.model.MailboxAnnotationKey;
+
+public class GetAnnotationRequest extends AbstractImapRequest {
+ public static class Builder {
+ private String tag;
+ private ImapCommand command;
+ private String mailboxName;
+ private Set<MailboxAnnotationKey> keys;
+ private Optional<Integer> maxsize;
+ private Depth depth;
+
+ private Builder() {
+ this.depth = Depth.ZERO;
+ this.maxsize = Optional.absent();
+ keys = ImmutableSet.of();
+ }
+
+ public Builder tag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ public Builder command(ImapCommand command) {
+ this.command = command;
+ return this;
+ }
+
+ public Builder mailboxName(String mailboxName) {
+ Preconditions.checkNotNull(mailboxName);
+ this.mailboxName = mailboxName;
+ return this;
+ }
+
+ public Builder keys(Set<MailboxAnnotationKey> keys) {
+ this.keys = ImmutableSet.copyOf(keys);
+ return this;
+ }
+
+ public Builder maxsize(Optional<Integer> maxsize) {
+ this.maxsize = maxsize;
+ return this;
+ }
+
+ public Builder depth(Depth depth) {
+ this.depth = depth;
+ return this;
+ }
+
+ public GetAnnotationRequest build() {
+ Preconditions.checkState(isNoDepth() || isDepthAndKeysNotEmpty());
+ Preconditions.checkArgument(isNoMaxsize() || maxsize.get() > 0);
+ return new GetAnnotationRequest(this);
+ }
+
+ private boolean isDepthAndKeysNotEmpty() {
+ return !Depth.ZERO.equals(depth) && !keys.isEmpty();
+ }
+
+ private boolean isNoDepth() {
+ return Depth.ZERO.equals(depth);
+ }
+
+ private boolean isNoMaxsize() {
+ return !maxsize.isPresent();
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final String mailboxName;
+ private final Set<MailboxAnnotationKey> keys;
+ private final Optional<Integer> maxsize;
+ private final Depth depth;
+
+ private GetAnnotationRequest(Builder builder) {
+ super(builder.tag, builder.command);
+ this.mailboxName = builder.mailboxName;
+ this.depth = builder.depth;
+ this.maxsize = builder.maxsize;
+ this.keys = builder.keys;
+ }
+
+ public String getMailboxName() {
+ return mailboxName;
+ }
+
+ public Set<MailboxAnnotationKey> getKeys() {
+ return keys;
+ }
+
+ public Optional<Integer> getMaxsize() {
+ return maxsize;
+ }
+
+ public Depth getDepth() {
+ return depth;
+ }
+
+ public enum Depth {
+ ZERO("0"), ONE("1"), INFINITY("infinity");
+
+ private final String code;
+
+ Depth(String code) {
+ this.code = code;
+ }
+
+ public final String getCode() {
+ return code;
+ }
+
+ public String toString() {
+ return code;
+ }
+
+ public static Depth fromString(String code) {
+ Preconditions.checkNotNull(code);
+
+ for (Depth depth : Depth.values()) {
+ if (code.equalsIgnoreCase(depth.code)) {
+ return depth;
+ }
+ }
+
+ throw new IllegalArgumentException("Cannot lookup Depth data for: " + code);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
index c8a8ceb..31b20b8 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
@@ -59,7 +59,9 @@ public class DefaultProcessorChain {
if (mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Annotation)) {
final SetAnnotationProcessor setAnnotationProcessor = new SetAnnotationProcessor(unsubscribeProcessor, mailboxManager, statusResponseFactory);
capabilityProcessor.addProcessor(setAnnotationProcessor);
- subscribeProcessor = new SubscribeProcessor(setAnnotationProcessor, mailboxManager, subscriptionManager, statusResponseFactory);
+ final GetAnnotationProcessor getAnnotationProcessor = new GetAnnotationProcessor(setAnnotationProcessor, mailboxManager, statusResponseFactory);
+ capabilityProcessor.addProcessor(getAnnotationProcessor);
+ subscribeProcessor = new SubscribeProcessor(getAnnotationProcessor, mailboxManager, subscriptionManager, statusResponseFactory);
} else {
subscribeProcessor = new SubscribeProcessor(unsubscribeProcessor, mailboxManager, subscriptionManager, statusResponseFactory);
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java
new file mode 100644
index 0000000..052e533
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java
@@ -0,0 +1,175 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.processor;
+
+import static org.apache.james.imap.api.message.response.StatusResponse.ResponseCode;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedSet;
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapSessionUtils;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapProcessor;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.message.request.GetAnnotationRequest;
+import org.apache.james.imap.message.response.AnnotationResponse;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.apache.james.mailbox.model.MailboxAnnotationKey;
+import org.apache.james.mailbox.model.MailboxPath;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+public class GetAnnotationProcessor extends AbstractMailboxProcessor<GetAnnotationRequest> implements CapabilityImplementingProcessor {
+ public GetAnnotationProcessor(ImapProcessor next, MailboxManager mailboxManager, StatusResponseFactory factory) {
+ super(GetAnnotationRequest.class, next, mailboxManager, factory);
+ }
+
+ public List<String> getImplementedCapabilities(ImapSession session) {
+ return ImmutableList.of(ImapConstants.SUPPORTS_ANNOTATION);
+ }
+
+ protected void doProcess(GetAnnotationRequest message, ImapSession session, String tag, ImapCommand command,
+ Responder responder) {
+ try {
+ proceed(message, session, tag, command, responder);
+ } catch (MailboxNotFoundException e) {
+ session.getLog().info("The command: {} is failed because not found mailbox {}", command.getName(), message.getMailboxName());
+ no(command, tag, responder, HumanReadableText.FAILURE_NO_SUCH_MAILBOX, ResponseCode.tryCreate());
+ } catch (MailboxException e) {
+ session.getLog().info("The command: {} on mailbox {} is failed", command.getName(), message.getMailboxName());
+ no(command, tag, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
+ }
+ }
+
+ private void proceed(GetAnnotationRequest message, ImapSession session, String tag, ImapCommand command, Responder responder) throws MailboxException {
+ String mailboxName = message.getMailboxName();
+ Optional<Integer> maxsize = message.getMaxsize();
+ MailboxPath mailboxPath = buildFullPath(session, mailboxName);
+
+ List<MailboxAnnotation> mailboxAnnotations = getMailboxAnnotations(session, message.getKeys(), message.getDepth(), mailboxPath);
+ Optional<Integer> maximumOversizedSize = getMaxSizeValue(mailboxAnnotations, maxsize);
+
+ respond(tag, command, responder, mailboxName, mailboxAnnotations, maxsize, maximumOversizedSize);
+ }
+
+ private void respond(String tag, ImapCommand command, Responder responder, String mailboxName,
+ List<MailboxAnnotation> mailboxAnnotations, Optional<Integer> maxsize, Optional<Integer> maximumOversizedSize) {
+ if (maximumOversizedSize.isPresent()) {
+ responder.respond(new AnnotationResponse(mailboxName, filterItemsBySize(responder, mailboxName, mailboxAnnotations, maxsize)));
+ okComplete(command, tag, ResponseCode.longestMetadataEntry(maximumOversizedSize.get()), responder);
+ } else {
+ responder.respond(new AnnotationResponse(mailboxName, mailboxAnnotations));
+ okComplete(command, tag, responder);
+ }
+ }
+
+ private Optional<Integer> getMaxSizeValue(final List<MailboxAnnotation> mailboxAnnotations, Optional<Integer> maxsize) {
+ if (maxsize.isPresent()) {
+ return maxsize.transform(new Function<Integer, Optional<Integer>>() {
+ @Override
+ public Optional<Integer> apply(Integer input) {
+ return getMaxSizeOfOversizedItems(mailboxAnnotations, input);
+ }
+ }).get();
+ }
+ return Optional.absent();
+ }
+
+ private List<MailboxAnnotation> filterItemsBySize(Responder responder, String mailboxName, List<MailboxAnnotation> mailboxAnnotations, final Optional<Integer> maxsize) {
+ Predicate<MailboxAnnotation> lowerPredicate = new Predicate<MailboxAnnotation>() {
+ @Override
+ public boolean apply(final MailboxAnnotation input) {
+ return maxsize.transform(new Function<Integer, Boolean>() {
+ @Override
+ public Boolean apply(Integer maxSizeInput) {
+ return (input.size() <= maxSizeInput);
+ }
+ }).or(true);
+ }
+ };
+
+ return FluentIterable.from(mailboxAnnotations).filter(lowerPredicate).toList();
+ }
+
+ private List<MailboxAnnotation> getMailboxAnnotations(ImapSession session, Set<MailboxAnnotationKey> keys, GetAnnotationRequest.Depth depth, MailboxPath mailboxPath) throws MailboxException {
+ MailboxSession mailboxSession = ImapSessionUtils.getMailboxSession(session);
+ switch (depth) {
+ case ZERO:
+ return getMailboxAnnotationsWithDepthZero(keys, mailboxPath, mailboxSession);
+ case ONE:
+ return getMailboxManager().getAnnotationsByKeysWithOneDepth(mailboxPath, mailboxSession, keys);
+ case INFINITY:
+ return getMailboxManager().getAnnotationsByKeysWithAllDepth(mailboxPath, mailboxSession, keys);
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ private List<MailboxAnnotation> getMailboxAnnotationsWithDepthZero(Set<MailboxAnnotationKey> keys, MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxException {
+ if (keys.isEmpty()) {
+ return getMailboxManager().getAllAnnotations(mailboxPath, mailboxSession);
+ } else {
+ return getMailboxManager().getAnnotationsByKeys(mailboxPath, mailboxSession, keys);
+ }
+ }
+
+ private Optional<Integer> getMaxSizeOfOversizedItems(List<MailboxAnnotation> mailboxAnnotations, final Integer maxsize) {
+ Predicate<MailboxAnnotation> filterOverSizedAnnotation = new Predicate<MailboxAnnotation>() {
+ @Override
+ public boolean apply(MailboxAnnotation input) {
+ return (input.size() > maxsize);
+ }
+ };
+
+ Function<MailboxAnnotation, Integer> transformToSize = new Function<MailboxAnnotation,Integer>(){
+ public Integer apply(MailboxAnnotation input) {
+ return input.size();
+ }
+ };
+
+ ImmutableSortedSet<Integer> overLimitSizes = FluentIterable.from(mailboxAnnotations)
+ .filter(filterOverSizedAnnotation)
+ .transform(transformToSize)
+ .toSortedSet(new Comparator<Integer>() {
+ @Override
+ public int compare(Integer annotationSize1, Integer annotationSize2) {
+ return annotationSize2.compareTo(annotationSize1);
+ }
+ });
+
+ if (overLimitSizes.isEmpty()) {
+ return Optional.absent();
+ }
+ return Optional.of(overLimitSizes.first());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java b/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java
index e650685..adcfccb 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java
@@ -19,9 +19,8 @@
package org.apache.james.imap.api.message.response;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
-import org.apache.james.imap.api.message.response.StatusResponse;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java
new file mode 100644
index 0000000..f8ee791
--- /dev/null
+++ b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java
@@ -0,0 +1,390 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.decode.parser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.google.common.base.Charsets;
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.decode.ImapRequestStreamLineReader;
+import org.apache.james.imap.message.request.GetAnnotationRequest;
+import org.apache.james.imap.message.request.GetAnnotationRequest.Depth;
+import org.apache.james.mailbox.model.MailboxAnnotationKey;
+import org.apache.james.protocols.imap.DecodingException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GetAnnotationCommandParserTest {
+
+ private static final String INBOX = "anyInboxName";
+ private static final String TAG = "A1";
+ private static final MailboxAnnotationKey PRIVATE_KEY = new MailboxAnnotationKey("/private/comment");
+ private static final MailboxAnnotationKey SHARED_KEY = new MailboxAnnotationKey("/shared/comment");
+ private static final ImapCommand command = ImapCommand.anyStateCommand("Command");
+ private static final ImapSession session = null;
+ private static final OutputStream outputStream = null;
+
+ private GetAnnotationCommandParser parser;
+
+ @Before
+ public void setUp() throws Exception {
+ parser = new GetAnnotationCommandParser();
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowsExceptionWhenCommandHasNotMailbox() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream(" \n".getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWhenCommandHasMailboxOnly() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getKeys()).isEmpty();
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).isAbsent();
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasOneKeyButInWrongFormat() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " /private/comment extrastring \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWhenCommandHasOnlyOneKey() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " /private/comment \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getKeys()).containsOnly(PRIVATE_KEY);
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).isAbsent();
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasOneInvalidKey() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + "/shared/comment private/comment \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWhenCommandHasMultiKeys() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getKeys()).contains(PRIVATE_KEY, SHARED_KEY);
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).isAbsent();
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButInWrongFormat() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (/another/key/group)\n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysAndSingleKey() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) /another/key \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButNotOpenQuote() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " /shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButNotCloseQuote() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeOptButInWrongPlace() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeWithWrongValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE invalid) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeWithoutValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeDoesNotInParenthesis() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " MAXSIZE 1024 (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeDoesNotInParenthesisAndNoValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " MAXSIZE (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWhenCommandHasMaxsizeOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getKeys()).contains(PRIVATE_KEY, SHARED_KEY);
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).contains(1024);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasWrongMaxsizeOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZErr 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasWrongMaxsizeValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 0) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasWrongDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH -1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasWrongDepthOptionName() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTHerr 1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButNoValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButInvalidValue() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH invalid) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButNotInParenthesis() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " DEPTH (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionAndValueButNotInParenthesis() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " DEPTH 1 (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWithZeroDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 0) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, null);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).contains(1024);
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWithOneDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.ONE);
+ assertThat(request.getMaxsize()).contains(1024);
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWhenCommandHasOptionsInAnyOrder() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (DEPTH 1) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.ONE);
+ assertThat(request.getMaxsize()).contains(1024);
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWithInfinityDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.INFINITY);
+ assertThat(request.getMaxsize()).contains(1024);
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWithOnlyInfinityDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.INFINITY);
+ assertThat(request.getMaxsize()).isAbsent();
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test
+ public void decodeMessageShouldReturnRequestWithDefaultDepthOptionWhenCommandHasDoesNotHaveDepthOption() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session);
+
+ assertThat(request.getTag()).isEqualTo(TAG);
+ assertThat(request.getCommand()).isEqualTo(command);
+ assertThat(request.getMailboxName()).isEqualTo(INBOX);
+ assertThat(request.getDepth()).isEqualTo(Depth.ZERO);
+ assertThat(request.getMaxsize()).contains(1024);
+ assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasOneDepthButWithoutKey() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 1) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasInfinityDepthButWithoutKey() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+
+ @Test(expected = DecodingException.class)
+ public void decodeMessageShouldThrowExceptionWhenCommandHasDepthOptionInWrongPlace() throws DecodingException {
+ InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (DEPTH infinity) \n").getBytes(Charsets.US_ASCII));
+ ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream);
+
+ parser.decode(command, lineReader, TAG, session);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java
----------------------------------------------------------------------
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java
new file mode 100644
index 0000000..5e4fd0d
--- /dev/null
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java
@@ -0,0 +1,329 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.processor;
+
+import static org.apache.james.imap.api.message.response.StatusResponse.ResponseCode;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapSessionState;
+import org.apache.james.imap.api.ImapSessionUtils;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.response.StatusResponse;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapProcessor;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.encode.FakeImapSession;
+import org.apache.james.imap.message.request.GetAnnotationRequest;
+import org.apache.james.imap.message.request.GetAnnotationRequest.Depth;
+import org.apache.james.imap.message.response.AnnotationResponse;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.mock.MockMailboxSession;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.apache.james.mailbox.model.MailboxAnnotationKey;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoAnnotations;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+public class GetAnnotationProcessorTest {
+ private static final String TAG = "TAG";
+ private static final int FIRST_ELEMENT_INDEX = 0;
+
+ private static final MailboxAnnotationKey PRIVATE_KEY = new MailboxAnnotationKey("/private/comment");
+ private static final MailboxAnnotationKey PRIVATE_CHILD_KEY = new MailboxAnnotationKey("/private/comment/user");
+ private static final MailboxAnnotationKey PRIVATE_GRANDCHILD_KEY = new MailboxAnnotationKey("/private/comment/user/name");
+ private static final MailboxAnnotationKey SHARED_KEY = new MailboxAnnotationKey("/shared/comment");
+
+ private static final MailboxAnnotation SHARED_ANNOTATION = MailboxAnnotation.newInstance(SHARED_KEY, "The shared size");
+ private static final MailboxAnnotation PRIVATE_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_KEY, "The short size");
+ private static final MailboxAnnotation PRIVATE_CHILD_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_CHILD_KEY, "The middle size");
+ private static final MailboxAnnotation PRIVATE_GRANDCHILD_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_GRANDCHILD_KEY, "The longest value size");
+
+ private GetAnnotationProcessor processor;
+
+ private ImapProcessor mockNextProcessor;
+ private MailboxManager mockMailboxManager;
+ private StatusResponseFactory mockStatusResponseFactory;
+ private ImapProcessor.Responder mockResponder;
+ private ImapSession mockImapSession;
+ private MailboxSession mailboxSession;
+
+ private Set<MailboxAnnotationKey> KEYS;
+ private StatusResponse statusResponse;
+
+ private GetAnnotationRequest.Builder annotationRequestBuilder;
+ private MailboxPath inbox;
+ private Logger log;
+ private ArgumentCaptor<HumanReadableText> humanTextCaptor;
+ private ArgumentCaptor<ResponseCode> captorResponsecode;
+ private ArgumentCaptor<AnnotationResponse> captorAnnotationResponse;
+
+ private void initAndMockData() {
+ statusResponse = mock(StatusResponse.class);
+ mockNextProcessor = mock(ImapProcessor.class);
+ mockMailboxManager = mock(MailboxManager.class);
+ mockStatusResponseFactory = mock(StatusResponseFactory.class);
+ mockResponder = mock(ImapProcessor.Responder.class);
+ mockImapSession = mock(ImapSession.class);
+ log = mock(Logger.class);
+
+ mailboxSession = new MockMailboxSession("username");
+ inbox = MailboxPath.inbox(mailboxSession);
+ KEYS = ImmutableSet.of(PRIVATE_KEY);
+ annotationRequestBuilder = GetAnnotationRequest.builder()
+ .tag(TAG)
+ .command(ImapCommand.anyStateCommand("Name"))
+ .mailboxName(ImapConstants.INBOX_NAME);
+ humanTextCaptor = ArgumentCaptor.forClass(HumanReadableText.class);
+ captorResponsecode = ArgumentCaptor.forClass(ResponseCode.class);
+ captorAnnotationResponse = ArgumentCaptor.forClass(AnnotationResponse.class);
+
+ when(mockImapSession.getState()).thenReturn(ImapSessionState.SELECTED);
+ when(mockImapSession.getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY)).thenReturn(mailboxSession);
+ when(mockImapSession.getLog()).thenReturn(log);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ initAndMockData();
+
+ processor = new GetAnnotationProcessor(mockNextProcessor, mockMailboxManager, mockStatusResponseFactory);
+ }
+
+ @Test
+ public void getImplementedCapabilitiesShouldContainSupportAnnotationWhenSupportedByMailboxManager() {
+ assertThat(processor.getImplementedCapabilities(new FakeImapSession())).containsExactly(ImapConstants.SUPPORTS_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldResponseNoWithFailureWhenMailboxDoesNotExist() throws Exception {
+ doThrow(MailboxNotFoundException.class).when(mockMailboxManager).getAllAnnotations(eq(inbox), eq(mailboxSession));
+ when(mockStatusResponseFactory.taggedNo(any(String.class), any(ImapCommand.class), any(HumanReadableText.class), any(ResponseCode.class)))
+ .thenReturn(statusResponse);
+
+ processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedNo(any(String.class), any(ImapCommand.class), humanTextCaptor.capture(), captorResponsecode.capture());
+ verify(mockResponder).respond(statusResponse);
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.FAILURE_NO_SUCH_MAILBOX);
+ assertThat(captorResponsecode.getAllValues()).containsOnly(ResponseCode.tryCreate());
+ }
+
+ @Test
+ public void processShouldResponseNoWithGenericFailureWhenManagerThrowMailboxException() throws Exception {
+ doThrow(MailboxException.class).when(mockMailboxManager).getAllAnnotations(eq(inbox), eq(mailboxSession));
+ when(mockStatusResponseFactory.taggedNo(any(String.class), any(ImapCommand.class), any(HumanReadableText.class)))
+ .thenReturn(statusResponse);
+
+ processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedNo(any(String.class), any(ImapCommand.class), humanTextCaptor.capture());
+ verify(mockResponder).respond(statusResponse);
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
+ }
+
+ @Test
+ public void processShouldGetAllAnnotationsAndReturnCompleteResponse() throws Exception {
+ processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession);
+
+ verify(mockMailboxManager, times(1)).getAllAnnotations(inbox, mailboxSession);
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), any(ImapCommand.class), humanTextCaptor.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsByKeysAndReturnCompleteResponse() throws Exception {
+ processor.process(annotationRequestBuilder.keys(KEYS).build(), mockResponder, mockImapSession);
+
+ verify(mockMailboxManager, times(1)).getAnnotationsByKeys(eq(inbox), eq(mailboxSession), eq(KEYS));
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), any(ImapCommand.class), humanTextCaptor.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsAndReturnCompleteResponseWithTheLongestEntryInfoWhenLimitMaxsize() throws Exception {
+ when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(10)).build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture(),
+ captorResponsecode.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED);
+ assertThat(captorResponsecode.getAllValues()).containsOnly(ResponseCode.longestMetadataEntry(22));
+ }
+
+ @Test
+ public void processShouldGetAnnotationsAndReturnCompleteResponseDoesNotTruncateDataByMaxsize() throws Exception {
+ when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(100)).build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception {
+ when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(SHARED_ANNOTATION, PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(15)).build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture(),
+ any(ResponseCode.class));
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsAndReturnCompleteResponseDoesnotTruncateDataByMaxsizeWhenNoMoreOverSizeItem() throws Exception {
+ when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(SHARED_ANNOTATION, PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(100)).build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsByOneDepthAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception {
+ when(mockMailboxManager.getAnnotationsByKeysWithOneDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(14)).depth(Depth.ONE).keys(KEYS).build(), mockResponder, mockImapSession);
+
+ verify(mockMailboxManager, times(1)).getAnnotationsByKeysWithOneDepth(eq(inbox), eq(mailboxSession), eq(KEYS));
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture(),
+ any(ResponseCode.class));
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsAndReturnCompleteResponseWithTruncateDataByLessThenOrEqualMaxsize() throws Exception {
+ when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(15)).build(), mockResponder, mockImapSession);
+
+ verify(mockMailboxManager, times(1)).getAllAnnotations(eq(inbox), eq(mailboxSession));
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture());
+
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsByInfinityDepthAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception {
+ when(mockMailboxManager.getAnnotationsByKeysWithAllDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.maxsize(Optional.of(14)).depth(Depth.INFINITY).keys(KEYS).build(), mockResponder, mockImapSession);
+
+ verify(mockMailboxManager, times(1)).getAnnotationsByKeysWithAllDepth(eq(inbox), eq(mailboxSession), eq(KEYS));
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture(),
+ any(ResponseCode.class));
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION);
+ }
+
+ @Test
+ public void processShouldGetAnnotationsByInfinityDepthAndReturnCompleteResponse() throws Exception {
+ when(mockMailboxManager.getAnnotationsByKeysWithAllDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION));
+
+ processor.process(annotationRequestBuilder.depth(Depth.INFINITY).keys(KEYS).build(), mockResponder, mockImapSession);
+
+ verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class),
+ any(ImapCommand.class),
+ humanTextCaptor.capture());
+ verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture());
+ verifyNoMoreInteractions(mockResponder);
+
+ AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX);
+ assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION);
+ }
+
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/src/site/xdoc/imap4.xml
----------------------------------------------------------------------
diff --git a/protocols/src/site/xdoc/imap4.xml b/protocols/src/site/xdoc/imap4.xml
index ee37a0a..0ec2cbd 100644
--- a/protocols/src/site/xdoc/imap4.xml
+++ b/protocols/src/site/xdoc/imap4.xml
@@ -55,8 +55,9 @@
<li>SASL-IR (in release 0.2.1)</li>
<li>ENABLE (in release 0.2.1)</li>
<li>CONDSTORE (RFC 4551 http://www.ietf.org/rfc/rfc4551.txt in release 0.3)</li>
- <li>RESYNCH (RFC 5162 http://www.ietf.org/rfc/rfc5162.txt in trunk)</li>
- <li>MOVE (RFC 6851 https://tools.ietf.org/html/rfc6851 in trunk). This is enabled only if you use a MailboxManager exposing the Move capability</li>
+ <li>RESYNCH (RFC 5162 http://www.ietf.org/rfc/rfc5162.txt on master)</li>
+ <li>MOVE (RFC 6851 https://tools.ietf.org/html/rfc6851 on master). This is enabled only if you use a MailboxManager exposing the Move capability</li>
+ <li>METADATA Extension (RFC 5464 http://www.ietf.org/rfc/rfc5464.txt on master). This is enabled only if you use a MailboxManager exposing the Annotation capability</li>
</ul>
<p>We follow RFC2683 recommendations for our implementations:</p>
<ul>
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org