1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-27 11:42:16 -05:00

Refactor IMAP code + tests

This commit is contained in:
Jan Berkel 2014-12-18 11:24:43 +01:00
parent 6a1fee90ee
commit 0a6920c63e
10 changed files with 341 additions and 254 deletions

View File

@ -33,9 +33,7 @@ import java.net.SocketException;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@ -84,41 +82,6 @@ class ImapConnection {
return "conn" + hashCode();
}
private List<ImapResponse> receiveCapabilities(List<ImapResponse> responses) {
for (ImapResponse response : responses) {
ImapList capabilityList = null;
if (!response.isEmpty() && ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
for (Object thisPart : response) {
if (thisPart instanceof ImapList) {
ImapList thisList = (ImapList)thisPart;
if (ImapResponseParser.equalsIgnoreCase(thisList.get(0), ImapCommands.CAPABILITY_CAPABILITY)) {
capabilityList = thisList;
break;
}
}
}
} else if (response.mTag == null) {
capabilityList = response;
}
if (capabilityList != null && !capabilityList.isEmpty() &&
ImapResponseParser.equalsIgnoreCase(capabilityList.get(0), ImapCommands.CAPABILITY_CAPABILITY)) {
if (K9MailLib.isDebug()) {
Log.d(LOG_TAG, "Saving " + capabilityList.size() + " capabilities for " + getLogId());
}
for (Object capability : capabilityList) {
if (capability instanceof String) {
// if (K9MailLib.isDebug())
// {
// Log.v(LOG_TAG, "Saving capability '" + capability + "' for " + getLogId());
// }
capabilities.add(((String)capability).toUpperCase(Locale.US));
}
}
}
}
return responses;
}
public void open() throws IOException, MessagingException {
if (isOpen()) {
@ -395,6 +358,14 @@ class ImapConnection {
}
}
private List<ImapResponse> receiveCapabilities(List<ImapResponse> responses) {
capabilities = ImapResponseParser.parseCapabilities(responses);
if (K9MailLib.isDebug()) {
Log.d(LOG_TAG, "Saving " + capabilities + " capabilities for " + getLogId());
}
return responses;
}
protected void login() throws IOException, MessagingException {
/*
* Use quoted strings which permit spaces and quotes. (Using IMAP
@ -412,7 +383,7 @@ class ImapConnection {
try {
receiveCapabilities(executeSimpleCommand(
String.format("LOGIN \"%s\" \"%s\"", username, password), true));
} catch (ImapStore.ImapException e) {
} catch (ImapException e) {
throw new AuthenticationFailedException(e.getMessage());
}
}
@ -433,7 +404,7 @@ class ImapConnection {
mOut.write('\n');
mOut.flush();
try {
receiveCapabilities(readStatusResponse(tag, command, null));
receiveCapabilities(mParser.readStatusResponse(tag, command, getLogId(), null));
} catch (MessagingException e) {
throw new AuthenticationFailedException(e.getMessage());
}
@ -449,7 +420,7 @@ class ImapConnection {
mOut.write('\n');
mOut.flush();
try {
receiveCapabilities(readStatusResponse(tag, command, null));
receiveCapabilities(mParser.readStatusResponse(tag, command, getLogId(), null));
} catch (MessagingException e) {
throw new AuthenticationFailedException(e.getMessage());
}
@ -460,7 +431,7 @@ class ImapConnection {
receiveCapabilities(executeSimpleCommand(
String.format("AUTHENTICATE EXTERNAL %s",
Base64.encode(mSettings.getUsername())), false));
} catch (ImapStore.ImapException e) {
} catch (ImapException e) {
/*
* Provide notification to the user of a problem authenticating
* using client certificates. We don't use an
@ -477,8 +448,8 @@ class ImapConnection {
ImapResponse response;
do {
response = readResponse();
if (response.mTag != null) {
if (response.mTag.equalsIgnoreCase(tag)) {
if (response.getTag() != null) {
if (response.getTag().equalsIgnoreCase(tag)) {
throw new MessagingException(
"Command continuation aborted: " + response);
} else {
@ -487,43 +458,10 @@ class ImapConnection {
+ response + " for " + getLogId());
}
}
} while (!response.mCommandContinuationRequested);
} while (!response.isContinuationRequested());
return response;
}
protected List<ImapResponse> readStatusResponse(String tag,
String commandToLog, ImapStore.UntaggedHandler untaggedHandler)
throws IOException, MessagingException {
List<ImapResponse> responses = new ArrayList<ImapResponse>();
ImapResponse response;
do {
response = mParser.readResponse();
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
Log.v(LOG_TAG, getLogId() + "<<<" + response);
if (response.mTag != null && !response.mTag.equalsIgnoreCase(tag)) {
Log.w(LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + getLogId());
Iterator<ImapResponse> iter = responses.iterator();
while (iter.hasNext()) {
ImapResponse delResponse = iter.next();
if (delResponse.mTag != null || delResponse.size() < 2
|| (!ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXISTS") && !ImapResponseParser.equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
iter.remove();
}
}
response.mTag = null;
continue;
}
if (untaggedHandler != null) {
untaggedHandler.handleAsyncUntaggedResponse(response);
}
responses.add(response);
} while (response.mTag == null);
if (response.size() < 1 || !ImapResponseParser.equalsIgnoreCase(response.get(0), "OK")) {
throw new ImapStore.ImapException("Command: " + commandToLog + "; response: " + response.toString(), response.getAlertText());
}
return responses;
}
protected void setReadTimeout(int millis) throws SocketException {
Socket sock = mSocket;
@ -567,7 +505,7 @@ class ImapConnection {
return readResponse(null);
}
public ImapResponse readResponse(ImapResponseParser.IImapResponseCallback callback) throws IOException {
public ImapResponse readResponse(ImapResponseCallback callback) throws IOException {
try {
ImapResponse response = mParser.readResponse(callback);
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP)
@ -591,8 +529,7 @@ class ImapConnection {
}
public String sendCommand(String command, boolean sensitive)
throws MessagingException, IOException {
public String sendCommand(String command, boolean sensitive) throws MessagingException, IOException {
try {
open();
String tag = Integer.toString(mNextCommandTag++);
@ -613,7 +550,7 @@ class ImapConnection {
} catch (IOException ioe) {
close();
throw ioe;
} catch (ImapStore.ImapException ie) {
} catch (ImapException ie) {
close();
throw ie;
} catch (MessagingException me) {
@ -632,21 +569,18 @@ class ImapConnection {
return executeSimpleCommand(command, sensitive, null);
}
public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive, ImapStore.UntaggedHandler untaggedHandler)
public List<ImapResponse> executeSimpleCommand(String command, boolean sensitive, UntaggedHandler untaggedHandler)
throws IOException, MessagingException {
String commandToLog = command;
if (sensitive && !K9MailLib.isDebugSensitive()) {
commandToLog = "*sensitive*";
}
//if (K9MailLib.isDebug())
// Log.v(LOG_TAG, "Sending IMAP command " + commandToLog + " on connection " + getLogId());
String tag = sendCommand(command, sensitive);
//if (K9MailLib.isDebug())
// Log.v(LOG_TAG, "Sent IMAP command " + commandToLog + " with tag " + tag + " for " + getLogId());
return readStatusResponse(tag, commandToLog, untaggedHandler);
return mParser.readStatusResponse(tag, commandToLog, getLogId(), untaggedHandler);
}
}

View File

@ -0,0 +1,13 @@
package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.MessagingException;
class ImapException extends MessagingException {
private static final long serialVersionUID = 3725007182205882394L;
private final String mAlertText;
public ImapException(String message, String alertText) {
super(message, true);
this.mAlertText = alertText;
}
}

View File

@ -11,20 +11,32 @@ package com.fsck.k9.mail.store.imap;
public class ImapResponse extends ImapList {
private static final long serialVersionUID = 6886458551615975669L;
private ImapResponseParser.IImapResponseCallback mCallback;
private ImapResponseCallback mCallback;
boolean mCommandContinuationRequested;
String mTag;
private final boolean mCommandContinuationRequested;
private final String mTag;
public ImapResponse(ImapResponseParser.IImapResponseCallback mCallback) {
this.mCallback = mCallback;
public ImapResponse(ImapResponseCallback callback,
boolean mCommandContinuationRequested, String mTag) {
this.mCallback = callback;
this.mCommandContinuationRequested = mCommandContinuationRequested;
this.mTag = mTag;
}
public ImapResponseParser.IImapResponseCallback getCallback() {
public boolean isContinuationRequested() {
return mCommandContinuationRequested;
}
public String getTag() {
return mTag;
}
public ImapResponseCallback getCallback() {
return mCallback;
}
public void setCallback(ImapResponseParser.IImapResponseCallback mCallback) {
public void setCallback(ImapResponseCallback mCallback) {
this.mCallback = mCallback;
}

View File

@ -0,0 +1,24 @@
package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.filter.FixedLengthInputStream;
public interface ImapResponseCallback {
/**
* Callback method that is called by the parser when a literal string
* is found in an IMAP response.
*
* @param response ImapResponse object with the fields that have been
* parsed up until now (excluding the literal string).
* @param literal FixedLengthInputStream that can be used to access
* the literal string.
*
* @return an Object that will be put in the ImapResponse object at the
* place of the literal string.
*
* @throws java.io.IOException passed-through if thrown by FixedLengthInputStream
* @throws Exception if something goes wrong. Parsing will be resumed
* and the exception will be thrown after the
* complete IMAP response has been parsed.
*/
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws Exception;
}

View File

@ -1,13 +1,25 @@
package com.fsck.k9.mail.store.imap;
import android.text.TextUtils;
import android.util.Log;
import com.fsck.k9.mail.K9MailLib;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.filter.FixedLengthInputStream;
import com.fsck.k9.mail.filter.PeekableInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import static com.fsck.k9.mail.K9MailLib.DEBUG_PROTOCOL_IMAP;
import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
import static com.fsck.k9.mail.store.imap.ImapCommands.CAPABILITY_CAPABILITY;
class ImapResponseParser {
private PeekableInputStream mIn;
private ImapResponse mResponse;
private Exception mException;
@ -24,20 +36,18 @@ class ImapResponseParser {
* Reads the next response available on the stream and returns an
* ImapResponse object that represents it.
*/
public ImapResponse readResponse(IImapResponseCallback callback) throws IOException {
public ImapResponse readResponse(ImapResponseCallback callback) throws IOException {
try {
mResponse = new ImapResponse(callback);
mResponse.setCallback(callback);
int ch = mIn.peek();
if (ch == '*') {
parseUntaggedResponse();
mResponse = new ImapResponse(callback, false, null);
readTokens(mResponse);
} else if (ch == '+') {
mResponse.mCommandContinuationRequested = parseCommandContinuationRequest();
mResponse = new ImapResponse(callback, parseCommandContinuationRequest(), null);
parseResponseText(mResponse);
} else {
mResponse.mTag = parseTaggedResponse();
mResponse = new ImapResponse(callback, false, parseTaggedResponse());
readTokens(mResponse);
}
@ -52,6 +62,80 @@ class ImapResponseParser {
}
}
protected List<ImapResponse> readStatusResponse(String tag,
String commandToLog,
String logId,
UntaggedHandler untaggedHandler)
throws IOException, MessagingException {
List<ImapResponse> responses = new ArrayList<ImapResponse>();
ImapResponse response;
do {
response = readResponse();
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) {
Log.v(LOG_TAG, logId + "<<<" + response);
}
if (response.getTag() != null && !response.getTag().equalsIgnoreCase(tag)) {
Log.w(LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response + " for " + logId);
Iterator<ImapResponse> iter = responses.iterator();
while (iter.hasNext()) {
ImapResponse delResponse = iter.next();
if (delResponse.getTag() != null
|| delResponse.size() < 2
|| (!equalsIgnoreCase(delResponse.get(1), "EXISTS") &&
!equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
iter.remove();
}
}
response = null;
continue;
}
if (untaggedHandler != null) {
untaggedHandler.handleAsyncUntaggedResponse(response);
}
responses.add(response);
} while (response == null || response.getTag() == null);
if (response.size() < 1 || !equalsIgnoreCase(response.get(0), "OK")) {
throw new ImapException("Command: " + commandToLog + "; response: " + response.toString(), response.getAlertText());
}
return responses;
}
protected static Set<String> parseCapabilities(List<ImapResponse> responses) {
HashSet<String> capabilities = new HashSet<String>();
for (ImapResponse response : responses) {
ImapList list = null;
if (!response.isEmpty() && equalsIgnoreCase(response.get(0), "OK")) {
for (Object thisPart : response) {
if (thisPart instanceof ImapList) {
ImapList thisList = (ImapList)thisPart;
if (equalsIgnoreCase(thisList.get(0), CAPABILITY_CAPABILITY)) {
list = thisList;
break;
}
}
}
} else if (response.getTag() == null) {
list = response;
}
if (list != null && list.size() > 1 &&
equalsIgnoreCase(list.get(0), CAPABILITY_CAPABILITY)) {
for (Object capability : list.subList(1, list.size())) {
if (capability instanceof String) {
capabilities.add(((String)capability).toUpperCase(Locale.US));
}
}
}
}
return capabilities;
}
private void readTokens(ImapResponse response) throws IOException {
response.clear();
@ -184,8 +268,7 @@ class ImapResponseParser {
// 3 OK [READ-WRITE] Select completed.
private String parseTaggedResponse() throws IOException {
String tag = readStringUntil(' ');
return tag;
return readStringUntil(' ');
}
private ImapList parseList(ImapList parent) throws IOException {
@ -377,24 +460,4 @@ class ImapResponseParser {
}
}
public interface IImapResponseCallback {
/**
* Callback method that is called by the parser when a literal string
* is found in an IMAP response.
*
* @param response ImapResponse object with the fields that have been
* parsed up until now (excluding the literal string).
* @param literal FixedLengthInputStream that can be used to access
* the literal string.
*
* @return an Object that will be put in the ImapResponse object at the
* place of the literal string.
*
* @throws IOException passed-through if thrown by FixedLengthInputStream
* @throws Exception if something goes wrong. Parsing will be resumed
* and the exception will be thrown after the
* complete IMAP response has been parsed.
*/
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws Exception;
}
}

View File

@ -789,7 +789,7 @@ public class ImapStore extends RemoteStore {
Object keyObj = bracketed.get(0);
if (keyObj instanceof String) {
String key = (String) keyObj;
if (response.mTag != null) {
if (response.getTag() != null) {
if ("READ-ONLY".equalsIgnoreCase(key)) {
mMode = OPEN_MODE_RO;
@ -1253,7 +1253,7 @@ public class ImapStore extends RemoteStore {
List<Long> uids = new ArrayList<Long>();
List<ImapResponse> responses = searcher.search(); //
for (ImapResponse response : responses) {
if (response.mTag == null) {
if (response.getTag() == null) {
if (ImapResponseParser.equalsIgnoreCase(response.get(0), "SEARCH")) {
for (int i = 1, count = response.size(); i < count; i++) {
uids.add(response.getLong(i));
@ -1385,7 +1385,7 @@ public class ImapStore extends RemoteStore {
ImapResponse response;
int messageNumber = 0;
ImapResponseParser.IImapResponseCallback callback = null;
ImapResponseCallback callback = null;
if (fp.contains(FetchProfile.Item.BODY) || fp.contains(FetchProfile.Item.BODY_SANE)) {
callback = new FetchBodyCallback(messageMap);
}
@ -1393,7 +1393,7 @@ public class ImapStore extends RemoteStore {
do {
response = mConnection.readResponse(callback);
if (response.mTag == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
if (response.getTag() == null && ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
String uid = fetchList.getKeyedString("UID");
long msgSeq = response.getLong(0);
@ -1444,7 +1444,7 @@ public class ImapStore extends RemoteStore {
handleUntaggedResponse(response);
}
} while (response.mTag == null);
} while (response.getTag() == null);
} catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
@ -1479,12 +1479,12 @@ public class ImapStore extends RemoteStore {
ImapResponse response;
int messageNumber = 0;
ImapResponseParser.IImapResponseCallback callback = new FetchPartCallback(part);
ImapResponseCallback callback = new FetchPartCallback(part);
do {
response = mConnection.readResponse(callback);
if ((response.mTag == null) &&
if ((response.getTag() == null) &&
(ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH"))) {
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
String uid = fetchList.getKeyedString("UID");
@ -1531,7 +1531,7 @@ public class ImapStore extends RemoteStore {
handleUntaggedResponse(response);
}
} while (response.mTag == null);
} while (response.getTag() == null);
} catch (IOException ioe) {
throw ioExceptionHandler(mConnection, ioe);
}
@ -1643,7 +1643,7 @@ public class ImapStore extends RemoteStore {
* @param response
*/
protected void handleUntaggedResponse(ImapResponse response) {
if (response.mTag == null && response.size() > 1) {
if (response.getTag() == null && response.size() > 1) {
if (ImapResponseParser.equalsIgnoreCase(response.get(1), "EXISTS")) {
mMessageCount = response.getNumber(0);
if (K9MailLib.isDebug())
@ -1873,14 +1873,14 @@ public class ImapStore extends RemoteStore {
do {
response = mConnection.readResponse();
handleUntaggedResponse(response);
if (response.mCommandContinuationRequested) {
if (response.isContinuationRequested()) {
EOLConvertingOutputStream eolOut = new EOLConvertingOutputStream(mConnection.getOutputStream());
message.writeTo(eolOut);
eolOut.write('\r');
eolOut.write('\n');
eolOut.flush();
}
} while (response.mTag == null);
} while (response.getTag() == null);
if (response.size() > 1) {
/*
@ -1957,7 +1957,7 @@ public class ImapStore extends RemoteStore {
executeSimpleCommand(
String.format("UID SEARCH HEADER MESSAGE-ID %s", encodeString(messageId)));
for (ImapResponse response1 : responses) {
if (response1.mTag == null && ImapResponseParser.equalsIgnoreCase(response1.get(0), "SEARCH")
if (response1.getTag() == null && ImapResponseParser.equalsIgnoreCase(response1.get(0), "SEARCH")
&& response1.size() > 1) {
return response1.getString(1);
}
@ -2232,16 +2232,6 @@ public class ImapStore extends RemoteStore {
}
}
static class ImapException extends MessagingException {
private static final long serialVersionUID = 3725007182205882394L;
private final String mAlertText;
public ImapException(String message, String alertText) {
super(message, true);
this.mAlertText = alertText;
}
}
public class ImapFolderPusher extends ImapFolder implements UntaggedHandler {
private final PushReceiver receiver;
private Thread listeningThread = null;
@ -2459,7 +2449,7 @@ public class ImapStore extends RemoteStore {
@Override
protected void handleUntaggedResponse(ImapResponse response) {
if (response.mTag == null && response.size() > 1) {
if (response.getTag() == null && response.size() > 1) {
Object responseType = response.get(1);
if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")
|| ImapResponseParser.equalsIgnoreCase(responseType, "EXPUNGE")
@ -2588,7 +2578,7 @@ public class ImapStore extends RemoteStore {
protected int processUntaggedResponse(long oldMessageCount, ImapResponse response, List<Long> flagSyncMsgSeqs, List<String> removeMsgUids) {
super.handleUntaggedResponse(response);
int messageCountDelta = 0;
if (response.mTag == null && response.size() > 1) {
if (response.getTag() == null && response.size() > 1) {
try {
Object responseType = response.get(1);
if (ImapResponseParser.equalsIgnoreCase(responseType, "FETCH")) {
@ -2703,7 +2693,7 @@ public class ImapStore extends RemoteStore {
Log.e(LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
}
} else {
if (response.mTag == null) {
if (response.getTag() == null) {
if (response.size() > 1) {
boolean started = false;
Object responseType = response.get(1);
@ -2723,7 +2713,7 @@ public class ImapStore extends RemoteStore {
Log.e(LOG_TAG, "Exception while sending DONE for " + getLogId(), e);
}
}
} else if (response.mCommandContinuationRequested) {
} else if (response.isContinuationRequested()) {
if (K9MailLib.isDebug())
Log.d(LOG_TAG, "Idling " + getLogId());
@ -2812,10 +2802,6 @@ public class ImapStore extends RemoteStore {
public void setLastRefresh(long lastRefresh) {
this.lastRefresh = lastRefresh;
}
}
public static interface UntaggedHandler {
void handleAsyncUntaggedResponse(ImapResponse respose);
}
protected static class ImapPushState {
@ -2856,7 +2842,7 @@ public class ImapStore extends RemoteStore {
List<ImapResponse> search() throws IOException, MessagingException;
}
private static class FetchBodyCallback implements ImapResponseParser.IImapResponseCallback {
private static class FetchBodyCallback implements ImapResponseCallback {
private Map<String, Message> mMessageMap;
FetchBodyCallback(Map<String, Message> messageMap) {
@ -2865,8 +2851,8 @@ public class ImapStore extends RemoteStore {
@Override
public Object foundLiteral(ImapResponse response,
FixedLengthInputStream literal) throws IOException, Exception {
if (response.mTag == null &&
FixedLengthInputStream literal) throws MessagingException, IOException {
if (response.getTag() == null &&
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
ImapList fetchList = (ImapList)response.getKeyedValue("FETCH");
String uid = fetchList.getKeyedString("UID");
@ -2875,13 +2861,13 @@ public class ImapStore extends RemoteStore {
message.parse(literal);
// Return placeholder object
return Integer.valueOf(1);
return 1;
}
return null;
}
}
private static class FetchPartCallback implements ImapResponseParser.IImapResponseCallback {
private static class FetchPartCallback implements ImapResponseCallback {
private Part mPart;
FetchPartCallback(Part part) {
@ -2890,8 +2876,8 @@ public class ImapStore extends RemoteStore {
@Override
public Object foundLiteral(ImapResponse response,
FixedLengthInputStream literal) throws IOException, Exception {
if (response.mTag == null &&
FixedLengthInputStream literal) throws MessagingException, IOException {
if (response.getTag() == null &&
ImapResponseParser.equalsIgnoreCase(response.get(1), "FETCH")) {
//TODO: check for correct UID

View File

@ -0,0 +1,5 @@
package com.fsck.k9.mail.store.imap;
interface UntaggedHandler {
void handleAsyncUntaggedResponse(ImapResponse response);
}

View File

@ -0,0 +1,37 @@
package com.fsck.k9.mail.store.imap;
import junit.framework.TestCase;
import java.io.IOException;
public class ImapListTest extends TestCase {
public void testImapListMethods() throws IOException {
ImapList list = new ImapList();
list.add("ONE");
list.add("TWO");
list.add("THREE");
assertTrue(list.containsKey("ONE"));
assertTrue(list.containsKey("TWO"));
assertFalse(list.containsKey("THREE"));
assertFalse(list.containsKey("nonexistent"));
assertEquals("TWO", list.getKeyedValue("ONE"));
assertEquals("THREE", list.getKeyedValue("TWO"));
assertNull(list.getKeyedValue("THREE"));
assertNull(list.getKeyedValue("nonexistent"));
assertEquals(0, list.getKeyIndex("ONE"));
assertEquals(1, list.getKeyIndex("TWO"));
try {
list.getKeyIndex("THREE");
fail("IllegalArgumentException should have been thrown");
} catch (IllegalArgumentException e) { /* do nothing */ }
try {
list.getKeyIndex("nonexistent");
fail("IllegalArgumentException should have been thrown");
} catch (IllegalArgumentException e) { /* do nothing */ }
}
}

View File

@ -0,0 +1,108 @@
package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.filter.PeekableInputStream;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static com.fsck.k9.mail.store.imap.ImapResponseParser.parseCapabilities;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class ImapResponseParserTest {
@Test public void testSimpleOkResponse() throws IOException {
ImapResponseParser parser = createParser("* OK\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(1, response.size());
assertEquals("OK", response.get(0));
}
@Test public void testOkResponseWithText() throws IOException {
ImapResponseParser parser = createParser("* OK Some text here\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(2, response.size());
assertEquals("OK", response.get(0));
assertEquals("Some text here", response.get(1));
}
@Test public void testOkResponseWithRespTextCode() throws IOException {
ImapResponseParser parser = createParser("* OK [UIDVALIDITY 3857529045]\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(2, response.size());
assertEquals("OK", response.get(0));
assertTrue(response.get(1) instanceof ImapList);
ImapList respTextCode = (ImapList) response.get(1);
assertEquals(2, respTextCode.size());
assertEquals("UIDVALIDITY", respTextCode.get(0));
assertEquals("3857529045", respTextCode.get(1));
}
@Test public void testOkResponseWithRespTextCodeAndText() throws IOException {
ImapResponseParser parser = createParser("* OK [token1 token2] {x} test [...]\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(3, response.size());
assertEquals("OK", response.get(0));
assertTrue(response.get(1) instanceof ImapList);
assertEquals("{x} test [...]", response.get(2));
ImapList respTextCode = (ImapList) response.get(1);
assertEquals(2, respTextCode.size());
assertEquals("token1", respTextCode.get(0));
assertEquals("token2", respTextCode.get(1));
}
@Test public void testReadStatusResponseWithOKResponse() throws Exception {
ImapResponseParser parser = createParser("* COMMAND BAR BAZ\r\nTAG OK COMMAND completed\r\n");
List<ImapResponse> responses = parser.readStatusResponse("TAG", null, null, null);
assertEquals(2, responses.size());
assertEquals(asList("COMMAND", "BAR", "BAZ"), responses.get(0));
assertEquals(asList("OK", "COMMAND completed"), responses.get(1));
}
@Test(expected = ImapException.class)
public void testReadStatusResponseWithErrorResponse() throws Exception {
ImapResponseParser parser = createParser("* COMMAND BAR BAZ\r\nTAG ERROR COMMAND errored\r\n");
parser.readStatusResponse("TAG", null, null, null);
}
@Test public void testParseCapabilities() throws Exception {
ImapResponse capabilityResponse = new ImapResponse(null, false, null);
capabilityResponse.addAll(Arrays.asList("CAPABILITY", "FOO", "BAR"));
Set<String> capabilities = parseCapabilities(Arrays.asList(capabilityResponse));
assertEquals(2, capabilities.size());
assertTrue(capabilities.contains("FOO"));
assertTrue(capabilities.contains("BAR"));
}
@Test public void testParseCapabilitiesWithInvalidResponse() throws Exception {
ImapResponse capabilityResponse = new ImapResponse(null, false, null);
capabilityResponse.addAll(Arrays.asList("FOO", "BAZ"));
assertTrue(parseCapabilities(Arrays.asList(capabilityResponse)).isEmpty());
}
private ImapResponseParser createParser(String response) {
ByteArrayInputStream in = new ByteArrayInputStream(response.getBytes());
PeekableInputStream pin = new PeekableInputStream(in);
return new ImapResponseParser(pin);
}
}

View File

@ -1,95 +0,0 @@
package com.fsck.k9.mail.store.imap;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import com.fsck.k9.mail.filter.PeekableInputStream;
import junit.framework.TestCase;
public class ImapResponseParserTest extends TestCase {
public void testSimpleOkResponse() throws IOException {
ImapResponseParser parser = createParser("* OK\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(1, response.size());
assertEquals("OK", response.get(0));
}
public void testOkResponseWithText() throws IOException {
ImapResponseParser parser = createParser("* OK Some text here\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(2, response.size());
assertEquals("OK", response.get(0));
assertEquals("Some text here", response.get(1));
}
public void testOkResponseWithRespTextCode() throws IOException {
ImapResponseParser parser = createParser("* OK [UIDVALIDITY 3857529045]\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(2, response.size());
assertEquals("OK", response.get(0));
assertTrue(response.get(1) instanceof ImapList);
ImapList respTextCode = (ImapList) response.get(1);
assertEquals(2, respTextCode.size());
assertEquals("UIDVALIDITY", respTextCode.get(0));
assertEquals("3857529045", respTextCode.get(1));
}
public void testOkResponseWithRespTextCodeAndText() throws IOException {
ImapResponseParser parser = createParser("* OK [token1 token2] {x} test [...]\r\n");
ImapResponse response = parser.readResponse();
assertNotNull(response);
assertEquals(3, response.size());
assertEquals("OK", response.get(0));
assertTrue(response.get(1) instanceof ImapList);
assertEquals("{x} test [...]", response.get(2));
ImapList respTextCode = (ImapList) response.get(1);
assertEquals(2, respTextCode.size());
assertEquals("token1", respTextCode.get(0));
assertEquals("token2", respTextCode.get(1));
}
public void testImapListMethods() throws IOException {
ImapList list = new ImapList();
list.add("ONE");
list.add("TWO");
list.add("THREE");
assertTrue(list.containsKey("ONE"));
assertTrue(list.containsKey("TWO"));
assertFalse(list.containsKey("THREE"));
assertFalse(list.containsKey("nonexistent"));
assertEquals("TWO", list.getKeyedValue("ONE"));
assertEquals("THREE", list.getKeyedValue("TWO"));
assertNull(list.getKeyedValue("THREE"));
assertNull(list.getKeyedValue("nonexistent"));
assertEquals(0, list.getKeyIndex("ONE"));
assertEquals(1, list.getKeyIndex("TWO"));
try {
list.getKeyIndex("THREE");
fail("IllegalArgumentException should have been thrown");
} catch (IllegalArgumentException e) { /* do nothing */ }
try {
list.getKeyIndex("nonexistent");
fail("IllegalArgumentException should have been thrown");
} catch (IllegalArgumentException e) { /* do nothing */ }
}
private ImapResponseParser createParser(String response) {
ByteArrayInputStream in = new ByteArrayInputStream(response.getBytes());
PeekableInputStream pin = new PeekableInputStream(in);
return new ImapResponseParser(pin);
}
}