2014-12-18 03:33:09 -05:00
|
|
|
package com.fsck.k9.mail.store.imap;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2011-08-01 16:49:12 -04:00
|
|
|
import android.text.TextUtils;
|
2014-12-18 05:24:43 -05:00
|
|
|
import android.util.Log;
|
2014-12-18 03:33:09 -05:00
|
|
|
|
2014-12-18 05:24:43 -05:00
|
|
|
import com.fsck.k9.mail.K9MailLib;
|
|
|
|
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;
|
2014-12-18 05:24:43 -05:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Set;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2014-12-18 05:24:43 -05:00
|
|
|
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;
|
2009-10-28 08:45:22 -04:00
|
|
|
|
2014-12-18 05:24:43 -05:00
|
|
|
class ImapResponseParser {
|
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.
|
|
|
|
*/
|
2014-12-18 05:24:43 -05:00
|
|
|
public ImapResponse readResponse(ImapResponseCallback callback) throws IOException {
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
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();
|
2014-12-18 05:24:43 -05:00
|
|
|
mResponse = new ImapResponse(callback, false, null);
|
2014-12-18 03:33:09 -05:00
|
|
|
readTokens(mResponse);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else if (ch == '+') {
|
2014-12-18 05:24:43 -05:00
|
|
|
mResponse = new ImapResponse(callback, parseCommandContinuationRequest(), null);
|
2014-12-18 03:33:09 -05:00
|
|
|
parseResponseText(mResponse);
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2014-12-18 05:24:43 -05:00
|
|
|
mResponse = new ImapResponse(callback, false, parseTaggedResponse());
|
2014-12-18 03:33:09 -05:00
|
|
|
readTokens(mResponse);
|
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 (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
|
|
|
|
2014-12-18 03:33:09 -05:00
|
|
|
return mResponse;
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2010-05-19 09:31:48 -04:00
|
|
|
mResponse = null;
|
|
|
|
mException = null;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-18 05:24:43 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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 {
|
2014-12-18 05:24:43 -05:00
|
|
|
return readStringUntil(' ');
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
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 "";
|
|
|
|
}
|
|
|
|
|
2014-12-18 03:33:09 -05:00
|
|
|
if (mResponse.getCallback() != 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 {
|
2014-12-18 03:33:09 -05:00
|
|
|
result = mResponse.getCallback().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;
|
2014-12-16 06:51:52 -05:00
|
|
|
//Log.e(LOG_TAG, "parseLiteral(): Exception in callback method", e);
|
2010-05-19 09:31:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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-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
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|