diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a253de0e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea
+html
+target
diff --git a/README.md b/README.md
index b615d211..101cfa27 100644
--- a/README.md
+++ b/README.md
@@ -1,126 +1,126 @@
-
-
-# imapnio
-A Java library that supports NIO (Non-blocking I/O) based IMAP clients.
-
-The IMAP NIO client provides a framework to access an IMAP message store using None-blocking I/O mechanism. This design scales well for thousands of connections per machine and reduces contention when using a large number of threads and CPUs.
-
-
-## Table of Contents
-
-- [Background](#background)
-- [Install](#install)
-- [Usage](#usage)
-- [Release](#release)
-- [Contribute](#contribute)
-- [License](#license)
-
-
-## Background
-
-The traditional accessing IMAP message store uses [JavaMail API](https://www.oracle.com/technetwork/java/javamail/index.html), which requires a blocking I/O. In this case, threads are blocked when performing I/O with the other end. This project was developed to relieve the waiting thread to perform other tasks, and it's design efficiently improves thread utilization to maximize hardware throughput and capacity.
-
-Some of the more distinguishing features of this library are:
-- Highly customizable thread model and server/client idle max limit.
-- Leverages the well-established framework [Netty](https://netty.io/)
-- Future-based design enables a clean separation of IMAP client threads pool versus consumers threads pool.
-- IMAPv4, [RFC3501](https://tools.ietf.org/html/rfc3501) support.
-- ID command, [RFC2971](https://tools.ietf.org/html/rfc2971) support.
-- IDLE command, [RFC2177](https://tools.ietf.org/html/rfc2177) support
-- MOVE command, [RFC6851](https://tools.ietf.org/html/rfc6851) support
-- UNSELECT command, [RFC3691](https://tools.ietf.org/html/rfc3691) support
-
-This project is ideal for applications that have a high requirement to optimize thread utilization and improve overall resource capacity. Specifically, this is best for situations where users perform a very high level of sessions and communication with the IMAP server.
-
-## Install
-
-This is a Java library. After downloading it, compile it using `mvn clean install`
-
-Then, update your project's pom.xml file dependencies, as follows:
-
-```
-
- com.yahoo.imapnio
- imapnio.core
- 4.3.7
-
-```
-Finally, import the relevant classes and use this library according to the usage section below.
-
-- For contributors run deploy to do a push to nexus servers
-
-```
- $ mvn clean deploy -Dgpg.passphrase=[pathPhrase]
-```
-
-## Usage
-
-The following code examples demonstrate basic functionality relate to connecting to and communicating with IMAP servers.
-
-### Create a client
-```java
- // Create a ImapAsyncClient instance with number of threads to handle the server requests
- final int numOfThreadsServed = 5;
- final ImapAsyncClient imapClient = new ImapAsyncClient(numOfThreads);
-```
-### Establish a session with an IMAP server
-```java
- // Create a new session via the ImapAsyncClient instance created above and connect to that server. For the illustration purpose,
- // "imaps://imap.server.com:993" is used
- final URI serverUri = new URI("imaps://imap.server.com:993");
- final ImapAsyncSessionConfig config = new ImapAsyncSessionConfig();
- config.setConnectionTimeoutMillis(5000);
- config.setReadTimeoutMillis(6000);
- final List sniNames = null;
-
- final InetSocketAddress localAddress = null;
- final Future future = imapClient.createSession(serverUri, config, localAddress, sniNames, DebugMode.DEBUG_OFF);
-
- //this version is a future-based nio client. Check whether future is done by following code.
- if (future.isDone()) {
- System.out.println("Future is done.");
- }
-```
-
-### Execute the IMAP command to IMAP server
-Following codes uses a Capability command as an example.
-
-```java
- // Executes the capability command
- final Future capaCmdFuture = session.execute(new CapaCommand());
-
-```
-
-### Handle the response from IMAP server
-If the future of the executed command is done, obtain the response.
-Following example shows how to read ImapAsyncResponse which wraps the content sent from the server.
-
-```java
- if (capaCmdFuture.isDone()) {
- System.out.println("Capability command is done.");
- final ImapAsyncResponse resp = future.get(5, TimeUnit.MILLISECONDS);
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final Capability capa = mapper.readValue(resp.getResponseLines().toArray(new IMAPResponse[0]), Capability.class);
- final List values = capa.getCapability("AUTH");
- }
-```
-
-## Release
-
-This release, 2.0.x, is a major release. Changes are:
-- It supports non-blocking IO functionality through providing callers with java.util.concurrent.Future object.
-- Listener-based non-blocking IO capability will not be supported in this release.
-
-## Contribute
-
-Please refer to the [contributing.md](Contributing.md) for information about how to get involved. We welcome issues, questions, and pull requests. Pull Requests are welcome.
-
-
-## Maintainers
-
-Luis Alves: lafa@verizonmedia.com
-
-
-## License
-
-This project is licensed under the terms of the [Apache 2.0](LICENSE-Apache-2.0) open source license. Please refer to [LICENSE](LICENSE) for the full terms.
+
+
+# imapnio
+A Java library that supports NIO (Non-blocking I/O) based IMAP clients.
+
+The IMAP NIO client provides a framework to access an IMAP message store using None-blocking I/O mechanism. This design scales well for thousands of connections per machine and reduces contention when using a large number of threads and CPUs.
+
+
+## Table of Contents
+
+- [Background](#background)
+- [Install](#install)
+- [Usage](#usage)
+- [Release](#release)
+- [Contribute](#contribute)
+- [License](#license)
+
+
+## Background
+
+The traditional accessing IMAP message store uses [JavaMail API](https://www.oracle.com/technetwork/java/javamail/index.html), which requires a blocking I/O. In this case, threads are blocked when performing I/O with the other end. This project was developed to relieve the waiting thread to perform other tasks, and it's design efficiently improves thread utilization to maximize hardware throughput and capacity.
+
+Some of the more distinguishing features of this library are:
+- Highly customizable thread model and server/client idle max limit.
+- Leverages the well-established framework [Netty](https://netty.io/)
+- Future-based design enables a clean separation of IMAP client threads pool versus consumers threads pool.
+- IMAPv4, [RFC3501](https://tools.ietf.org/html/rfc3501) support.
+- ID command, [RFC2971](https://tools.ietf.org/html/rfc2971) support.
+- IDLE command, [RFC2177](https://tools.ietf.org/html/rfc2177) support
+- MOVE command, [RFC6851](https://tools.ietf.org/html/rfc6851) support
+- UNSELECT command, [RFC3691](https://tools.ietf.org/html/rfc3691) support
+
+This project is ideal for applications that have a high requirement to optimize thread utilization and improve overall resource capacity. Specifically, this is best for situations where users perform a very high level of sessions and communication with the IMAP server.
+
+## Install
+
+This is a Java library. After downloading it, compile it using `mvn clean install`
+
+Then, update your project's pom.xml file dependencies, as follows:
+
+```
+
+ com.yahoo.imapnio
+ imapnio.core
+ 4.3.8
+
+```
+Finally, import the relevant classes and use this library according to the usage section below.
+
+- For contributors run deploy to do a push to nexus servers
+
+```
+ $ mvn clean deploy -Dgpg.passphrase=[pathPhrase]
+```
+
+## Usage
+
+The following code examples demonstrate basic functionality relate to connecting to and communicating with IMAP servers.
+
+### Create a client
+```java
+ // Create a ImapAsyncClient instance with number of threads to handle the server requests
+ final int numOfThreadsServed = 5;
+ final ImapAsyncClient imapClient = new ImapAsyncClient(numOfThreads);
+```
+### Establish a session with an IMAP server
+```java
+ // Create a new session via the ImapAsyncClient instance created above and connect to that server. For the illustration purpose,
+ // "imaps://imap.server.com:993" is used
+ final URI serverUri = new URI("imaps://imap.server.com:993");
+ final ImapAsyncSessionConfig config = new ImapAsyncSessionConfig();
+ config.setConnectionTimeoutMillis(5000);
+ config.setReadTimeoutMillis(6000);
+ final List sniNames = null;
+
+ final InetSocketAddress localAddress = null;
+ final Future future = imapClient.createSession(serverUri, config, localAddress, sniNames, DebugMode.DEBUG_OFF);
+
+ //this version is a future-based nio client. Check whether future is done by following code.
+ if (future.isDone()) {
+ System.out.println("Future is done.");
+ }
+```
+
+### Execute the IMAP command to IMAP server
+Following codes uses a Capability command as an example.
+
+```java
+ // Executes the capability command
+ final Future capaCmdFuture = session.execute(new CapaCommand());
+
+```
+
+### Handle the response from IMAP server
+If the future of the executed command is done, obtain the response.
+Following example shows how to read ImapAsyncResponse which wraps the content sent from the server.
+
+```java
+ if (capaCmdFuture.isDone()) {
+ System.out.println("Capability command is done.");
+ final ImapAsyncResponse resp = future.get(5, TimeUnit.MILLISECONDS);
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final Capability capa = mapper.readValue(resp.getResponseLines().toArray(new IMAPResponse[0]), Capability.class);
+ final List values = capa.getCapability("AUTH");
+ }
+```
+
+## Release
+
+This release, 2.0.x, is a major release. Changes are:
+- It supports non-blocking IO functionality through providing callers with java.util.concurrent.Future object.
+- Listener-based non-blocking IO capability will not be supported in this release.
+
+## Contribute
+
+Please refer to the [contributing.md](Contributing.md) for information about how to get involved. We welcome issues, questions, and pull requests. Pull Requests are welcome.
+
+
+## Maintainers
+
+Yahoo Inc.: https://github.com/yahoo
+
+
+## License
+
+This project is licensed under the terms of the [Apache 2.0](LICENSE-Apache-2.0) open source license. Please refer to [LICENSE](LICENSE) for the full terms.
diff --git a/core/pom.xml b/core/pom.xml
index c297839b..a6e354a5 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -1,142 +1,142 @@
-
-
-
-
- com.yahoo.imapnio
- imapnio
- 4.3.7
- ../pom.xml
-
- 4.0.0
- imapnio.core
- jar
- ${project.artifactId}
- https://github.com/yahoo/imapnio
- imapnio component ${project.name}
-
-
- Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
-
-
-
-
- ${project.parent.basedir}
- 5
- 0
-
-
-
-
-
- org.testng
- testng
- test
-
-
-
- com.google.code.findbugs
- jsr305
-
-
-
- io.netty
- netty-handler
-
-
- com.sun.mail
- javax.mail
-
-
- org.slf4j
- slf4j-api
-
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- org.mockito
- mockito-all
- test
-
-
- commons-codec
- commons-codec
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
- default-check
-
- check
-
-
-
-
- BUNDLE
-
-
- INSTRUCTION
- COVEREDRATIO
- 0.98
-
-
- COMPLEXITY
- COVEREDRATIO
- 0.97
-
-
- LINE
- COVEREDRATIO
- 0.98
-
-
- CLASS
- COVEREDRATIO
- 1
-
-
- METHOD
- COVEREDRATIO
- 0.98
-
-
- BRANCH
- COVEREDRATIO
- 0.98
-
-
- CLASS
- MISSEDCOUNT
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ com.yahoo.imapnio
+ imapnio
+ 4.3.8
+ ../pom.xml
+
+ 4.0.0
+ imapnio.core
+ jar
+ ${project.artifactId}
+ https://github.com/yahoo/imapnio
+ imapnio component ${project.name}
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ ${project.parent.basedir}
+ 5
+ 0
+
+
+
+
+
+ org.testng
+ testng
+ test
+
+
+
+ com.google.code.findbugs
+ jsr305
+
+
+
+ io.netty
+ netty-handler
+
+
+ com.sun.mail
+ javax.mail
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.mockito
+ mockito-all
+ test
+
+
+ commons-codec
+ commons-codec
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ default-check
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ 0.98
+
+
+ COMPLEXITY
+ COVEREDRATIO
+ 0.97
+
+
+ LINE
+ COVEREDRATIO
+ 0.98
+
+
+ CLASS
+ COVEREDRATIO
+ 1
+
+
+ METHOD
+ COVEREDRATIO
+ 0.98
+
+
+ BRANCH
+ COVEREDRATIO
+ 0.98
+
+
+ CLASS
+ MISSEDCOUNT
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTerm.java b/core/src/main/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTerm.java
new file mode 100644
index 00000000..fad9f47f
--- /dev/null
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTerm.java
@@ -0,0 +1,127 @@
+package com.yahoo.imapnio.async.data;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.mail.Flags;
+import javax.mail.Message;
+import javax.mail.search.SearchTerm;
+
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
+import com.yahoo.imapnio.async.request.EntryTypeRequest;
+
+/**
+ * This class models search-modsequence with {@code EntryTypeReq} from RFC7162. ABNF from RFC7162 is documented below.
+ *
+ *
+ * {@code
+ * 3.1.5. MODSEQ Search Criterion in SEARCH
+ *
+ * The MODSEQ criterion for the SEARCH (or UID SEARCH) command allows a
+ * client to search for the metadata items that were modified since a
+ * specified moment.
+ * Syntax: MODSEQ []
+ * ============================================================================
+ * ABNF:
+ * search-modsequence = "MODSEQ" [search-modseq-ext] SP mod-sequence-valzer
+ *
+ * entry-name = entry-flag-name
+ *
+ * entry-flag-name = DQUOTE "/flags/" attr-flag DQUOTE
+ * ;; Each system or user-defined flag
+ * ;; is mapped to "/flags/".
+ * ;;
+ * ;; follows the escape rules
+ * ;; used by "quoted" string as described in
+ * ;; Section 4.3 of [RFC3501]; e.g., for the
+ * ;; flag \Seen, the corresponding
+ * ;; is "/flags/\\seen", and for the flag
+ * ;; $MDNSent, the corresponding
+ * ;; is "/flags/$mdnsent".
+ *
+ * entry-type-resp = "priv" / "shared"
+ * ;; Metadata item type.
+ *
+ * entry-type-req = entry-type-resp / "all"
+ * ;; Perform SEARCH operation on a private
+ * ;; metadata item, shared metadata item,
+ * ;; or both.
+ *
+ * }
+ *
+ */
+public final class ExtendedModifiedSinceTerm extends SearchTerm {
+
+ /** The name of the metadata item. */
+ private final Flags entryName;
+
+ /** The type of metadata item. */
+ private final EntryTypeRequest entryType;
+
+ /** The given modification sequence. */
+ private final long modSeq;
+
+ /**
+ * Initializes the {@code ExtendedModifiedSinceTerm} class with modification sequence.
+ *
+ * @param modSeq modification sequence number
+ */
+ public ExtendedModifiedSinceTerm(final long modSeq) {
+ this.entryName = null;
+ this.entryType = null;
+ this.modSeq = modSeq;
+ }
+
+ /**
+ * Initializes the {@code ExtendedModifiedSinceTerm} class with entry name, entry type, and modification sequence.
+ *
+ * @param entryName name of the metadata item
+ * @param entryType type of the metadata item
+ * @param modSeq modification sequence number
+ * @throws ImapAsyncClientException if entryName has more than one flag.
+ */
+ public ExtendedModifiedSinceTerm(@Nonnull final Flags entryName, @Nonnull final EntryTypeRequest entryType, final long modSeq)
+ throws ImapAsyncClientException {
+ final Flags.Flag[] sf = entryName.getSystemFlags(); // get the system flags
+ final String[] uf = entryName.getUserFlags(); // get the user flag strings
+ if (sf.length + uf.length != 1) {
+ throw new ImapAsyncClientException(ImapAsyncClientException.FailureType.INVALID_INPUT);
+ }
+ this.entryName = entryName;
+ this.entryType = entryType;
+ this.modSeq = modSeq;
+ }
+
+ /**
+ * @return the entry name
+ */
+ @Nullable
+ public Flags getEntryName() {
+ return entryName;
+ }
+
+ /**
+ * @return the entry type
+ */
+ @Nullable
+ public EntryTypeRequest getEntryType() {
+ return entryType;
+ }
+
+ /**
+ * @return the modification sequence
+ */
+ public long getModSeq() {
+ return modSeq;
+ }
+
+ /**
+ * This method should never be called for ExtendedModifiedSinceTerm.
+ *
+ * @param msg the date comparator is applied to this Message's MODSEQ
+ * @return UnsupportedOperationException if the method is called
+ */
+ @Override
+ public boolean match(final Message msg) {
+ throw new UnsupportedOperationException("This method should not be used. Only for override purpose.");
+ }
+}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfo.java b/core/src/main/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfo.java
index 741fec41..1e1760c4 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfo.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfo.java
@@ -15,9 +15,15 @@ public class ExtensionMailboxInfo extends MailboxInfo {
/** Literal for MAILBOXID. */
private static final String MAILBOX_ID = "MAILBOXID";
+ /** Literal for NOMODSEQ. */
+ private static final String NOMODSEQ = "NOMODSEQ";
+
/** Variable to store mailbox Id. */
private String mailboxId;
+ /** Variable to indicate whether a server doesn't support the persistent storage of mod-sequencese after enabling CONDSTORE command. */
+ private boolean isNoModSeq;
+
/**
* Initializes an instance of {@link ExtensionMailboxInfo} from the server responses for the select or examine command.
*
@@ -43,16 +49,26 @@ public ExtensionMailboxInfo(@Nonnull final IMAPResponse[] resps) throws ParsingE
ir.reset();
continue;
}
+ boolean handled = true;
key = key.toUpperCase();
if (key.equals(MAILBOX_ID)) { // example when 26 is the mailbox id:"* OK [MAILBOXID (26)] Ok"
final String[] values = ir.readSimpleList(); // reading the string, aka as above example, "(26)", within parentheses
if (values != null && values.length >= 1) {
mailboxId = values[0];
- resps[i] = null; // Nulls out this element in array to be consistent with MailboxInfo behavior
- break;
+ } else {
+ handled = false;
}
+ } else if (key.equals(NOMODSEQ)) { // example: * OK [NOMODSEQ] Sorry, this mailbox format doesn't support modsequences
+ isNoModSeq = true;
+ } else {
+ handled = false;
+ }
+
+ if (handled) {
+ resps[i] = null; // Nulls out this element in array to be consistent with MailboxInfo behavior
+ } else {
+ ir.reset(); // default back the parsing index
}
- ir.reset(); // default back the parsing index
}
}
@@ -63,4 +79,11 @@ public ExtensionMailboxInfo(@Nonnull final IMAPResponse[] resps) throws ParsingE
public String getMailboxId() {
return mailboxId;
}
+
+ /**
+ * @return isNoModSeq, true if the server return NOMODSEQ response.
+ */
+ public boolean isNoModSeq() {
+ return isNoModSeq;
+ }
}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/FetchResult.java b/core/src/main/java/com/yahoo/imapnio/async/data/FetchResult.java
new file mode 100644
index 00000000..8e785d25
--- /dev/null
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/FetchResult.java
@@ -0,0 +1,33 @@
+package com.yahoo.imapnio.async.data;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import com.sun.mail.imap.protocol.FetchResponse;
+
+/**
+ * This class provides the list of Fetch responses from fetch command response with parsing from FetchResponseMapper.
+ */
+public class FetchResult {
+
+ /** The collection of Fetch responses. */
+ private final List fetchResponses;
+
+ /**
+ * Initializes a {@link FetchResult} object with Fetch responses collection.
+ *
+ * @param fetchResponses collection of Fetch responses from fetch command result
+ */
+ public FetchResult(@Nonnull final List fetchResponses) {
+ this.fetchResponses = fetchResponses;
+ }
+
+ /**
+ * @return Fetch responses collection from fetch or UID fetch command result
+ */
+ @Nonnull
+ public List getFetchResponses() {
+ return fetchResponses;
+ }
+}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/MessageNumberSet.java b/core/src/main/java/com/yahoo/imapnio/async/data/MessageNumberSet.java
index e61862e3..d1fe15d1 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/data/MessageNumberSet.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/MessageNumberSet.java
@@ -257,4 +257,51 @@ public static String buildString(@Nullable final MessageNumberSet[] msgsets) {
}
return s.toString();
}
+
+ /**
+ * Converts an IMAP RFC3501 sequence-set syntax into an array of MessageNumberSet.
+ *
+ * @param msgNumbers the message numbers string
+ * @return the array of MessageNumberSet
+ * @throws ImapAsyncClientException will not throw
+ */
+ public static MessageNumberSet[] buildMessageNumberSets(@Nullable final String msgNumbers) throws ImapAsyncClientException {
+ if (msgNumbers == null || msgNumbers.isEmpty()) {
+ return null;
+ }
+
+ final String[] msgSetStrs = msgNumbers.split(",");
+ final MessageNumberSet[] msgSets = new MessageNumberSet[msgSetStrs.length];
+
+ int i = 0; // msgset index
+ try {
+ for (final String element : msgSetStrs) {
+ if (element.contains(":")) {
+ final String[] elements = element.split(":");
+ if (elements.length != 2) {
+ throw new ImapAsyncClientException(ImapAsyncClientException.FailureType.INVALID_INPUT);
+ }
+ if (elements[1].equals("*")) { // Ex: 1:*
+ msgSets[i] = new MessageNumberSet(Long.parseLong(elements[0]), LastMessage.LAST_MESSAGE);
+ } else if (elements[0].equals("*")) { // Ex: *:1
+ msgSets[i] = new MessageNumberSet(Long.parseLong(elements[1]), LastMessage.LAST_MESSAGE);
+ } else { // Ex: 1:2
+ msgSets[i] = new MessageNumberSet(Long.parseLong(elements[0]), Long.parseLong(elements[1]));
+ }
+ } else {
+ if (element.equals("*")) { // Ex: *
+ msgSets[i] = new MessageNumberSet(LastMessage.LAST_MESSAGE);
+ } else { // Ex: 1
+ final long num = Long.parseLong(element);
+ msgSets[i] = new MessageNumberSet(num, num);
+ }
+ }
+ i++;
+ }
+ } catch (final NumberFormatException ex) {
+ throw new ImapAsyncClientException(ImapAsyncClientException.FailureType.INVALID_INPUT);
+ }
+
+ return msgSets;
+ }
}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/SearchResult.java b/core/src/main/java/com/yahoo/imapnio/async/data/SearchResult.java
index 413c6d56..44372f4e 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/data/SearchResult.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/SearchResult.java
@@ -6,27 +6,40 @@
import javax.annotation.Nullable;
/**
- * This class provides the list of message sequence numbers from search command response.
+ * This class provides the modification sequence and the list of message sequence numbers from search command response.
*/
public class SearchResult {
/** Search command response sequence number, could be message sequence or UID. */
@Nonnull
private final List msgNumbers;
+ /** Modification sequence, is only shown when CondStore is enabled. */
+ private final Long highestModSeq;
+
/**
- * Initializes a {@link SearchResult} object with message number collection.
+ * Initializes a {@link SearchResult} object with message number collection and modification sequence.
*
* @param msgNumbers collection of message number from search command result
+ * @param highestModSeq the highest modification sequence from search command result
*/
- public SearchResult(@Nonnull final List msgNumbers) {
+ public SearchResult(@Nonnull final List msgNumbers, @Nullable final Long highestModSeq) {
this.msgNumbers = msgNumbers;
+ this.highestModSeq = highestModSeq;
}
/**
* @return message number collection from search command or UID search command result
*/
- @Nullable
+ @Nonnull
public List getMessageNumbers() {
return this.msgNumbers;
}
+
+ /**
+ * @return the highest modification sequence from search command or UID search command result
+ */
+ @Nullable
+ public Long getHighestModSeq() {
+ return this.highestModSeq;
+ }
}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/data/StoreResult.java b/core/src/main/java/com/yahoo/imapnio/async/data/StoreResult.java
new file mode 100644
index 00000000..6ab580da
--- /dev/null
+++ b/core/src/main/java/com/yahoo/imapnio/async/data/StoreResult.java
@@ -0,0 +1,68 @@
+package com.yahoo.imapnio.async.data;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.sun.mail.imap.protocol.FetchResponse;
+
+/**
+ * This class provides the highest modification sequence, the list of Fetch responses, and the list of modified messages number
+ * from store command response based on RFC3501 and RFC7162 as following.
+ *
+ *
+ */
+public class StoreResult {
+
+ /** The highest modification sequence. */
+ private Long highestModSeq;
+
+ /** The collection of Fetch responses. */
+ private final List fetchResponses;
+
+ /** The collection of message number as sequence-set, only shown when CondStore is enabled. */
+ private final MessageNumberSet[] modifiedMsgSets;
+
+ /**
+ * Initializes a {@link StoreResult} object with the highest modification sequence, Fetch responses collection,
+ * and modified message number collection.
+ *
+ * @param highestModSeq the highest modification from store command result
+ * @param fetchResponses collection of Fetch responses from store command result
+ * @param modifiedMsgSets collection of modified message number from store command result
+ */
+ public StoreResult(@Nullable final Long highestModSeq, @Nonnull final List fetchResponses,
+ @Nullable final MessageNumberSet[] modifiedMsgSets) {
+ this.highestModSeq = highestModSeq;
+ this.fetchResponses = fetchResponses;
+ this.modifiedMsgSets = modifiedMsgSets;
+ }
+
+ /**
+ * @return the highest modification sequence from store or UID store command result
+ */
+ @Nullable
+ public Long getHighestModSeq() {
+ return highestModSeq;
+ }
+
+ /**
+ * @return Fetch responses collection from store or UID store command result
+ */
+ @Nonnull
+ public List getFetchResponses() {
+ return fetchResponses;
+ }
+
+ /**
+ * @return modified message number collection from store or UID store command result
+ */
+ @Nullable
+ public MessageNumberSet[] getModifiedMsgSets() {
+ return modifiedMsgSets;
+ }
+}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractFetchCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractFetchCommand.java
index 2c63e73e..ef4d884a 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractFetchCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractFetchCommand.java
@@ -3,6 +3,7 @@
import java.nio.charset.StandardCharsets;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import com.yahoo.imapnio.async.data.MessageNumberSet;
@@ -10,18 +11,47 @@
import io.netty.buffer.Unpooled;
/**
- * This class defines IMAP fetch command request from client. ABNF in RFC3501 is described as following:
+ * This class defines IMAP fetch command request from client. ABNF in RFC3501 and RFC7162 is described as following:
*
*
*/
@@ -39,6 +69,18 @@ public abstract class AbstractFetchCommand extends ImapRequestAdapter {
/** Byte array for UID FETCH. */
private static final byte[] UID_FETCH_SP_B = UID_FETCH_SP.getBytes(StandardCharsets.US_ASCII);
+ /** Changed Since and space. */
+ private static final String CHANGEDSINCE_SP = "CHANGEDSINCE ";
+
+ /** Byte array for Changed Since and Space. */
+ private static final byte[] CHANGEDSINCE_SP_B = CHANGEDSINCE_SP.getBytes(StandardCharsets.US_ASCII);
+
+ /** Space and Vanished. */
+ private static final String SP_VANISHED = " VANISHED";
+
+ /** Byte array for Space and Vanished. */
+ private static final byte[] SP_VANISHED_B = SP_VANISHED.getBytes(StandardCharsets.US_ASCII);
+
/** Byte array for CR and LF, keeping the array local so it cannot be modified by others. */
private static final byte[] CRLF_B = { '\r', '\n' };
@@ -54,54 +96,125 @@ public abstract class AbstractFetchCommand extends ImapRequestAdapter {
/** True if prepending UID; false otherwise. */
private boolean isUid;
+ /** Changed since the given modification sequence. */
+ private Long changedSince;
+
+ /** Indicate whether vanished flag is used. */
+ private boolean isVanished;
+
/**
- * Initializes a {@link FetchCommand} with the {@link MessageNumberSet} array.
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the {@link MessageNumberSet} array and the data
+ * items.
*
* @param isUid whether prepending UID
* @param msgsets the set of message set
* @param items the data items
*/
- public AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items) {
- this(isUid, MessageNumberSet.buildString(msgsets), items);
+ protected AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items) {
+ this(isUid, MessageNumberSet.buildString(msgsets), items, null, false);
}
/**
- * Initializes a {@link FetchCommand} with the {@link MessageNumberSet} array.
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the {@link MessageNumberSet} array, and the macro.
*
* @param isUid whether prepending UID
* @param msgsets the set of message set
* @param macro the macro
*/
- public AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro) {
+ protected AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro) {
this(isUid, MessageNumberSet.buildString(msgsets), macro);
}
/**
- * Initializes a {@link FetchCommand} with the {@link MessageNumberSet} array.
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the the message numbers string, and the macro.
+ *
+ * @param isUid whether prepending UID
+ * @param msgNumbers the message numbers string
+ * @param macro the macro
+ */
+ protected AbstractFetchCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final FetchMacro macro) {
+ this(isUid, msgNumbers, macro, null, false);
+ }
+
+ /**
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the the message numbers string, and the data items.
*
* @param isUid whether prepending UID
* @param msgNumbers the message numbers string
* @param items the data items
*/
protected AbstractFetchCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final String items) {
+ this(isUid, msgNumbers, items, null, false);
+ }
+
+ /**
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the {@link MessageNumberSet} array, the data items,
+ * changed since the given modification sequence, and the flag to check whether uid fetch with vanished option.
+ *
+ * @param isUid whether prepending UID
+ * @param msgsets the set of message set
+ * @param items the data items
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished whether uid fetch with vanished option
+ */
+ public AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items,
+ @Nullable final Long changedSince, final boolean isVanished) {
+ this(isUid, MessageNumberSet.buildString(msgsets), items, changedSince, isVanished);
+ }
+
+ /**
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the {@link MessageNumberSet} array, the macro,
+ * changed since the given modification sequence, and the flag to check whether uid fetch with vanished option.
+ *
+ * @param isUid whether prepending UID
+ * @param msgsets the set of message set
+ * @param macro the macro
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished whether uid fetch with vanished option
+ */
+ public AbstractFetchCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro,
+ @Nullable final Long changedSince, final boolean isVanished) {
+ this(isUid, MessageNumberSet.buildString(msgsets), macro, changedSince, isVanished);
+ }
+
+ /**
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the message numbers string, the data items,
+ * changed since the given modification sequence, and the flag to check whether uid fetch with vanished option.
+ *
+ * @param isUid whether prepending UID
+ * @param msgNumbers the message numbers string
+ * @param items the data items
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished whether uid fetch with vanished option
+ */
+ protected AbstractFetchCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final String items,
+ @Nullable final Long changedSince, final boolean isVanished) {
this.isUid = isUid;
this.msgNumbers = msgNumbers;
this.dataItems = items;
this.macro = null;
+ this.changedSince = changedSince;
+ this.isVanished = isVanished;
}
/**
- * Initializes a {@link FetchCommand} with the {@link MessageNumberSet} array.
+ * Initializes a {@link AbstractFetchCommand} with the flag indicate whether prepending UID, the message numbers string, the macro,
+ * changed since the given modification sequence, and the flag to check whether uid fetch with vanished option.
*
* @param isUid whether prepending UID
* @param msgNumbers the message numbers string
* @param macro the macro
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished whether uid fetch with vanished option
*/
- protected AbstractFetchCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final FetchMacro macro) {
+ protected AbstractFetchCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final FetchMacro macro,
+ @Nullable final Long changedSince, final boolean isVanished) {
this.isUid = isUid;
this.msgNumbers = msgNumbers;
this.macro = macro;
this.dataItems = null;
+ this.changedSince = changedSince;
+ this.isVanished = isVanished;
}
@Override
@@ -109,11 +222,18 @@ public void cleanup() {
this.msgNumbers = null;
this.dataItems = null;
this.macro = null;
+ this.changedSince = null;
}
@Override
public ByteBuf getCommandLineBytes() {
- final ByteBuf sb = Unpooled.buffer();
+ // Ex:UID FETCH 300:500 (FLAGS) (CHANGEDSINCE 1234 VANISHED)
+ final int dataItemsSize = dataItems == null ? macro.name().length() : dataItems.length();
+ final String changedSinceStr = changedSince == null ? "" : changedSince.toString();
+ final int vanishedSize = isVanished ? SP_VANISHED.length() : 0;
+ final int len = ImapClientConstants.PAD_LEN + dataItemsSize + msgNumbers.length() + vanishedSize;
+ final ByteBuf sb = Unpooled.buffer(len);
+
sb.writeBytes(isUid ? UID_FETCH_SP_B : FETCH_SP_B);
sb.writeBytes(msgNumbers.getBytes(StandardCharsets.US_ASCII));
sb.writeByte(ImapClientConstants.SPACE);
@@ -125,6 +245,18 @@ public ByteBuf getCommandLineBytes() {
} else {
sb.writeBytes(macro.name().getBytes(StandardCharsets.US_ASCII));
}
+
+ if (changedSince != null) {
+ sb.writeByte(ImapClientConstants.SPACE);
+ sb.writeByte(ImapClientConstants.L_PAREN);
+ sb.writeBytes(CHANGEDSINCE_SP_B);
+ sb.writeBytes(changedSinceStr.getBytes(StandardCharsets.US_ASCII));
+ if (isVanished) {
+ sb.writeBytes(SP_VANISHED_B);
+ }
+ sb.writeByte(ImapClientConstants.R_PAREN);
+ }
+
sb.writeBytes(CRLF_B);
return sb;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractSearchCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractSearchCommand.java
index 67e2826e..da3d28f4 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractSearchCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractSearchCommand.java
@@ -24,26 +24,38 @@
* This class defines IMAP search command request from client.
*
*
*/
public abstract class AbstractSearchCommand extends ImapRequestAdapter {
@@ -124,7 +136,7 @@ protected AbstractSearchCommand(final boolean isUid, @Nullable final String msgN
this.charset = SearchSequence.isAscii(term) ? null : StandardCharsets.UTF_8.name();
if (term != null) {
- final SearchSequence searchSeq = new SearchSequence();
+ final ExtendedSearchSequence searchSeq = new ExtendedSearchSequence();
this.searchExpr = searchSeq.generateSequence(term, charset == null ? null : MimeUtility.javaCharset(charset));
}
this.isLiteralPlusEnabled = (capa != null) ? capa.hasCapability(ImapClientConstants.LITERAL_PLUS) : false;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractStoreFlagsCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractStoreFlagsCommand.java
index 5593bb12..7b335ace 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/AbstractStoreFlagsCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/AbstractStoreFlagsCommand.java
@@ -15,22 +15,34 @@
*
*
*
- * store = "STORE" SP sequence-set SP store-att-flags
+ * store = "STORE" SP sequence-set SP store-att-flags
*
- * store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP
- * (flag-list / (flag *(SP flag)))
- * flag-list = "(" [flag *(SP flag)] ")"
- * flag = "\Answered" / "\Flagged" / "\Deleted" /
- * "\Seen" / "\Draft" / flag-keyword / flag-extension
- * ; Does not include "\Recent"
+ * store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP
+ * (flag-list / (flag *(SP flag)))
+ * flag-list = "(" [flag *(SP flag)] ")"
*
- * flag-extension = "\" atom
- * ; Future expansion. Client implementations
- * ; MUST accept flag-extension flags. Server
- * ; implementations MUST NOT generate
- * ; flag-extension flags except as defined by
- * ; future standard or standards-track
- * ; revisions of this specification.
+ * flag = "\Answered" / "\Flagged" / "\Deleted" /
+ * "\Seen" / "\Draft" / flag-keyword / flag-extension
+ * ; Does not include "\Recent"
+ *
+ * flag-extension = "\" atom
+ * ; Future expansion. Client implementations
+ * ; MUST accept flag-extension flags. Server
+ * ; implementations MUST NOT generate
+ * ; flag-extension flags except as defined by
+ * ; future standard or standards-track
+ * ; revisions of this specification.
+ *
+ * store-modifier =/ "UNCHANGEDSINCE" SP mod-sequence-valzer
+ * ;; Only a single "UNCHANGEDSINCE" may be
+ * ;; specified in a STORE operation.
+ *
+ * mod-sequence-value = 1*DIGIT
+ * ;; Positive unsigned 63-bit integer
+ * ;; (mod-sequence)
+ * ;; (1 \leq n \leq 9,223,372,036,854,775,807).
+ *
+ * mod-sequence-valzer = "0" / mod-sequence-value
*
*/
public abstract class AbstractStoreFlagsCommand extends ImapRequestAdapter {
@@ -62,6 +74,15 @@ public abstract class AbstractStoreFlagsCommand extends ImapRequestAdapter {
/** Byte array for SILENT. */
private static final byte[] SILENT_B = SILENT.getBytes(StandardCharsets.US_ASCII);
+ /** Literal for UNCHANGEDSINCE and Space. */
+ private static final String UNCHANGEDSINCE_SP = "UNCHANGEDSINCE ";
+
+ /** Byte array for UNCHANGEDSINCE and Space. */
+ private static final byte[] UNCHANGEDSINCE_SP_B = UNCHANGEDSINCE_SP.getBytes(StandardCharsets.US_ASCII);
+
+ /** Unchanged since the modification sequence. */
+ private Long unchangedSince;
+
/** Flag whether adding UID before store command. */
private boolean isUid;
@@ -93,7 +114,24 @@ protected AbstractStoreFlagsCommand(final boolean isUid, @Nonnull final MessageN
}
/**
- * Initializes a {@link AbstractStoreFlagsCommand} with string form message numbers (could be sequence sets or UIDs) and all other parameters.
+ * Initializes a {@link AbstractStoreFlagsCommand} with the MessageNumberSet array, flags, action, silent flag whether server should return new
+ * values, and unchanged since the given modification sequence.
+ *
+ * @param isUid whether to have UID prepended
+ * @param msgsets the set of message set
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ protected AbstractStoreFlagsCommand(final boolean isUid, @Nonnull final MessageNumberSet[] msgsets, @Nonnull final Flags flags,
+ @Nonnull final FlagsAction action, final boolean silent, @Nonnull final Long unchangedSince) {
+ this(isUid, MessageNumberSet.buildString(msgsets), flags, action, silent, unchangedSince);
+ }
+
+ /**
+ * Initializes a {@link AbstractStoreFlagsCommand} with string form message numbers (could be sequence sets or UIDs), flags, action,
+ * and silent flag whether server should return new value.
*
* @param isUid whether to have UID prepended
* @param msgNumbers the message id
@@ -108,6 +146,28 @@ protected AbstractStoreFlagsCommand(final boolean isUid, @Nonnull final String m
this.flags = flags;
this.action = action;
this.isSilent = silent;
+ this.unchangedSince = null;
+ }
+
+ /**
+ * Initializes a {@link AbstractStoreFlagsCommand} with string form message numbers (could be sequence sets or UIDs), flags, action,
+ * silent flag whether server should return new values, and unchanged since the given modification sequence.
+ *
+ * @param isUid whether to have UID prepended
+ * @param msgNumbers the message id
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ protected AbstractStoreFlagsCommand(final boolean isUid, @Nonnull final String msgNumbers, @Nonnull final Flags flags,
+ @Nonnull final FlagsAction action, final boolean silent, @Nonnull final Long unchangedSince) {
+ this.isUid = isUid;
+ this.msgNumbers = msgNumbers;
+ this.flags = flags;
+ this.action = action;
+ this.isSilent = silent;
+ this.unchangedSince = unchangedSince;
}
@Override
@@ -115,16 +175,29 @@ public void cleanup() {
this.msgNumbers = null;
this.flags = null;
this.action = null;
+ this.unchangedSince = null;
}
@Override
public ByteBuf getCommandLineBytes() {
// Ex:STORE 2:4 +FLAGS (\Deleted)
- final ByteBuf sb = Unpooled.buffer();
+ final ImapArgumentFormatter argWriter = new ImapArgumentFormatter();
+ final String flagListString = argWriter.buildFlagString(flags);
+ final String unchangedSinceStr = unchangedSince == null ? "" : unchangedSince.toString();
+ final int len = ImapClientConstants.PAD_LEN + msgNumbers.length() + flagListString.length();
+ final ByteBuf sb = Unpooled.buffer(len);
sb.writeBytes(isUid ? UID_STORE_SP_B : STORE_SP_B);
sb.writeBytes(msgNumbers.getBytes(StandardCharsets.US_ASCII));
sb.writeByte(ImapClientConstants.SPACE);
+ if (unchangedSince != null) {
+ sb.writeByte(ImapClientConstants.L_PAREN);
+ sb.writeBytes(UNCHANGEDSINCE_SP_B);
+ sb.writeBytes(unchangedSinceStr.getBytes(StandardCharsets.US_ASCII));
+ sb.writeByte(ImapClientConstants.R_PAREN);
+ sb.writeByte(ImapClientConstants.SPACE);
+ }
+
if (action == FlagsAction.ADD) {
sb.writeByte(ImapClientConstants.PLUS);
} else if (action == FlagsAction.REMOVE) {
@@ -138,9 +211,8 @@ public ByteBuf getCommandLineBytes() {
}
// buildFlagString generates "(" [flag *(SP flag)] ")"
- final ImapArgumentFormatter argWriter = new ImapArgumentFormatter();
sb.writeByte(ImapClientConstants.SPACE);
- sb.writeBytes(argWriter.buildFlagString(flags).getBytes(StandardCharsets.US_ASCII));
+ sb.writeBytes(flagListString.getBytes(StandardCharsets.US_ASCII));
sb.writeBytes(CRLF_B);
return sb;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/EntryTypeRequest.java b/core/src/main/java/com/yahoo/imapnio/async/request/EntryTypeRequest.java
new file mode 100644
index 00000000..809870f6
--- /dev/null
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/EntryTypeRequest.java
@@ -0,0 +1,35 @@
+package com.yahoo.imapnio.async.request;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Metadata item type (entry-type-req) for search command with the modification sequence extension.
+ */
+public enum EntryTypeRequest {
+ /** Entry type for a private metadata item. */
+ PRIVATE("PRIV"),
+ /** Entry type for a shared metadata item. */
+ SHARED("SHARED"),
+ /** Entry type to indicate to use the biggest value among "PRIV" and "SHARED" mod-sequences for the metadata item. */
+ ALL("ALL");
+
+ /** Name of entry type. */
+ private final String typeName;
+
+ /**
+ * Initializes a {@link EntryTypeRequest} with type name.
+ *
+ * @param typeName name of entry type
+ */
+ EntryTypeRequest(@Nonnull final String typeName) {
+ this.typeName = typeName;
+ }
+
+ /**
+ * @return the entry type name
+ */
+ @Nonnull
+ public String getTypeName() {
+ return this.typeName;
+ }
+}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/ExamineFolderCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/ExamineFolderCommand.java
index 7177ab23..d039bfef 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/ExamineFolderCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/ExamineFolderCommand.java
@@ -33,6 +33,16 @@ public ExamineFolderCommand(@Nonnull final String folderName, @Nonnull final QRe
super(EXAMINE, folderName, qResyncParameter);
}
+ /**
+ * Initializes a {@link ExamineFolderCommand}.
+ *
+ * @param folderName folder name to examine
+ * @param isCondstoreEnabled whether to enable CondStore
+ */
+ public ExamineFolderCommand(@Nonnull final String folderName, final boolean isCondstoreEnabled) {
+ super(EXAMINE, folderName, isCondstoreEnabled);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.EXAMINE_FOLDER;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/ExtendedSearchSequence.java b/core/src/main/java/com/yahoo/imapnio/async/request/ExtendedSearchSequence.java
new file mode 100644
index 00000000..74d09793
--- /dev/null
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/ExtendedSearchSequence.java
@@ -0,0 +1,75 @@
+package com.yahoo.imapnio.async.request;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.mail.search.SearchException;
+import javax.mail.search.SearchTerm;
+
+import com.sun.mail.iap.Argument;
+import com.sun.mail.imap.protocol.SearchSequence;
+import com.yahoo.imapnio.async.data.ExtendedModifiedSinceTerm;
+
+/**
+ * This class extends the search sequence for modification sequence with the optional fields entry
+ * name and type defined in https://tools.ietf.org/html/rfc7162#section-3.1.5.
+ */
+public class ExtendedSearchSequence extends SearchSequence {
+
+ /** Literal for MODSEQ. */
+ private static final String MODSEQ = "MODSEQ";
+
+ /**
+ * Generate the IMAP search sequence for the given search expression.
+ *
+ * @param term the search term
+ * @return the IMAP search sequence argument
+ * @throws SearchException when search too complex
+ * @throws IOException when failing to convert the given string into bytes in the specified charset
+ */
+ public Argument generateSequence(@Nonnull final SearchTerm term) throws SearchException, IOException {
+ if (term instanceof ExtendedModifiedSinceTerm) {
+ return modifiedSince((ExtendedModifiedSinceTerm) term);
+ } else {
+ return super.generateSequence(term, null);
+ }
+ }
+
+ /**
+ * Generate the IMAP search sequence for the given search expression.
+ *
+ * @param term the search term
+ * @param charset the character set
+ * @return the IMAP search sequence argument
+ * @throws SearchException when search too complex
+ * @throws IOException when failing to convert the given string into bytes in the specified charset
+ */
+ @Override
+ public Argument generateSequence(@Nonnull final SearchTerm term, @Nullable final String charset) throws SearchException, IOException {
+ if (term instanceof ExtendedModifiedSinceTerm) {
+ return modifiedSince((ExtendedModifiedSinceTerm) term);
+ } else {
+ return super.generateSequence(term, charset);
+ }
+ }
+
+ /**
+ * Modification of SearchSequence modifiedSince method to support optional entry name and entry type.
+ *
+ * @param term the extended modified since search term
+ * @return the IMAP search sequence argument
+ */
+ private Argument modifiedSince(@Nonnull final ExtendedModifiedSinceTerm term) {
+ final Argument result = new Argument();
+ result.writeAtom(MODSEQ);
+ if (term.getEntryName() != null && term.getEntryType() != null) {
+ final ImapArgumentFormatter argWriter = new ImapArgumentFormatter();
+ result.writeAtom(argWriter.buildEntryFlagName(term.getEntryName()));
+ result.writeAtom(term.getEntryType().getTypeName());
+ }
+
+ result.writeNumber(term.getModSeq());
+ return result;
+ }
+}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/FetchCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/FetchCommand.java
index bd51ea0f..1e1cc882 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/FetchCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/FetchCommand.java
@@ -29,6 +29,28 @@ public FetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final Fe
super(false, msgsets, macro);
}
+ /**
+ * Initializes a {@link FetchCommand} with the {@code MessageNumberSet} array, fetch items, and changed since the modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param items the data items
+ * @param changedSince changed since the given modification sequence
+ */
+ public FetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items, @Nonnull final Long changedSince) {
+ super(false, msgsets, items, changedSince, false);
+ }
+
+ /**
+ * Initializes a {@link FetchCommand} with the {@code MessageNumberSet} array, macro, and changed since the modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param macro the macro
+ * @param changedSince changed since the given modification sequence
+ */
+ public FetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro, @Nonnull final Long changedSince) {
+ super(false, msgsets, macro, changedSince, false);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.FETCH;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/ImapArgumentFormatter.java b/core/src/main/java/com/yahoo/imapnio/async/request/ImapArgumentFormatter.java
index bfb02973..bba521aa 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/ImapArgumentFormatter.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/ImapArgumentFormatter.java
@@ -226,4 +226,51 @@ String buildFlagString(@Nonnull final Flags flags) {
sb.append(ImapClientConstants.R_PAREN); // terminate flag_list
return sb.toString();
}
+
+ /**
+ * Creates an IMAP entry-flag-name from the given Flags with only one flag object. The following is the ABNF from RFC7162
+ *
+ * entry-flag-name = DQUOTE "/flags/" attr-flag DQUOTE
+ * ;; Each system or user-defined flag
+ * ;; is mapped to "/flags/".
+ * ;;
+ * ;; follows the escape rules
+ * ;; used by "quoted" string as described in
+ * ;; Section 4.3 of [RFC3501]; e.g., for the
+ * ;; flag \Seen, the corresponding
+ * ;; is "/flags/\\seen", and for the flag
+ * ;; $MDNSent, the corresponding
+ * ;; is "/flags/$mdnsent".
+ *
+ *
+ * @param flags the flags with one flag
+ * @return the entry flag name string
+ */
+ String buildEntryFlagName(@Nonnull final Flags flags) {
+ final Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags
+ final String[] uf = flags.getUserFlags(); // get the user flag strings
+ final StringBuilder s = new StringBuilder("\"/flags/"); // start of entry flag name
+
+ if (sf.length == 1) {
+ s.append(ImapClientConstants.BACKSLASH);
+ if (sf[0] == Flags.Flag.ANSWERED) {
+ s.append(ANSWERED);
+ } else if (sf[0] == Flags.Flag.DELETED) {
+ s.append(DELETED);
+ } else if (sf[0] == Flags.Flag.DRAFT) {
+ s.append(DRAFT);
+ } else if (sf[0] == Flags.Flag.FLAGGED) {
+ s.append(FLAGGED);
+ } else if (sf[0] == Flags.Flag.RECENT) {
+ s.append(RECENT);
+ } else if (sf[0] == Flags.Flag.SEEN) {
+ s.append(SEEN);
+ }
+ } else { // user flag == 1
+ s.append(uf[0]);
+ }
+
+ s.append(ImapClientConstants.DQUOTA);
+ return s.toString();
+ }
}
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/ImapClientConstants.java b/core/src/main/java/com/yahoo/imapnio/async/request/ImapClientConstants.java
index a58f3af2..044321ba 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/ImapClientConstants.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/ImapClientConstants.java
@@ -47,6 +47,12 @@ final class ImapClientConstants {
/** Extra buffer length for command line builder to add. */
static final int PAD_LEN = 100;
+ /** Literal for double quota. */
+ static final char DQUOTA = '\"';
+
+ /** Literal for back slash. */
+ static final char BACKSLASH = '\\';
+
/**
* Private constructor to avoid constructing instance of this class.
*/
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/OpenFolderActionCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/OpenFolderActionCommand.java
index 3062cf9c..9e81797c 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/OpenFolderActionCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/OpenFolderActionCommand.java
@@ -20,6 +20,12 @@ abstract class OpenFolderActionCommand extends ImapRequestAdapter {
/** Byte array for CR and LF, keeping the array local so it cannot be modified by others. */
private static final byte[] CRLF_B = { '\r', '\n' };
+ /** Literal for CONDSTORE. */
+ private static final String SP_CONDSTORE = " (CONDSTORE)";
+
+ /** Byte array for CONDSTORE. */
+ private static final byte[] SP_CONDSTORE_B = SP_CONDSTORE.getBytes(StandardCharsets.US_ASCII);
+
/** Command operator, for example, "SELECT". */
private String op;
@@ -29,6 +35,9 @@ abstract class OpenFolderActionCommand extends ImapRequestAdapter {
/** Optional QResync parameter. */
private QResyncParameter qResyncParameter;
+ /** Optional CondStore parameter. */
+ private boolean isCondStoreEnabled;
+
/**
* Initializes a {@link OpenFolderActionCommand}.
*
@@ -39,6 +48,7 @@ protected OpenFolderActionCommand(@Nonnull final String op, @Nonnull final Strin
this.op = op;
this.folderName = folderName;
this.qResyncParameter = null;
+ this.isCondStoreEnabled = false;
}
/**
@@ -52,6 +62,21 @@ public OpenFolderActionCommand(@Nonnull final String op, @Nonnull final String f
this.op = op;
this.folderName = folderName;
this.qResyncParameter = qResyncParameter;
+ this.isCondStoreEnabled = false;
+ }
+
+ /**
+ * Initializes a {@link OpenFolderActionCommand}.
+ *
+ * @param op command operator
+ * @param folderName folder name
+ * @param isCondStoreEnabled whether to enable CondStore
+ */
+ protected OpenFolderActionCommand(@Nonnull final String op, @Nonnull final String folderName, final boolean isCondStoreEnabled) {
+ this.op = op;
+ this.folderName = folderName;
+ this.qResyncParameter = null;
+ this.isCondStoreEnabled = isCondStoreEnabled;
}
@Override
@@ -83,8 +108,11 @@ public ByteBuf getCommandLineBytes() throws ImapAsyncClientException {
sb.append("))");
qResyncParameterSize = sb.length();
}
+
+ final int condStoreSize = isCondStoreEnabled ? SP_CONDSTORE.length() : 0;
+
// 2 * base64Folder.length(): assuming every char needs to be escaped, goal is eliminating resizing, and avoid complex length calculation
- final int len = 2 * base64Folder.length() + ImapClientConstants.PAD_LEN + qResyncParameterSize;
+ final int len = 2 * base64Folder.length() + ImapClientConstants.PAD_LEN + qResyncParameterSize + condStoreSize;
final ByteBuf byteBuf = Unpooled.buffer(len);
byteBuf.writeBytes(op.getBytes(StandardCharsets.US_ASCII));
byteBuf.writeByte(ImapClientConstants.SPACE);
@@ -97,6 +125,10 @@ public ByteBuf getCommandLineBytes() throws ImapAsyncClientException {
byteBuf.writeBytes(sb.toString().getBytes(StandardCharsets.US_ASCII));
}
+ if (isCondStoreEnabled) {
+ byteBuf.writeBytes(SP_CONDSTORE_B);
+ }
+
byteBuf.writeBytes(CRLF_B);
return byteBuf;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/SelectFolderCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/SelectFolderCommand.java
index bd567082..42ae26fd 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/SelectFolderCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/SelectFolderCommand.java
@@ -31,6 +31,16 @@ public SelectFolderCommand(@Nonnull final String folderName, @Nonnull final QRes
super(SELECT, folderName, qResyncParameter);
}
+ /**
+ * Initializes a {@link SelectFolderCommand}.
+ *
+ * @param folderName folder name to select
+ * @param isCondstoreEnabled whether to enable CondStore
+ */
+ public SelectFolderCommand(@Nonnull final String folderName, final boolean isCondstoreEnabled) {
+ super(SELECT, folderName, isCondstoreEnabled);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.SELECT_FOLDER;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/StoreFlagsCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/StoreFlagsCommand.java
index 04baf568..cf65565e 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/StoreFlagsCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/StoreFlagsCommand.java
@@ -47,6 +47,50 @@ public StoreFlagsCommand(@Nonnull final String msgNumbers, @Nonnull final Flags
super(false, msgNumbers, flags, action, silent);
}
+ /**
+ * Initializes a {@link StoreFlagsCommand} with the MessageNumberSet array, Flags, action, and unchanged since modification sequence.
+ * Requests server to return the new value.
+ *
+ * @param msgsets the set of message set
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public StoreFlagsCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ @Nonnull final Long unchangedSince) {
+ super(false, msgsets, flags, action, false, unchangedSince);
+ }
+
+ /**
+ * Initializes a {@link StoreFlagsCommand} with the MessageNumberSet array, flags, action, flag whether to request server to return the new
+ * value, and unchanged since modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public StoreFlagsCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ final boolean silent, @Nonnull final Long unchangedSince) {
+ super(false, msgsets, flags, action, silent, unchangedSince);
+ }
+
+ /**
+ * Initializes a {@link StoreFlagsCommand} with string form message numbers, Flags, action, flag whether to request server to return the new
+ * value.
+ *
+ * @param msgNumbers the message numbers in string format
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently; false if requesting server to return the new values
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public StoreFlagsCommand(@Nonnull final String msgNumbers, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ final boolean silent, @Nonnull final Long unchangedSince) {
+ super(false, msgNumbers, flags, action, silent, unchangedSince);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.STORE_FLAGS;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/UidFetchCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/UidFetchCommand.java
index 712c3397..a8a41172 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/UidFetchCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/UidFetchCommand.java
@@ -49,6 +49,56 @@ public UidFetchCommand(@Nonnull final String uids, @Nonnull final FetchMacro mac
super(true, uids, macro);
}
+ /**
+ * Initializes a {@link UidFetchCommand} with the {@code MessageNumberSet} array, data items, and changed since the modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param items the data items
+ * @param changedSince changed since the given modification sequence
+ */
+ public UidFetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items, @Nonnull final Long changedSince) {
+ super(true, msgsets, items, changedSince, false);
+ }
+
+ /**
+ * Initializes a {@link UidFetchCommand} with the {@code MessageNumberSet} array, macro, and changed since the modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param macro the macro, for example, ALL
+ * @param changedSince changed since the given modification sequence
+ */
+ public UidFetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro, @Nonnull final Long changedSince) {
+ super(true, msgsets, macro, changedSince, false);
+ }
+
+ /**
+ * Initializes a {@link UidFetchCommand} with the {@code MessageNumberSet} array, data items, changed since the modification sequence,
+ * and isVanished flag.
+ *
+ * @param msgsets the set of message set
+ * @param items the data items
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished the flag to check whether uid fetch with isVanished option
+ */
+ public UidFetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final String items, @Nonnull final Long changedSince,
+ final boolean isVanished) {
+ super(true, msgsets, items, changedSince, isVanished);
+ }
+
+ /**
+ * Initializes a {@link UidFetchCommand} with the {@code MessageNumberSet} array, macro, changed since the modification sequence,
+ * and isVanished flag.
+ *
+ * @param msgsets the set of message set
+ * @param macro the macro, for example, ALL
+ * @param changedSince changed since the given modification sequence
+ * @param isVanished the flag to check whether uid fetch with isVanished option
+ */
+ public UidFetchCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final FetchMacro macro, @Nonnull final Long changedSince,
+ final boolean isVanished) {
+ super(true, msgsets, macro, changedSince, isVanished);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.UID_FETCH;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommand.java b/core/src/main/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommand.java
index d8239872..95c94660 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommand.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommand.java
@@ -48,6 +48,49 @@ public UidStoreFlagsCommand(@Nonnull final String uids, @Nonnull final Flags fla
super(true, uids, flags, action, silent);
}
+ /**
+ * Initializes a {@link UidStoreFlagsCommand} with the MessageNumberSet array, Flags and action. Requests server to return the new value.
+ *
+ * @param msgsets the set of message set
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public UidStoreFlagsCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ @Nonnull final Long unchangedSince) {
+ super(true, msgsets, flags, action, false, unchangedSince);
+ }
+
+ /**
+ * Initializes a {@link UidStoreFlagsCommand} with the MessageNumberSet array, Flags, action, flag whether to request server to return the new
+ * value, and unchanged since the modification sequence.
+ *
+ * @param msgsets the set of message set
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently; false if requesting server to return the new values
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public UidStoreFlagsCommand(@Nonnull final MessageNumberSet[] msgsets, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ final boolean silent, @Nonnull final Long unchangedSince) {
+ super(true, msgsets, flags, action, silent, unchangedSince);
+ }
+
+ /**
+ * Initializes a {@link UidStoreFlagsCommand} with string form message numbers, Flags, action, flag whether to request server to return the new
+ * value, and the unchanged since the given modification sequence.
+ *
+ * @param uids the string representing UID based on RFC3501
+ * @param flags the flags to be stored
+ * @param action whether to replace, add or remove the flags
+ * @param silent true if asking server to respond silently; false if requesting server to return the new values
+ * @param unchangedSince unchanged since the given modification sequence
+ */
+ public UidStoreFlagsCommand(@Nonnull final String uids, @Nonnull final Flags flags, @Nonnull final FlagsAction action,
+ final boolean silent, @Nonnull final Long unchangedSince) {
+ super(true, uids, flags, action, silent, unchangedSince);
+ }
+
@Override
public ImapRFCSupportedCommandType getCommandType() {
return ImapRFCSupportedCommandType.UID_STORE_FLAGS;
diff --git a/core/src/main/java/com/yahoo/imapnio/async/response/ImapResponseMapper.java b/core/src/main/java/com/yahoo/imapnio/async/response/ImapResponseMapper.java
index 06acf2c5..6400c5c3 100644
--- a/core/src/main/java/com/yahoo/imapnio/async/response/ImapResponseMapper.java
+++ b/core/src/main/java/com/yahoo/imapnio/async/response/ImapResponseMapper.java
@@ -1,541 +1,677 @@
-package com.yahoo.imapnio.async.response;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.mail.Folder;
-
-import com.sun.mail.iap.ParsingException;
-import com.sun.mail.iap.Response;
-import com.sun.mail.imap.AppendUID;
-import com.sun.mail.imap.CopyUID;
-import com.sun.mail.imap.protocol.IMAPResponse;
-import com.sun.mail.imap.protocol.ListInfo;
-import com.sun.mail.imap.protocol.MailboxInfo;
-import com.sun.mail.imap.protocol.Status;
-import com.sun.mail.imap.protocol.UIDSet;
-import com.yahoo.imapnio.async.data.Capability;
-import com.yahoo.imapnio.async.data.EnableResult;
-import com.yahoo.imapnio.async.data.ExtensionListInfo;
-import com.yahoo.imapnio.async.data.ExtensionMailboxInfo;
-import com.yahoo.imapnio.async.data.IdResult;
-import com.yahoo.imapnio.async.data.ListInfoList;
-import com.yahoo.imapnio.async.data.ListStatusResult;
-import com.yahoo.imapnio.async.data.SearchResult;
-import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
-import com.yahoo.imapnio.async.exception.ImapAsyncClientException.FailureType;
-
-/**
- * This class parses the IMAP response to the proper IMAP object that SUN supports.
- */
-public class ImapResponseMapper {
-
- /** APPENDUID keyword. */
- private static final String APPENDUID = "APPENDUID";
-
- /** EQUAL sign. */
- private static final String EQUAL = "=";
-
- /** [ char. */
- private static final char L_BRACKET = '[';
-
- /** ] char. */
- private static final char R_BRACKET = ']';
-
- /** Inner class instance parser. */
- private ImapResponseParser parser;
-
- /**
- * Initializes a {@link ImapResponseMapper} object.
- */
- public ImapResponseMapper() {
- parser = new ImapResponseParser();
- }
-
- /**
- * Method to deserialize IMAPResponse content from given IMAPResponse content String.
- *
- * @param the object to serialize to
- * @param content list of IMAPResponse obtained from server.
- * @param valueType class name that this api will convert to.
- * @return the serialized object
- * @throws ParsingException if underlying input contains invalid content of type for the returned type
- * @throws ImapAsyncClientException when target class to covert to is not supported
- */
- @SuppressWarnings("unchecked")
- @Nonnull
- public T readValue(@Nonnull final IMAPResponse[] content, @Nonnull final Class valueType)
- throws ImapAsyncClientException, ParsingException {
- if (valueType == Capability.class) {
- return (T) parser.parseToCapabilities(content);
- }
- if (valueType == AppendUID.class) {
- return (T) parser.parseToAppendUid(content);
- }
- if (valueType == CopyUID.class) {
- return (T) parser.parseToCopyUid(content);
- }
- if (valueType == ExtensionMailboxInfo.class) {
- return (T) parser.parseToExtensionMailboxInfo(content);
- }
- if (valueType == MailboxInfo.class) {
- return (T) parser.parseToMailboxInfo(content);
- }
- if (valueType == ListInfoList.class) {
- return (T) parser.parseToListInfoList(content);
- }
- if (valueType == Status.class) {
- return (T) parser.parseToStatus(content);
- }
- if (valueType == IdResult.class) {
- return (T) parser.parseToIdResult(content);
- }
- if (valueType == SearchResult.class) {
- return (T) parser.parseToSearchResult(content);
- }
- if (valueType == ListStatusResult.class) {
- return (T) parser.parseToListStatusResult(content);
- }
- if (valueType == EnableResult.class) {
- return (T) parser.parseToEnableResult(content);
- }
- throw new ImapAsyncClientException(FailureType.UNKNOWN_PARSE_RESULT_TYPE);
- }
-
- /**
- * Inner class to perform the parsing of IMAPResponse to various objects.
- */
- private class ImapResponseParser {
-
- /** Capability string. */
- private static final String CAPABILITY = "CAPABILITY";
-
- /**
- * Parses the capabilities from a CAPABILITY response or from a CAPABILITY response code attached to (e.g.) an OK response.
- *
- * @param rs the CAPABILITY responses
- * @return a map that has the key (token) and all its values (after = sign), if there is no equal sign, value is same as key
- * @throws ImapAsyncClientException when input IMAPResponse array is not valid
- */
- @Nonnull
- private Capability parseToCapabilities(@Nonnull final IMAPResponse[] rs) throws ImapAsyncClientException {
- String s;
- final Map> capas = new HashMap>();
- if (rs.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
-
- for (final IMAPResponse r : rs) {
- if (!hasCapability(r)) {
- continue;
- }
- while ((s = r.readAtom()) != null) {
- if (s.length() == 0) {
- if (r.peekByte() == (byte) R_BRACKET) {
- break;
- }
- // Probably found something here that's not an atom. Rather than loop forever or fail completely, we'll try to skip this bogus
- // capability. This is known to happen with: Netscape Messaging Server 4.03 (built Apr 27 1999) that returns:
- // * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ...
- // The "*" in the middle of the capability list causes us to loop forever here.
- r.skipToken();
- } else {
- final String[] tokens = s.split(EQUAL);
- final String key = tokens[0];
- final String value = (tokens.length > 1) ? tokens[1] : null;
- final String upperCase = key.toUpperCase(Locale.ENGLISH);
- List values = capas.get(upperCase);
- if (values == null) {
- values = new ArrayList<>();
- capas.put(upperCase, values);
- }
- // AUTH key allows more than one pair(ex:AUTH=XOAUTH2 AUTH=PLAIN), parsing value out to List, otherwise add key to list
- if (value != null) {
- values.add(value);
- }
- }
- }
- }
- // making the value list immutable
- for (final Map.Entry> entry : capas.entrySet()) {
- entry.setValue(Collections.unmodifiableList(entry.getValue()));
- }
- return new Capability(capas);
- }
-
- /**
- * Returns true if the response has capability keyword; false otherwise.
- *
- * @param r the response to check
- * @return true if the response has capability keyword; false otherwise
- */
- private boolean hasCapability(final IMAPResponse r) {
- // case 1, from capability or authenticate command. EX: * CAPABILITY IMAP4rev1 SASL-IR
- if (r.keyEquals(CAPABILITY)) {
- return true;
- }
-
- // case 2. from server greeting. EX: OK [CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN] IMAP4rev1 Hello
- byte b;
- while ((b = r.readByte()) > 0 && b != (byte) '[') {
- // eat chars till [
- }
- if (b == 0) { // left bracket not found
- return false;
- }
- final String s = r.readAtom();
- return s.equalsIgnoreCase(CAPABILITY);
- }
-
- /**
- * Parses APPEND response to a AppendUID instance.
- *
- * @param rs the APPEND responses
- * @return AppendUID instance
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nullable
- private AppendUID parseToAppendUid(@Nonnull final IMAPResponse[] rs) throws ImapAsyncClientException {
- if (rs.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final IMAPResponse r = rs[rs.length - 1];
- if (!r.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- byte b;
- while ((b = r.readByte()) > 0 && b != (byte) L_BRACKET) {
- // eat chars till [
- }
-
- if (b == 0) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final String s = r.readAtom();
- if (!s.equalsIgnoreCase(APPENDUID)) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final long uidvalidity = r.readLong();
- final long uid = r.readLong();
- return new AppendUID(uidvalidity, uid);
- }
-
- /**
- * Parses COPY or MOVE command responses to a CopyUID instance.
- *
- * @param rr the COPY responses
- * @return COPYUID instance built from copy command responses
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private CopyUID parseToCopyUid(@Nonnull final IMAPResponse[] rr) throws ImapAsyncClientException {
- // For copy response, it is at the last response, for move command response, it is the first response
- for (int i = rr.length - 1; i >= 0; i--) {
- final Response r = rr[i];
- if (r == null || !r.isOK()) {
- continue;
- }
- byte b;
- while ((b = r.readByte()) > 0 && b != (byte) L_BRACKET) {
- // eat chars till [
- }
-
- if (b == 0) {
- continue;
- }
- final String s = r.readAtom();
- if (!s.equalsIgnoreCase("COPYUID")) { // expunge response from MOVE, for ex: 2 EXPUNGE
- continue;
- }
- final long uidvalidity = r.readLong();
- final String src = r.readAtom();
- final String dst = r.readAtom();
- return new CopyUID(uidvalidity, UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst));
- }
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT); // when rr length is 0
- }
-
- /**
- * Parses the SELECT or EXAMINE responses to a MailboxInfo instance.
- *
- * @param rr the list of responses from SELECT or EXAMINE, this input r array should contain the tagged/final one
- * @return MailboxInfo instance
- * @throws ParsingException when encountering parsing exception
- * @throws ImapAsyncClientException when input is invalid
- */
- @Nonnull
- private MailboxInfo parseToMailboxInfo(@Nonnull final IMAPResponse[] rr) throws ParsingException, ImapAsyncClientException {
- if (rr.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final MailboxInfo minfo = new MailboxInfo(rr);
- setupMailboxInfoAccessMode(rr[rr.length - 1], minfo);
-
- return minfo;
- }
-
- /**
- * Sets up the mode for the {@link MailboxInfo}.
- *
- * @param lastResp the tagged response
- * @param minfo the {@link MailboxInfo} instance
- */
- private void setupMailboxInfoAccessMode(@Nonnull final Response lastResp, @Nonnull final MailboxInfo minfo) {
- if (lastResp.isTagged() && lastResp.isOK()) { // command successful
- if (lastResp.toString().indexOf("READ-ONLY") != -1) {
- minfo.mode = Folder.READ_ONLY;
- } else {
- minfo.mode = Folder.READ_WRITE;
- }
- }
- }
-
- /**
- * Parses the SELECT or EXAMINE responses to a {@link ExtensionMailboxInfo} instance.
- *
- * @param rr the list of responses from SELECT or EXAMINE, this input r array should contain the tagged/final one
- * @return MailboxInfo instance
- * @throws ParsingException when encountering parsing exception
- * @throws ImapAsyncClientException when input is invalid
- */
- @Nonnull
- private ExtensionMailboxInfo parseToExtensionMailboxInfo(@Nonnull final IMAPResponse[] rr) throws ParsingException, ImapAsyncClientException {
- if (rr.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final ExtensionMailboxInfo minfo = new ExtensionMailboxInfo(rr);
- setupMailboxInfoAccessMode(rr[rr.length - 1], minfo);
- return minfo;
- }
-
- /**
- * Parses the LIST or LSUB responses to a {@link ListInfo} list. List responses example:
- *
- *
- * * LIST () "/" INBOX
- * * LIST (\NoSelect) "/" "Public mailboxes"
- *
- *
- * @param r the list of responses from SELECT, the input responses array should contain the tagged/final one
- * @return list of ListInfo objects
- * @throws ParsingException when encountering parsing exception
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private ListInfoList parseToListInfoList(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
- if (r.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response response = r[r.length - 1];
- if (!response.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
-
- // command successful reaching here
- final List v = new ArrayList();
- for (int i = 0, len = r.length - 1; i < len; i++) {
- final IMAPResponse ir = r[i];
- if (ir.keyEquals("LIST") || ir.keyEquals("LSUB")) {
- v.add(new ListInfo(ir));
- }
- }
-
- // could be an empty list if the search criteria ends up no result. Ex:
- // a002 LIST "" "*t3*"
- // a002 OK LIST completed
- return new ListInfoList(v);
- }
-
- /**
- * Parses the Status responses to a {@link Status}.
- *
- * @param r the list of responses from Status command, the input responses array should contain the tagged/final one
- * @return Status object constructed based on the r array
- * @throws ParsingException when encountering parsing exception
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private Status parseToStatus(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
- if (r.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response taggedResponse = r[r.length - 1];
- if (!taggedResponse.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- Status status = null;
- for (int i = 0, len = r.length; i < len; i++) {
- final IMAPResponse ir = r[i];
- if (ir.keyEquals("STATUS")) {
- if (status == null) {
- status = new Status(ir);
- } else { // collect them all if each attributes comes in its own line
- Status.add(status, new Status(ir));
- }
- }
- }
- if (status == null) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT); // when r length is 0 or no Status response
- }
- return status;
- }
-
- /**
- * Parses the LIST-STATUS command responses to a ListStatusResult object. See RFC 5819 for details. For each selectable mailbox matching the
- * list pattern and selection options, the server MUST return an untagged LIST response followed by an untagged STATUS response containing the
- * information requested in the STATUS return option. If an attempted STATUS for a listed mailbox fails because the mailbox can't be selected
- * , the STATUS response MUST NOT be returned and the LIST response MUST include the \NoSelect attribute.
- *
- * @param r the list of responses from Status command, the input responses array should contain the tagged/final one
- * @return Status object constructed based on the r array
- * @throws ParsingException when encountering parsing exception
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private ListStatusResult parseToListStatusResult(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
- if (r.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response taggedResponse = r[r.length - 1];
- if (!taggedResponse.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- // command successful reaching here
- final List v = new ArrayList();
- final Map m = new HashMap();
-
- for (int i = 0; i < r.length; i++) {
- final IMAPResponse ir = r[i];
-
- if (ir.keyEquals("LIST")) {
- v.add(new ExtensionListInfo(ir));
- } else if (ir.keyEquals("STATUS")) {
- final Status status = new Status(ir);
- m.put(status.mbox, status);
- }
- }
-
- return new ListStatusResult(v, m);
- }
-
- /**
- * Parses the ID responses to a {@link IdResult} object.
- *
- * @param ir the list of responses from ID command, the input responses array should contain the tagged/final one
- * @return IdResult object constructed based on the given IMAPResponse array
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private IdResult parseToIdResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
- if (ir.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response taggedResponse = ir[ir.length - 1];
- if (!taggedResponse.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
-
- final Map serverParams = new HashMap();
- for (int j = 0, len = ir.length; j < len; j++) {
- final IMAPResponse r = ir[j];
- if (r.keyEquals("ID")) {
- r.skipSpaces();
- int c = r.peekByte();
- if (c == 'N' || c == 'n') { // assume NIL
- return new IdResult(Collections.unmodifiableMap(Collections.EMPTY_MAP));
- }
-
- final String[] v = r.readStringList();
- if (v == null) {
- // this means it does not start with (, ID result is expected to have () enclosed
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
-
- for (int i = 0; i < v.length; i += 2) {
- final String name = v[i];
- if (name == null || (i + 1 >= v.length)) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final String value = v[i + 1];
- serverParams.put(name, value);
- }
- }
- }
-
- return new IdResult(Collections.unmodifiableMap(serverParams));
- }
-
- /**
- * Parses the Enable responses to a {@link EnableResult} object.
- *
- * @param ir the list of responses from ENABLE command, the input responses array should contain the tagged/final one
- * @return EnableResult object constructed based on the given IMAPResponse array
- * @throws ImapAsyncClientException when input value is not valid
- */
- @Nonnull
- private EnableResult parseToEnableResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
- if (ir.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response taggedResponse = ir[ir.length - 1];
- if (!taggedResponse.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
-
- final Set enabledCapabilities = new HashSet();
- for (final IMAPResponse resp : ir) {
- if (resp.isTagged()) {
- continue;
- }
- if (resp.keyEquals("ENABLED")) {
- String s;
- while ((s = resp.readAtom()) != null && s.length() != 0) {
- enabledCapabilities.add(s.toUpperCase());
- }
- }
- }
-
- return new EnableResult(enabledCapabilities);
- }
-
- /**
- * Parses the responses from UID search command to a {@link SearchResult} object.
- *
- * @param ir the list of responses from UID search command, the input responses array should contain the tagged/final one
- * @return SearchResult object constructed based on the given IMAPResponse array
- * @throws ImapAsyncClientException when tagged response is not OK or given response length is 0
- */
- @Nonnull
- private SearchResult parseToSearchResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
- if (ir.length < 1) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final Response taggedResponse = ir[ir.length - 1];
- if (!taggedResponse.isOK()) {
- throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
- }
- final List v = new ArrayList(); // will always return a non-null array
-
- // Grab all SEARCH responses
- long num;
- for (final IMAPResponse sr : ir) {
- // There *will* be one SEARCH response.
- if (sr.keyEquals("SEARCH")) {
- while ((num = sr.readLong()) != -1) {
- v.add(Long.valueOf(num));
- }
- }
- }
-
- return new SearchResult(v);
- }
- }
-}
+package com.yahoo.imapnio.async.response;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.mail.Folder;
+
+import com.sun.mail.iap.ParsingException;
+import com.sun.mail.iap.ProtocolException;
+import com.sun.mail.iap.Response;
+import com.sun.mail.imap.AppendUID;
+import com.sun.mail.imap.CopyUID;
+import com.sun.mail.imap.protocol.FetchItem;
+import com.sun.mail.imap.protocol.FetchResponse;
+import com.sun.mail.imap.protocol.IMAPResponse;
+import com.sun.mail.imap.protocol.ListInfo;
+import com.sun.mail.imap.protocol.MailboxInfo;
+import com.sun.mail.imap.protocol.Status;
+import com.sun.mail.imap.protocol.UIDSet;
+import com.yahoo.imapnio.async.data.Capability;
+import com.yahoo.imapnio.async.data.EnableResult;
+import com.yahoo.imapnio.async.data.ExtensionListInfo;
+import com.yahoo.imapnio.async.data.ExtensionMailboxInfo;
+import com.yahoo.imapnio.async.data.FetchResult;
+import com.yahoo.imapnio.async.data.IdResult;
+import com.yahoo.imapnio.async.data.ListInfoList;
+import com.yahoo.imapnio.async.data.ListStatusResult;
+import com.yahoo.imapnio.async.data.MessageNumberSet;
+import com.yahoo.imapnio.async.data.SearchResult;
+import com.yahoo.imapnio.async.data.StoreResult;
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException.FailureType;
+
+/**
+ * This class parses the IMAP response to the proper IMAP object that SUN supports.
+ */
+public class ImapResponseMapper {
+
+ /** APPENDUID keyword. */
+ private static final String APPENDUID = "APPENDUID";
+
+ /** HIGHESTMODSEQ keyword. */
+ private static final String HIGHESTMODSEQ = "HIGHESTMODSEQ";
+
+ /** MODIFIED keyword. */
+ private static final String MODIFIED = "MODIFIED";
+
+ /** MODSEQ keyword. */
+ private static final String MODSEQ = "MODSEQ";
+
+ /** FETCH keyword. */
+ private static final String FETCH = "FETCH";
+
+ /** EQUAL sign. */
+ private static final String EQUAL = "=";
+
+ /** [ char. */
+ private static final char L_BRACKET = '[';
+
+ /** ] char. */
+ private static final char R_BRACKET = ']';
+
+ /** ( char. */
+ private static final char L_PAREN = '(';
+
+ /** Inner class instance parser. */
+ private ImapResponseParser parser;
+
+ /**
+ * Initializes a {@link ImapResponseMapper} object.
+ */
+ public ImapResponseMapper() {
+ parser = new ImapResponseParser();
+ }
+
+ /**
+ * Method to deserialize IMAPResponse content from given IMAPResponse content String.
+ *
+ * @param the object to serialize to
+ * @param content list of IMAPResponse obtained from server.
+ * @param valueType class name that this api will convert to.
+ * @return the serialized object
+ * @throws ImapAsyncClientException when target class to covert to is not supported
+ * @throws ParsingException if underlying input contains invalid content of type for the returned type
+ */
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public T readValue(@Nonnull final IMAPResponse[] content, @Nonnull final Class valueType)
+ throws ImapAsyncClientException, ParsingException {
+ if (valueType == Capability.class) {
+ return (T) parser.parseToCapabilities(content);
+ }
+ if (valueType == AppendUID.class) {
+ return (T) parser.parseToAppendUid(content);
+ }
+ if (valueType == CopyUID.class) {
+ return (T) parser.parseToCopyUid(content);
+ }
+ if (valueType == ExtensionMailboxInfo.class) {
+ return (T) parser.parseToExtensionMailboxInfo(content);
+ }
+ if (valueType == MailboxInfo.class) {
+ return (T) parser.parseToMailboxInfo(content);
+ }
+ if (valueType == ListInfoList.class) {
+ return (T) parser.parseToListInfoList(content);
+ }
+ if (valueType == Status.class) {
+ return (T) parser.parseToStatus(content);
+ }
+ if (valueType == IdResult.class) {
+ return (T) parser.parseToIdResult(content);
+ }
+ if (valueType == SearchResult.class) {
+ return (T) parser.parseToSearchResult(content);
+ }
+ if (valueType == ListStatusResult.class) {
+ return (T) parser.parseToListStatusResult(content);
+ }
+ if (valueType == EnableResult.class) {
+ return (T) parser.parseToEnableResult(content);
+ }
+ if (valueType == FetchResult.class) {
+ return (T) parser.parseToFetchResult(content, new FetchItem[0]);
+ }
+ if (valueType == StoreResult.class) {
+ return (T) parser.parseToStoreResult(content);
+ }
+
+ throw new ImapAsyncClientException(FailureType.UNKNOWN_PARSE_RESULT_TYPE);
+ }
+
+ /**
+ * Method to deserialize IMAPResponse content from given IMAPResponse content String.
+ *
+ * @param the object to serialize to
+ * @param content list of IMAPResponse obtained from server.
+ * @param valueType class name that this api will convert to.
+ * @param extensionItems the array of extension FetchItem
+ * @return the serialized object
+ * @throws ImapAsyncClientException when target class to covert to is not supported
+ */
+ @Nonnull
+ public T readValue(@Nonnull final IMAPResponse[] content, @Nonnull final Class valueType, @Nonnull final FetchItem[] extensionItems)
+ throws ImapAsyncClientException {
+ if (valueType == FetchResult.class) {
+ return (T) parser.parseToFetchResult(content, extensionItems);
+ }
+
+ throw new ImapAsyncClientException(FailureType.UNKNOWN_PARSE_RESULT_TYPE);
+ }
+
+ /**
+ * Inner class to perform the parsing of IMAPResponse to various objects.
+ */
+ private class ImapResponseParser {
+
+ /** Capability string. */
+ private static final String CAPABILITY = "CAPABILITY";
+
+ /**
+ * Parses the capabilities from a CAPABILITY response or from a CAPABILITY response code attached to (e.g.) an OK response.
+ *
+ * @param rs the CAPABILITY responses
+ * @return a map that has the key (token) and all its values (after = sign), if there is no equal sign, value is same as key
+ * @throws ImapAsyncClientException when input IMAPResponse array is not valid
+ */
+ @Nonnull
+ private Capability parseToCapabilities(@Nonnull final IMAPResponse[] rs) throws ImapAsyncClientException {
+ String s;
+ final Map> capas = new HashMap>();
+ if (rs.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ for (final IMAPResponse r : rs) {
+ if (!hasCapability(r)) {
+ continue;
+ }
+ while ((s = r.readAtom()) != null) {
+ if (s.length() == 0) {
+ if (r.peekByte() == (byte) R_BRACKET) {
+ break;
+ }
+ // Probably found something here that's not an atom. Rather than loop forever or fail completely, we'll try to skip this bogus
+ // capability. This is known to happen with: Netscape Messaging Server 4.03 (built Apr 27 1999) that returns:
+ // * CAPABILITY * CAPABILITY IMAP4 IMAP4rev1 ...
+ // The "*" in the middle of the capability list causes us to loop forever here.
+ r.skipToken();
+ } else {
+ final String[] tokens = s.split(EQUAL);
+ final String key = tokens[0];
+ final String value = (tokens.length > 1) ? tokens[1] : null;
+ final String upperCase = key.toUpperCase(Locale.ENGLISH);
+ List values = capas.get(upperCase);
+ if (values == null) {
+ values = new ArrayList<>();
+ capas.put(upperCase, values);
+ }
+ // AUTH key allows more than one pair(ex:AUTH=XOAUTH2 AUTH=PLAIN), parsing value out to List, otherwise add key to list
+ if (value != null) {
+ values.add(value);
+ }
+ }
+ }
+ }
+ // making the value list immutable
+ for (final Map.Entry> entry : capas.entrySet()) {
+ entry.setValue(Collections.unmodifiableList(entry.getValue()));
+ }
+ return new Capability(capas);
+ }
+
+ /**
+ * Returns true if the response has capability keyword; false otherwise.
+ *
+ * @param r the response to check
+ * @return true if the response has capability keyword; false otherwise
+ */
+ private boolean hasCapability(final IMAPResponse r) {
+ // case 1, from capability or authenticate command. EX: * CAPABILITY IMAP4rev1 SASL-IR
+ if (r.keyEquals(CAPABILITY)) {
+ return true;
+ }
+
+ // case 2. from server greeting. EX: OK [CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN] IMAP4rev1 Hello
+ byte b;
+ while ((b = r.readByte()) > 0 && b != (byte) '[') {
+ // eat chars till [
+ }
+ if (b == 0) { // left bracket not found
+ return false;
+ }
+ final String s = r.readAtom();
+ return s.equalsIgnoreCase(CAPABILITY);
+ }
+
+ /**
+ * Parses APPEND response to a AppendUID instance.
+ *
+ * @param rs the APPEND responses
+ * @return AppendUID instance
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nullable
+ private AppendUID parseToAppendUid(@Nonnull final IMAPResponse[] rs) throws ImapAsyncClientException {
+ if (rs.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final IMAPResponse r = rs[rs.length - 1];
+ if (!r.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ byte b;
+ while ((b = r.readByte()) > 0 && b != (byte) L_BRACKET) {
+ // eat chars till [
+ }
+
+ if (b == 0) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final String s = r.readAtom();
+ if (!s.equalsIgnoreCase(APPENDUID)) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final long uidvalidity = r.readLong();
+ final long uid = r.readLong();
+ return new AppendUID(uidvalidity, uid);
+ }
+
+ /**
+ * Parses COPY or MOVE command responses to a CopyUID instance.
+ *
+ * @param rr the COPY responses
+ * @return COPYUID instance built from copy command responses
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private CopyUID parseToCopyUid(@Nonnull final IMAPResponse[] rr) throws ImapAsyncClientException {
+ // For copy response, it is at the last response, for move command response, it is the first response
+ for (int i = rr.length - 1; i >= 0; i--) {
+ final Response r = rr[i];
+ if (r == null || !r.isOK()) {
+ continue;
+ }
+ byte b;
+ while ((b = r.readByte()) > 0 && b != (byte) L_BRACKET) {
+ // eat chars till [
+ }
+
+ if (b == 0) {
+ continue;
+ }
+ final String s = r.readAtom();
+ if (!s.equalsIgnoreCase("COPYUID")) { // expunge response from MOVE, for ex: 2 EXPUNGE
+ continue;
+ }
+ final long uidvalidity = r.readLong();
+ final String src = r.readAtom();
+ final String dst = r.readAtom();
+ return new CopyUID(uidvalidity, UIDSet.parseUIDSets(src), UIDSet.parseUIDSets(dst));
+ }
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT); // when rr length is 0
+ }
+
+ /**
+ * Parses the SELECT or EXAMINE responses to a MailboxInfo instance.
+ *
+ * @param rr the list of responses from SELECT or EXAMINE, this input r array should contain the tagged/final one
+ * @return MailboxInfo instance
+ * @throws ParsingException when encountering parsing exception
+ * @throws ImapAsyncClientException when input is invalid
+ */
+ @Nonnull
+ private MailboxInfo parseToMailboxInfo(@Nonnull final IMAPResponse[] rr) throws ParsingException, ImapAsyncClientException {
+ if (rr.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final MailboxInfo minfo = new MailboxInfo(rr);
+ setupMailboxInfoAccessMode(rr[rr.length - 1], minfo);
+
+ return minfo;
+ }
+
+ /**
+ * Sets up the mode for the {@link MailboxInfo}.
+ *
+ * @param lastResp the tagged response
+ * @param minfo the {@link MailboxInfo} instance
+ */
+ private void setupMailboxInfoAccessMode(@Nonnull final Response lastResp, @Nonnull final MailboxInfo minfo) {
+ if (lastResp.isTagged() && lastResp.isOK()) { // command successful
+ if (lastResp.toString().indexOf("READ-ONLY") != -1) {
+ minfo.mode = Folder.READ_ONLY;
+ } else {
+ minfo.mode = Folder.READ_WRITE;
+ }
+ }
+ }
+
+ /**
+ * Parses the SELECT or EXAMINE responses to a {@link ExtensionMailboxInfo} instance.
+ *
+ * @param rr the list of responses from SELECT or EXAMINE, this input r array should contain the tagged/final one
+ * @return MailboxInfo instance
+ * @throws ParsingException when encountering parsing exception
+ * @throws ImapAsyncClientException when input is invalid
+ */
+ @Nonnull
+ private ExtensionMailboxInfo parseToExtensionMailboxInfo(@Nonnull final IMAPResponse[] rr) throws ParsingException, ImapAsyncClientException {
+ if (rr.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final ExtensionMailboxInfo minfo = new ExtensionMailboxInfo(rr);
+ setupMailboxInfoAccessMode(rr[rr.length - 1], minfo);
+ return minfo;
+ }
+
+ /**
+ * Parses the LIST or LSUB responses to a {@link ListInfo} list. List responses example:
+ *
+ *
+ * * LIST () "/" INBOX
+ * * LIST (\NoSelect) "/" "Public mailboxes"
+ *
+ *
+ * @param r the list of responses from SELECT, the input responses array should contain the tagged/final one
+ * @return list of ListInfo objects
+ * @throws ParsingException when encountering parsing exception
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private ListInfoList parseToListInfoList(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
+ if (r.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response response = r[r.length - 1];
+ if (!response.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ // command successful reaching here
+ final List v = new ArrayList();
+ for (int i = 0, len = r.length - 1; i < len; i++) {
+ final IMAPResponse ir = r[i];
+ if (ir.keyEquals("LIST") || ir.keyEquals("LSUB")) {
+ v.add(new ListInfo(ir));
+ }
+ }
+
+ // could be an empty list if the search criteria ends up no result. Ex:
+ // a002 LIST "" "*t3*"
+ // a002 OK LIST completed
+ return new ListInfoList(v);
+ }
+
+ /**
+ * Parses the Status responses to a {@link Status}.
+ *
+ * @param r the list of responses from Status command, the input responses array should contain the tagged/final one
+ * @return Status object constructed based on the r array
+ * @throws ParsingException when encountering parsing exception
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private Status parseToStatus(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
+ if (r.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = r[r.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ Status status = null;
+ for (int i = 0, len = r.length; i < len; i++) {
+ final IMAPResponse ir = r[i];
+ if (ir.keyEquals("STATUS")) {
+ if (status == null) {
+ status = new Status(ir);
+ } else { // collect them all if each attributes comes in its own line
+ Status.add(status, new Status(ir));
+ }
+ }
+ }
+ if (status == null) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT); // when r length is 0 or no Status response
+ }
+ return status;
+ }
+
+ /**
+ * Parses the LIST-STATUS command responses to a ListStatusResult object. See RFC 5819 for details. For each selectable mailbox matching the
+ * list pattern and selection options, the server MUST return an untagged LIST response followed by an untagged STATUS response containing the
+ * information requested in the STATUS return option. If an attempted STATUS for a listed mailbox fails because the mailbox can't be selected
+ * , the STATUS response MUST NOT be returned and the LIST response MUST include the \NoSelect attribute.
+ *
+ * @param r the list of responses from Status command, the input responses array should contain the tagged/final one
+ * @return Status object constructed based on the r array
+ * @throws ParsingException when encountering parsing exception
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private ListStatusResult parseToListStatusResult(@Nonnull final IMAPResponse[] r) throws ParsingException, ImapAsyncClientException {
+ if (r.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = r[r.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ // command successful reaching here
+ final List v = new ArrayList();
+ final Map m = new HashMap();
+
+ for (int i = 0; i < r.length; i++) {
+ final IMAPResponse ir = r[i];
+
+ if (ir.keyEquals("LIST")) {
+ v.add(new ExtensionListInfo(ir));
+ } else if (ir.keyEquals("STATUS")) {
+ final Status status = new Status(ir);
+ m.put(status.mbox, status);
+ }
+ }
+
+ return new ListStatusResult(v, m);
+ }
+
+ /**
+ * Parses the ID responses to a {@link IdResult} object.
+ *
+ * @param ir the list of responses from ID command, the input responses array should contain the tagged/final one
+ * @return IdResult object constructed based on the given IMAPResponse array
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private IdResult parseToIdResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
+ if (ir.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = ir[ir.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ final Map serverParams = new HashMap();
+ for (int j = 0, len = ir.length; j < len; j++) {
+ final IMAPResponse r = ir[j];
+ if (r.keyEquals("ID")) {
+ r.skipSpaces();
+ int c = r.peekByte();
+ if (c == 'N' || c == 'n') { // assume NIL
+ return new IdResult(Collections.unmodifiableMap(Collections.EMPTY_MAP));
+ }
+
+ final String[] v = r.readStringList();
+ if (v == null) {
+ // this means it does not start with (, ID result is expected to have () enclosed
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ for (int i = 0; i < v.length; i += 2) {
+ final String name = v[i];
+ if (name == null || (i + 1 >= v.length)) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final String value = v[i + 1];
+ serverParams.put(name, value);
+ }
+ }
+ }
+
+ return new IdResult(Collections.unmodifiableMap(serverParams));
+ }
+
+ /**
+ * Parses the Enable responses to a {@link EnableResult} object.
+ *
+ * @param ir the list of responses from ENABLE command, the input responses array should contain the tagged/final one
+ * @return EnableResult object constructed based on the given IMAPResponse array
+ * @throws ImapAsyncClientException when input value is not valid
+ */
+ @Nonnull
+ private EnableResult parseToEnableResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
+ if (ir.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = ir[ir.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ final Set enabledCapabilities = new HashSet();
+ for (final IMAPResponse resp : ir) {
+ if (resp.isTagged()) {
+ continue;
+ }
+ if (resp.keyEquals("ENABLED")) {
+ String s;
+ while ((s = resp.readAtom()) != null && s.length() != 0) {
+ enabledCapabilities.add(s.toUpperCase());
+ }
+ }
+ }
+
+ return new EnableResult(enabledCapabilities);
+ }
+
+ /**
+ * Parses the responses from UID search command to a {@link SearchResult} object.
+ *
+ * @param ir the list of responses from UID search command, the input responses array should contain the tagged/final one
+ * @return SearchResult object constructed based on the given IMAPResponse array
+ * @throws ImapAsyncClientException when tagged response is not OK or given response length is 0
+ */
+ @Nonnull
+ private SearchResult parseToSearchResult(@Nonnull final IMAPResponse[] ir) throws ImapAsyncClientException {
+ if (ir.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = ir[ir.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final List v = new ArrayList(); // will always return a non-null array
+
+ Long modSeq = null;
+
+ // Grab all SEARCH responses
+ long num;
+ for (final IMAPResponse sr : ir) {
+ // There *will* be one SEARCH response.
+ if (sr.keyEquals("SEARCH")) {
+ while ((num = sr.readLong()) != -1) {
+ v.add(Long.valueOf(num));
+ }
+ if (sr.readByte() == L_PAREN) {
+ final String s = sr.readAtom();
+ if (MODSEQ.equals(s)) {
+ modSeq = sr.readLong();
+ }
+ }
+ }
+ }
+
+ return new SearchResult(v, modSeq);
+ }
+
+ /**
+ * Parses the responses from Store command and UID Store command to a {@link StoreResult} object.
+ *
+ * @param ir the list of responses from Store or UID Store command, the input responses array should contain the tagged/final one
+ * @return StoreResult object constructed based on the given IMAPResponse array,
+ * @throws ImapAsyncClientException when tagged response is bad or given response length is 0 or fail to parse Fetch Response
+ */
+ @Nonnull
+ private StoreResult parseToStoreResult(@Nonnull final IMAPResponse[] ir)
+ throws ImapAsyncClientException {
+ if (ir.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = ir[ir.length - 1];
+ taggedResponse.skipSpaces();
+ MessageNumberSet[] modifiedMsgsets = null; // only one response will contain modified numbers
+ if (!taggedResponse.isBAD() && taggedResponse.readByte() == (byte) L_BRACKET && MODIFIED.equals(taggedResponse.readAtom())) {
+ // ex: d105 OK [MODIFIED 7,9] Conditional STORE failed
+ modifiedMsgsets = MessageNumberSet.buildMessageNumberSets(taggedResponse.readAtom());
+ } else if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+
+ final List fetchResponses = new ArrayList<>(); // will always return a non-null array
+
+ Long highestModSeq = null;
+
+ for (final IMAPResponse sr: ir) {
+ sr.skipSpaces();
+ if (sr.keyEquals(FETCH)) {
+ try {
+ fetchResponses.add(new FetchResponse(sr));
+ } catch (final IOException | ProtocolException e) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ } else if (sr.readByte() == (byte) L_BRACKET) { // HIGHESTMODSEQ or MODIFIED
+ final String responseCode = sr.readAtom();
+ if (HIGHESTMODSEQ.equals(responseCode)) {
+ highestModSeq = sr.readLong();
+ }
+ }
+ }
+
+ return new StoreResult(highestModSeq, fetchResponses, modifiedMsgsets);
+ }
+
+ /**
+ * Parses the responses from Fetch command and UID Fetch command to a {@link FetchResult} object.
+ *
+ * @param ir the list of responses from Fetch or UID Fetch command, the input responses array should contain the tagged/final one
+ * @param extensionItems the array of extension FetchItem
+ * @return FetchResult object constructed based on the given IMAPResponse array,
+ * @throws ImapAsyncClientException when tagged response is not OK or given response length is 0 or fail to parse Fetch Response
+ */
+ @Nonnull
+ private FetchResult parseToFetchResult(@Nonnull final IMAPResponse[] ir, @Nonnull final FetchItem[] extensionItems)
+ throws ImapAsyncClientException {
+ if (ir.length < 1) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final Response taggedResponse = ir[ir.length - 1];
+ if (!taggedResponse.isOK()) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ final List fetchResponses = new ArrayList<>(); // will always return a non-null array
+
+ for (final IMAPResponse sr: ir) {
+ if (sr.keyEquals(FETCH)) {
+ try {
+ fetchResponses.add(new FetchResponse(sr, extensionItems));
+ } catch (final IOException | ProtocolException e) {
+ throw new ImapAsyncClientException(FailureType.INVALID_INPUT);
+ }
+ }
+ }
+
+ return new FetchResult(fetchResponses);
+ }
+ }
+}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTermTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTermTest.java
new file mode 100644
index 00000000..9febde66
--- /dev/null
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/ExtendedModifiedSinceTermTest.java
@@ -0,0 +1,72 @@
+package com.yahoo.imapnio.async.data;
+
+import javax.mail.Flags;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
+import com.yahoo.imapnio.async.request.EntryTypeRequest;
+
+/**
+ * Unit test for {@code ExtendedModifiedSinceTerm}.
+ */
+public class ExtendedModifiedSinceTermTest {
+
+ /**
+ * Tests ExtendedModifiedSinceTerm constructor and getters.
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testExtendedModifiedSinceTermWithOptionalField() throws ImapAsyncClientException {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.ALL, 1L);
+
+ Assert.assertEquals(extendedModifiedSinceTerm.getModSeq(), 1L, "getModSeq() mismatched.");
+ Assert.assertNotNull(extendedModifiedSinceTerm.getEntryName(), "getEntryName() should not return null.");
+ Assert.assertTrue(extendedModifiedSinceTerm.getEntryName().contains(Flags.Flag.SEEN), "getEntryName() mismatched.");
+ Assert.assertNotNull(extendedModifiedSinceTerm.getEntryType(), "getEntryType() should not return null.");
+ Assert.assertEquals(extendedModifiedSinceTerm.getEntryType().name(), "ALL", "getEntryType() mismatched.");
+ }
+
+ /**
+ * Tests ExtendedModifiedSinceTerm constructor and getters.
+ */
+ @Test
+ public void testExtendedModifiedSinceTermWithoutOptionalField() {
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(1L);
+
+ Assert.assertEquals(extendedModifiedSinceTerm.getModSeq(), 1L, "getModSeq() mismatched.");
+ Assert.assertNull(extendedModifiedSinceTerm.getEntryName(), "Entry name should be null");
+ Assert.assertNull(extendedModifiedSinceTerm.getEntryType(), "Entry type should be null.");
+ }
+
+ /**
+ * Tests ExtendedModifiedSinceTerm match throws UnsupportedOperationException.
+ */
+ @Test(expectedExceptions = UnsupportedOperationException.class)
+ public void testExtendedModifiedSinceTermMatchException() {
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(1L);
+ extendedModifiedSinceTerm.match(null);
+ }
+
+ /**
+ * Tests buildEntryFlagName with ImapAsyncClientException.
+ */
+ @Test
+ public void testBuildEntryFlagNameFailed() {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.ANSWERED);
+ flags.add(Flags.Flag.DELETED);
+ ImapAsyncClientException actual = null;
+
+ try {
+ new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.ALL, 1L);
+ } catch (final ImapAsyncClientException e) {
+ actual = e;
+ }
+ Assert.assertNotNull(actual, "Should throw ImapAsyncClientException");
+ Assert.assertEquals(actual.getFailureType(), ImapAsyncClientException.FailureType.INVALID_INPUT, "FailureType should be INVALID_INPUT");
+ }
+}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfoTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfoTest.java
index 3fe13787..39a07b58 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfoTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/ExtensionMailboxInfoTest.java
@@ -48,6 +48,7 @@ public void testCreateExtensionMailboxInfoSuccess() throws IOException, Protocol
Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
Assert.assertEquals(minfo.getMailboxId(), "214-mailbox", "MailboxId mismatched.");
+ Assert.assertFalse(minfo.isNoModSeq(), "isNoModSeq() mismatched.");
Assert.assertNull(content[7], "This element should be nulled out");
}
@@ -198,4 +199,42 @@ public void testCreateExtensionMailboxInfoNoValueInParenthsis() throws IOExcepti
Assert.assertNotNull(content[7], "This element should not be nulled out");
Assert.assertEquals(content[7].getRest(), "[MAILBOXID ()] Ok", "The index is not reset to the point of the status code.");
}
+
+ /**
+ * Tests calling constructor with no mod seq successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testCreateExtensionMailboxInfoSuccessNoModSequence() throws IOException, ProtocolException {
+
+ final IMAPResponse[] content = new IMAPResponse[9];
+ content[0] = new IMAPResponse("* 3 EXISTS");
+ content[1] = new IMAPResponse("* 0 RECENT");
+ content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
+ content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
+ content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
+ content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
+ content[6] = new IMAPResponse("* OK [MAILBOXID (214-mailbox)] Ok");
+ content[7] = new IMAPResponse("* OK [NOMODSEQ] Sorry, this mailbox format doesn't support modsequences");
+ content[8] = new IMAPResponse("002 OK [READ-ONLY] EXAMINE completed; now in selected state");
+ final ExtensionMailboxInfo minfo = new ExtensionMailboxInfo(content);
+
+ // verify the result
+ Assert.assertNotNull(minfo, "result mismatched.");
+ Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
+ Assert.assertEquals(minfo.highestmodseq, -1, "highestmodseq mismatched.");
+ Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
+ Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
+ Assert.assertEquals(minfo.getMailboxId(), "214-mailbox", "MailboxId mismatched.");
+ Assert.assertTrue(minfo.isNoModSeq(), "isNoModSeq() mismatched.");
+ Assert.assertNull(content[6], "This element should be nulled out");
+ Assert.assertNull(content[7], "This element should be nulled out");
+ }
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/FetchResultTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/FetchResultTest.java
new file mode 100644
index 00000000..31694196
--- /dev/null
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/FetchResultTest.java
@@ -0,0 +1,33 @@
+package com.yahoo.imapnio.async.data;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.sun.mail.iap.ProtocolException;
+import com.sun.mail.imap.protocol.FetchResponse;
+import com.sun.mail.imap.protocol.IMAPResponse;
+
+/**
+ * Unit test for {@code FetchResult}.
+ */
+public class FetchResultTest {
+ /**
+ * Tests FetchResult constructor and getters.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testFetchResult() throws IOException, ProtocolException {
+ final FetchResponse fetchResponse = new FetchResponse(new IMAPResponse("* 1 FETCH (UID 4 MODSEQ (12121231000))"));
+ final List expectedFetchResponses = Collections.singletonList(fetchResponse);
+ final FetchResult fr = new FetchResult(expectedFetchResponses);
+ final ListfetchResponsesResult = fr.getFetchResponses();
+ Assert.assertEquals(fetchResponsesResult.size(), 1, "getFetchResponses() mismatched.");
+ Assert.assertTrue(fetchResponsesResult.get(0).keyEquals("FETCH"), "getFetchResponses() mismatched.");
+ }
+}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/MessageNumberSetTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/MessageNumberSetTest.java
index d82e0599..1a8896bd 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/data/MessageNumberSetTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/MessageNumberSetTest.java
@@ -141,6 +141,103 @@ public void testBuildStringWith0LengthMessageNumberSets() {
Assert.assertNull(MessageNumberSet.buildString(new MessageNumberSet[0]), "Result mismatched.");
}
+ /**
+ * Tests buildMessageNumberSets(String) method with only one number.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithOneString() throws ImapAsyncClientException {
+ final MessageNumberSet[] expectedMsgSets = { new MessageNumberSet(1, 1) };
+ final String msgSetStr = MessageNumberSet.buildString(expectedMsgSets);
+ final MessageNumberSet[] actualMsgSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(actualMsgSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(actualMsgSets.length, 1, "buildMessageNumberSets() size mismatched.");
+ Assert.assertEquals(actualMsgSets[0], expectedMsgSets[0], "buildMessageNumberSets() mismatched.");
+ }
+
+ /**
+ * Tests buildMessageNumberSets(String) method having both start and end as number.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithStartEnd() throws ImapAsyncClientException {
+ final MessageNumberSet[] expectedMsgSets = { new MessageNumberSet(1, 100) };
+ final String msgSetStr = MessageNumberSet.buildString(expectedMsgSets);
+ final MessageNumberSet[] actualMsgSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(actualMsgSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(actualMsgSets.length, 1, "buildMessageNumberSets() size mismatched.");
+ Assert.assertEquals(actualMsgSets[0], expectedMsgSets[0], "buildMessageNumberSets() mismatched.");
+ }
+
+ /**
+ * Tests buildMessageNumberSets(String) method having start with a number and end with last.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithStartEndWithLast() throws ImapAsyncClientException {
+ final MessageNumberSet[] expectedMsgSets = { new MessageNumberSet(1, LastMessage.LAST_MESSAGE) };
+ final String msgSetStr = MessageNumberSet.buildString(expectedMsgSets);
+ final MessageNumberSet[] actualMsgSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(actualMsgSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(actualMsgSets.length, 1, "buildMessageNumberSets() size mismatched.");
+ Assert.assertEquals(actualMsgSets[0], expectedMsgSets[0], "buildMessageNumberSets() mismatched.");
+ }
+
+ /**
+ * Tests buildMessageNumberSets(String) method with last message only.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithLastMessageOnly() throws ImapAsyncClientException {
+ final MessageNumberSet[] expectedMsgSets = { new MessageNumberSet(LastMessage.LAST_MESSAGE) };
+ final String msgSetStr = MessageNumberSet.buildString(expectedMsgSets);
+ final MessageNumberSet[] actualMsgSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(actualMsgSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(actualMsgSets.length, 1, "buildMessageNumberSets() size mismatched.");
+ Assert.assertEquals(actualMsgSets[0], expectedMsgSets[0], "buildMessageNumberSets() mismatched.");
+ }
+
+ /**
+ * Tests buildMessageNumberSets(String) method with last message only.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithEndStartWithLast() throws ImapAsyncClientException {
+ final MessageNumberSet[] expectedMsgSets = { new MessageNumberSet(1, LastMessage.LAST_MESSAGE) };
+ final String msgSetStr = "*:1";
+ final MessageNumberSet[] actualMsgSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(actualMsgSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(actualMsgSets.length, 1, "buildMessageNumberSets() size mismatched.");
+ Assert.assertEquals(actualMsgSets[0], expectedMsgSets[0], "buildMessageNumberSets() mismatched.");
+ }
+
+ /**
+ * Tests buildMessageNumberSets(String) method with multiple of message number sets.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithMultipleMsgNumSets() throws ImapAsyncClientException {
+ final int numMsgs = 4;
+ final MessageNumberSet[] expectedMsgs = new MessageNumberSet[numMsgs];
+ expectedMsgs[0] = new MessageNumberSet(1, 5);
+ expectedMsgs[1] = new MessageNumberSet(1, LastMessage.LAST_MESSAGE);
+ expectedMsgs[2] = new MessageNumberSet(2, 2);
+ expectedMsgs[3] = new MessageNumberSet(LastMessage.LAST_MESSAGE);
+ final String msgSetStr = MessageNumberSet.buildString(expectedMsgs);
+ final MessageNumberSet[] messageNumberSets = MessageNumberSet.buildMessageNumberSets(msgSetStr);
+ Assert.assertNotNull(messageNumberSets, "buildMessageNumberSets() should not return null.");
+ Assert.assertEquals(messageNumberSets.length, numMsgs, "buildMessageNumberSets() size mismatched.");
+ for (int i = 0; i < numMsgs; i++) {
+ Assert.assertEquals(messageNumberSets[i], expectedMsgs[i], "buildMessageNumberSets() mismatched.");
+ }
+ }
+
/**
* Tests constructor and converting it to string.
*
@@ -219,4 +316,124 @@ public void testCommandTypeEnum() {
final LastMessage value = LastMessage.valueOf("LAST_MESSAGE");
Assert.assertSame(value, LastMessage.LAST_MESSAGE, "Enum does not match.");
}
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with three elements in one sequence set.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputTwoColonThreeElements() {
+ final String input = "1,2,1:2:3";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with one and two colons in one sequence set.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputTwoColonOneElement() {
+ final String input = "1,2,1::";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with only one element and one colon in one sequence set.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputOneColonOneElement() {
+ final String input = "1,2,:3";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with extra space.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputWithSpaceAfterColon() {
+ final String input = "1,2: 3";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with extra space after number.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputWithSpace() {
+ final String input = "1 ,2:3";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with bad input with only one element and one colon in one sequence set.
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithBadInputCharacter() {
+ final String input = "1,2,a:3";
+ ImapAsyncClientException ex = null;
+ try {
+ MessageNumberSet.buildMessageNumberSets(input);
+ } catch (final ImapAsyncClientException e) {
+ ex = e;
+ }
+ Assert.assertNotNull(ex, "IMAP Async Client Exception should be thrown.");
+ Assert.assertEquals(ex.getFailureType(), FailureType.INVALID_INPUT, "Wrong fail type was thrown.");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with null input.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithNull() throws ImapAsyncClientException {
+ final String input = null;
+ final MessageNumberSet[] mns = MessageNumberSet.buildMessageNumberSets(input);
+ Assert.assertNull(mns, "buildMessageNumberSets() should return null");
+ }
+
+ /**
+ * Tests BuildMessageNumberSets with empty string input.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testBuildMessageNumberSetsWithEmptyString() throws ImapAsyncClientException {
+ final String input = "";
+ final MessageNumberSet[] mns = MessageNumberSet.buildMessageNumberSets(input);
+ Assert.assertNull(mns, "buildMessageNumberSets() should return null");
+ }
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/SearchResultTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/SearchResultTest.java
index a44dd95d..c34d11a0 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/data/SearchResultTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/SearchResultTest.java
@@ -1,6 +1,6 @@
package com.yahoo.imapnio.async.data;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.testng.Assert;
@@ -16,21 +16,15 @@ public class SearchResultTest {
*/
@Test
public void testSearchResult() {
- final List ll = new ArrayList<>();
- ll.add(Long.MAX_VALUE - 1);
- final SearchResult infos = new SearchResult(ll);
- final List result = infos.getMessageNumbers();
- Assert.assertEquals(result.size(), 1, "Result mismatched.");
- Assert.assertEquals(result.get(0), Long.valueOf(Long.MAX_VALUE - 1), "Result mismatched.");
- }
+ final List ll = Collections.singletonList(Long.MAX_VALUE - 1);
+ final SearchResult sr = new SearchResult(ll, 1L);
+ final List result = sr.getMessageNumbers();
+ final Long modSeq = sr.getHighestModSeq();
- /**
- * Tests SearchResult constructor and getters when passing null list.
- */
- @Test
- public void testSearchResultNullList() {
- final SearchResult infos = new SearchResult(null);
- final List result = infos.getMessageNumbers();
- Assert.assertNull(result, "Result mismatched.");
+ Assert.assertNotNull(result, "getMessageNumbers() should not return null.");
+ Assert.assertEquals(result.size(), 1, "getMessageNumbers() size mismatched.");
+ Assert.assertEquals(result.get(0), Long.valueOf(Long.MAX_VALUE - 1), "getMessageNumbers() mismatched.");
+ Assert.assertNotNull(modSeq, "getHighestModSeq() should not return null");
+ Assert.assertEquals(modSeq, Long.valueOf(1), "getHighestModSeq() mismatched.");
}
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/data/StoreResultTest.java b/core/src/test/java/com/yahoo/imapnio/async/data/StoreResultTest.java
new file mode 100644
index 00000000..5a8cb017
--- /dev/null
+++ b/core/src/test/java/com/yahoo/imapnio/async/data/StoreResultTest.java
@@ -0,0 +1,41 @@
+package com.yahoo.imapnio.async.data;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.sun.mail.iap.ProtocolException;
+import com.sun.mail.imap.protocol.FetchResponse;
+import com.sun.mail.imap.protocol.IMAPResponse;
+
+/**
+ * Unit test for {@code StoreResult}.
+ */
+public class StoreResultTest {
+ /**
+ * Tests StoreResult constructor and getters.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testStoreResult() throws IOException, ProtocolException {
+ final FetchResponse fr = new FetchResponse(new IMAPResponse("* 1 FETCH (UID 4 MODSEQ (12121231000))"));
+ final List fetchResponses = Collections.singletonList(fr);
+ final MessageNumberSet[] modifiedMsgSet = { new MessageNumberSet(1L, 1L) };
+ final StoreResult storeResult = new StoreResult(1L, fetchResponses, modifiedMsgSet);
+ final Long highestModSeq = storeResult.getHighestModSeq();
+ final List responses = storeResult.getFetchResponses();
+
+ Assert.assertEquals(responses.size(), 1, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(highestModSeq, "getHighestModSeq() should not return null");
+ Assert.assertEquals(highestModSeq, Long.valueOf(1L), "getHighestModSeq() mismatched.");
+ final MessageNumberSet[] modifiedMsgsets = storeResult.getModifiedMsgSets();
+ Assert.assertNotNull(modifiedMsgsets, "getModifiedMsgSets() should not return null.");
+ Assert.assertEquals(modifiedMsgsets.length, 1, "getModifiedMsgSets() size mismatched.");
+ Assert.assertEquals(modifiedMsgsets[0], modifiedMsgSet[0], "getModifiedMsgSets() mismatched.");
+ }
+}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/ExamineFolderCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/ExamineFolderCommandTest.java
index 0d62edae..d5a8264e 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/ExamineFolderCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/ExamineFolderCommandTest.java
@@ -120,4 +120,16 @@ public void testGetCommandLineWithQResyncParam() throws ImapAsyncClientException
cmd = new ExamineFolderCommand(folderName, qResyncParameter);
Assert.assertEquals(cmd.getCommandLine(), EXAMINE + "&bUuL1Q- (QRESYNC (100 4223212 1:200 (1 1:10)))\r\n", "Expected result mismatched.");
}
+
+ /**
+ * Tests getCommandLine method with CONDSTORE enable.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithCondStore() throws ImapAsyncClientException {
+ final String folderName = "测试";
+ ImapRequest cmd = new ExamineFolderCommand(folderName, true);
+ Assert.assertEquals(cmd.getCommandLine(), EXAMINE + "&bUuL1Q- (CONDSTORE)\r\n", "getCommandLine() mismatched.");
+ }
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/FetchCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/FetchCommandTest.java
index 7897ab31..cd1d473c 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/FetchCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/FetchCommandTest.java
@@ -109,6 +109,54 @@ public void testGetCommandLineWithStartEndConstructor()
}
}
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], macro, and changed since the given modification sequence.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithChangedSince() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final long[] msgs = { 4294967293L, 4294967294L, 4294967295L };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new FetchCommand(msgsets, FetchMacro.FAST, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "FETCH 4294967293:4294967295 FAST (CHANGEDSINCE 1)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], data items, and changed since the given modification sequence.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithDataItemsChangedSince() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final int[] msgs = { 1, 2, 3 };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new FetchCommand(msgsets, DATA_ITEMS, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "FETCH 1:3 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) (CHANGEDSINCE 1)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
/**
* Tests getStreamingResponsesQueue method.
*/
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/ImapArgumentFormatterTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/ImapArgumentFormatterTest.java
index a3b03304..b00cb426 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/ImapArgumentFormatterTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/ImapArgumentFormatterTest.java
@@ -215,4 +215,36 @@ public void testBuildFlagStringWithUserFlagString() {
Assert.assertNotNull(s, "buildFlagString() should not return null.");
Assert.assertEquals(s, "(userflag1 userflag2)", "result mismatched.");
}
+
+ /**
+ * Tests buildEntryFlagName with all flags.
+ */
+ @Test
+ public void testBuildEntryFlagName() {
+ final String[] expectedFlags = new String[] {"\\Answered", "\\Deleted", "\\Draft", "\\Flagged", "\\Recent", "\\Seen"};
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.ANSWERED);
+ flags.add(Flags.Flag.DELETED);
+ flags.add(Flags.Flag.DRAFT);
+ flags.add(Flags.Flag.FLAGGED);
+ flags.add(Flags.Flag.RECENT);
+ flags.add(Flags.Flag.SEEN);
+ flags.add("userflag1");
+ final ImapArgumentFormatter writer = new ImapArgumentFormatter();
+ final Flags.Flag[] systemFlags = flags.getSystemFlags();
+ for (int i = 0; i < systemFlags.length; i++) {
+ final Flags singleSystemFlag = new Flags();
+ singleSystemFlag.add(systemFlags[i]);
+ final String systemEntryName = writer.buildEntryFlagName(singleSystemFlag);
+ Assert.assertEquals(systemEntryName, "\"/flags/\\" + expectedFlags[i] + "\"", "buildEntryFlagName() mismatched.");
+ }
+
+ final String[] userFlags = flags.getUserFlags();
+ final Flags singleUserFlag = new Flags();
+ singleUserFlag.add(userFlags[0]);
+ final String userEntryName = writer.buildEntryFlagName(singleUserFlag);
+
+ Assert.assertEquals(userFlags.length, 1, "result mismatched");
+ Assert.assertEquals(userEntryName, "\"/flags/userflag1\"", "buildEntryFlagName() mismatched");
+ }
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/SearchCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/SearchCommandTest.java
index d5e60d09..d4b0cbb1 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/SearchCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/SearchCommandTest.java
@@ -14,7 +14,9 @@
import java.util.Set;
import javax.mail.Flags;
+import javax.mail.search.BodyTerm;
import javax.mail.search.FlagTerm;
+import javax.mail.search.OrTerm;
import javax.mail.search.SearchException;
import javax.mail.search.SubjectTerm;
@@ -25,6 +27,7 @@
import com.sun.mail.iap.Argument;
import com.sun.mail.iap.Literal;
import com.yahoo.imapnio.async.data.Capability;
+import com.yahoo.imapnio.async.data.ExtendedModifiedSinceTerm;
import com.yahoo.imapnio.async.data.MessageNumberSet;
import com.yahoo.imapnio.async.data.MessageNumberSet.LastMessage;
import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
@@ -237,6 +240,137 @@ public void testGetCommandLineNoneNullMessageSeqSetsNullSearchTermNullCharset()
}
}
+ /**
+ * Tests getCommandLine method with null message sequences set, MODSEQ SearchTerm.
+ *
+ * @throws IOException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testGetCommandLineNonNullModSeq() throws IOException, IllegalArgumentException, IllegalAccessException, SearchException,
+ ImapAsyncClientException {
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(1L);
+ final MessageNumberSet[] msgsets = null;
+ final ImapRequest cmd = new SearchCommand(msgsets, extendedModifiedSinceTerm, null);
+ Assert.assertEquals(cmd.getCommandLine(), "SEARCH MODSEQ 1\r\n", "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with null message sequences set, MODSEQ SearchTerm with entry name and entry type.
+ *
+ * @throws IOException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testGetCommandLineNonNullModSeqWithFlagAnsweredTypeAll() throws IOException, IllegalArgumentException, IllegalAccessException,
+ SearchException, ImapAsyncClientException {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.ANSWERED);
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.ALL, 1L);
+ final MessageNumberSet[] msgsets = null;
+ final ImapRequest cmd = new SearchCommand(msgsets, extendedModifiedSinceTerm, null);
+ Assert.assertEquals(cmd.getCommandLine(), "SEARCH MODSEQ \"/flags/\\\\Answered\" ALL 1\r\n", "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with null message sequences set, MODSEQ SearchTerm with entry name and entry type.
+ *
+ * @throws IOException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws SearchException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineNonNullModSeqWithFlagDeletedTypePriv() throws IOException, IllegalArgumentException, IllegalAccessException,
+ SearchException, ImapAsyncClientException {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.DELETED);
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.PRIVATE, 1L);
+ final MessageNumberSet[] msgsets = null;
+ final ImapRequest cmd = new SearchCommand(msgsets, extendedModifiedSinceTerm, null);
+ Assert.assertEquals(cmd.getCommandLine(), "SEARCH MODSEQ \"/flags/\\\\Deleted\" PRIV 1\r\n", "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with null message sequences set, MODSEQ SearchTerm with entry name and entry type.
+ *
+ * @throws IOException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testGetCommandLineNonNullModSeqWithFlagDraftTypeShared() throws IOException, IllegalArgumentException, IllegalAccessException,
+ SearchException, ImapAsyncClientException {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.DRAFT);
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.SHARED, 1L);
+ final MessageNumberSet[] msgsets = null;
+ final ImapRequest cmd = new SearchCommand(msgsets, extendedModifiedSinceTerm, null);
+ Assert.assertEquals(cmd.getCommandLine(), "SEARCH MODSEQ \"/flags/\\\\Draft\" SHARED 1\r\n", "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with null message sequences set, MODSEQ SearchTerm with entry name and entry type.
+ *
+ * @throws IOException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testGetCommandLineOrNonNullModSeqWithFlagDraftTypeShared() throws IOException, IllegalArgumentException, IllegalAccessException,
+ SearchException, ImapAsyncClientException {
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.DRAFT);
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(flags, EntryTypeRequest.SHARED, 1L);
+ final BodyTerm bodyTerm = new BodyTerm("Text");
+ final OrTerm orTerm = new OrTerm(extendedModifiedSinceTerm, bodyTerm);
+ final MessageNumberSet[] msgsets = null;
+ final ImapRequest cmd = new SearchCommand(msgsets, orTerm, null);
+ Assert.assertEquals(cmd.getCommandLine(), "SEARCH OR MODSEQ \"/flags/\\\\Draft\" SHARED 1 BODY Text\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
/**
* Tests getCommandLine method with none-null message sequences set, null SearchTerm.
*
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/SelectFolderCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/SelectFolderCommandTest.java
index 0d2edad4..2afa9029 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/SelectFolderCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/SelectFolderCommandTest.java
@@ -121,4 +121,16 @@ public void testGetCommandLineWithQResyncParam() throws ImapAsyncClientException
cmd = new SelectFolderCommand(folderName, qResyncParameter);
Assert.assertEquals(cmd.getCommandLine(), SELECT + "&bUuL1Q- (QRESYNC (100 4223212 1:200 (1 1:10)))\r\n", "Expected result mismatched.");
}
+
+ /**
+ * Tests getCommandLine method with CONDSTORE enabled.
+ *
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithCondStore() throws ImapAsyncClientException {
+ final String folderName = "测试";
+ ImapRequest cmd = new SelectFolderCommand(folderName, true);
+ Assert.assertEquals(cmd.getCommandLine(), SELECT + "&bUuL1Q- (CONDSTORE)\r\n", "getCommandLine() mismatched.");
+ }
}
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/StoreFlagsCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/StoreFlagsCommandTest.java
index 64e8b03b..24710d69 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/StoreFlagsCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/StoreFlagsCommandTest.java
@@ -216,6 +216,85 @@ public void testGetCommandLineWithMessageSeqStringFlagsAddedAndSilent()
}
}
+ /**
+ * Tests getCommandLine method using message sequences, flags, adding flags, not silent, and the given unchanged since modification sequence.
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithFlagsAddedNotSilentUnchangedSince() throws IllegalArgumentException, IllegalAccessException,
+ ImapAsyncClientException {
+
+ final int[] msgs = { 1, 2, 3 };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final ImapRequest cmd = new StoreFlagsCommand(msgsets, flags, FlagsAction.ADD, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "STORE 1:3 (UNCHANGEDSINCE 1) +FLAGS (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using message sequences, flags, adding flags, silent, and the given unchanged since modification sequence.
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithFlagsAddedSilentUnchangedSince() throws IllegalArgumentException, IllegalAccessException,
+ ImapAsyncClientException {
+
+ final int[] msgs = { 1, 2, 3 };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final boolean isSilent = true;
+ final ImapRequest cmd = new StoreFlagsCommand(msgsets, flags, FlagsAction.ADD, isSilent, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "STORE 1:3 (UNCHANGEDSINCE 1) +FLAGS.SILENT (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with message sequences, adding flags, silent, and the given unchanged since modification sequence.
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithMessageSeqStringFlagsAddedAndSilentUnchangedSince() throws IllegalAccessException, ImapAsyncClientException {
+
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final boolean isSilent = true;
+ final ImapRequest cmd = new StoreFlagsCommand("1:*", flags, FlagsAction.ADD, isSilent, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "STORE 1:* (UNCHANGEDSINCE 1) +FLAGS.SILENT (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
/**
* Tests getStreamingResponsesQueue method.
*/
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/UidFetchCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/UidFetchCommandTest.java
index cab47b64..e8055083 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/UidFetchCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/UidFetchCommandTest.java
@@ -129,6 +129,102 @@ public void testGetCommandLineFromConstructorWithUidStringAndMacro()
}
}
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], data items, and changed since the given modification sequence.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithChangedSince() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final long[] msgs = { 1L, 2L, 3L };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new UidFetchCommand(msgsets, DATA_ITEMS, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "UID FETCH 1:3 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) (CHANGEDSINCE 1)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], macro, and changed since the given modification sequence.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithMacroChangedSince() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final long[] msgs = { 4294967293L, 4294967294L, 4294967295L };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new UidFetchCommand(msgsets, FetchMacro.FAST, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "UID FETCH 4294967293:4294967295 FAST (CHANGEDSINCE 1)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], data items, changed since the given modification sequence and vanished flag.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithChangedSinceVanished() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final long[] msgs = { 1L, 2L, 3L };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new UidFetchCommand(msgsets, DATA_ITEMS, 1L, true);
+ Assert.assertEquals(cmd.getCommandLine(), "UID FETCH 1:3 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) (CHANGEDSINCE 1 VANISHED)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using MessageNumberSet[], macro, changed since the given modification sequence and vanished flag.
+ *
+ * @throws ImapAsyncClientException will not throw
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ */
+ @Test
+ public void testGetCommandLineFromConstructorWithMacroChangedSinceVanished() throws ImapAsyncClientException, IllegalArgumentException,
+ IllegalAccessException {
+
+ final long[] msgs = { 4294967293L, 4294967294L, 4294967295L };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final ImapRequest cmd = new UidFetchCommand(msgsets, FetchMacro.FAST, 1L, true);
+ Assert.assertEquals(cmd.getCommandLine(), "UID FETCH 4294967293:4294967295 FAST (CHANGEDSINCE 1 VANISHED)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
/**
* Tests getCommandType method.
*/
diff --git a/core/src/test/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommandTest.java b/core/src/test/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommandTest.java
index 2f8fb100..11dc2a01 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommandTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/request/UidStoreFlagsCommandTest.java
@@ -216,6 +216,85 @@ public void testGetCommandLineWithMessageSeqStringFlagsAddedAndSilent()
}
}
+ /**
+ * Tests getCommandLine method using message sequences, flags, adding flags, not silent, and unchanged since modification sequence..
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithFlagsAddedNotSilentUnchangedSince() throws IllegalArgumentException, IllegalAccessException,
+ ImapAsyncClientException {
+
+ final int[] msgs = { 1, 2, 3 };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final ImapRequest cmd = new UidStoreFlagsCommand(msgsets, flags, FlagsAction.ADD, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "UID STORE 1:3 (UNCHANGEDSINCE 1) +FLAGS (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method using message sequences, flags, adding flags, silent, and unchanged since modification sequence.
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws IllegalArgumentException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithFlagsAddedSilentUnchangedSince() throws IllegalArgumentException, IllegalAccessException,
+ ImapAsyncClientException {
+
+ final int[] msgs = { 1, 2, 3 };
+ final MessageNumberSet[] msgsets = MessageNumberSet.createMessageNumberSets(msgs);
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final boolean isSilent = true;
+ final ImapRequest cmd = new UidStoreFlagsCommand(msgsets, flags, FlagsAction.ADD, isSilent, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "UID STORE 1:3 (UNCHANGEDSINCE 1) +FLAGS.SILENT (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
+ /**
+ * Tests getCommandLine method with message sequences, adding flags, silent, and unchanged since modification sequence.
+ *
+ * @throws IllegalAccessException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testGetCommandLineWithMessageSeqStringFlagsAddedAndSilentUnchangedSince() throws IllegalAccessException, ImapAsyncClientException {
+
+ final Flags flags = new Flags();
+ flags.add(Flags.Flag.SEEN);
+ flags.add(Flags.Flag.DELETED);
+ final boolean isSilent = true;
+ final ImapRequest cmd = new UidStoreFlagsCommand("1:*", flags, FlagsAction.ADD, isSilent, 1L);
+ Assert.assertEquals(cmd.getCommandLine(), "UID STORE 1:* (UNCHANGEDSINCE 1) +FLAGS.SILENT (\\Deleted \\Seen)\r\n",
+ "getCommandLine() mismatched.");
+
+ cmd.cleanup();
+ // Verify if cleanup happened correctly.
+ for (final Field field : fieldsToCheck) {
+ Assert.assertNull(field.get(cmd), "Cleanup should set " + field.getName() + " as null");
+ }
+ }
+
/**
* Tests getCommandType method.
*/
diff --git a/core/src/test/java/com/yahoo/imapnio/async/response/ImapResponseMapperTest.java b/core/src/test/java/com/yahoo/imapnio/async/response/ImapResponseMapperTest.java
index c2a218d5..d28f406a 100644
--- a/core/src/test/java/com/yahoo/imapnio/async/response/ImapResponseMapperTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/async/response/ImapResponseMapperTest.java
@@ -1,1441 +1,1987 @@
-package com.yahoo.imapnio.async.response;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.mail.Flags.Flag;
-import javax.mail.Folder;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import com.sun.mail.iap.ProtocolException;
-import com.sun.mail.imap.AppendUID;
-import com.sun.mail.imap.CopyUID;
-import com.sun.mail.imap.protocol.ID;
-import com.sun.mail.imap.protocol.IMAPResponse;
-import com.sun.mail.imap.protocol.ListInfo;
-import com.sun.mail.imap.protocol.MailboxInfo;
-import com.sun.mail.imap.protocol.Status;
-import com.yahoo.imapnio.async.data.Capability;
-import com.yahoo.imapnio.async.data.EnableResult;
-import com.yahoo.imapnio.async.data.ExtensionListInfo;
-import com.yahoo.imapnio.async.data.ExtensionMailboxInfo;
-import com.yahoo.imapnio.async.data.IdResult;
-import com.yahoo.imapnio.async.data.ListInfoList;
-import com.yahoo.imapnio.async.data.ListStatusResult;
-import com.yahoo.imapnio.async.data.SearchResult;
-import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
-import com.yahoo.imapnio.async.exception.ImapAsyncClientException.FailureType;
-
-/**
- * Unit test for {@link ImapResponseMapper}.
- */
-public class ImapResponseMapperTest {
- /** Imap server greeting. */
- private static final String GREETING = "* OK [CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE "
- + "XYMHIGHESTMODSEQ UIDPLUS LITERAL+ CHILDREN X-MSG-EXT] IMAP4rev1 Hello";
-
- /**
- * Tests parseToCapabilities method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToCapabilitiesFromCapaCommand() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* some junks\r\n"));
- content.add(new IMAPResponse("* CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE\r\n"));
- content.add(new IMAPResponse("* more junks\r\n"));
- content.add(new IMAPResponse("a1 OK CAPABILITY completed\r\n"));
- final Capability capa = mapper.readValue(content.toArray(new IMAPResponse[0]), Capability.class);
-
- // verify the result
- Assert.assertNotNull(capa, "result should never return null.");
- Assert.assertTrue(capa.hasCapability("IMAP4rev1".toUpperCase()), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
- final List authValues = capa.getCapability("AUTH");
- Assert.assertNotNull(authValues, "AUTH values missed.");
- Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
- Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
- Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
- Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
- }
-
- /**
- * Tests parseToCapabilities method when ImapResponse array has zero length.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToCapabilitiesArrayLengthZero() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = {};
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, Capability.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToCapabilities method successfully from an OK response that has Capability response attached to.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToCapabilitiesFromGreeting() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = { new IMAPResponse(GREETING) };
- final Capability capa = mapper.readValue(content, Capability.class);
-
- // verify the result
- Assert.assertNotNull(capa, "result should never return null.");
- Assert.assertTrue(capa.hasCapability("IMAP4rev1".toUpperCase()), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("X-MSG-EXT"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("LITERAL+"), "One capability missed.");
- final List authValues = capa.getCapability("AUTH");
- Assert.assertNotNull(authValues, "AUTH values missed.");
- Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
- Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
- Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
- Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
- }
-
- /**
- * Tests parseToCapabilities method successfully when it has Netscape Messaging Server response.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToCapabilitiesSkipStar() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = {
- new IMAPResponse("* CAPABILITY * IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE") };
- final Capability capa = mapper.readValue(content, Capability.class);
-
- // verify the result
- Assert.assertTrue(capa.hasCapability("IMAP4rev1"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
- Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
- final List authValues = capa.getCapability("AUTH");
- Assert.assertNotNull(authValues, "AUTH values missed.");
- Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
- Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
- Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
- Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
- }
-
- /**
- * Tests parseCopyUid successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseCopyUidSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[5];
- content[0] = new IMAPResponse("* OK [COPYUID 1549405125 150395 3]"); // a good response
- content[1] = new IMAPResponse("* BAD Some junks"); // test if it skips the bad response
- content[2] = null; // test if it skips null
- content[3] = new IMAPResponse("* OK [SOMETHING 111 222 3]"); // test if it detects it is not COPYUID keyword
- content[4] = new IMAPResponse("* OK"); // test when b is 0
-
- final CopyUID copyUid = mapper.readValue(content, CopyUID.class);
-
- // verify the result
- Assert.assertNotNull(copyUid, "result mismatched.");
- Assert.assertEquals(copyUid.uidvalidity, 1549405125, "result mismatched.");
- Assert.assertNotNull(copyUid.src, "result mismatched.");
- Assert.assertNotNull(copyUid.dst, "result mismatched.");
- Assert.assertEquals(copyUid.src.length, 1, "result mismatched.");
- Assert.assertEquals(copyUid.src[0].start, 150395, "result mismatched.");
- Assert.assertEquals(copyUid.src[0].end, 150395, "result mismatched.");
- Assert.assertEquals(copyUid.dst.length, 1, "result mismatched.");
- Assert.assertEquals(copyUid.dst[0].start, 3, "result mismatched.");
- Assert.assertEquals(copyUid.dst[0].end, 3, "result mismatched.");
- }
-
- /**
- * Tests ImapResponseParse parseCopyUid when Responses array is empty.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseCopyUidResponseArrayZero() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[0];
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, CopyUID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
-
- }
-
- /**
- * Tests parseAppendUid successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseAppendUidSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[3];
- content[0] = new IMAPResponse("+ Ready for literal data");
- content[1] = new IMAPResponse("* 3 EXISTS");
- content[2] = new IMAPResponse("a5 OK [APPENDUID 1459808247 150399] APPEND completed");
- final AppendUID appendUid = mapper.readValue(content, AppendUID.class);
-
- // verify the result
- Assert.assertNotNull(appendUid, "result mismatched.");
- Assert.assertEquals(appendUid.uidvalidity, 1459808247, "result mismatched.");
- Assert.assertEquals(appendUid.uid, 150399, "result mismatched.");
- }
-
- /**
- * Tests parseToCapabilities method when ImapResponse array has zero length.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToAppendUidsArrayLengthZero() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = {};
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, AppendUID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseAppendUid with a BAD response.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseAppendUidNotOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = { new IMAPResponse("* BAD Some junks") };
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, AppendUID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
-
- }
-
- /**
- * Tests parseAppendUid when b is 0, or when left bracket is not found.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseAppendUidByteReadExhausted() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = { new IMAPResponse("* OK") }; // test when b is 0
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, AppendUID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseAppendUid when b is 0.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseAppendUidNoAppendUidKeyword() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = { new IMAPResponse("* OK [appendButNotUid 111 222 3]") }; // test when b is 0
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, AppendUID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
-
- }
-
- /**
- * Tests parseMailboxInfo method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseMailboxInfoReadOnlySuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[8];
- content[0] = new IMAPResponse("* 3 EXISTS");
- content[1] = new IMAPResponse("* 0 RECENT");
- content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
- content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
- content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
- content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
- content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
- content[7] = new IMAPResponse("002 OK [READ-ONLY] EXAMINE completed; now in selected state");
- final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
-
- // verify the result
- Assert.assertNotNull(minfo, "result mismatched.");
- Assert.assertEquals(minfo.mode, Folder.READ_ONLY, "mode mismatched.");
- Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
- Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
- Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
- Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
- }
-
- /**
- * Tests parseExtensionMailboxInfo method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseExtensionMailboxInfoReadOnlySuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[9];
- content[0] = new IMAPResponse("* 3 EXISTS");
- content[1] = new IMAPResponse("* 0 RECENT");
- content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
- content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
- content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
- content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
- content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
- content[7] = new IMAPResponse("* OK [MAILBOXID (A26)] Ok");
- content[8] = new IMAPResponse("002 OK [READ-ONLY] EXAMINE completed; now in selected state");
- final ExtensionMailboxInfo minfo = mapper.readValue(content, ExtensionMailboxInfo.class);
-
- // verify the result
- Assert.assertNotNull(minfo, "result mismatched.");
- Assert.assertEquals(minfo.mode, Folder.READ_ONLY, "mode mismatched.");
- Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
- Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
- Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
- Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
- Assert.assertEquals(minfo.getMailboxId(), "A26", "MailboxId mismatched.");
- }
-
- /**
- * Tests parseMailboxInfo method successfully with READ-WRITE mode.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseMailboxInfoReadWriteSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[8];
- content[0] = new IMAPResponse("* 3 EXISTS");
- content[1] = new IMAPResponse("* 0 RECENT");
- content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
- content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
- content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
- content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
- content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
- content[7] = new IMAPResponse("002 OK [READ-WRITE] EXAMINE completed; now in selected state");
- final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
-
- // verify the result
- Assert.assertNotNull(minfo, "result mismatched.");
- Assert.assertEquals(minfo.mode, Folder.READ_WRITE, "mode mismatched.");
- Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
- Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
- Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
- Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
- }
-
- /**
- * Tests parseMailboxInfo method successfully with READ-WRITE mode.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseMailboxInfoBad() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[8];
- content[0] = new IMAPResponse("* 3 EXISTS");
- content[1] = new IMAPResponse("* 0 RECENT");
- content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
- content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
- content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
- content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
- content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
- content[7] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
- final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
-
- // verify the result
- Assert.assertNotNull(minfo, "result mismatched.");
- Assert.assertEquals(minfo.mode, 0, "mode mismatched.");
- Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
- Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
- Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
- Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
- Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
- }
-
- /**
- * Tests parseMailboxInfo method successfully with READ-WRITE mode.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseMailboxInfoResponseArray0() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[0];
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, MailboxInfo.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Adds the data for test input.
- *
- * @param rr output parameter, list of IMAPResponse
- * @param expectedNames output parameter, list of expected folder names
- * @param respStr response string
- * @param folder folder name
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- private void buildListInfoIMAPResponse(final List rr, final List expectedNames, final String respStr, final String folder)
- throws IOException, ProtocolException {
- expectedNames.add(folder);
- rr.add(new IMAPResponse(respStr + " \"" + folder + "\"\r\n"));
- }
-
- /**
- * Tests parseListInfos method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseListInfosSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- final List names = new ArrayList<>();
- // add a non-relevant response
- content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
- buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "Archive");
- content.add(new IMAPResponse("* some junks\r\n"));
- buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "Bulk Mail");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "Draft");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "Inbox");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "Sent");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "Trash");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "test1");
- content.add(new IMAPResponse("* 115141 EXISTS\r\n"));
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "test1/test1_1");
- content.add(new IMAPResponse("a3 OK LIST completed"));
- final ListInfoList ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
- final List infos = ll.getListInfo();
-
- // verify the result
- Assert.assertNotNull(infos, "result mismatched.");
- Assert.assertEquals(infos.size(), 8, "ListInfo count mismatched.");
- Assert.assertEquals(infos.size(), names.size(), "ListInfo count mismatched.");
- for (int i = 0; i < infos.size(); i++) {
- final ListInfo info = infos.get(i);
- final String expectedFolder = names.get(i);
- Assert.assertNotNull(info, "ListInfo should not be null.");
- Assert.assertNotNull(expectedFolder, "folder name should not be null.");
- Assert.assertTrue(info.hasInferiors, "hasInferiors mismatched.");
- Assert.assertNotNull(info.name, "Name mismatched.");
- Assert.assertEquals(info.name, expectedFolder, "folder name mismatched.");
- }
- }
-
- /**
- * Tests parseListInfos method successfully when responses are results of LSUB command.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseListInfosFromLSubSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- final List names = new ArrayList<>();
- buildListInfoIMAPResponse(content, names, "* LSUB (\\Archive \\HasNoChildren) \"/\"", "Archive");
- content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
- content.add(new IMAPResponse("* MORE JUNKS\r\n"));
- buildListInfoIMAPResponse(content, names, "* LSUB (\\Junk \\HasNoChildren) \"/\"", "Bulk Mail");
- buildListInfoIMAPResponse(content, names, "* LSUB (\\Drafts \\HasNoChildren) \"/\"", "Draft");
- buildListInfoIMAPResponse(content, names, "* LSUB (\\HasNoChildren) \"/\"", "Inbox");
- buildListInfoIMAPResponse(content, names, "* LSUB (\\Sent \\HasNoChildren) \"/\"", "Sent");
- buildListInfoIMAPResponse(content, names, "* LSUB (\\Trash \\HasNoChildren) \"/\"", "Trash");
- buildListInfoIMAPResponse(content, names, "* LSUB (\\HasChildren) \"/\"", "test1");
- content.add(new IMAPResponse("* 115141 EXISTS\r\n"));
- buildListInfoIMAPResponse(content, names, "* LSUB (\\HasNoChildren) \"/\"", "test1/test1_1");
- content.add(new IMAPResponse("a3 OK LSUB completed"));
- final ListInfoList ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
- final List infos = ll.getListInfo();
-
- // verify the result
- Assert.assertNotNull(infos, "result mismatched.");
- Assert.assertEquals(infos.size(), 8, "ListInfo count mismatched.");
- Assert.assertEquals(infos.size(), names.size(), "ListInfo count mismatched.");
- for (int i = 0; i < infos.size(); i++) {
- final ListInfo info = infos.get(i);
- final String expectedFolder = names.get(i);
- Assert.assertNotNull(info, "ListInfo should not be null.");
- Assert.assertNotNull(expectedFolder, "folder name should not be null.");
- Assert.assertTrue(info.hasInferiors, "hasInferiors mismatched.");
- Assert.assertNotNull(info.name, "Name mismatched.");
- Assert.assertEquals(info.name, expectedFolder, "folder name mismatched.");
- }
- }
-
- /**
- * Tests parseListInfos method when final response is not OK.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseListInfosNoOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- final List names = new ArrayList<>();
- buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "\"Archive\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "\"Bulk Mail\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "\"Draft\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"Inbox\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "\"Sent\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "\"Trash\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "\"test1\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"test1/test1_1\"");
- content.add(new IMAPResponse("a3 BAD LIST completed"));
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
-
- }
-
- /**
- * Tests parseListInfos method when final response is not OK.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseListInfosOnlyOKResponse() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List rr = new ArrayList<>();
- rr.add(new IMAPResponse("a3 OK LIST completed"));
- final ListInfoList infos = mapper.readValue(rr.toArray(new IMAPResponse[0]), ListInfoList.class);
-
- // verify the result
- Assert.assertNotNull(infos, "result mismatched.");
- }
-
- /**
- * Tests parseListInfos method when response array length is 0.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseListInfosEmptyResponses() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseListStatus method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseListStatusSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- final List expectedLinfo = new ArrayList<>();
- final List expectedStatuses = new ArrayList<>();
-
- // 0
- final String lresp0 = "* LIST (\\HasNoChildren) \"/\" \"INBOX\"";
- content.add(new IMAPResponse(lresp0));
- // add some non-relevant responses
- content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
- content.add(new IMAPResponse("* 115141 EXPUNGE\r\n"));
- final String sresp0 = "* STATUS \"INBOX\" (HIGHESTMODSEQ 82676 MESSAGES 774 UIDNEXT 913 UIDVALIDITY 1 UNSEEN 769)";
- popluateContentAndBuildStatus(lresp0, sresp0, content, expectedLinfo, expectedStatuses);
-
- // 1
- content.add(new IMAPResponse("* 115142 EXPUNGE\r\n"));
- final String lresp1 = "* LIST (\\HasChildren \\NonExistent) \"/\" \"[Zmail]\"";
- content.add(new IMAPResponse(lresp1));
- content.add(new IMAPResponse("* 115143 EXPUNGE\r\n"));
- popluateContentAndBuildStatus(lresp1, null, content, expectedLinfo, expectedStatuses); // status is null
-
- // 2
- final String lresp2 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/All Mail\"";
- content.add(new IMAPResponse(lresp2));
- final String sresp2 = "* STATUS \"[Zmail]/All Mail\" (HIGHESTMODSEQ 82676 MESSAGES 777 UIDNEXT 1109 UIDVALIDITY 12 UNSEEN 770)";
- popluateContentAndBuildStatus(lresp2, sresp2, content, expectedLinfo, expectedStatuses);
-
- // 3
- final String lresp3 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Drafts\"";
- content.add(new IMAPResponse(lresp3));
- final String sresp3 = "* STATUS \"[Zmail]/Drafts\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 98 UIDVALIDITY 6 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp3, sresp3, content, expectedLinfo, expectedStatuses);
-
- // 4
- final String lresp4 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Important\"";
- content.add(new IMAPResponse(lresp4));
- final String sresp4 = "* STATUS \"[Zmail]/Important\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 118 UIDVALIDITY 9 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp4, sresp4, content, expectedLinfo, expectedStatuses);
-
- // 5
- final String lresp5 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Sent Mail\"";
- content.add(new IMAPResponse(lresp5));
- final String sresp5 = "* STATUS \"[Zmail]/Sent Mail\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 88 UIDVALIDITY 5 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp5, sresp5, content, expectedLinfo, expectedStatuses);
-
- // 6
- final String lresp6 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Spam\"";
- content.add(new IMAPResponse(lresp6));
- final String sresp6 = "* STATUS \"[Zmail]/Spam\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 101 UIDVALIDITY 3 UNSEEN 1)";
- popluateContentAndBuildStatus(lresp6, sresp6, content, expectedLinfo, expectedStatuses);
-
- // 7
- final String lresp7 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Starred\"";
- content.add(new IMAPResponse(lresp7));
- final String sresp7 = "* STATUS \"[Zmail]/Starred\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 9 UIDVALIDITY 4 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp7, sresp7, content, expectedLinfo, expectedStatuses);
-
- // 8
- final String lresp8 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Trash\"";
- content.add(new IMAPResponse(lresp8));
- final String sresp8 = "* STATUS \"[Zmail]/Trash\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 137 UIDVALIDITY 2 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp8, sresp8, content, expectedLinfo, expectedStatuses);
-
- // 9
- final String lresp9 = "* LIST (\\HasChildren) \"/\" \"parent_folder\"";
- content.add(new IMAPResponse(lresp9));
- final String sresp9 = "* STATUS \"parent_folder\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 1 UIDVALIDITY 15 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp9, sresp9, content, expectedLinfo, expectedStatuses);
-
- // 10
- final String lresp10 = "* LIST (\\HasNoChildren) \"/\" \"parent_folder/child_folder\"";
- content.add(new IMAPResponse(lresp10));
- final String sresp10 = "* STATUS \"parent_folder/child_folder\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 2 UIDVALIDITY 16 UNSEEN 0)";
- popluateContentAndBuildStatus(lresp10, sresp10, content, expectedLinfo, expectedStatuses);
-
- // 11
- final String lresp11 = "* LIST (\\HasChildren \\NonExistent) \"/\" \"abc_folder\"";
- content.add(new IMAPResponse(lresp11));
- content.add(new IMAPResponse("* 115143 EXPUNGE\r\n"));
- popluateContentAndBuildStatus(lresp11, null, content, expectedLinfo, expectedStatuses); // status is null
-
- content.add(new IMAPResponse("a3 OK Success"));
-
- final ListStatusResult ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
-
- // verify the result
- final List infos = ll.getListInfos();
- Assert.assertNotNull(infos, "result mismatched.");
- Assert.assertEquals(infos.size(), 12, "ListInfo count mismatched.");
- final Map statuses = ll.getStatuses();
-
- for (int i = 0; i < infos.size(); i++) {
- final ExtensionListInfo info = infos.get(i);
- final ExtensionListInfo expectedInfo = expectedLinfo.get(i);
- final Status expectedStatus = expectedStatuses.get(i);
-
- Assert.assertNotNull(info, "ListStatus should not be null.");
-
- // verify ListInfo
- Assert.assertNotNull(info, "ListInfo should not be null.");
- Assert.assertEquals(info.getAvailableExtendedAttributes(), expectedInfo.getAvailableExtendedAttributes(), "Data mismatched.");
-
- Assert.assertEquals(info.hasInferiors, expectedInfo.hasInferiors, "hasInferiors mismatched.");
- Assert.assertEquals(info.name, expectedInfo.name, "ListInfo name mismatched.");
- Assert.assertEquals(info.attrs.length, expectedInfo.attrs.length, "ListInfo attrs size mismatched.");
- for (int j = 0; j < expectedInfo.attrs.length; j++) {
- Assert.assertEquals(info.attrs[j], expectedInfo.attrs[j], "info.attrs[j] mismatched.");
- }
- final Status st = statuses.get(info.name);
-
- // Verify Status
- if (expectedStatus == null) { // if expecting no status for this folder
- Assert.assertNull(st, "Expected Status mismatched.");
- continue;
- }
- Assert.assertEquals(st.mbox, expectedStatus.mbox, "Status.mbox mismatched.");
- Assert.assertEquals(info.name, st.mbox, "ListInfo and Status mismatched. ");
- Assert.assertEquals(st.highestmodseq, expectedStatus.highestmodseq, "highestmodseq mismatched.");
- Assert.assertEquals(st.total, expectedStatus.total, "total mismatched.");
- Assert.assertEquals(st.recent, expectedStatus.recent, "recent mismatched.");
- Assert.assertEquals(st.uidnext, expectedStatus.uidnext, "highestmodseq mismatched.");
- Assert.assertEquals(st.uidvalidity, expectedStatus.uidvalidity, "highestmodseq mismatched.");
- Assert.assertEquals(st.unseen, expectedStatus.unseen, "highestmodseq mismatched.");
- Assert.assertEquals(st.items, expectedStatus.items, "items mismatched.");
- if (expectedStatus.items != null) {
- Assert.assertEquals(st.items.size(), expectedStatus.items.size(), "items mismatched.");
- for (final String itemName : expectedStatus.items.keySet()) {
- Assert.assertEquals(st.items.get(itemName), expectedStatus.items.get(itemName), "Item value mismatched.");
- }
- }
- }
- }
-
- /**
- * Builds the Status for expected Status and populate given Content for input.
- *
- * @param listResp List response in string format
- * @param statusResp Status response in string format
- * @param content IMAPResponse list
- * @param expectedInfo expected ListInfo list
- * @param expectedSt expected Status list
- * @return
- * @throws ProtocolException will not throw
- * @throws IOException will not throw
- */
- private void popluateContentAndBuildStatus(final String listResp, final String statusResp, final List content,
- final List expectedInfo, final List expectedSt) throws IOException, ProtocolException {
-
- expectedInfo.add(new ExtensionListInfo(new IMAPResponse(listResp)));
- if (statusResp != null) {
- content.add(new IMAPResponse(statusResp));
- expectedSt.add(new Status(new IMAPResponse(statusResp)));
- } else {
- expectedSt.add(null);
- }
- }
-
- /**
- * Tests parseListInfos method when response array length is 0.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseListStausEmptyResponses() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseListInfos method when final response is not OK.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseListStatusNoOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- final List names = new ArrayList<>();
- buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "\"Archive\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "\"Bulk Mail\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "\"Draft\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"Inbox\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "\"Sent\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "\"Trash\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "\"test1\"");
- buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"test1/test1_1\"");
- content.add(new IMAPResponse("a3 BAD LIST completed"));
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
-
- }
-
- /**
- * Tests parseListStatus method when final response is not OK.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseListStatusOnlyOKResponse() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List rr = new ArrayList<>();
- rr.add(new IMAPResponse("a3 OK LIST completed"));
- final ListStatusResult infos = mapper.readValue(rr.toArray(new IMAPResponse[0]), ListStatusResult.class);
-
- // verify the result
- Assert.assertNotNull(infos, "result mismatched.");
- }
-
- /**
- * Tests ExtensionMailboxInfo method when response array length is 0.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseMailboxExtensionInfosEmptyResponses() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), ExtensionMailboxInfo.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseStatus method with response array 0.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseStatusArray0() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[0];
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, Status.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parse a class that mapper does not support.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseClassUnknown() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[1];
- content[0] = new IMAPResponse("002 OK"); // make it bad so it does not update mode
-
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, ID.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.UNKNOWN_PARSE_RESULT_TYPE, "Failure type mismatched.");
- }
-
- /**
- * Tests parseStatus method with not OK response.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseStatusNotOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[1];
- content[0] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
-
- // verify the result
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, Status.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseStatus method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseStatusNoStatusResponse() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* NOSTATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)");
- content[1] = new IMAPResponse("A042 OK STATUS completed");
-
- // verify the result
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, Status.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseStatus method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseStatusOK() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292 UNSEEN 3)"));
- content.add(new IMAPResponse("* S2TATUS blurdybloop (MESSAGES 232 UIDNEXT 44293)"));
- content.add(new IMAPResponse("* STATUS blurdybloop (UNSEEN 4)"));
- content.add(new IMAPResponse("* STATUS blurdybloop (UIDVALIDITY 999333)"));
- content.add(new IMAPResponse("A042 OK STATUS completed"));
-
- final Status status = mapper.readValue(content.toArray(new IMAPResponse[0]), Status.class);
-
- // verify the result
- Assert.assertNotNull(status, "status mismatched.");
- Assert.assertEquals(status.uidnext, 44292, "uidnext mismatched.");
- Assert.assertEquals(status.total, 231, "total mismatched.");
- Assert.assertEquals(status.unseen, 4, "unseen mismatched, should take the latter one.");
- Assert.assertEquals(status.uidvalidity, 999333, "uidvalidity mismatched.");
- }
-
- /**
- * Tests parseToID method with response array 0.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToIdResultArray0() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[0];
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, IdResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToID method with not OK response.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToIdResultNotOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[1];
- content[0] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
-
- // verify the result
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, IdResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToIdResult with first byte is N.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToIdResultFirstByteIsN() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* ID NIL \n");
- content[1] = new IMAPResponse("a042 OK ID command completed");
-
- final IdResult id = mapper.readValue(content, IdResult.class);
-
- // verify the result
- Assert.assertNotNull(id, "id mismatched.");
- Assert.assertFalse(id.hasKey("name"), "name key mismatched.");
- Assert.assertNull(id.getValue("name"), "name value mismatched.");
- }
-
- /**
- * Tests parseToIdResult with first byte is n.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToIdResultFirstByteIsLowercaseN() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* ID nIL \n");
- content[1] = new IMAPResponse("a042 OK ID command completed");
-
- final IdResult id = mapper.readValue(content, IdResult.class);
-
- // verify the result
- Assert.assertNotNull(id, "id mismatched.");
- Assert.assertFalse(id.hasKey("name"), "name key mismatched.");
- Assert.assertNull(id.getValue("name"), "name value mismatched.");
- }
-
- /**
- * Tests parseToIdResult with first byte not left parenthesis.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToIdResultNotStartWithLeftParenthesis() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* ID X \n");
- content[1] = new IMAPResponse("a042 OK ID command completed");
-
- // verify the result
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, IdResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToIdResult with first byte not left parenthesis.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToIdResultNameAbsent() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- // sun java mail library 1.5.5 has bug fix in readStringList() to return 0 length array instead of 1 length with a null element
-
- content[0] = new IMAPResponse("* ID () \n");
- content[1] = new IMAPResponse("a042 OK ID command completed");
-
- // verify the result
- final IdResult id = mapper.readValue(content, IdResult.class);
- // verify the result
- Assert.assertNotNull(id, "result mismatched.");
- }
-
- /**
- * Tests parseToIdResult with first byte not left parenthesis.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToIdResultValueAbsent() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* ID (') \n");
- content[1] = new IMAPResponse("a042 OK ID command completed");
-
- // verify the result
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content, IdResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToID method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToIdResultOK() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[3];
- content[0] = new IMAPResponse("* ID (\"name\" \"Cyrus\" \"version\" \"1.5\" \"os\" \"sunos\"\n\"os-version\" \"5.5\" \"support-url\"\n"
- + "\"mailto:cyrus-bugs+@andrew.cmu.edu\")\n");
- content[1] = new IMAPResponse("junk");
- content[2] = new IMAPResponse("a042 OK ID command completed");
-
- final IdResult id = mapper.readValue(content, IdResult.class);
-
- // verify the result
- Assert.assertNotNull(id, "id mismatched.");
- Assert.assertTrue(id.hasKey("name"), "name should be present.");
- Assert.assertEquals(id.getValue("name"), "Cyrus", "name value mismatched.");
- }
-
- /**
- * Tests parseSearchResult method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToSearchResultOK() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* SEARCH 150404 150406 150407\r\n");
- content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
-
- final SearchResult result = mapper.readValue(content, SearchResult.class);
-
- // verify the result
- Assert.assertNotNull(result, "result mismatched.");
- final List list = result.getMessageNumbers();
- Assert.assertNotNull(list, "getMessageSequence() mismatched.");
- Assert.assertEquals(list.size(), 3, "getMessageSequence() mismatched.");
- Assert.assertEquals(list.get(0), Long.valueOf(150404), "getMessageSequence() mismatched.");
- Assert.assertEquals(list.get(1), Long.valueOf(150406), "getMessageSequence() mismatched.");
- Assert.assertEquals(list.get(2), Long.valueOf(150407), "getMessageSequence() mismatched.");
- }
-
- /**
- * Tests parseSearchResult method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToSearchResultOKNoSearchResult() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[2];
- content[0] = new IMAPResponse("* SEARCH\r\n");
- content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
-
- final SearchResult result = mapper.readValue(content, SearchResult.class);
-
- // verify the result
- Assert.assertNotNull(result, "result mismatched.");
- final List list = result.getMessageNumbers();
- Assert.assertNotNull(list, "getMessageSequence() mismatched.");
- Assert.assertEquals(list.size(), 0, "getMessageSequence() mismatched.");
- }
-
- /**
- * Tests parseToSearchResult method when tagged response is not OK.
- *
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToSearchResultZeroLengthResponse() throws ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[0];
-
- ImapAsyncClientException actual = null;
- try {
- final SearchResult result = mapper.readValue(content, SearchResult.class);
- } catch (final ImapAsyncClientException e) {
- actual = e;
- }
- // verify the result
- Assert.assertNotNull(actual, "ImapAsyncClientException should occur.");
- Assert.assertEquals(actual.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToSearchResult method when tagged response is not OK.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- */
- @Test
- public void testParseToSearchResultNotOK() throws IOException, ProtocolException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final IMAPResponse[] content = new IMAPResponse[1];
- content[0] = new IMAPResponse("a3 BAD SEARCH completed (Failure)\r\n");
-
- ImapAsyncClientException actual = null;
- try {
- final SearchResult result = mapper.readValue(content, SearchResult.class);
- } catch (final ImapAsyncClientException e) {
- actual = e;
- }
- // verify the result
- Assert.assertNotNull(actual, "ImapAsyncClientException should occur.");
- Assert.assertEquals(actual.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToEnableResult method successfully.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResult() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* some junks\r\n"));
- content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
- content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
- final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
-
- // verify the result
- Assert.assertNotNull(enableResult, "result should never return null.");
- final Set capas = enableResult.getEnabledCapabilities();
- Assert.assertEquals(capas.size(), 2, "capability missed.");
- Assert.assertTrue(capas.contains("CONDSTORE"), "One capability missed.");
- Assert.assertTrue(capas.contains("QRSYNC"), "One capability missed.");
- }
-
- /**
- * Tests parseToEnableResult method with response contains length zero line.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResultLengthZero() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* some junks\r\n"));
- content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
- content.add(new IMAPResponse(""));
- content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
- final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
-
- // verify the result
- Assert.assertNotNull(enableResult, "result should never return null.");
- final Set capas = enableResult.getEnabledCapabilities();
- Assert.assertEquals(capas.size(), 2, "capability missed.");
- Assert.assertTrue(capas.contains("CONDSTORE"), "One capability missed.");
- Assert.assertTrue(capas.contains("QRSYNC"), "One capability missed.");
- }
-
- /**
- * Tests parseToEnableResult method with response contains only * ENABLED but no capability.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResultNoCapa() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* ENABLED\r\n"));
- content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
- final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
-
- // verify the result
- Assert.assertNotNull(enableResult, "result should never return null.");
- final Set capas = enableResult.getEnabledCapabilities();
- Assert.assertEquals(capas.size(), 0, "capability missed.");
- }
-
- /**
- * Tests parseToEnableResult method when server does not enable all the capability.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResultNotEnabled() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
- final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
-
- // verify the result
- Assert.assertNotNull(enableResult, "result should never return null.");
- final Set capas = enableResult.getEnabledCapabilities();
- Assert.assertEquals(capas.size(), 0, "capability missed.");
- }
-
- /**
- * Tests parseToEnableResult method when server returns with no response contents.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResultNoContent() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-
- /**
- * Tests parseToEnableResult method when server returns with not OK response.
- *
- * @throws IOException will not throw
- * @throws ProtocolException will not throw
- * @throws ImapAsyncClientException will not throw
- */
- @Test
- public void testParseToEnableResultNotOK() throws IOException, ProtocolException, ImapAsyncClientException {
- final ImapResponseMapper mapper = new ImapResponseMapper();
- final List content = new ArrayList<>();
- content.add(new IMAPResponse("* some junks\r\n"));
- content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
- content.add(new IMAPResponse("a3 BAD ENABLE fail\r\n"));
- ImapAsyncClientException cause = null;
- try {
- mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
- } catch (final ImapAsyncClientException e) {
- cause = e;
- }
- // verify the result
- Assert.assertNotNull(cause, "cause mismatched.");
- Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
- }
-}
+package com.yahoo.imapnio.async.response;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.mail.Flags.Flag;
+import javax.mail.Folder;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.sun.mail.iap.ProtocolException;
+import com.sun.mail.imap.AppendUID;
+import com.sun.mail.imap.CopyUID;
+import com.sun.mail.imap.protocol.FetchItem;
+import com.sun.mail.imap.protocol.FetchResponse;
+import com.sun.mail.imap.protocol.ID;
+import com.sun.mail.imap.protocol.IMAPResponse;
+import com.sun.mail.imap.protocol.ListInfo;
+import com.sun.mail.imap.protocol.MailboxInfo;
+import com.sun.mail.imap.protocol.Status;
+import com.yahoo.imapnio.async.data.Capability;
+import com.yahoo.imapnio.async.data.EnableResult;
+import com.yahoo.imapnio.async.data.ExtensionListInfo;
+import com.yahoo.imapnio.async.data.ExtensionMailboxInfo;
+import com.yahoo.imapnio.async.data.FetchResult;
+import com.yahoo.imapnio.async.data.IdResult;
+import com.yahoo.imapnio.async.data.ListInfoList;
+import com.yahoo.imapnio.async.data.ListStatusResult;
+import com.yahoo.imapnio.async.data.MessageNumberSet;
+import com.yahoo.imapnio.async.data.SearchResult;
+import com.yahoo.imapnio.async.data.StoreResult;
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException;
+import com.yahoo.imapnio.async.exception.ImapAsyncClientException.FailureType;
+
+/**
+ * Unit test for {@link ImapResponseMapper}.
+ */
+public class ImapResponseMapperTest {
+ /** Imap server greeting. */
+ private static final String GREETING = "* OK [CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE "
+ + "XYMHIGHESTMODSEQ UIDPLUS LITERAL+ CHILDREN X-MSG-EXT] IMAP4rev1 Hello";
+
+ /**
+ * Tests parseToCapabilities method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToCapabilitiesFromCapaCommand() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* some junks\r\n"));
+ content.add(new IMAPResponse("* CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE\r\n"));
+ content.add(new IMAPResponse("* more junks\r\n"));
+ content.add(new IMAPResponse("a1 OK CAPABILITY completed\r\n"));
+ final Capability capa = mapper.readValue(content.toArray(new IMAPResponse[0]), Capability.class);
+
+ // verify the result
+ Assert.assertNotNull(capa, "result should never return null.");
+ Assert.assertTrue(capa.hasCapability("IMAP4rev1".toUpperCase()), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
+ final List authValues = capa.getCapability("AUTH");
+ Assert.assertNotNull(authValues, "AUTH values missed.");
+ Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
+ Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
+ Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
+ Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
+ }
+
+ /**
+ * Tests parseToCapabilities method when ImapResponse array has zero length.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToCapabilitiesArrayLengthZero() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = {};
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, Capability.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToCapabilities method successfully from an OK response that has Capability response attached to.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToCapabilitiesFromGreeting() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse(GREETING) };
+ final Capability capa = mapper.readValue(content, Capability.class);
+
+ // verify the result
+ Assert.assertNotNull(capa, "result should never return null.");
+ Assert.assertTrue(capa.hasCapability("IMAP4rev1".toUpperCase()), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("X-MSG-EXT"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("LITERAL+"), "One capability missed.");
+ final List authValues = capa.getCapability("AUTH");
+ Assert.assertNotNull(authValues, "AUTH values missed.");
+ Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
+ Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
+ Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
+ Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
+ }
+
+ /**
+ * Tests parseToCapabilities method successfully when it has Netscape Messaging Server response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToCapabilitiesSkipStar() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = {
+ new IMAPResponse("* CAPABILITY * IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=XOAUTH2 AUTH=OAUTHBEARER ID MOVE NAMESPACE") };
+ final Capability capa = mapper.readValue(content, Capability.class);
+
+ // verify the result
+ Assert.assertTrue(capa.hasCapability("IMAP4rev1"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("SASL-IR"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("ID"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("MOVE"), "One capability missed.");
+ Assert.assertTrue(capa.hasCapability("NAMESPACE"), "One capability missed.");
+ final List authValues = capa.getCapability("AUTH");
+ Assert.assertNotNull(authValues, "AUTH values missed.");
+ Assert.assertEquals(authValues.size(), 3, "One Auth value missed");
+ Assert.assertEquals(authValues.get(0), "PLAIN", "One Auth value missed");
+ Assert.assertEquals(authValues.get(1), "XOAUTH2", "One Auth value missed");
+ Assert.assertEquals(authValues.get(2), "OAUTHBEARER", "One Auth value missed");
+ }
+
+ /**
+ * Tests parseCopyUid successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseCopyUidSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [COPYUID 1549405125 150395 3]"); // a good response
+ content[1] = new IMAPResponse("* BAD Some junks"); // test if it skips the bad response
+ content[2] = null; // test if it skips null
+ content[3] = new IMAPResponse("* OK [SOMETHING 111 222 3]"); // test if it detects it is not COPYUID keyword
+ content[4] = new IMAPResponse("* OK"); // test when b is 0
+
+ final CopyUID copyUid = mapper.readValue(content, CopyUID.class);
+
+ // verify the result
+ Assert.assertNotNull(copyUid, "result mismatched.");
+ Assert.assertEquals(copyUid.uidvalidity, 1549405125, "result mismatched.");
+ Assert.assertNotNull(copyUid.src, "result mismatched.");
+ Assert.assertNotNull(copyUid.dst, "result mismatched.");
+ Assert.assertEquals(copyUid.src.length, 1, "result mismatched.");
+ Assert.assertEquals(copyUid.src[0].start, 150395, "result mismatched.");
+ Assert.assertEquals(copyUid.src[0].end, 150395, "result mismatched.");
+ Assert.assertEquals(copyUid.dst.length, 1, "result mismatched.");
+ Assert.assertEquals(copyUid.dst[0].start, 3, "result mismatched.");
+ Assert.assertEquals(copyUid.dst[0].end, 3, "result mismatched.");
+ }
+
+ /**
+ * Tests ImapResponseParse parseCopyUid when Responses array is empty.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseCopyUidResponseArrayZero() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, CopyUID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+
+ }
+
+ /**
+ * Tests parseAppendUid successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseAppendUidSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[3];
+ content[0] = new IMAPResponse("+ Ready for literal data");
+ content[1] = new IMAPResponse("* 3 EXISTS");
+ content[2] = new IMAPResponse("a5 OK [APPENDUID 1459808247 150399] APPEND completed");
+ final AppendUID appendUid = mapper.readValue(content, AppendUID.class);
+
+ // verify the result
+ Assert.assertNotNull(appendUid, "result mismatched.");
+ Assert.assertEquals(appendUid.uidvalidity, 1459808247, "result mismatched.");
+ Assert.assertEquals(appendUid.uid, 150399, "result mismatched.");
+ }
+
+ /**
+ * Tests parseToCapabilities method when ImapResponse array has zero length.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToAppendUidsArrayLengthZero() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = {};
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, AppendUID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseAppendUid with a BAD response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseAppendUidNotOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("* BAD Some junks") };
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, AppendUID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+
+ }
+
+ /**
+ * Tests parseAppendUid when b is 0, or when left bracket is not found.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseAppendUidByteReadExhausted() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("* OK") }; // test when b is 0
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, AppendUID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseAppendUid when b is 0.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseAppendUidNoAppendUidKeyword() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("* OK [appendButNotUid 111 222 3]") }; // test when b is 0
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, AppendUID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+
+ }
+
+ /**
+ * Tests parseMailboxInfo method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseMailboxInfoReadOnlySuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[8];
+ content[0] = new IMAPResponse("* 3 EXISTS");
+ content[1] = new IMAPResponse("* 0 RECENT");
+ content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
+ content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
+ content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
+ content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
+ content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
+ content[7] = new IMAPResponse("002 OK [READ-ONLY] EXAMINE completed; now in selected state");
+ final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
+
+ // verify the result
+ Assert.assertNotNull(minfo, "result mismatched.");
+ Assert.assertEquals(minfo.mode, Folder.READ_ONLY, "mode mismatched.");
+ Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
+ Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
+ Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
+ Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
+ }
+
+ /**
+ * Tests parseExtensionMailboxInfo method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseExtensionMailboxInfoReadOnlySuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[9];
+ content[0] = new IMAPResponse("* 3 EXISTS");
+ content[1] = new IMAPResponse("* 0 RECENT");
+ content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
+ content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
+ content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
+ content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
+ content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
+ content[7] = new IMAPResponse("* OK [MAILBOXID (A26)] Ok");
+ content[8] = new IMAPResponse("002 OK [READ-ONLY] EXAMINE completed; now in selected state");
+ final ExtensionMailboxInfo minfo = mapper.readValue(content, ExtensionMailboxInfo.class);
+
+ // verify the result
+ Assert.assertNotNull(minfo, "result mismatched.");
+ Assert.assertEquals(minfo.mode, Folder.READ_ONLY, "mode mismatched.");
+ Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
+ Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
+ Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
+ Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
+ Assert.assertEquals(minfo.getMailboxId(), "A26", "MailboxId mismatched.");
+ }
+
+ /**
+ * Tests parseMailboxInfo method successfully with READ-WRITE mode.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseMailboxInfoReadWriteSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[8];
+ content[0] = new IMAPResponse("* 3 EXISTS");
+ content[1] = new IMAPResponse("* 0 RECENT");
+ content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
+ content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
+ content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
+ content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
+ content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
+ content[7] = new IMAPResponse("002 OK [READ-WRITE] EXAMINE completed; now in selected state");
+ final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
+
+ // verify the result
+ Assert.assertNotNull(minfo, "result mismatched.");
+ Assert.assertEquals(minfo.mode, Folder.READ_WRITE, "mode mismatched.");
+ Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
+ Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
+ Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
+ Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
+ }
+
+ /**
+ * Tests parseMailboxInfo method successfully with READ-WRITE mode.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseMailboxInfoBad() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[8];
+ content[0] = new IMAPResponse("* 3 EXISTS");
+ content[1] = new IMAPResponse("* 0 RECENT");
+ content[2] = new IMAPResponse("* OK [UIDVALIDITY 1459808247] UIDs valid");
+ content[3] = new IMAPResponse("* OK [UIDNEXT 150400] Predicted next UID");
+ content[4] = new IMAPResponse("* FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen $Forwarded $Junk $NotJunk)");
+ content[5] = new IMAPResponse("* OK [PERMANENTFLAGS ()] No permanent flags permitted");
+ content[6] = new IMAPResponse("* OK [HIGHESTMODSEQ 614]");
+ content[7] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
+ final MailboxInfo minfo = mapper.readValue(content, MailboxInfo.class);
+
+ // verify the result
+ Assert.assertNotNull(minfo, "result mismatched.");
+ Assert.assertEquals(minfo.mode, 0, "mode mismatched.");
+ Assert.assertNotNull(minfo.availableFlags, "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.ANSWERED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DELETED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.DRAFT), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.FLAGGED), "availableFlags mismatched.");
+ Assert.assertTrue(minfo.availableFlags.contains(Flag.SEEN), "availableFlags mismatched.");
+ Assert.assertEquals(minfo.highestmodseq, 614, "highestmodseq mismatched.");
+ Assert.assertEquals(minfo.uidvalidity, 1459808247, "uidvalidity mismatched.");
+ Assert.assertEquals(minfo.uidnext, 150400, "uidnext mismatched.");
+ }
+
+ /**
+ * Tests parseMailboxInfo method successfully with READ-WRITE mode.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseMailboxInfoResponseArray0() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, MailboxInfo.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Adds the data for test input.
+ *
+ * @param rr output parameter, list of IMAPResponse
+ * @param expectedNames output parameter, list of expected folder names
+ * @param respStr response string
+ * @param folder folder name
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ private void buildListInfoIMAPResponse(final List rr, final List expectedNames, final String respStr, final String folder)
+ throws IOException, ProtocolException {
+ expectedNames.add(folder);
+ rr.add(new IMAPResponse(respStr + " \"" + folder + "\"\r\n"));
+ }
+
+ /**
+ * Tests parseListInfos method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseListInfosSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ final List names = new ArrayList<>();
+ // add a non-relevant response
+ content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "Archive");
+ content.add(new IMAPResponse("* some junks\r\n"));
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "Bulk Mail");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "Draft");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "Inbox");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "Sent");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "Trash");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "test1");
+ content.add(new IMAPResponse("* 115141 EXISTS\r\n"));
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "test1/test1_1");
+ content.add(new IMAPResponse("a3 OK LIST completed"));
+ final ListInfoList ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
+ final List infos = ll.getListInfo();
+
+ // verify the result
+ Assert.assertNotNull(infos, "result mismatched.");
+ Assert.assertEquals(infos.size(), 8, "ListInfo count mismatched.");
+ Assert.assertEquals(infos.size(), names.size(), "ListInfo count mismatched.");
+ for (int i = 0; i < infos.size(); i++) {
+ final ListInfo info = infos.get(i);
+ final String expectedFolder = names.get(i);
+ Assert.assertNotNull(info, "ListInfo should not be null.");
+ Assert.assertNotNull(expectedFolder, "folder name should not be null.");
+ Assert.assertTrue(info.hasInferiors, "hasInferiors mismatched.");
+ Assert.assertNotNull(info.name, "Name mismatched.");
+ Assert.assertEquals(info.name, expectedFolder, "folder name mismatched.");
+ }
+ }
+
+ /**
+ * Tests parseListInfos method successfully when responses are results of LSUB command.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseListInfosFromLSubSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ final List names = new ArrayList<>();
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\Archive \\HasNoChildren) \"/\"", "Archive");
+ content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
+ content.add(new IMAPResponse("* MORE JUNKS\r\n"));
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\Junk \\HasNoChildren) \"/\"", "Bulk Mail");
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\Drafts \\HasNoChildren) \"/\"", "Draft");
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\HasNoChildren) \"/\"", "Inbox");
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\Sent \\HasNoChildren) \"/\"", "Sent");
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\Trash \\HasNoChildren) \"/\"", "Trash");
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\HasChildren) \"/\"", "test1");
+ content.add(new IMAPResponse("* 115141 EXISTS\r\n"));
+ buildListInfoIMAPResponse(content, names, "* LSUB (\\HasNoChildren) \"/\"", "test1/test1_1");
+ content.add(new IMAPResponse("a3 OK LSUB completed"));
+ final ListInfoList ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
+ final List infos = ll.getListInfo();
+
+ // verify the result
+ Assert.assertNotNull(infos, "result mismatched.");
+ Assert.assertEquals(infos.size(), 8, "ListInfo count mismatched.");
+ Assert.assertEquals(infos.size(), names.size(), "ListInfo count mismatched.");
+ for (int i = 0; i < infos.size(); i++) {
+ final ListInfo info = infos.get(i);
+ final String expectedFolder = names.get(i);
+ Assert.assertNotNull(info, "ListInfo should not be null.");
+ Assert.assertNotNull(expectedFolder, "folder name should not be null.");
+ Assert.assertTrue(info.hasInferiors, "hasInferiors mismatched.");
+ Assert.assertNotNull(info.name, "Name mismatched.");
+ Assert.assertEquals(info.name, expectedFolder, "folder name mismatched.");
+ }
+ }
+
+ /**
+ * Tests parseListInfos method when final response is not OK.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseListInfosNoOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ final List names = new ArrayList<>();
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "\"Archive\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "\"Bulk Mail\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "\"Draft\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"Inbox\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "\"Sent\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "\"Trash\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "\"test1\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"test1/test1_1\"");
+ content.add(new IMAPResponse("a3 BAD LIST completed"));
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+
+ }
+
+ /**
+ * Tests parseListInfos method when final response is not OK.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseListInfosOnlyOKResponse() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List rr = new ArrayList<>();
+ rr.add(new IMAPResponse("a3 OK LIST completed"));
+ final ListInfoList infos = mapper.readValue(rr.toArray(new IMAPResponse[0]), ListInfoList.class);
+
+ // verify the result
+ Assert.assertNotNull(infos, "result mismatched.");
+ }
+
+ /**
+ * Tests parseListInfos method when response array length is 0.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseListInfosEmptyResponses() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), ListInfoList.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseListStatus method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseListStatusSuccess() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ final List expectedLinfo = new ArrayList<>();
+ final List expectedStatuses = new ArrayList<>();
+
+ // 0
+ final String lresp0 = "* LIST (\\HasNoChildren) \"/\" \"INBOX\"";
+ content.add(new IMAPResponse(lresp0));
+ // add some non-relevant responses
+ content.add(new IMAPResponse("* 115140 EXPUNGE\r\n"));
+ content.add(new IMAPResponse("* 115141 EXPUNGE\r\n"));
+ final String sresp0 = "* STATUS \"INBOX\" (HIGHESTMODSEQ 82676 MESSAGES 774 UIDNEXT 913 UIDVALIDITY 1 UNSEEN 769)";
+ popluateContentAndBuildStatus(lresp0, sresp0, content, expectedLinfo, expectedStatuses);
+
+ // 1
+ content.add(new IMAPResponse("* 115142 EXPUNGE\r\n"));
+ final String lresp1 = "* LIST (\\HasChildren \\NonExistent) \"/\" \"[Zmail]\"";
+ content.add(new IMAPResponse(lresp1));
+ content.add(new IMAPResponse("* 115143 EXPUNGE\r\n"));
+ popluateContentAndBuildStatus(lresp1, null, content, expectedLinfo, expectedStatuses); // status is null
+
+ // 2
+ final String lresp2 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/All Mail\"";
+ content.add(new IMAPResponse(lresp2));
+ final String sresp2 = "* STATUS \"[Zmail]/All Mail\" (HIGHESTMODSEQ 82676 MESSAGES 777 UIDNEXT 1109 UIDVALIDITY 12 UNSEEN 770)";
+ popluateContentAndBuildStatus(lresp2, sresp2, content, expectedLinfo, expectedStatuses);
+
+ // 3
+ final String lresp3 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Drafts\"";
+ content.add(new IMAPResponse(lresp3));
+ final String sresp3 = "* STATUS \"[Zmail]/Drafts\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 98 UIDVALIDITY 6 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp3, sresp3, content, expectedLinfo, expectedStatuses);
+
+ // 4
+ final String lresp4 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Important\"";
+ content.add(new IMAPResponse(lresp4));
+ final String sresp4 = "* STATUS \"[Zmail]/Important\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 118 UIDVALIDITY 9 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp4, sresp4, content, expectedLinfo, expectedStatuses);
+
+ // 5
+ final String lresp5 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Sent Mail\"";
+ content.add(new IMAPResponse(lresp5));
+ final String sresp5 = "* STATUS \"[Zmail]/Sent Mail\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 88 UIDVALIDITY 5 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp5, sresp5, content, expectedLinfo, expectedStatuses);
+
+ // 6
+ final String lresp6 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Spam\"";
+ content.add(new IMAPResponse(lresp6));
+ final String sresp6 = "* STATUS \"[Zmail]/Spam\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 101 UIDVALIDITY 3 UNSEEN 1)";
+ popluateContentAndBuildStatus(lresp6, sresp6, content, expectedLinfo, expectedStatuses);
+
+ // 7
+ final String lresp7 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Starred\"";
+ content.add(new IMAPResponse(lresp7));
+ final String sresp7 = "* STATUS \"[Zmail]/Starred\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 9 UIDVALIDITY 4 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp7, sresp7, content, expectedLinfo, expectedStatuses);
+
+ // 8
+ final String lresp8 = "* LIST (\\HasNoChildren) \"/\" \"[Zmail]/Trash\"";
+ content.add(new IMAPResponse(lresp8));
+ final String sresp8 = "* STATUS \"[Zmail]/Trash\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 137 UIDVALIDITY 2 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp8, sresp8, content, expectedLinfo, expectedStatuses);
+
+ // 9
+ final String lresp9 = "* LIST (\\HasChildren) \"/\" \"parent_folder\"";
+ content.add(new IMAPResponse(lresp9));
+ final String sresp9 = "* STATUS \"parent_folder\" (HIGHESTMODSEQ 82676 MESSAGES 0 UIDNEXT 1 UIDVALIDITY 15 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp9, sresp9, content, expectedLinfo, expectedStatuses);
+
+ // 10
+ final String lresp10 = "* LIST (\\HasNoChildren) \"/\" \"parent_folder/child_folder\"";
+ content.add(new IMAPResponse(lresp10));
+ final String sresp10 = "* STATUS \"parent_folder/child_folder\" (HIGHESTMODSEQ 82676 MESSAGES 1 UIDNEXT 2 UIDVALIDITY 16 UNSEEN 0)";
+ popluateContentAndBuildStatus(lresp10, sresp10, content, expectedLinfo, expectedStatuses);
+
+ // 11
+ final String lresp11 = "* LIST (\\HasChildren \\NonExistent) \"/\" \"abc_folder\"";
+ content.add(new IMAPResponse(lresp11));
+ content.add(new IMAPResponse("* 115143 EXPUNGE\r\n"));
+ popluateContentAndBuildStatus(lresp11, null, content, expectedLinfo, expectedStatuses); // status is null
+
+ content.add(new IMAPResponse("a3 OK Success"));
+
+ final ListStatusResult ll = mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
+
+ // verify the result
+ final List infos = ll.getListInfos();
+ Assert.assertNotNull(infos, "result mismatched.");
+ Assert.assertEquals(infos.size(), 12, "ListInfo count mismatched.");
+ final Map statuses = ll.getStatuses();
+
+ for (int i = 0; i < infos.size(); i++) {
+ final ExtensionListInfo info = infos.get(i);
+ final ExtensionListInfo expectedInfo = expectedLinfo.get(i);
+ final Status expectedStatus = expectedStatuses.get(i);
+
+ Assert.assertNotNull(info, "ListStatus should not be null.");
+
+ // verify ListInfo
+ Assert.assertNotNull(info, "ListInfo should not be null.");
+ Assert.assertEquals(info.getAvailableExtendedAttributes(), expectedInfo.getAvailableExtendedAttributes(), "Data mismatched.");
+
+ Assert.assertEquals(info.hasInferiors, expectedInfo.hasInferiors, "hasInferiors mismatched.");
+ Assert.assertEquals(info.name, expectedInfo.name, "ListInfo name mismatched.");
+ Assert.assertEquals(info.attrs.length, expectedInfo.attrs.length, "ListInfo attrs size mismatched.");
+ for (int j = 0; j < expectedInfo.attrs.length; j++) {
+ Assert.assertEquals(info.attrs[j], expectedInfo.attrs[j], "info.attrs[j] mismatched.");
+ }
+ final Status st = statuses.get(info.name);
+
+ // Verify Status
+ if (expectedStatus == null) { // if expecting no status for this folder
+ Assert.assertNull(st, "Expected Status mismatched.");
+ continue;
+ }
+ Assert.assertEquals(st.mbox, expectedStatus.mbox, "Status.mbox mismatched.");
+ Assert.assertEquals(info.name, st.mbox, "ListInfo and Status mismatched. ");
+ Assert.assertEquals(st.highestmodseq, expectedStatus.highestmodseq, "highestmodseq mismatched.");
+ Assert.assertEquals(st.total, expectedStatus.total, "total mismatched.");
+ Assert.assertEquals(st.recent, expectedStatus.recent, "recent mismatched.");
+ Assert.assertEquals(st.uidnext, expectedStatus.uidnext, "highestmodseq mismatched.");
+ Assert.assertEquals(st.uidvalidity, expectedStatus.uidvalidity, "highestmodseq mismatched.");
+ Assert.assertEquals(st.unseen, expectedStatus.unseen, "highestmodseq mismatched.");
+ Assert.assertEquals(st.items, expectedStatus.items, "items mismatched.");
+ if (expectedStatus.items != null) {
+ Assert.assertEquals(st.items.size(), expectedStatus.items.size(), "items mismatched.");
+ for (final String itemName : expectedStatus.items.keySet()) {
+ Assert.assertEquals(st.items.get(itemName), expectedStatus.items.get(itemName), "Item value mismatched.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Builds the Status for expected Status and populate given Content for input.
+ *
+ * @param listResp List response in string format
+ * @param statusResp Status response in string format
+ * @param content IMAPResponse list
+ * @param expectedInfo expected ListInfo list
+ * @param expectedSt expected Status list
+ * @return
+ * @throws ProtocolException will not throw
+ * @throws IOException will not throw
+ */
+ private void popluateContentAndBuildStatus(final String listResp, final String statusResp, final List content,
+ final List expectedInfo, final List expectedSt) throws IOException, ProtocolException {
+
+ expectedInfo.add(new ExtensionListInfo(new IMAPResponse(listResp)));
+ if (statusResp != null) {
+ content.add(new IMAPResponse(statusResp));
+ expectedSt.add(new Status(new IMAPResponse(statusResp)));
+ } else {
+ expectedSt.add(null);
+ }
+ }
+
+ /**
+ * Tests parseListInfos method when response array length is 0.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseListStausEmptyResponses() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseListInfos method when final response is not OK.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseListStatusNoOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ final List names = new ArrayList<>();
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Archive \\HasNoChildren) \"/\"", "\"Archive\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Junk \\HasNoChildren) \"/\"", "\"Bulk Mail\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Drafts \\HasNoChildren) \"/\"", "\"Draft\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"Inbox\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Sent \\HasNoChildren) \"/\"", "\"Sent\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\Trash \\HasNoChildren) \"/\"", "\"Trash\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasChildren) \"/\"", "\"test1\"");
+ buildListInfoIMAPResponse(content, names, "* LIST (\\HasNoChildren) \"/\"", "\"test1/test1_1\"");
+ content.add(new IMAPResponse("a3 BAD LIST completed"));
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), ListStatusResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+
+ }
+
+ /**
+ * Tests parseListStatus method when final response is not OK.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseListStatusOnlyOKResponse() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List rr = new ArrayList<>();
+ rr.add(new IMAPResponse("a3 OK LIST completed"));
+ final ListStatusResult infos = mapper.readValue(rr.toArray(new IMAPResponse[0]), ListStatusResult.class);
+
+ // verify the result
+ Assert.assertNotNull(infos, "result mismatched.");
+ }
+
+ /**
+ * Tests ExtensionMailboxInfo method when response array length is 0.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseMailboxExtensionInfosEmptyResponses() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), ExtensionMailboxInfo.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStatus method with response array 0.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStatusArray0() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, Status.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parse a class that mapper does not support.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseClassUnknown() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("002 OK"); // make it bad so it does not update mode
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, ID.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.UNKNOWN_PARSE_RESULT_TYPE, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStatus method with not OK response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStatusNotOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
+
+ // verify the result
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, Status.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStatus method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStatusNoStatusResponse() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* NOSTATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)");
+ content[1] = new IMAPResponse("A042 OK STATUS completed");
+
+ // verify the result
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, Status.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStatus method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStatusOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292 UNSEEN 3)"));
+ content.add(new IMAPResponse("* S2TATUS blurdybloop (MESSAGES 232 UIDNEXT 44293)"));
+ content.add(new IMAPResponse("* STATUS blurdybloop (UNSEEN 4)"));
+ content.add(new IMAPResponse("* STATUS blurdybloop (UIDVALIDITY 999333)"));
+ content.add(new IMAPResponse("* STATUS blurdybloop (HIGHESTMODSEQ 2483)"));
+ content.add(new IMAPResponse("A042 OK STATUS completed"));
+
+ final Status status = mapper.readValue(content.toArray(new IMAPResponse[0]), Status.class);
+
+ // verify the result
+ Assert.assertNotNull(status, "status mismatched.");
+ Assert.assertEquals(status.uidnext, 44292, "uidnext mismatched.");
+ Assert.assertEquals(status.total, 231, "total mismatched.");
+ Assert.assertEquals(status.unseen, 4, "unseen mismatched, should take the latter one.");
+ Assert.assertEquals(status.uidvalidity, 999333, "uidvalidity mismatched.");
+ Assert.assertEquals(status.highestmodseq, 2483, "highest modseq mismatched.");
+ }
+
+ /**
+ * Tests parseToID method with response array 0.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToIdResultArray0() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, IdResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToID method with not OK response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToIdResultNotOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("002 BAD"); // make it bad so it does not update mode
+
+ // verify the result
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, IdResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToIdResult with first byte is N.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToIdResultFirstByteIsN() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* ID NIL \n");
+ content[1] = new IMAPResponse("a042 OK ID command completed");
+
+ final IdResult id = mapper.readValue(content, IdResult.class);
+
+ // verify the result
+ Assert.assertNotNull(id, "id mismatched.");
+ Assert.assertFalse(id.hasKey("name"), "name key mismatched.");
+ Assert.assertNull(id.getValue("name"), "name value mismatched.");
+ }
+
+ /**
+ * Tests parseToIdResult with first byte is n.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToIdResultFirstByteIsLowercaseN() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* ID nIL \n");
+ content[1] = new IMAPResponse("a042 OK ID command completed");
+
+ final IdResult id = mapper.readValue(content, IdResult.class);
+
+ // verify the result
+ Assert.assertNotNull(id, "id mismatched.");
+ Assert.assertFalse(id.hasKey("name"), "name key mismatched.");
+ Assert.assertNull(id.getValue("name"), "name value mismatched.");
+ }
+
+ /**
+ * Tests parseToIdResult with first byte not left parenthesis.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToIdResultNotStartWithLeftParenthesis() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* ID X \n");
+ content[1] = new IMAPResponse("a042 OK ID command completed");
+
+ // verify the result
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, IdResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToIdResult with first byte not left parenthesis.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToIdResultNameAbsent() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ // sun java mail library 1.5.5 has bug fix in readStringList() to return 0 length array instead of 1 length with a null element
+
+ content[0] = new IMAPResponse("* ID () \n");
+ content[1] = new IMAPResponse("a042 OK ID command completed");
+
+ // verify the result
+ final IdResult id = mapper.readValue(content, IdResult.class);
+ // verify the result
+ Assert.assertNotNull(id, "result mismatched.");
+ }
+
+ /**
+ * Tests parseToIdResult with first byte not left parenthesis.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToIdResultValueAbsent() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* ID (') \n");
+ content[1] = new IMAPResponse("a042 OK ID command completed");
+
+ // verify the result
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, IdResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToID method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToIdResultOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[3];
+ content[0] = new IMAPResponse("* ID (\"name\" \"Cyrus\" \"version\" \"1.5\" \"os\" \"sunos\"\n\"os-version\" \"5.5\" \"support-url\"\n"
+ + "\"mailto:cyrus-bugs+@andrew.cmu.edu\")\n");
+ content[1] = new IMAPResponse("junk");
+ content[2] = new IMAPResponse("a042 OK ID command completed");
+
+ final IdResult id = mapper.readValue(content, IdResult.class);
+
+ // verify the result
+ Assert.assertNotNull(id, "id mismatched.");
+ Assert.assertTrue(id.hasKey("name"), "name should be present.");
+ Assert.assertEquals(id.getValue("name"), "Cyrus", "name value mismatched.");
+ }
+
+ /**
+ * Tests parseSearchResult method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToSearchResultOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* SEARCH 150404 150406 150407\r\n");
+ content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
+
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+
+ // verify the result
+ Assert.assertNotNull(result, "result mismatched.");
+ final List list = result.getMessageNumbers();
+ Assert.assertNotNull(list, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.size(), 3, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(0), Long.valueOf(150404), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(1), Long.valueOf(150406), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(2), Long.valueOf(150407), "getMessageNumbers() mismatched.");
+ Assert.assertNull(result.getHighestModSeq(), "getHighestModSeq() mismatch.");
+ }
+
+ /**
+ * Tests parseSearchResult method successfully with modification sequence.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToSearchResultModSeqOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* SEARCH 150404 150406 150407 (MODSEQ 2473)\r\n");
+ content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
+
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+
+ // verify the result
+ Assert.assertNotNull(result, "result mismatched.");
+ final List list = result.getMessageNumbers();
+ final Long modSeq = result.getHighestModSeq();
+ Assert.assertNotNull(list, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.size(), 3, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(0), Long.valueOf(150404), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(1), Long.valueOf(150406), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(2), Long.valueOf(150407), "getMessageNumbers() mismatched.");
+ Assert.assertNotNull(modSeq, "getHighestModSeq() should not return null.");
+ Assert.assertEquals(modSeq, Long.valueOf(2473), "getHighestModSeq() mismatched.");
+ }
+
+ /**
+ * Tests parseSearchResult method successfully without correct modification sequence string.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToSearchResultNoModSeqOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* SEARCH 150404 150406 150407 (MODSE 2473)\r\n");
+ content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
+
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+
+ // verify the result
+ Assert.assertNotNull(result, "result mismatched.");
+ final List list = result.getMessageNumbers();
+ final Long modSeq = result.getHighestModSeq();
+ Assert.assertNotNull(list, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.size(), 3, "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(0), Long.valueOf(150404), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(1), Long.valueOf(150406), "getMessageNumbers() mismatched.");
+ Assert.assertEquals(list.get(2), Long.valueOf(150407), "getMessageNumbers() mismatched.");
+ Assert.assertNull(modSeq, "getHighestModSeq() should return null.");
+ }
+
+ /**
+ * Tests parseSearchResult method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToSearchResultOKNoSearchResult() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* SEARCH\r\n");
+ content[1] = new IMAPResponse("a3 OK UID SEARCH completed\r\n");
+
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+
+ // verify the result
+ Assert.assertNotNull(result, "result mismatched.");
+ final List list = result.getMessageNumbers();
+ Assert.assertNotNull(list, "getMessageSequence() mismatched.");
+ Assert.assertEquals(list.size(), 0, "getMessageSequence() mismatched.");
+ }
+
+ /**
+ * Tests parseToSearchResult method when tagged response is not OK.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToSearchResultZeroLengthResponse() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+
+ ImapAsyncClientException actual = null;
+ try {
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+ } catch (final ImapAsyncClientException e) {
+ actual = e;
+ }
+ // verify the result
+ Assert.assertNotNull(actual, "ImapAsyncClientException should occur.");
+ Assert.assertEquals(actual.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToSearchResult method when tagged response is not OK.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseToSearchResultNotOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("a3 BAD SEARCH completed (Failure)\r\n");
+
+ ImapAsyncClientException actual = null;
+ try {
+ final SearchResult result = mapper.readValue(content, SearchResult.class);
+ } catch (final ImapAsyncClientException e) {
+ actual = e;
+ }
+ // verify the result
+ Assert.assertNotNull(actual, "ImapAsyncClientException should occur.");
+ Assert.assertEquals(actual.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToEnableResult method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResult() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* some junks\r\n"));
+ content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
+ content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
+ final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+
+ // verify the result
+ Assert.assertNotNull(enableResult, "result should never return null.");
+ final Set capas = enableResult.getEnabledCapabilities();
+ Assert.assertEquals(capas.size(), 2, "capability missed.");
+ Assert.assertTrue(capas.contains("CONDSTORE"), "One capability missed.");
+ Assert.assertTrue(capas.contains("QRSYNC"), "One capability missed.");
+ }
+
+ /**
+ * Tests parseToEnableResult method with response contains length zero line.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResultLengthZero() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* some junks\r\n"));
+ content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
+ content.add(new IMAPResponse(""));
+ content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
+ final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+
+ // verify the result
+ Assert.assertNotNull(enableResult, "result should never return null.");
+ final Set capas = enableResult.getEnabledCapabilities();
+ Assert.assertEquals(capas.size(), 2, "capability missed.");
+ Assert.assertTrue(capas.contains("CONDSTORE"), "One capability missed.");
+ Assert.assertTrue(capas.contains("QRSYNC"), "One capability missed.");
+ }
+
+ /**
+ * Tests parseToEnableResult method with response contains only * ENABLED but no capability.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResultNoCapa() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* ENABLED\r\n"));
+ content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
+ final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+
+ // verify the result
+ Assert.assertNotNull(enableResult, "result should never return null.");
+ final Set capas = enableResult.getEnabledCapabilities();
+ Assert.assertEquals(capas.size(), 0, "capability missed.");
+ }
+
+ /**
+ * Tests parseToEnableResult method when server does not enable all the capability.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResultNotEnabled() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("a3 OK ENABLE completed\r\n"));
+ final EnableResult enableResult = mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+
+ // verify the result
+ Assert.assertNotNull(enableResult, "result should never return null.");
+ final Set capas = enableResult.getEnabledCapabilities();
+ Assert.assertEquals(capas.size(), 0, "capability missed.");
+ }
+
+ /**
+ * Tests parseToEnableResult method when server returns with no response contents.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResultNoContent() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseToEnableResult method when server returns with not OK response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseToEnableResultNotOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final List content = new ArrayList<>();
+ content.add(new IMAPResponse("* some junks\r\n"));
+ content.add(new IMAPResponse("* ENABLED CONDSTORE QRSYNC\r\n"));
+ content.add(new IMAPResponse("a3 BAD ENABLE fail\r\n"));
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content.toArray(new IMAPResponse[0]), EnableResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+ /**
+ * Tests parseStore method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("A042 OK Store success");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result mismatched.");
+ Assert.assertEquals(storeResult.getFetchResponses().size(), 0, "getFetchResponses() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with CondStore.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreOKCondStore() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [HIGHESTMODSEQ 2682");
+ content[1] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[3] = new IMAPResponse("* 3 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[4] = new IMAPResponse("a4 OK [MODIFIED 1] Conditional Store Failed (Success)");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result should not be null.");
+ Assert.assertNotNull(storeResult.getHighestModSeq(), "getHighestModSeq() should not return null.");
+ Assert.assertEquals(storeResult.getHighestModSeq(), Long.valueOf(2682), "getHighestModSeq() mismatched.");
+ final List irs = storeResult.getFetchResponses();
+ Assert.assertEquals(irs.size(), 3, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(0) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(1) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(2) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should not return null.");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 1, "getModifiedMsgsets() size mismatched");
+ final String msgSets = MessageNumberSet.buildString(storeResult.getModifiedMsgSets());
+ Assert.assertEquals(msgSets, "1", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with CondStore and wrong modified string.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreOKCondStoreWithWrongModified() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [HIGHESTMODSEQ 2682");
+ content[1] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[3] = new IMAPResponse("* 3 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[4] = new IMAPResponse("a4 OK [MODIFIE 1] Conditional Store Failed (Success)");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result should not be null.");
+ Assert.assertNotNull(storeResult.getHighestModSeq(), "getHighestModSeq() should not return null.");
+ Assert.assertEquals(storeResult.getHighestModSeq(), Long.valueOf(2682), "getHighestModSeq() mismatched.");
+ final List irs = storeResult.getFetchResponses();
+ Assert.assertEquals(irs.size(), 3, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(0) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(1) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(2) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should return null.");
+ }
+
+ /**
+ * Tests parseStore method successfully with No response and CondStore.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreNoCondStore() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [HIGHEST 2682");
+ content[1] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[3] = new IMAPResponse("* 3 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[4] = new IMAPResponse("a4 No [MODIFIED 1] Conditional Store Failed (Success)");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result should not be null.");
+ Assert.assertNull(storeResult.getHighestModSeq(), "getHighestModSeq() should return null.");
+ final List irs = storeResult.getFetchResponses();
+ Assert.assertEquals(irs.size(), 3, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(0) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(1) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertTrue(irs.get(2) instanceof FetchResponse, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should not return null.");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 1, "getModifiedMsgsets() size mismatched");
+ final String msgSets = MessageNumberSet.buildString(storeResult.getModifiedMsgSets());
+ Assert.assertEquals(msgSets, "1", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with multiple modification numbers.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreOKCondStoreWithMultipleModified() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [HIGHESTMODSEQ 2682");
+ content[1] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[3] = new IMAPResponse("* 3 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[4] = new IMAPResponse("a4 OK [MODIFIED 1:2,5:4] Conditional Store Failed (Success)");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result should not be null.");
+ final List irs = storeResult.getFetchResponses();
+ Assert.assertNotNull(storeResult.getHighestModSeq(), "getHighestModSeq() should not return null.");
+ Assert.assertEquals(storeResult.getHighestModSeq(), Long.valueOf(2682), "getHighestModSeq() mismatched.");
+ Assert.assertEquals(irs.size(), 3, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() mismatched");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 2, "getModifiedMsgsets() mismatched");
+ final String msgSets = MessageNumberSet.buildString(storeResult.getModifiedMsgSets());
+ Assert.assertEquals(msgSets, "1:2,4:5", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with vanished response..
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreOKCondStoreVanished() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[5];
+ content[0] = new IMAPResponse("* OK [HIGHESTMODSEQ 2682");
+ content[1] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2600))");
+ content[3] = new IMAPResponse("* VANISHED (EARLIER) 41,43:116,118,120:211,214:540");
+ content[4] = new IMAPResponse("a4 OK [MODIFIED 1,2,3] Conditional Store Failed (Success)");
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result mismatched.");
+ final List irs = storeResult.getFetchResponses();
+ Assert.assertNotNull(storeResult.getHighestModSeq(), "getHighestModSeq() should not return null.");
+ Assert.assertEquals(storeResult.getHighestModSeq(), Long.valueOf(2682), "getHighestModSeq() mismatched.");
+ Assert.assertEquals(irs.size(), 2, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should not return null");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 3, "getModifiedMsgsets() mismatched");
+ final String msgSets = MessageNumberSet.buildString(storeResult.getModifiedMsgSets());
+ Assert.assertEquals(msgSets, "1,2,3", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method with Bad response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStoreBad() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("002 BAD") }; // make it bad so it does not update mode
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, StoreResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStore method with No response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStoreNo() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("002 BAD") }; // make it bad so it does not update mode
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, StoreResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStore method with 0 response throw exception.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStoreZeroResponse() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, StoreResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStore method with invalid fetch response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseStoreFailParsingFetchResponse() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* FETCH abc (FLAG (\\Seen SEEN))");
+ content[1] = new IMAPResponse("* OK");
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, StoreResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with NO response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreNoResponseCondStore() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("B001 NO [MODIFIED 2] Some of the messages no longer exist.") };
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result mismatched.");
+ Assert.assertEquals(storeResult.getFetchResponses().size(), 0, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should not return null");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 1, "getModifiedMsgsets() mismatched.");
+ Assert.assertEquals(MessageNumberSet.buildString(storeResult.getModifiedMsgSets()), "2", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseStore method successfully with NO response and space after left bracket.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseStoreNoResponseCondStoreWithSpace() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("B001 NO [ MODIFIED 2] Some of the messages no longer exist.") };
+ final StoreResult storeResult = mapper.readValue(content, StoreResult.class);
+
+ // verify the result
+ Assert.assertNotNull(storeResult, "store result mismatched.");
+ Assert.assertEquals(storeResult.getFetchResponses().size(), 0, "getFetchResponses() mismatched.");
+ Assert.assertNotNull(storeResult.getModifiedMsgSets(), "getModifiedMsgsets() should not return null");
+ Assert.assertEquals(storeResult.getModifiedMsgSets().length, 1, "getModifiedMsgsets() mismatched.");
+ Assert.assertEquals(MessageNumberSet.buildString(storeResult.getModifiedMsgSets()), "2", "getModifiedMsgsets() mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseFetchOK() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("A042 OK Fetch success") };
+ final FetchResult fetchResult = mapper.readValue(content, FetchResult.class);
+
+ // verify the result
+ Assert.assertNotNull(fetchResult, "Fetch result mismatched.");
+ Assert.assertEquals(fetchResult.getFetchResponses().size(), 0, "getFetchResponses() mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseFetchOKCondStore() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[4];
+ content[0] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[1] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[2] = new IMAPResponse("* 3 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[3] = new IMAPResponse("a4 OK Success");
+ final FetchResult fetchResult = mapper.readValue(content, FetchResult.class);
+ final List irs = fetchResult.getFetchResponses();
+
+ // verify the result
+ Assert.assertNotNull(fetchResult, "Fetch result mismatched.");
+ Assert.assertEquals(irs.size(), 3, "getFetchResponses() mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method with not OK response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseFetchNotOK() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = { new IMAPResponse("002 BAD") }; // make it bad so it does not update mode
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, FetchResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method with 0 response.
+ *
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseFetchZeroResponse() throws ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[0];
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, FetchResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method with invalid fetch response.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseFetchFailParsingFetchResponse() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* FETCH abc (FLAG (\\Seen SEEN))");
+ content[1] = new IMAPResponse("* OK");
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, FetchResult.class);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.INVALID_INPUT, "Failure type mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseFetchOKCondStoreExpunge() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[6];
+ content[0] = new IMAPResponse("* 1 EXPUNGE");
+ content[1] = new IMAPResponse("* 2 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[2] = new IMAPResponse("* 3 EXPUNGE");
+ content[3] = new IMAPResponse("* 4 FETCH (FLAGS (\\Seen) MODSEQ (2531))");
+ content[4] = new IMAPResponse("* 5 FETCH (FLAGS (\\Seen) MODSEQ (2648))");
+ content[5] = new IMAPResponse("a4 OK Success");
+ final FetchResult fetchResult = mapper.readValue(content, FetchResult.class);
+ final List irs = fetchResult.getFetchResponses();
+
+ // verify the result
+ Assert.assertNotNull(fetchResult, "fetch result mismatched.");
+ Assert.assertEquals(irs.size(), 3, "fetch result mismatched.");
+ }
+
+ /**
+ * Tests parseFetch method successfully.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ * @throws ImapAsyncClientException will not throw
+ */
+ @Test
+ public void testParseFetchExtension() throws IOException, ProtocolException, ImapAsyncClientException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[2];
+ content[0] = new IMAPResponse("* 1 FETCH (FLAGS (\\Seen SEEN) MODSEQ (2529))");
+ content[1] = new IMAPResponse("a4 OK Success");
+ final FetchResult fetchResult = mapper.readValue(content, FetchResult.class, new FetchItem[0]);
+ final List irs = fetchResult.getFetchResponses();
+
+ // verify the result
+ Assert.assertNotNull(fetchResult, "fetch result mismatched.");
+ Assert.assertEquals(irs.size(), 1, "fetch result mismatched.");
+ }
+
+ /**
+ * Tests parse a class that mapper does not support.
+ *
+ * @throws IOException will not throw
+ * @throws ProtocolException will not throw
+ */
+ @Test
+ public void testParseClassUnknownWithFetchExtension() throws IOException, ProtocolException {
+ final ImapResponseMapper mapper = new ImapResponseMapper();
+ final IMAPResponse[] content = new IMAPResponse[1];
+ content[0] = new IMAPResponse("002 OK"); // make it bad so it does not update mode
+
+ ImapAsyncClientException cause = null;
+ try {
+ mapper.readValue(content, ID.class, new FetchItem[0]);
+ } catch (final ImapAsyncClientException e) {
+ cause = e;
+ }
+ // verify the result
+ Assert.assertNotNull(cause, "cause mismatched.");
+ Assert.assertEquals(cause.getFailureType(), FailureType.UNKNOWN_PARSE_RESULT_TYPE, "Failure type mismatched.");
+ }
+}
diff --git a/core/src/test/java/com/yahoo/imapnio/command/ArgumentTest.java b/core/src/test/java/com/yahoo/imapnio/command/ArgumentTest.java
index f9c25981..993fe34c 100644
--- a/core/src/test/java/com/yahoo/imapnio/command/ArgumentTest.java
+++ b/core/src/test/java/com/yahoo/imapnio/command/ArgumentTest.java
@@ -4,7 +4,11 @@
import javax.mail.Flags;
import javax.mail.internet.MimeUtility;
+import javax.mail.search.AndTerm;
+import javax.mail.search.BodyTerm;
import javax.mail.search.FlagTerm;
+import javax.mail.search.NotTerm;
+import javax.mail.search.OrTerm;
import javax.mail.search.SearchException;
import javax.mail.search.SubjectTerm;
@@ -12,6 +16,8 @@
import org.testng.annotations.Test;
import com.sun.mail.imap.protocol.SearchSequence;
+import com.yahoo.imapnio.async.data.ExtendedModifiedSinceTerm;
+import com.yahoo.imapnio.async.request.ExtendedSearchSequence;
/**
* Unit test for {@link Argument}.
@@ -74,4 +80,63 @@ public void testConstructorAndToStringNoneNullCharset() throws SearchException,
final String searchStr = args.toString();
Assert.assertEquals(searchStr, "SUBJECT {4+}\r\nᅫ뢔ᄅ 1:5", "result mismatched.");
}
-}
\ No newline at end of file
+
+ /**
+ * Tests constructor and toString() method with null character set and extended search sequence.
+ *
+ * @throws IOException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testConstructorModifiedSince() throws SearchException, IOException {
+
+ final ExtendedSearchSequence searchSeq = new ExtendedSearchSequence();
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(1L);
+ final Argument args = new Argument();
+ args.append(searchSeq.generateSequence(extendedModifiedSinceTerm));
+
+ final String searchStr = args.toString();
+ Assert.assertEquals(searchStr, "MODSEQ 1", "generateSequence() mismatched.");
+ }
+
+ /**
+ * Tests constructor and toString() method with null character set and extended search sequence with Or And search terms.
+ *
+ * @throws IOException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testConstructorOrAnd() throws SearchException, IOException {
+
+ final ExtendedSearchSequence searchSeq = new ExtendedSearchSequence();
+ final BodyTerm bodyTerm = new BodyTerm("test");
+ final AndTerm andTerm = new AndTerm(bodyTerm, bodyTerm);
+ final OrTerm orTerm = new OrTerm(andTerm, andTerm);
+ final Argument args = new Argument();
+ args.append(searchSeq.generateSequence(orTerm, null));
+
+ final String searchStr = args.toString();
+ Assert.assertEquals(searchStr, "OR (BODY test BODY test) (BODY test BODY test)", "generateSequence() mismatched.");
+ }
+
+ /**
+ * Tests constructor and toString() method with null character set and extended search sequence with Not And Modified Since terms.
+ *
+ * @throws IOException will not throw
+ * @throws SearchException will not throw
+ */
+ @Test
+ public void testConstructorNotAndModifiedSince() throws SearchException, IOException {
+
+ final ExtendedSearchSequence searchSeq = new ExtendedSearchSequence();
+ final ExtendedModifiedSinceTerm extendedModifiedSinceTerm = new ExtendedModifiedSinceTerm(1L);
+ final BodyTerm bodyTerm = new BodyTerm("test");
+ final AndTerm andTerm = new AndTerm(bodyTerm, extendedModifiedSinceTerm);
+ final NotTerm notTerm = new NotTerm(andTerm);
+ final Argument args = new Argument();
+ args.append(searchSeq.generateSequence(notTerm));
+
+ final String searchStr = args.toString();
+ Assert.assertEquals(searchStr, "NOT (BODY test MODSEQ 1)", "generateSequence() mismatched.");
+ }
+}
diff --git a/pom.xml b/pom.xml
index abf0eaa3..27ab3239 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,604 +1,598 @@
-
-
- 4.0.0
- com.yahoo.imapnio
- imapnio
- pom
- 4.3.7
- ${project.artifactId}
- https://github.com/yahoo/imapnio
- Parent POM file for imapnio project
-
- 3.0
-
-
-
- core
-
-
-
-
- lafa
- Lafa
- luis.alves@lafaspot.com
-
-
- kraman
- Kumar Raman
- kumar.raman@yahoo.com
-
-
-
-
- scm:git:https://github.com/yahoo/imapnio.git
- scm:git:https://github.com/yahoo/imapnio.git
- https://github.com/yahoo/imapnio.git
- imapnio-1.0
-
-
-
- GitHub
- https://github.com/yahoo/imapnio/issues
-
-
-
-
- Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
-
-
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
- imapnio
- UTF-8
- ${project.build.sourceEncoding}
-
-
- 1.8
- 3.0.5
- 2.17
- 3.4
- 2.13
- 2.18
- 2.5.3
- 0.8.5
- 0
- ${project.basedir}
-
-
-
-
-
-
-
-
-
-
- org.testng
- testng
- 6.8.8
- test
-
-
- junit
- junit
-
-
-
-
-
-
- com.google.code.findbugs
- jsr305
- 1.3.9
-
-
- io.netty
- netty-handler
- 4.1.48.Final
-
-
- com.sun.mail
- javax.mail
- 1.5.5
-
-
- org.slf4j
- slf4j-api
- 1.7.12
-
-
- ch.qos.logback
- logback-core
- 1.2.0
-
-
- ch.qos.logback
- logback-classic
- 1.2.0
-
-
- commons-codec
- commons-codec
- 1.10
-
-
- org.mockito
- mockito-all
- 1.10.19
- test
-
-
-
-
-
-
- ${project.artifactId}
-
-
- org.apache.maven.wagon
- wagon-ssh-external
- 1.0-beta-6
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
- ${jacoco-maven-plugin.version}
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.3
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 1.9.1
-
-
- org.apache.maven.plugins
- maven-release-plugin
- 2.5.2
-
-
- maven-assembly-plugin
- 2.5.5
-
-
- maven-clean-plugin
- 2.6.1
-
-
- maven-enforcer-plugin
- 1.4
-
-
- maven-pmd-plugin
- ${maven-pmd-plugin.version}
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- ${maven-checkstyle-plugin.version}
-
-
- maven-project-info-reports-plugin
- 2.8
-
-
- maven-site-plugin
- 3.4
-
-
- maven-source-plugin
- 3.2.0
-
-
- org.codehaus.mojo
- exec-maven-plugin
- 1.2.1
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
- ${maven-findbugs-plugin.version}
-
-
- org.apache.maven.plugins
- maven-scm-plugin
- 1.9.4
- false
-
-
- org.apache.maven.plugins
- maven-surefire-report-plugin
- ${maven-surefire-plugin.version}
-
-
-
- org.eclipse.m2e
- lifecycle-mapping
- 1.0.0
-
-
-
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
- [3.0.1,)
-
- check
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- javacc-maven-plugin
- [2.6,)
-
- javacc
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- [2.9,)
-
- copy-dependencies
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
- [1.3.1,)
-
- enforce
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
-
-
- maven-checkstyle-plugin
-
-
- [2.13,)
-
-
- check
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- true
- 128m
- 512m
- ${jdk.version}
- ${jdk.version}
- true
- true
- true
- UTF-8
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
-
-
- enforce
-
- enforce
-
-
-
-
- ${jdk.version}
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-scm-plugin
- false
-
- connection
- *.state,*.yidf
- ${main.basedir}/ci/buildtime
- Auto-update-build
-
-
-
- pre-site
-
- checkin
-
-
-
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
-
-
- true
- 2048
-
- true
-
- low
-
- true
- ${main.basedir}/findbugs-include.xml
- ${main.basedir}/findbugs-exclude.xml
- medium
-
- false
- false
-
-
-
-
- check-compile
- verify
-
- check
-
-
-
-
-
- org.apache.maven.plugins
- maven-pmd-plugin
-
- false
- true
- 1
-
- ${main.basedir}/pmd-ruleset.xml
-
- false
- true
- true
-
-
-
- pmd
-
- check
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven-surefire-plugin.version}
-
- once
-
- -Dfile.encoding=ANSI_X3.4-1968
- -Djava.library.path=
- -javaagent:"${settings.localRepository}"/org/jacoco/org.jacoco.agent/${jacoco-maven-plugin.version}/org.jacoco.agent-${jacoco-maven-plugin.version}-runtime.jar=destfile=${basedir}/target/jacoco.exec
- notIsolate,EventListenersRegression
-
- true
- false
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
-
-
- checkstyle
- validate
-
- UTF-8
- true
- false
- checkstyle.xml
- true
- true
- true
- config_loc=${main.basedir}
- checkstyle_suppressions.xml
- checkstyle.suppressions.file
-
-
- check
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
- jacoco-initialize
- initialize
-
- prepare-agent
-
-
- ${basedir}/target/jacoco.exec
-
-
-
- jacoco-site
- package
-
- report
-
-
- ${basedir}/target/jacoco.exec
- ${basedir}/target/jacoco
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.2.0
-
- 8
-
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
-
-
-
-
- commit
-
- test
-
-
-
- commit
-
-
-
-
-
-
-
-
- ossrh
-
- gpg
- ${env.GPG_KEYNAME}
- ${env.GPG_PASSPHRASE}
- false
- ${main.basedir}/ci/deploy
- pubring.gpg
- secring.gpg
-
-
-
- performRelease
- true
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
- --pinentry-mode
- loopback
-
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.7
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-
-
-
-
-
-
-
+
+
+ 4.0.0
+ com.yahoo.imapnio
+ imapnio
+ pom
+ 4.3.8
+ ${project.artifactId}
+ https://github.com/yahoo/imapnio
+ Parent POM file for imapnio project
+
+ 3.0
+
+
+
+ core
+
+
+
+
+ Yahoo Inc.
+ https://github.com/yahoo
+
+
+
+
+ scm:git:https://github.com/yahoo/imapnio.git
+ scm:git:https://github.com/yahoo/imapnio.git
+ https://github.com/yahoo/imapnio.git
+ imapnio-1.0
+
+
+
+ GitHub
+ https://github.com/yahoo/imapnio/issues
+
+
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+ imapnio
+ UTF-8
+ ${project.build.sourceEncoding}
+
+
+ 1.8
+ 3.0.5
+ 2.17
+ 3.4
+ 2.13
+ 2.18
+ 2.5.3
+ 0.8.5
+ 0
+ ${project.basedir}
+
+
+
+
+
+
+
+
+
+
+ org.testng
+ testng
+ 6.8.8
+ test
+
+
+ junit
+ junit
+
+
+
+
+
+
+ com.google.code.findbugs
+ jsr305
+ 1.3.9
+
+
+ io.netty
+ netty-handler
+ 4.1.48.Final
+
+
+ com.sun.mail
+ javax.mail
+ 1.5.5
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.12
+
+
+ ch.qos.logback
+ logback-core
+ 1.2.0
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.0
+
+
+ commons-codec
+ commons-codec
+ 1.10
+
+
+ org.mockito
+ mockito-all
+ 1.10.19
+ test
+
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.apache.maven.wagon
+ wagon-ssh-external
+ 1.0-beta-6
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco-maven-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.9.1
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.2
+
+
+ maven-assembly-plugin
+ 2.5.5
+
+
+ maven-clean-plugin
+ 2.6.1
+
+
+ maven-enforcer-plugin
+ 1.4
+
+
+ maven-pmd-plugin
+ ${maven-pmd-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${maven-checkstyle-plugin.version}
+
+
+ maven-project-info-reports-plugin
+ 2.8
+
+
+ maven-site-plugin
+ 3.4
+
+
+ maven-source-plugin
+ 3.2.0
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.2.1
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ ${maven-findbugs-plugin.version}
+
+
+ org.apache.maven.plugins
+ maven-scm-plugin
+ 1.9.4
+ false
+
+
+ org.apache.maven.plugins
+ maven-surefire-report-plugin
+ ${maven-surefire-plugin.version}
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ [3.0.1,)
+
+ check
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ javacc-maven-plugin
+ [2.6,)
+
+ javacc
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ [2.9,)
+
+ copy-dependencies
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ [1.3.1,)
+
+ enforce
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+
+
+ maven-checkstyle-plugin
+
+
+ [2.13,)
+
+
+ check
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ true
+ 128m
+ 512m
+ ${jdk.version}
+ ${jdk.version}
+ true
+ true
+ true
+ UTF-8
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+ enforce
+
+ enforce
+
+
+
+
+ ${jdk.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-scm-plugin
+ false
+
+ connection
+ *.state,*.yidf
+ ${main.basedir}/ci/buildtime
+ Auto-update-build
+
+
+
+ pre-site
+
+ checkin
+
+
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+
+
+ true
+ 2048
+
+ true
+
+ low
+
+ true
+ ${main.basedir}/findbugs-include.xml
+ ${main.basedir}/findbugs-exclude.xml
+ medium
+
+ false
+ false
+
+
+
+
+ check-compile
+ verify
+
+ check
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+
+ false
+ true
+ 1
+
+ ${main.basedir}/pmd-ruleset.xml
+
+ false
+ true
+ true
+
+
+
+ pmd
+
+ check
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ once
+
+ -Dfile.encoding=ANSI_X3.4-1968
+ -Djava.library.path=
+ -javaagent:"${settings.localRepository}"/org/jacoco/org.jacoco.agent/${jacoco-maven-plugin.version}/org.jacoco.agent-${jacoco-maven-plugin.version}-runtime.jar=destfile=${basedir}/target/jacoco.exec
+ notIsolate,EventListenersRegression
+
+ true
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ checkstyle
+ validate
+
+ UTF-8
+ true
+ false
+ checkstyle.xml
+ true
+ true
+ true
+ config_loc=${main.basedir}
+ checkstyle_suppressions.xml
+ checkstyle.suppressions.file
+
+
+ check
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ jacoco-initialize
+ initialize
+
+ prepare-agent
+
+
+ ${basedir}/target/jacoco.exec
+
+
+
+ jacoco-site
+ package
+
+ report
+
+
+ ${basedir}/target/jacoco.exec
+ ${basedir}/target/jacoco
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ 8
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
+ commit
+
+ test
+
+
+
+ commit
+
+
+
+
+
+
+
+
+ ossrh
+
+ gpg
+ ${env.GPG_KEYNAME}
+ ${env.GPG_PASSPHRASE}
+ false
+ ${main.basedir}/ci/deploy
+ pubring.gpg
+ secring.gpg
+
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.7
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+
+
+
+
+