2009-12-14 21:50:53 -05:00
|
|
|
package com.fsck.k9.mail.store;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-08-01 16:49:12 -04:00
|
|
|
import android.text.TextUtils;
|
2009-12-14 21:50:53 -05:00
|
|
|
import com.fsck.k9.mail.MessagingException;
|
2010-05-19 14:17:06 -04:00
|
|
|
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
|
|
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
2008-11-01 17:32:06 -04:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Date;
|
2009-03-04 14:49:39 -05:00
|
|
|
import java.util.Locale;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public class ImapResponseParser {
|
2010-05-19 09:31:48 -04:00
|
|
|
private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
|
|
|
|
private static final SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US);
|
|
|
|
private static final SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US);
|
2011-04-12 08:32:42 -04:00
|
|
|
private static final SimpleDateFormat badDateTimeFormat3 = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss", Locale.US);
|
2009-10-28 08:45:22 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
private PeekableInputStream mIn;
|
|
|
|
private ImapResponse mResponse;
|
|
|
|
private Exception mException;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapResponseParser(PeekableInputStream in) {
|
2008-11-01 17:32:06 -04:00
|
|
|
this.mIn = in;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapResponse readResponse() throws IOException {
|
2010-05-19 09:31:48 -04:00
|
|
|
return readResponse(null);
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
/**
|
|
|
|
* Reads the next response available on the stream and returns an
|
|
|
|
* ImapResponse object that represents it.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapResponse readResponse(IImapResponseCallback callback) throws IOException {
|
|
|
|
try {
|
2010-05-19 09:31:48 -04:00
|
|
|
ImapResponse response = new ImapResponse();
|
|
|
|
mResponse = response;
|
|
|
|
mResponse.mCallback = callback;
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
int ch = mIn.peek();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ch == '*') {
|
2010-05-19 09:31:48 -04:00
|
|
|
parseUntaggedResponse();
|
|
|
|
readTokens(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '+') {
|
2011-11-15 21:23:59 -05:00
|
|
|
response.mCommandContinuationRequested = parseCommandContinuationRequest();
|
2011-11-15 21:17:36 -05:00
|
|
|
parseResponseText(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-05-19 09:31:48 -04:00
|
|
|
response.mTag = parseTaggedResponse();
|
|
|
|
readTokens(response);
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mException != null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
throw new RuntimeException("readResponse(): Exception in callback method", mException);
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
return response;
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2010-05-19 09:31:48 -04:00
|
|
|
mResponse.mCallback = null;
|
|
|
|
mResponse = null;
|
|
|
|
mException = null;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private void readTokens(ImapResponse response) throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
response.clear();
|
2011-01-10 22:10:24 -05:00
|
|
|
|
2011-11-15 21:10:26 -05:00
|
|
|
String firstToken = (String) readToken(response);
|
2011-08-04 14:00:27 -04:00
|
|
|
response.add(firstToken);
|
2011-08-01 16:49:12 -04:00
|
|
|
|
2011-08-04 14:00:27 -04:00
|
|
|
if (isStatusResponse(firstToken)) {
|
2011-11-15 21:11:35 -05:00
|
|
|
parseResponseText(response);
|
2011-08-01 16:49:12 -04:00
|
|
|
} else {
|
2011-11-15 21:10:26 -05:00
|
|
|
Object token;
|
2011-08-01 16:49:12 -04:00
|
|
|
while ((token = readToken(response)) != null) {
|
|
|
|
if (!(token instanceof ImapList)) {
|
|
|
|
response.add(token);
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 21:59:27 -05:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* read the rest as sequence of characters to avoid the parser interpreting things like
|
|
|
|
* "{123}" as start of a literal.
|
|
|
|
* </p>
|
|
|
|
* <p>Example:</p>
|
|
|
|
* <p>
|
|
|
|
* {@code * OK [UIDVALIDITY 3857529045] UIDs valid}
|
|
|
|
* </p>
|
|
|
|
* <p>
|
|
|
|
* See RFC 3501, Section 9 Formal Syntax (resp-text)
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* @param parent
|
|
|
|
* The {@link ImapResponse} instance that holds the parsed tokens of the response.
|
|
|
|
*
|
|
|
|
* @throws IOException
|
|
|
|
* If there's a network error.
|
|
|
|
*
|
|
|
|
* @see #isStatusResponse(String)
|
|
|
|
*/
|
2011-11-15 21:23:59 -05:00
|
|
|
private void parseResponseText(ImapResponse parent) throws IOException {
|
2011-11-15 21:10:26 -05:00
|
|
|
skipIfSpace();
|
|
|
|
|
2011-08-01 16:49:12 -04:00
|
|
|
int next = mIn.peek();
|
|
|
|
if (next == '[') {
|
|
|
|
parseSequence(parent);
|
2011-08-04 14:00:27 -04:00
|
|
|
skipIfSpace();
|
2011-08-01 16:49:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
String rest = readStringUntil('\r');
|
|
|
|
expect('\n');
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(rest)) {
|
|
|
|
// The rest is free-form text.
|
|
|
|
parent.add(rest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 21:23:59 -05:00
|
|
|
private void skipIfSpace() throws IOException {
|
2011-08-04 14:00:27 -04:00
|
|
|
if (mIn.peek() == ' ') {
|
|
|
|
expect(' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
/**
|
|
|
|
* Reads the next token of the response. The token can be one of: String -
|
2011-01-10 22:40:43 -05:00
|
|
|
* for NIL, QUOTED, NUMBER, ATOM. Object - for LITERAL.
|
|
|
|
* ImapList - for PARENTHESIZED LIST. Can contain any of the above
|
2008-11-01 17:32:06 -04:00
|
|
|
* elements including List.
|
|
|
|
*
|
|
|
|
* @return The next token in the response or null if there are no more
|
|
|
|
* tokens.
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private Object readToken(ImapResponse response) throws IOException {
|
|
|
|
while (true) {
|
2010-05-19 09:31:48 -04:00
|
|
|
Object token = parseToken(response);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (token == null || !(token.equals(")") || token.equals("]"))) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private Object parseToken(ImapList parent) throws IOException {
|
|
|
|
while (true) {
|
2008-11-01 17:32:06 -04:00
|
|
|
int ch = mIn.peek();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ch == '(') {
|
2010-05-19 09:31:48 -04:00
|
|
|
return parseList(parent);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '[') {
|
2010-05-19 09:31:48 -04:00
|
|
|
return parseSequence(parent);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == ')') {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect(')');
|
|
|
|
return ")";
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == ']') {
|
2009-11-24 19:40:29 -05:00
|
|
|
expect(']');
|
|
|
|
return "]";
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '"') {
|
2008-11-01 17:32:06 -04:00
|
|
|
return parseQuoted();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '{') {
|
2010-05-19 09:31:48 -04:00
|
|
|
return parseLiteral();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == ' ') {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect(' ');
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '\r') {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('\r');
|
|
|
|
expect('\n');
|
|
|
|
return null;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '\n') {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('\n');
|
|
|
|
return null;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '\t') {
|
2009-11-24 19:40:29 -05:00
|
|
|
expect('\t');
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
return parseAtom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private boolean parseCommandContinuationRequest() throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('+');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// * OK [UIDNEXT 175] Predicted next UID
|
2011-02-06 17:09:48 -05:00
|
|
|
private void parseUntaggedResponse() throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('*');
|
|
|
|
expect(' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3 OK [READ-WRITE] Select completed.
|
2011-02-06 17:09:48 -05:00
|
|
|
private String parseTaggedResponse() throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
String tag = readStringUntil(' ');
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private ImapList parseList(ImapList parent) throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('(');
|
|
|
|
ImapList list = new ImapList();
|
2010-05-19 09:31:48 -04:00
|
|
|
parent.add(list);
|
2008-11-01 17:32:06 -04:00
|
|
|
Object token;
|
2011-02-06 17:09:48 -05:00
|
|
|
while (true) {
|
2010-05-19 09:31:48 -04:00
|
|
|
token = parseToken(list);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (token == null) {
|
2011-01-10 22:10:24 -05:00
|
|
|
return null;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (token.equals(")")) {
|
2008-11-01 17:32:06 -04:00
|
|
|
break;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (token instanceof ImapList) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// Do nothing
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
list.add(token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private ImapList parseSequence(ImapList parent) throws IOException {
|
2009-10-21 20:41:06 -04:00
|
|
|
expect('[');
|
|
|
|
ImapList list = new ImapList();
|
2010-05-19 09:31:48 -04:00
|
|
|
parent.add(list);
|
2009-10-21 20:41:06 -04:00
|
|
|
Object token;
|
2011-02-06 17:09:48 -05:00
|
|
|
while (true) {
|
2010-05-19 09:31:48 -04:00
|
|
|
token = parseToken(list);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (token == null) {
|
2011-01-10 22:10:24 -05:00
|
|
|
return null;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (token.equals("]")) {
|
2009-10-21 20:41:06 -04:00
|
|
|
break;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (token instanceof ImapList) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// Do nothing
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2009-10-21 20:41:06 -04:00
|
|
|
list.add(token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String parseAtom() throws IOException {
|
2011-05-24 21:21:03 -04:00
|
|
|
StringBuilder sb = new StringBuilder();
|
2008-11-01 17:32:06 -04:00
|
|
|
int ch;
|
2011-02-06 17:09:48 -05:00
|
|
|
while (true) {
|
2008-11-01 17:32:06 -04:00
|
|
|
ch = mIn.peek();
|
2011-02-06 17:09:48 -05:00
|
|
|
if (ch == -1) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new IOException("parseAtom(): end of stream reached");
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '(' || ch == ')' || ch == '{' || ch == ' ' ||
|
|
|
|
ch == '[' || ch == ']' ||
|
|
|
|
// docs claim that flags are \ atom but atom isn't supposed to
|
|
|
|
// contain
|
2011-09-27 00:03:13 -04:00
|
|
|
// * and some flags contain *
|
2011-02-06 17:09:48 -05:00
|
|
|
// ch == '%' || ch == '*' ||
|
2009-10-28 08:45:22 -04:00
|
|
|
// ch == '%' ||
|
2011-02-06 17:09:48 -05:00
|
|
|
// TODO probably should not allow \ and should recognize
|
|
|
|
// it as a flag instead
|
|
|
|
// ch == '"' || ch == '\' ||
|
|
|
|
ch == '"' || (ch >= 0x00 && ch <= 0x1f) || ch == 0x7f) {
|
|
|
|
if (sb.length() == 0) {
|
2011-01-18 20:21:27 -05:00
|
|
|
throw new IOException(String.format("parseAtom(): (%04x %c)", ch, ch));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
return sb.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
sb.append((char)mIn.read());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-01-10 22:40:43 -05:00
|
|
|
* A "{" has been read. Read the rest of the size string, the space and then
|
|
|
|
* notify the callback with an InputStream.
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
private Object parseLiteral() throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('{');
|
|
|
|
int size = Integer.parseInt(readStringUntil('}'));
|
|
|
|
expect('\r');
|
|
|
|
expect('\n');
|
2010-05-19 09:31:48 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (size == 0) {
|
2010-05-19 09:31:48 -04:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mResponse.mCallback != null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
FixedLengthInputStream fixed = new FixedLengthInputStream(mIn, size);
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
Object result = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-05-19 09:31:48 -04:00
|
|
|
result = mResponse.mCallback.foundLiteral(mResponse, fixed);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (IOException e) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// Pass IOExceptions through
|
|
|
|
throw e;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// Catch everything else and save it for later.
|
|
|
|
mException = e;
|
|
|
|
//Log.e(K9.LOG_TAG, "parseLiteral(): Exception in callback method", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if only some of the literal data was read
|
|
|
|
int available = fixed.available();
|
2011-02-06 17:09:48 -05:00
|
|
|
if ((available > 0) && (available != size)) {
|
2010-05-19 09:31:48 -04:00
|
|
|
// If so, skip the rest
|
2011-02-06 17:09:48 -05:00
|
|
|
while (fixed.available() > 0) {
|
2011-01-10 22:27:58 -05:00
|
|
|
fixed.skip(fixed.available());
|
|
|
|
}
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (result != null) {
|
2010-05-19 09:31:48 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] data = new byte[size];
|
|
|
|
int read = 0;
|
2011-02-06 17:09:48 -05:00
|
|
|
while (read != size) {
|
2010-05-19 09:31:48 -04:00
|
|
|
int count = mIn.read(data, read, size - read);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (count == -1) {
|
2010-05-19 09:31:48 -04:00
|
|
|
throw new IOException("parseLiteral(): end of stream reached");
|
|
|
|
}
|
|
|
|
read += count;
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2010-05-19 09:31:48 -04:00
|
|
|
return new String(data, "US-ASCII");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String parseQuoted() throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
expect('"');
|
2010-04-30 09:51:19 -04:00
|
|
|
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder sb = new StringBuilder();
|
2010-04-30 09:51:19 -04:00
|
|
|
int ch;
|
|
|
|
boolean escape = false;
|
2011-02-06 17:09:48 -05:00
|
|
|
while ((ch = mIn.read()) != -1) {
|
|
|
|
if (!escape && (ch == '\\')) {
|
2010-04-30 09:51:19 -04:00
|
|
|
// Found the escape character
|
|
|
|
escape = true;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (!escape && (ch == '"')) {
|
2010-04-30 09:51:19 -04:00
|
|
|
return sb.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2010-04-30 09:51:19 -04:00
|
|
|
sb.append((char)ch);
|
|
|
|
escape = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new IOException("parseQuoted(): end of stream reached");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private String readStringUntil(char end) throws IOException {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder sb = new StringBuilder();
|
2008-11-01 17:32:06 -04:00
|
|
|
int ch;
|
2011-02-06 17:09:48 -05:00
|
|
|
while ((ch = mIn.read()) != -1) {
|
|
|
|
if (ch == end) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return sb.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
sb.append((char)ch);
|
|
|
|
}
|
|
|
|
}
|
2010-04-30 09:51:19 -04:00
|
|
|
throw new IOException("readStringUntil(): end of stream reached");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private int expect(char ch) throws IOException {
|
2008-11-01 17:32:06 -04:00
|
|
|
int d;
|
2011-02-06 17:09:48 -05:00
|
|
|
if ((d = mIn.read()) != ch) {
|
2008-11-01 17:32:06 -04:00
|
|
|
throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)", (int)ch,
|
2009-11-24 19:40:29 -05:00
|
|
|
ch, d, (char)d));
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-01-10 22:40:43 -05:00
|
|
|
* Represents an IMAP list response and is also the base class for the
|
2008-11-01 17:32:06 -04:00
|
|
|
* ImapResponse.
|
|
|
|
*/
|
2011-10-06 00:55:23 -04:00
|
|
|
public static class ImapList extends ArrayList<Object> {
|
2011-01-31 18:45:14 -05:00
|
|
|
private static final long serialVersionUID = -4067248341419617583L;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapList getList(int index) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return (ImapList)get(index);
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Object getObject(int index) {
|
2009-11-24 19:40:29 -05:00
|
|
|
return get(index);
|
2009-04-21 00:22:02 -04:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getString(int index) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return (String)get(index);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getNumber(int index) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return Integer.parseInt(getString(index));
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Date getDate(int index) throws MessagingException {
|
2011-04-03 00:48:15 -04:00
|
|
|
return getDate(getString(index));
|
|
|
|
}
|
|
|
|
|
|
|
|
public Date getKeyedDate(Object key) throws MessagingException {
|
|
|
|
return getDate(getKeyedString(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
private Date getDate(String value) throws MessagingException {
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2011-04-03 00:48:15 -04:00
|
|
|
if (value == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return parseDate(value);
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (ParseException pe) {
|
2011-04-12 08:16:22 -04:00
|
|
|
throw new MessagingException("Unable to parse IMAP datetime '" + value + "' ", pe);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 00:48:15 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public Object getKeyedValue(Object key) {
|
2012-04-03 01:35:19 -04:00
|
|
|
for (int i = 0, count = size() - 1; i < count; i++) {
|
2011-02-06 17:09:48 -05:00
|
|
|
if (equalsIgnoreCase(get(i), key)) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return get(i + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public ImapList getKeyedList(Object key) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return (ImapList)getKeyedValue(key);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getKeyedString(Object key) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return (String)getKeyedValue(key);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getKeyedNumber(Object key) {
|
2008-11-01 17:32:06 -04:00
|
|
|
return Integer.parseInt(getKeyedString(key));
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean containsKey(Object key) {
|
|
|
|
if (key == null) {
|
2010-02-10 08:52:25 -05:00
|
|
|
return false;
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2012-04-03 01:35:19 -04:00
|
|
|
for (int i = 0, count = size() - 1; i < count; i++) {
|
2011-02-06 17:09:48 -05:00
|
|
|
if (equalsIgnoreCase(key, get(i))) {
|
2010-02-10 08:52:25 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getKeyIndex(Object key) {
|
2012-04-03 01:35:19 -04:00
|
|
|
for (int i = 0, count = size() - 1; i < count; i++) {
|
2011-02-06 17:09:48 -05:00
|
|
|
if (equalsIgnoreCase(key, get(i))) {
|
2010-02-10 08:52:25 -05:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
2010-04-29 00:59:14 -04:00
|
|
|
|
2010-02-10 08:52:25 -05:00
|
|
|
throw new IllegalArgumentException("getKeyIndex() only works for keys that are in the collection.");
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
private Date parseDate(String value) throws ParseException {
|
2010-03-25 11:21:05 -04:00
|
|
|
//TODO: clean this up a bit
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
synchronized (mDateTimeFormat) {
|
2009-10-28 08:45:22 -04:00
|
|
|
return mDateTimeFormat.parse(value);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
|
|
|
try {
|
|
|
|
synchronized (badDateTimeFormat) {
|
2010-03-25 11:21:05 -04:00
|
|
|
return badDateTimeFormat.parse(value);
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e2) {
|
2011-04-12 08:32:42 -04:00
|
|
|
try {
|
2011-04-12 08:16:22 -04:00
|
|
|
synchronized (badDateTimeFormat2) {
|
2011-04-12 08:32:42 -04:00
|
|
|
return badDateTimeFormat2.parse(value);
|
|
|
|
}
|
|
|
|
} catch (Exception e3) {
|
|
|
|
synchronized (badDateTimeFormat3) {
|
|
|
|
return badDateTimeFormat3.parse(value);
|
|
|
|
}
|
2010-03-25 11:21:05 -04:00
|
|
|
}
|
2009-10-28 08:45:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-01-12 01:56:52 -05:00
|
|
|
* Represents a single response from the IMAP server.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* Tagged responses will have a non-null tag. Untagged responses will have a null tag. The
|
|
|
|
* object will contain all of the available tokens at the time the response is received.
|
|
|
|
* </p>
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class ImapResponse extends ImapList {
|
2011-01-31 18:45:14 -05:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private static final long serialVersionUID = 6886458551615975669L;
|
2010-05-19 09:31:48 -04:00
|
|
|
private IImapResponseCallback mCallback;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
boolean mCommandContinuationRequested;
|
|
|
|
String mTag;
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public String getAlertText() {
|
|
|
|
if (size() > 1 && equalsIgnoreCase("[ALERT]", get(1))) {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder sb = new StringBuilder();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 2, count = size(); i < count; i++) {
|
2008-11-01 17:32:06 -04:00
|
|
|
sb.append(get(i).toString());
|
|
|
|
sb.append(' ');
|
|
|
|
}
|
|
|
|
return sb.toString();
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2008-11-01 17:32:06 -04:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String toString() {
|
2010-04-14 23:17:25 -04:00
|
|
|
return "#" + (mCommandContinuationRequested ? "+" : mTag) + "# " + super.toString();
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
2011-01-10 22:40:43 -05:00
|
|
|
|
2011-08-01 16:49:12 -04:00
|
|
|
public boolean isStatusResponse(String symbol) {
|
2011-07-16 16:11:14 -04:00
|
|
|
return symbol.equalsIgnoreCase("OK") ||
|
|
|
|
symbol.equalsIgnoreCase("NO") ||
|
|
|
|
symbol.equalsIgnoreCase("BAD") ||
|
|
|
|
symbol.equalsIgnoreCase("PREAUTH") ||
|
|
|
|
symbol.equalsIgnoreCase("BYE");
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public static boolean equalsIgnoreCase(Object o1, Object o2) {
|
|
|
|
if (o1 != null && o2 != null && o1 instanceof String && o2 instanceof String) {
|
2010-05-09 11:27:41 -04:00
|
|
|
String s1 = (String)o1;
|
|
|
|
String s2 = (String)o2;
|
|
|
|
return s1.equalsIgnoreCase(s2);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (o1 != null) {
|
2010-05-09 11:27:41 -04:00
|
|
|
return o1.equals(o2);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (o2 != null) {
|
2010-05-09 11:27:41 -04:00
|
|
|
return o2.equals(o1);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2011-01-18 20:30:13 -05:00
|
|
|
// Both o1 and o2 are null
|
|
|
|
return true;
|
2010-05-09 11:27:41 -04:00
|
|
|
}
|
|
|
|
}
|
2010-05-30 00:17:00 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public interface IImapResponseCallback {
|
2010-05-19 09:31:48 -04:00
|
|
|
/**
|
|
|
|
* 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)
|
2010-05-30 00:17:00 -04:00
|
|
|
throws IOException, Exception;
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|