Merge pull request #825 from k9mail/GH-786_fix_parsing_for_folders_with_brackets
Fix parsing IMAP LIST responses containing folder names with brackets
This commit is contained in:
commit
e6c52d3580
4 changed files with 553 additions and 196 deletions
|
@ -44,7 +44,7 @@ public class ImapList extends ArrayList<Object> {
|
|||
return getDate(getString(index));
|
||||
}
|
||||
|
||||
public Date getKeyedDate(Object key) throws MessagingException {
|
||||
public Date getKeyedDate(String key) throws MessagingException {
|
||||
return getDate(getKeyedString(key));
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ public class ImapList extends ArrayList<Object> {
|
|||
}
|
||||
|
||||
|
||||
public Object getKeyedValue(Object key) {
|
||||
public Object getKeyedValue(String key) {
|
||||
for (int i = 0, count = size() - 1; i < count; i++) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(get(i), key)) {
|
||||
return get(i + 1);
|
||||
|
@ -69,34 +69,34 @@ public class ImapList extends ArrayList<Object> {
|
|||
return null;
|
||||
}
|
||||
|
||||
public ImapList getKeyedList(Object key) {
|
||||
public ImapList getKeyedList(String key) {
|
||||
return (ImapList)getKeyedValue(key);
|
||||
}
|
||||
|
||||
public String getKeyedString(Object key) {
|
||||
public String getKeyedString(String key) {
|
||||
return (String)getKeyedValue(key);
|
||||
}
|
||||
|
||||
public int getKeyedNumber(Object key) {
|
||||
public int getKeyedNumber(String key) {
|
||||
return Integer.parseInt(getKeyedString(key));
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
public boolean containsKey(String key) {
|
||||
if (key == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0, count = size() - 1; i < count; i++) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(key, get(i))) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(get(i), key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getKeyIndex(Object key) {
|
||||
public int getKeyIndex(String key) {
|
||||
for (int i = 0, count = size() - 1; i < count; i++) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(key, get(i))) {
|
||||
if (ImapResponseParser.equalsIgnoreCase(get(i), key)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,40 +8,51 @@ package com.fsck.k9.mail.store.imap;
|
|||
* object will contain all of the available tokens at the time the response is received.
|
||||
* </p>
|
||||
*/
|
||||
public class ImapResponse extends ImapList {
|
||||
class ImapResponse extends ImapList {
|
||||
private static final long serialVersionUID = 6886458551615975669L;
|
||||
|
||||
private ImapResponseCallback mCallback;
|
||||
|
||||
private final boolean mCommandContinuationRequested;
|
||||
private final String mTag;
|
||||
private ImapResponseCallback callback;
|
||||
private final boolean commandContinuationRequested;
|
||||
private final String tag;
|
||||
|
||||
|
||||
public ImapResponse(ImapResponseCallback callback,
|
||||
boolean mCommandContinuationRequested, String mTag) {
|
||||
this.mCallback = callback;
|
||||
this.mCommandContinuationRequested = mCommandContinuationRequested;
|
||||
this.mTag = mTag;
|
||||
private ImapResponse(ImapResponseCallback callback, boolean commandContinuationRequested, String tag) {
|
||||
this.callback = callback;
|
||||
this.commandContinuationRequested = commandContinuationRequested;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public static ImapResponse newContinuationRequest(ImapResponseCallback callback) {
|
||||
return new ImapResponse(callback, true, null);
|
||||
}
|
||||
|
||||
public static ImapResponse newUntaggedResponse(ImapResponseCallback callback) {
|
||||
return new ImapResponse(callback, false, null);
|
||||
}
|
||||
|
||||
public static ImapResponse newTaggedResponse(ImapResponseCallback callback, String tag) {
|
||||
return new ImapResponse(callback, false, tag);
|
||||
}
|
||||
|
||||
public boolean isContinuationRequested() {
|
||||
return mCommandContinuationRequested;
|
||||
return commandContinuationRequested;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return mTag;
|
||||
return tag;
|
||||
}
|
||||
|
||||
public ImapResponseCallback getCallback() {
|
||||
return mCallback;
|
||||
return callback;
|
||||
}
|
||||
|
||||
public void setCallback(ImapResponseCallback mCallback) {
|
||||
this.mCallback = mCallback;
|
||||
public void setCallback(ImapResponseCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public String getAlertText() {
|
||||
if (size() > 1 && ImapResponseParser.equalsIgnoreCase("[ALERT]", get(1))) {
|
||||
if (size() > 1 && ImapResponseParser.equalsIgnoreCase(get(1), "[ALERT]")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 2, count = size(); i < count; i++) {
|
||||
sb.append(get(i).toString());
|
||||
|
@ -55,6 +66,6 @@ public class ImapResponse extends ImapList {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "#" + (mCommandContinuationRequested ? "+" : mTag) + "# " + super.toString();
|
||||
return "#" + (commandContinuationRequested ? "+" : tag) + "# " + super.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
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;
|
||||
|
@ -15,17 +9,27 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
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 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;
|
||||
private PeekableInputStream inputStream;
|
||||
private ImapResponse response;
|
||||
private Exception exception;
|
||||
|
||||
|
||||
public ImapResponseParser(PeekableInputStream in) {
|
||||
this.mIn = in;
|
||||
this.inputStream = in;
|
||||
}
|
||||
|
||||
public ImapResponse readResponse() throws IOException {
|
||||
|
@ -33,87 +37,107 @@ class ImapResponseParser {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reads the next response available on the stream and returns an
|
||||
* ImapResponse object that represents it.
|
||||
* Reads the next response available on the stream and returns an {@code ImapResponse} object that represents it.
|
||||
*/
|
||||
public ImapResponse readResponse(ImapResponseCallback callback) throws IOException {
|
||||
try {
|
||||
int ch = mIn.peek();
|
||||
if (ch == '*') {
|
||||
parseUntaggedResponse();
|
||||
mResponse = new ImapResponse(callback, false, null);
|
||||
readTokens(mResponse);
|
||||
} else if (ch == '+') {
|
||||
mResponse = new ImapResponse(callback, parseCommandContinuationRequest(), null);
|
||||
parseResponseText(mResponse);
|
||||
int peek = inputStream.peek();
|
||||
if (peek == '+') {
|
||||
readContinuationRequest(callback);
|
||||
} else if (peek == '*') {
|
||||
readUntaggedResponse(callback);
|
||||
} else {
|
||||
mResponse = new ImapResponse(callback, false, parseTaggedResponse());
|
||||
readTokens(mResponse);
|
||||
readTaggedResponse(callback);
|
||||
}
|
||||
|
||||
if (mException != null) {
|
||||
throw new RuntimeException("readResponse(): Exception in callback method", mException);
|
||||
if (exception != null) {
|
||||
throw new RuntimeException("readResponse(): Exception in callback method", exception);
|
||||
}
|
||||
|
||||
return mResponse;
|
||||
return response;
|
||||
} finally {
|
||||
mResponse = null;
|
||||
mException = null;
|
||||
response = null;
|
||||
exception = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ImapResponse> readStatusResponse(String tag,
|
||||
String commandToLog,
|
||||
String logId,
|
||||
UntaggedHandler untaggedHandler)
|
||||
throws IOException, MessagingException {
|
||||
private void readContinuationRequest(ImapResponseCallback callback) throws IOException {
|
||||
parseCommandContinuationRequest();
|
||||
response = ImapResponse.newContinuationRequest(callback);
|
||||
|
||||
skipIfSpace();
|
||||
String rest = readStringUntilEndOfLine();
|
||||
response.add(rest);
|
||||
}
|
||||
|
||||
private void readUntaggedResponse(ImapResponseCallback callback) throws IOException {
|
||||
parseUntaggedResponse();
|
||||
response = ImapResponse.newUntaggedResponse(callback);
|
||||
|
||||
readTokens(response);
|
||||
}
|
||||
|
||||
private void readTaggedResponse(ImapResponseCallback callback) throws IOException {
|
||||
String tag = parseTaggedResponse();
|
||||
response = ImapResponse.newTaggedResponse(callback, tag);
|
||||
|
||||
readTokens(response);
|
||||
}
|
||||
|
||||
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);
|
||||
Log.w(LOG_TAG, "After sending tag " + tag + ", got tag response from previous command " + response +
|
||||
" for " + logId);
|
||||
|
||||
Iterator<ImapResponse> iter = responses.iterator();
|
||||
Iterator<ImapResponse> responseIterator = 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();
|
||||
while (responseIterator.hasNext()) {
|
||||
ImapResponse delResponse = responseIterator.next();
|
||||
if (delResponse.getTag() != null || delResponse.size() < 2 || (
|
||||
!equalsIgnoreCase(delResponse.get(1), "EXISTS") &&
|
||||
!equalsIgnoreCase(delResponse.get(1), "EXPUNGE"))) {
|
||||
responseIterator.remove();
|
||||
}
|
||||
}
|
||||
response = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (untaggedHandler != null) {
|
||||
untaggedHandler.handleAsyncUntaggedResponse(response);
|
||||
}
|
||||
responses.add(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());
|
||||
throw new ImapException("Command: " + commandToLog + "; response: " + response.toString(),
|
||||
response.getAlertText());
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
protected static Set<String> parseCapabilities(List<ImapResponse> responses) {
|
||||
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;
|
||||
ImapList thisList = (ImapList) thisPart;
|
||||
if (equalsIgnoreCase(thisList.get(0), CAPABILITY_CAPABILITY)) {
|
||||
list = thisList;
|
||||
break;
|
||||
|
@ -124,11 +148,11 @@ class ImapResponseParser {
|
|||
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));
|
||||
if (list != null && list.size() > 1 && equalsIgnoreCase(list.get(0), CAPABILITY_CAPABILITY)) {
|
||||
for (Object listItem : list.subList(1, list.size())) {
|
||||
if (listItem instanceof String) {
|
||||
String capability = (String) listItem;
|
||||
capabilities.add(capability.toUpperCase(Locale.US));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +168,8 @@ class ImapResponseParser {
|
|||
|
||||
if (isStatusResponse(firstToken)) {
|
||||
parseResponseText(response);
|
||||
} else if (equalsIgnoreCase(firstToken, "LIST")) {
|
||||
parseListResponse(response);
|
||||
} else {
|
||||
Object token;
|
||||
while ((token = readToken(response)) != null) {
|
||||
|
@ -156,7 +182,6 @@ class ImapResponseParser {
|
|||
|
||||
/**
|
||||
* Parse {@code resp-text} tokens
|
||||
*
|
||||
* <p>
|
||||
* Responses "OK", "PREAUTH", "BYE", "NO", "BAD", and continuation request responses can
|
||||
* contain {@code resp-text} tokens. We parse the {@code resp-text-code} part as tokens and
|
||||
|
@ -175,21 +200,20 @@ class ImapResponseParser {
|
|||
* The {@link ImapResponse} instance that holds the parsed tokens of the response.
|
||||
*
|
||||
* @throws IOException
|
||||
* If there's a network error.
|
||||
* If there's a network error.
|
||||
*
|
||||
* @see #isStatusResponse(String)
|
||||
*/
|
||||
private void parseResponseText(ImapResponse parent) throws IOException {
|
||||
skipIfSpace();
|
||||
|
||||
int next = mIn.peek();
|
||||
int next = inputStream.peek();
|
||||
if (next == '[') {
|
||||
parseSequence(parent);
|
||||
parseList(parent, '[', ']');
|
||||
skipIfSpace();
|
||||
}
|
||||
|
||||
String rest = readStringUntil('\r');
|
||||
expect('\n');
|
||||
String rest = readStringUntilEndOfLine();
|
||||
|
||||
if (!TextUtils.isEmpty(rest)) {
|
||||
// The rest is free-form text.
|
||||
|
@ -197,8 +221,19 @@ class ImapResponseParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseListResponse(ImapResponse response) throws IOException {
|
||||
expect(' ');
|
||||
parseList(response, '(', ')');
|
||||
expect(' ');
|
||||
String delimiter = parseQuoted();
|
||||
response.add(delimiter);
|
||||
expect(' ');
|
||||
String name = parseString();
|
||||
response.add(name);
|
||||
}
|
||||
|
||||
private void skipIfSpace() throws IOException {
|
||||
if (mIn.peek() == ' ') {
|
||||
if (inputStream.peek() == ' ') {
|
||||
expect(' ');
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +245,7 @@ class ImapResponseParser {
|
|||
* elements including List.
|
||||
*
|
||||
* @return The next token in the response or null if there are no more
|
||||
* tokens.
|
||||
* tokens.
|
||||
*/
|
||||
private Object readToken(ImapResponse response) throws IOException {
|
||||
while (true) {
|
||||
|
@ -223,11 +258,12 @@ class ImapResponseParser {
|
|||
|
||||
private Object parseToken(ImapList parent) throws IOException {
|
||||
while (true) {
|
||||
int ch = mIn.peek();
|
||||
int ch = inputStream.peek();
|
||||
|
||||
if (ch == '(') {
|
||||
return parseList(parent);
|
||||
return parseList(parent, '(', ')');
|
||||
} else if (ch == '[') {
|
||||
return parseSequence(parent);
|
||||
return parseList(parent, '[', ']');
|
||||
} else if (ch == ')') {
|
||||
expect(')');
|
||||
return ")";
|
||||
|
@ -250,98 +286,88 @@ class ImapResponseParser {
|
|||
} else if (ch == '\t') {
|
||||
expect('\t');
|
||||
} else {
|
||||
return parseAtom();
|
||||
return parseBareString(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String parseString() throws IOException {
|
||||
int ch = inputStream.peek();
|
||||
|
||||
if (ch == '"') {
|
||||
return parseQuoted();
|
||||
} else if (ch == '{') {
|
||||
return (String) parseLiteral();
|
||||
} else {
|
||||
return parseBareString(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseCommandContinuationRequest() throws IOException {
|
||||
expect('+');
|
||||
return true;
|
||||
}
|
||||
|
||||
// * OK [UIDNEXT 175] Predicted next UID
|
||||
private void parseUntaggedResponse() throws IOException {
|
||||
expect('*');
|
||||
expect(' ');
|
||||
}
|
||||
|
||||
// 3 OK [READ-WRITE] Select completed.
|
||||
private String parseTaggedResponse() throws IOException {
|
||||
return readStringUntil(' ');
|
||||
}
|
||||
|
||||
private ImapList parseList(ImapList parent) throws IOException {
|
||||
expect('(');
|
||||
private ImapList parseList(ImapList parent, char start, char end) throws IOException {
|
||||
expect(start);
|
||||
|
||||
ImapList list = new ImapList();
|
||||
parent.add(list);
|
||||
|
||||
String endString = String.valueOf(end);
|
||||
|
||||
Object token;
|
||||
while (true) {
|
||||
token = parseToken(list);
|
||||
if (token == null) {
|
||||
return null;
|
||||
} else if (token.equals(")")) {
|
||||
} else if (token.equals(endString)) {
|
||||
break;
|
||||
} else if (token instanceof ImapList) {
|
||||
// Do nothing
|
||||
} else {
|
||||
} else if (!(token instanceof ImapList)) {
|
||||
list.add(token);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private ImapList parseSequence(ImapList parent) throws IOException {
|
||||
expect('[');
|
||||
ImapList list = new ImapList();
|
||||
parent.add(list);
|
||||
Object token;
|
||||
while (true) {
|
||||
token = parseToken(list);
|
||||
if (token == null) {
|
||||
return null;
|
||||
} else if (token.equals("]")) {
|
||||
break;
|
||||
} else if (token instanceof ImapList) {
|
||||
// Do nothing
|
||||
} else {
|
||||
list.add(token);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String parseAtom() throws IOException {
|
||||
private String parseBareString(boolean allowBrackets) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int ch;
|
||||
while (true) {
|
||||
ch = mIn.peek();
|
||||
ch = inputStream.peek();
|
||||
if (ch == -1) {
|
||||
throw new IOException("parseAtom(): end of stream reached");
|
||||
} else if (ch == '(' || ch == ')' || ch == '{' || ch == ' ' ||
|
||||
ch == '[' || ch == ']' ||
|
||||
// docs claim that flags are \ atom but atom isn't supposed to
|
||||
// contain
|
||||
// * and some flags contain *
|
||||
// ch == '%' || ch == '*' ||
|
||||
// ch == '%' ||
|
||||
// TODO probably should not allow \ and should recognize
|
||||
// it as a flag instead
|
||||
// ch == '"' || ch == '\' ||
|
||||
ch == '"' || (ch >= 0x00 && ch <= 0x1f) || ch == 0x7f) {
|
||||
throw new IOException("parseBareString(): end of stream reached");
|
||||
}
|
||||
|
||||
if (ch == '(' || ch == ')' || (allowBrackets && (ch == '[' || ch == ']')) ||
|
||||
ch == '{' || ch == ' ' || ch == '"' ||
|
||||
(ch >= 0x00 && ch <= 0x1f) || ch == 0x7f) {
|
||||
|
||||
if (sb.length() == 0) {
|
||||
throw new IOException(String.format("parseAtom(): (%04x %c)", ch, ch));
|
||||
throw new IOException(String.format("parseBareString(): (%04x %c)", ch, ch));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} else {
|
||||
sb.append((char)mIn.read());
|
||||
sb.append((char) inputStream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A "{" has been read. Read the rest of the size string, the space and then
|
||||
* notify the callback with an InputStream.
|
||||
* A "{" has been read. Read the rest of the size string, the space and then notify the callback with an
|
||||
* {@code InputStream}.
|
||||
*/
|
||||
private Object parseLiteral() throws IOException {
|
||||
expect('{');
|
||||
|
@ -353,24 +379,23 @@ class ImapResponseParser {
|
|||
return "";
|
||||
}
|
||||
|
||||
if (mResponse.getCallback() != null) {
|
||||
FixedLengthInputStream fixed = new FixedLengthInputStream(mIn, size);
|
||||
if (response.getCallback() != null) {
|
||||
FixedLengthInputStream fixed = new FixedLengthInputStream(inputStream, size);
|
||||
|
||||
Object result = null;
|
||||
try {
|
||||
result = mResponse.getCallback().foundLiteral(mResponse, fixed);
|
||||
result = response.getCallback().foundLiteral(response, fixed);
|
||||
} catch (IOException e) {
|
||||
// Pass IOExceptions through
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Catch everything else and save it for later.
|
||||
mException = e;
|
||||
//Log.e(LOG_TAG, "parseLiteral(): Exception in callback method", e);
|
||||
exception = e;
|
||||
}
|
||||
|
||||
// Check if only some of the literal data was read
|
||||
int available = fixed.available();
|
||||
if ((available > 0) && (available != size)) {
|
||||
if (available > 0 && available != size) {
|
||||
// If so, skip the rest
|
||||
while (fixed.available() > 0) {
|
||||
fixed.skip(fixed.available());
|
||||
|
@ -385,7 +410,7 @@ class ImapResponseParser {
|
|||
byte[] data = new byte[size];
|
||||
int read = 0;
|
||||
while (read != size) {
|
||||
int count = mIn.read(data, read, size - read);
|
||||
int count = inputStream.read(data, read, size - read);
|
||||
if (count == -1) {
|
||||
throw new IOException("parseLiteral(): end of stream reached");
|
||||
}
|
||||
|
@ -401,14 +426,14 @@ class ImapResponseParser {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
int ch;
|
||||
boolean escape = false;
|
||||
while ((ch = mIn.read()) != -1) {
|
||||
if (!escape && (ch == '\\')) {
|
||||
while ((ch = inputStream.read()) != -1) {
|
||||
if (!escape && ch == '\\') {
|
||||
// Found the escape character
|
||||
escape = true;
|
||||
} else if (!escape && (ch == '"')) {
|
||||
} else if (!escape && ch == '"') {
|
||||
return sb.toString();
|
||||
} else {
|
||||
sb.append((char)ch);
|
||||
sb.append((char) ch);
|
||||
escape = false;
|
||||
}
|
||||
}
|
||||
|
@ -417,47 +442,47 @@ class ImapResponseParser {
|
|||
|
||||
private String readStringUntil(char end) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int ch;
|
||||
while ((ch = mIn.read()) != -1) {
|
||||
while ((ch = inputStream.read()) != -1) {
|
||||
if (ch == end) {
|
||||
return sb.toString();
|
||||
} else {
|
||||
sb.append((char)ch);
|
||||
sb.append((char) ch);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("readStringUntil(): end of stream reached");
|
||||
}
|
||||
|
||||
private int expect(char ch) throws IOException {
|
||||
int d;
|
||||
if ((d = mIn.read()) != ch) {
|
||||
throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)", (int)ch,
|
||||
ch, d, (char)d));
|
||||
}
|
||||
return d;
|
||||
private String readStringUntilEndOfLine() throws IOException {
|
||||
String rest = readStringUntil('\r');
|
||||
expect('\n');
|
||||
|
||||
return rest;
|
||||
}
|
||||
|
||||
public boolean isStatusResponse(String symbol) {
|
||||
private void expect(char expected) throws IOException {
|
||||
int readByte = inputStream.read();
|
||||
if (readByte != expected) {
|
||||
throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)",
|
||||
(int) expected, expected, readByte, (char) readByte));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStatusResponse(String symbol) {
|
||||
return symbol.equalsIgnoreCase("OK") ||
|
||||
symbol.equalsIgnoreCase("NO") ||
|
||||
symbol.equalsIgnoreCase("BAD") ||
|
||||
symbol.equalsIgnoreCase("PREAUTH") ||
|
||||
symbol.equalsIgnoreCase("BYE");
|
||||
symbol.equalsIgnoreCase("NO") ||
|
||||
symbol.equalsIgnoreCase("BAD") ||
|
||||
symbol.equalsIgnoreCase("PREAUTH") ||
|
||||
symbol.equalsIgnoreCase("BYE");
|
||||
}
|
||||
|
||||
public static boolean equalsIgnoreCase(Object o1, Object o2) {
|
||||
if (o1 != null && o2 != null && o1 instanceof String && o2 instanceof String) {
|
||||
String s1 = (String)o1;
|
||||
String s2 = (String)o2;
|
||||
return s1.equalsIgnoreCase(s2);
|
||||
} else if (o1 != null) {
|
||||
return o1.equals(o2);
|
||||
} else if (o2 != null) {
|
||||
return o2.equals(o1);
|
||||
} else {
|
||||
// Both o1 and o2 are null
|
||||
return true;
|
||||
static boolean equalsIgnoreCase(Object token, String symbol) {
|
||||
if (token == null || !(token instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return symbol.equalsIgnoreCase((String) token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
package com.fsck.k9.mail.store.imap;
|
||||
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class ImapResponseParserTest {
|
||||
public class ImapResponseParserTest {
|
||||
|
||||
@Test public void testSimpleOkResponse() throws IOException {
|
||||
@Test
|
||||
public void testSimpleOkResponse() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
|
@ -32,8 +38,10 @@ public class ImapResponseParserTest {
|
|||
assertEquals("OK", response.get(0));
|
||||
}
|
||||
|
||||
@Test public void testOkResponseWithText() throws IOException {
|
||||
@Test
|
||||
public void testOkResponseWithText() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK Some text here\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
|
@ -42,8 +50,10 @@ public class ImapResponseParserTest {
|
|||
assertEquals("Some text here", response.get(1));
|
||||
}
|
||||
|
||||
@Test public void testOkResponseWithRespTextCode() throws IOException {
|
||||
@Test
|
||||
public void testOkResponseWithRespTextCode() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK [UIDVALIDITY 3857529045]\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
|
@ -57,8 +67,10 @@ public class ImapResponseParserTest {
|
|||
assertEquals("3857529045", respTextCode.get(1));
|
||||
}
|
||||
|
||||
@Test public void testOkResponseWithRespTextCodeAndText() throws IOException {
|
||||
@Test
|
||||
public void testOkResponseWithRespTextCodeAndText() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK [token1 token2] {x} test [...]\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
|
@ -73,9 +85,10 @@ public class ImapResponseParserTest {
|
|||
assertEquals("token2", respTextCode.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStatusResponseWithOKResponse() throws Exception {
|
||||
ImapResponseParser parser = createParser("* COMMAND BAR\tBAZ\r\nTAG OK COMMAND completed\r\n");
|
||||
|
||||
@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());
|
||||
|
@ -83,31 +96,339 @@ public class ImapResponseParserTest {
|
|||
assertEquals(asList("OK", "COMMAND completed"), responses.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStatusResponseSkippingWrongTag() throws Exception {
|
||||
ImapResponseParser parser = createParser("* UNTAGGED\r\n" +
|
||||
"* 0 EXPUNGE\r\n" +
|
||||
"* 42 EXISTS\r\n" +
|
||||
"A1 COMMAND BAR BAZ\r\n" +
|
||||
"A2 OK COMMAND completed\r\n");
|
||||
TestUntaggedHandler untaggedHandler = new TestUntaggedHandler();
|
||||
|
||||
List<ImapResponse> responses = parser.readStatusResponse("A2", null, null, untaggedHandler);
|
||||
|
||||
assertEquals(3, responses.size());
|
||||
assertEquals(asList("0", "EXPUNGE"), responses.get(0));
|
||||
assertEquals(asList("42", "EXISTS"), responses.get(1));
|
||||
assertEquals(asList("OK", "COMMAND completed"), responses.get(2));
|
||||
assertEquals(asList("UNTAGGED"), untaggedHandler.responses.get(0));
|
||||
assertEquals(responses.get(0), untaggedHandler.responses.get(1));
|
||||
assertEquals(responses.get(1), untaggedHandler.responses.get(2));
|
||||
}
|
||||
|
||||
@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));
|
||||
@Test
|
||||
public void testRespTextCodeWithList() throws Exception {
|
||||
ImapResponseParser parser = createParser("* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen " +
|
||||
"\\Draft NonJunk $MDNSent \\*)] Flags permitted.\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(3, response.size());
|
||||
assertTrue(response.get(1) instanceof ImapList);
|
||||
assertEquals(2, response.getList(1).size());
|
||||
assertEquals("PERMANENTFLAGS", response.getList(1).getString(0));
|
||||
assertTrue(response.getList(1).get(1) instanceof ImapList);
|
||||
assertEquals("\\Answered", response.getList(1).getList(1).getString(0));
|
||||
assertEquals("\\Flagged", response.getList(1).getList(1).getString(1));
|
||||
assertEquals("\\Deleted", response.getList(1).getList(1).getString(2));
|
||||
assertEquals("\\Seen", response.getList(1).getList(1).getString(3));
|
||||
assertEquals("\\Draft", response.getList(1).getList(1).getString(4));
|
||||
assertEquals("NonJunk", response.getList(1).getList(1).getString(5));
|
||||
assertEquals("$MDNSent", response.getList(1).getList(1).getString(6));
|
||||
assertEquals("\\*", response.getList(1).getList(1).getString(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExistsResponse() throws Exception {
|
||||
ImapResponseParser parser = createParser("* 23 EXISTS\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(2, response.size());
|
||||
assertEquals(23, response.getNumber(0));
|
||||
assertEquals("EXISTS", response.getString(1));
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testReadStringUntilEndOfStream() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK Some text ");
|
||||
|
||||
parser.readResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommandContinuation() throws Exception {
|
||||
ImapResponseParser parser = createParser("+ Ready for additional command text\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("Ready for additional command text", response.getString(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLiteral() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("test", response.getString(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLiteralWithEmptyString() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {0}\r\n\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("", response.getString(0));
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testParseLiteralToEndOfStream() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\nabc");
|
||||
|
||||
parser.readResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLiteralWithConsumingCallbackReturningNull() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
TestImapResponseCallback callback = new TestImapResponseCallback(4, "cheeseburger");
|
||||
|
||||
ImapResponse response = parser.readResponse(callback);
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("cheeseburger", response.getString(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLiteralWithNonConsumingCallbackReturningNull() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
TestImapResponseCallback callback = new TestImapResponseCallback(0, null);
|
||||
|
||||
ImapResponse response = parser.readResponse(callback);
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("test", response.getString(0));
|
||||
assertTrue(callback.foundLiteralCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseLiteralWithIncompleteConsumingCallbackReturningString() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
TestImapResponseCallback callback = new TestImapResponseCallback(2, "ninja");
|
||||
|
||||
ImapResponse response = parser.readResponse(callback);
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("ninja", response.getString(0));
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testParseLiteralWithThrowingCallback() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
ImapResponseCallback callback = new ImapResponseCallback() {
|
||||
@Override
|
||||
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws Exception {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
};
|
||||
|
||||
parser.readResponse(callback);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testParseLiteralWithCallbackThrowingIOException() throws Exception {
|
||||
ImapResponseParser parser = createParser("* {4}\r\ntest\r\n");
|
||||
ImapResponseCallback callback = new ImapResponseCallback() {
|
||||
@Override
|
||||
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws Exception {
|
||||
throw new IOException();
|
||||
}
|
||||
};
|
||||
|
||||
parser.readResponse(callback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseQuoted() throws Exception {
|
||||
ImapResponseParser parser = createParser("* \"qu\\\"oted\"\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("qu\"oted", response.getString(0));
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testParseQuotedToEndOfStream() throws Exception {
|
||||
ImapResponseParser parser = createParser("* \"abc");
|
||||
|
||||
parser.readResponse();
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testParseAtomToEndOfStream() throws Exception {
|
||||
ImapResponseParser parser = createParser("* abc");
|
||||
|
||||
parser.readResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCapabilities() throws Exception {
|
||||
ImapResponse capabilityResponse = createResponse("CAPABILITY", "FOO", "BAR");
|
||||
List<ImapResponse> responses = Collections.singletonList(capabilityResponse);
|
||||
|
||||
Set<String> capabilities = parseCapabilities(responses);
|
||||
|
||||
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());
|
||||
@Test
|
||||
public void testParseCapabilitiesWithInvalidResponse() throws Exception {
|
||||
ImapResponse capabilityResponse = createResponse("FOO", "BAZ");
|
||||
List<ImapResponse> responses = Collections.singletonList(capabilityResponse);
|
||||
|
||||
Set<String> capabilities = parseCapabilities(responses);
|
||||
|
||||
assertTrue(capabilities.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCapabilitiesWithMultipleResponses() throws Exception {
|
||||
ImapResponse responseOne = createResponse("CAPABILITY", "foo");
|
||||
ImapResponse responseTwo = createResponse("capability", "bar");
|
||||
List<ImapResponse> responses = Arrays.asList(responseOne, responseTwo);
|
||||
|
||||
Set<String> capabilities = parseCapabilities(responses);
|
||||
|
||||
assertEquals(2, capabilities.size());
|
||||
assertTrue(capabilities.contains("FOO"));
|
||||
assertTrue(capabilities.contains("BAR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOkResponseWithCapabilities() throws Exception {
|
||||
ImapResponseParser parser = createParser("* OK [CAPABILITY foo bar]\r\n");
|
||||
List<ImapResponse> responses = Collections.singletonList(parser.readResponse());
|
||||
|
||||
Set<String> capabilities = parseCapabilities(responses);
|
||||
|
||||
assertEquals(2, capabilities.size());
|
||||
assertTrue(capabilities.contains("FOO"));
|
||||
assertTrue(capabilities.contains("BAR"));
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testParseUntaggedResponseWithoutSpace() throws Exception {
|
||||
ImapResponseParser parser = createParser("*\r\n");
|
||||
|
||||
parser.readResponse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListResponseContainingFolderNameWithBrackets() throws Exception {
|
||||
ImapResponseParser parser = createParser("* LIST (\\HasNoChildren) \".\" [FolderName]\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(4, response.size());
|
||||
assertEquals("LIST", response.get(0));
|
||||
assertEquals(1, response.getList(1).size());
|
||||
assertEquals("\\HasNoChildren", response.getList(1).getString(0));
|
||||
assertEquals(".", response.get(2));
|
||||
assertEquals("[FolderName]", response.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchResponse() throws Exception {
|
||||
ImapResponseParser parser = createParser("* 1 FETCH (" +
|
||||
"UID 23 " +
|
||||
"INTERNALDATE \"01-Jul-2015 12:34:56 +0200\" " +
|
||||
"RFC822.SIZE 3456 " +
|
||||
"BODY[HEADER.FIELDS (date subject from)] \"<headers>\" " +
|
||||
"FLAGS (\\Seen))\r\n");
|
||||
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertEquals(3, response.size());
|
||||
assertEquals("1", response.getString(0));
|
||||
assertEquals("FETCH", response.getString(1));
|
||||
assertEquals("UID", response.getList(2).getString(0));
|
||||
assertEquals(23, response.getList(2).getNumber(1));
|
||||
assertEquals("INTERNALDATE", response.getList(2).getString(2));
|
||||
assertEquals("01-Jul-2015 12:34:56 +0200", response.getList(2).getString(3));
|
||||
assertEquals("RFC822.SIZE", response.getList(2).getString(4));
|
||||
assertEquals(3456, response.getList(2).getNumber(5));
|
||||
assertEquals("BODY", response.getList(2).getString(6));
|
||||
assertEquals(2, response.getList(2).getList(7).size());
|
||||
assertEquals("HEADER.FIELDS", response.getList(2).getList(7).getString(0));
|
||||
assertEquals(3, response.getList(2).getList(7).getList(1).size());
|
||||
assertEquals("date", response.getList(2).getList(7).getList(1).getString(0));
|
||||
assertEquals("subject", response.getList(2).getList(7).getList(1).getString(1));
|
||||
assertEquals("from", response.getList(2).getList(7).getList(1).getString(2));
|
||||
assertEquals("<headers>", response.getList(2).getString(8));
|
||||
assertEquals("FLAGS", response.getList(2).getString(9));
|
||||
assertEquals(1, response.getList(2).getList(10).size());
|
||||
assertEquals("\\Seen", response.getList(2).getList(10).getString(0));
|
||||
}
|
||||
|
||||
private ImapResponseParser createParser(String response) {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(response.getBytes());
|
||||
PeekableInputStream pin = new PeekableInputStream(in);
|
||||
return new ImapResponseParser(pin);
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(response.getBytes());
|
||||
PeekableInputStream peekableInputStream = new PeekableInputStream(byteArrayInputStream);
|
||||
return new ImapResponseParser(peekableInputStream);
|
||||
}
|
||||
|
||||
private ImapResponse createResponse(Object... tokens) {
|
||||
ImapResponse response = ImapResponse.newUntaggedResponse(null);
|
||||
response.addAll(Arrays.asList(tokens));
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private class TestImapResponseCallback implements ImapResponseCallback {
|
||||
private final int readNumberOfBytes;
|
||||
private final Object returnValue;
|
||||
public boolean foundLiteralCalled = false;
|
||||
|
||||
TestImapResponseCallback(int readNumberOfBytes, Object returnValue) {
|
||||
this.readNumberOfBytes = readNumberOfBytes;
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object foundLiteral(ImapResponse response, FixedLengthInputStream literal) throws Exception {
|
||||
foundLiteralCalled = true;
|
||||
|
||||
int skipBytes = readNumberOfBytes;
|
||||
long skippedBytes;
|
||||
do {
|
||||
skippedBytes = literal.skip(skipBytes);
|
||||
skipBytes -= skippedBytes;
|
||||
} while (skippedBytes > 0);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestUntaggedHandler implements UntaggedHandler {
|
||||
public final List<ImapResponse> responses = new ArrayList<ImapResponse>();
|
||||
|
||||
@Override
|
||||
public void handleAsyncUntaggedResponse(ImapResponse response) {
|
||||
responses.add(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue