summaryrefslogtreecommitdiff
path: root/java/com/android/voicemailomtp/mail/store/imap
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/voicemailomtp/mail/store/imap')
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java335
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java144
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapElement.java120
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapList.java235
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java76
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java158
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java432
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java62
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapString.java192
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java123
-rw-r--r--java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java125
11 files changed, 0 insertions, 2002 deletions
diff --git a/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java b/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java
deleted file mode 100644
index b78f55293..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/DigestMd5Utils.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import android.annotation.TargetApi;
-import android.os.Build.VERSION_CODES;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.util.ArrayMap;
-import android.util.Base64;
-import com.android.voicemailomtp.VvmLog;
-import com.android.voicemailomtp.mail.MailTransport;
-import com.android.voicemailomtp.mail.MessagingException;
-import com.android.voicemailomtp.mail.store.ImapStore;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Map;
-
-@SuppressWarnings("AndroidApiChecker") // Map.getOrDefault() is java8
-@TargetApi(VERSION_CODES.CUR_DEVELOPMENT)
-public class DigestMd5Utils {
-
- private static final String TAG = "DigestMd5Utils";
-
- private static final String DIGEST_CHARSET = "CHARSET";
- private static final String DIGEST_USERNAME = "username";
- private static final String DIGEST_REALM = "realm";
- private static final String DIGEST_NONCE = "nonce";
- private static final String DIGEST_NC = "nc";
- private static final String DIGEST_CNONCE = "cnonce";
- private static final String DIGEST_URI = "digest-uri";
- private static final String DIGEST_RESPONSE = "response";
- private static final String DIGEST_QOP = "qop";
-
- private static final String RESPONSE_AUTH_HEADER = "rspauth=";
- private static final String HEX_CHARS = "0123456789abcdef";
-
- /**
- * Represents the set of data we need to generate the DIGEST-MD5 response.
- */
- public static class Data {
-
- private static final String CHARSET = "utf-8";
-
- public String username;
- public String password;
- public String realm;
- public String nonce;
- public String nc;
- public String cnonce;
- public String digestUri;
- public String qop;
-
- @VisibleForTesting
- Data() {
- // Do nothing
- }
-
- public Data(ImapStore imapStore, MailTransport transport, Map<String, String> challenge) {
- username = imapStore.getUsername();
- password = imapStore.getPassword();
- realm = challenge.getOrDefault(DIGEST_REALM, "");
- nonce = challenge.get(DIGEST_NONCE);
- cnonce = createCnonce();
- nc = "00000001"; // Subsequent Authentication not supported, nounce count always 1.
- qop = "auth"; // Other config not supported
- digestUri = "imap/" + transport.getHost();
- }
-
- private static String createCnonce() {
- SecureRandom generator = new SecureRandom();
-
- // At least 64 bits of entropy is required
- byte[] rawBytes = new byte[8];
- generator.nextBytes(rawBytes);
-
- return Base64.encodeToString(rawBytes, Base64.NO_WRAP);
- }
-
- /**
- * Verify the response-auth returned by the server is correct.
- */
- public void verifyResponseAuth(String response)
- throws MessagingException {
- if (!response.startsWith(RESPONSE_AUTH_HEADER)) {
- throw new MessagingException("response-auth expected");
- }
- if (!response.substring(RESPONSE_AUTH_HEADER.length())
- .equals(DigestMd5Utils.getResponse(this, true))) {
- throw new MessagingException("invalid response-auth return from the server.");
- }
- }
-
- public String createResponse() {
- String response = getResponse(this, false);
- ResponseBuilder builder = new ResponseBuilder();
- builder
- .append(DIGEST_CHARSET, CHARSET)
- .appendQuoted(DIGEST_USERNAME, username)
- .appendQuoted(DIGEST_REALM, realm)
- .appendQuoted(DIGEST_NONCE, nonce)
- .append(DIGEST_NC, nc)
- .appendQuoted(DIGEST_CNONCE, cnonce)
- .appendQuoted(DIGEST_URI, digestUri)
- .append(DIGEST_RESPONSE, response)
- .append(DIGEST_QOP, qop);
- return builder.toString();
- }
-
- private static class ResponseBuilder {
-
- private StringBuilder mBuilder = new StringBuilder();
-
- public ResponseBuilder appendQuoted(String key, String value) {
- if (mBuilder.length() != 0) {
- mBuilder.append(",");
- }
- mBuilder.append(key).append("=\"").append(value).append("\"");
- return this;
- }
-
- public ResponseBuilder append(String key, String value) {
- if (mBuilder.length() != 0) {
- mBuilder.append(",");
- }
- mBuilder.append(key).append("=").append(value);
- return this;
- }
-
- @Override
- public String toString() {
- return mBuilder.toString();
- }
- }
- }
-
- /*
- response-value =
- toHex( getKeyDigest ( toHex(getMd5(a1)),
- { nonce-value, ":" nc-value, ":",
- cnonce-value, ":", qop-value, ":", toHex(getMd5(a2)) }))
- * @param isResponseAuth is the response the one the server is returning us. response-auth has
- * different a2 format.
- */
- @VisibleForTesting
- static String getResponse(Data data, boolean isResponseAuth) {
- StringBuilder a1 = new StringBuilder();
- a1.append(new String(
- getMd5(data.username + ":" + data.realm + ":" + data.password),
- StandardCharsets.ISO_8859_1));
- a1.append(":").append(data.nonce).append(":").append(data.cnonce);
-
- StringBuilder a2 = new StringBuilder();
- if (!isResponseAuth) {
- a2.append("AUTHENTICATE");
- }
- a2.append(":").append(data.digestUri);
-
- return toHex(getKeyDigest(
- toHex(getMd5(a1.toString())),
- data.nonce + ":" + data.nc + ":" + data.cnonce + ":" + data.qop + ":" + toHex(
- getMd5(a2.toString()))
- ));
- }
-
- /**
- * Let getMd5(s) be the 16 octet MD5 hash [RFC 1321] of the octet string s.
- */
- private static byte[] getMd5(String s) {
- try {
- MessageDigest digester = MessageDigest.getInstance("MD5");
- digester.update(s.getBytes(StandardCharsets.ISO_8859_1));
- return digester.digest();
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError(e);
- }
- }
-
- /**
- * Let getKeyDigest(k, s) be getMd5({k, ":", s}), i.e., the 16 octet hash of the string k, a colon and the
- * string s.
- */
- private static byte[] getKeyDigest(String k, String s) {
- StringBuilder builder = new StringBuilder(k).append(":").append(s);
- return getMd5(builder.toString());
- }
-
- /**
- * Let toHex(n) be the representation of the 16 octet MD5 hash n as a string of 32 hex digits
- * (with alphabetic characters always in lower case, since MD5 is case sensitive).
- */
- private static String toHex(byte[] n) {
- StringBuilder result = new StringBuilder();
- for (byte b : n) {
- int unsignedByte = b & 0xFF;
- result.append(HEX_CHARS.charAt(unsignedByte / 16))
- .append(HEX_CHARS.charAt(unsignedByte % 16));
- }
- return result.toString();
- }
-
- public static Map<String, String> parseDigestMessage(String message) throws MessagingException {
- Map<String, String> result = new DigestMessageParser(message).parse();
- if (!result.containsKey(DIGEST_NONCE)) {
- throw new MessagingException("nonce missing from server DIGEST-MD5 challenge");
- }
- return result;
- }
-
- /**
- * Parse the key-value pair returned by the server.
- */
- private static class DigestMessageParser {
-
- private final String mMessage;
- private int mPosition = 0;
- private Map<String, String> mResult = new ArrayMap<>();
-
- public DigestMessageParser(String message) {
- mMessage = message;
- }
-
- @Nullable
- public Map<String, String> parse() {
- try {
- while (mPosition < mMessage.length()) {
- parsePair();
- if (mPosition != mMessage.length()) {
- expect(',');
- }
- }
- } catch (IndexOutOfBoundsException e) {
- VvmLog.e(TAG, e.toString());
- return null;
- }
- return mResult;
- }
-
- private void parsePair() {
- String key = parseKey();
- expect('=');
- String value = parseValue();
- mResult.put(key, value);
- }
-
- private void expect(char c) {
- if (pop() != c) {
- throw new IllegalStateException(
- "unexpected character " + mMessage.charAt(mPosition));
- }
- }
-
- private char pop() {
- char result = peek();
- mPosition++;
- return result;
- }
-
- private char peek() {
- return mMessage.charAt(mPosition);
- }
-
- private void goToNext(char c) {
- while (peek() != c) {
- mPosition++;
- }
- }
-
- private String parseKey() {
- int start = mPosition;
- goToNext('=');
- return mMessage.substring(start, mPosition);
- }
-
- private String parseValue() {
- if (peek() == '"') {
- return parseQuotedValue();
- } else {
- return parseUnquotedValue();
- }
- }
-
- private String parseQuotedValue() {
- expect('"');
- StringBuilder result = new StringBuilder();
- while (true) {
- char c = pop();
- if (c == '\\') {
- result.append(pop());
- } else if (c == '"') {
- break;
- } else {
- result.append(c);
- }
- }
- return result.toString();
- }
-
- private String parseUnquotedValue() {
- StringBuilder result = new StringBuilder();
- while (true) {
- char c = pop();
- if (c == '\\') {
- result.append(pop());
- } else if (c == ',') {
- mPosition--;
- break;
- } else {
- result.append(c);
- }
-
- if (mPosition == mMessage.length()) {
- break;
- }
- }
- return result.toString();
- }
- }
-}
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java b/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java
deleted file mode 100644
index d8e75752f..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapConstants.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.mail.store.ImapStore;
-
-import java.util.Locale;
-
-public final class ImapConstants {
- private ImapConstants() {}
-
- public static final String FETCH_FIELD_BODY_PEEK_BARE = "BODY.PEEK";
- public static final String FETCH_FIELD_BODY_PEEK = FETCH_FIELD_BODY_PEEK_BARE + "[]";
- public static final String FETCH_FIELD_BODY_PEEK_SANE = String.format(
- Locale.US, "BODY.PEEK[]<0.%d>", ImapStore.FETCH_BODY_SANE_SUGGESTED_SIZE);
- public static final String FETCH_FIELD_HEADERS =
- "BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc message-id)]";
-
- public static final String ALERT = "ALERT";
- public static final String APPEND = "APPEND";
- public static final String AUTHENTICATE = "AUTHENTICATE";
- public static final String BAD = "BAD";
- public static final String BADCHARSET = "BADCHARSET";
- public static final String BODY = "BODY";
- public static final String BODY_BRACKET_HEADER = "BODY[HEADER";
- public static final String BODYSTRUCTURE = "BODYSTRUCTURE";
- public static final String BYE = "BYE";
- public static final String CAPABILITY = "CAPABILITY";
- public static final String CHECK = "CHECK";
- public static final String CLOSE = "CLOSE";
- public static final String COPY = "COPY";
- public static final String COPYUID = "COPYUID";
- public static final String CREATE = "CREATE";
- public static final String DELETE = "DELETE";
- public static final String EXAMINE = "EXAMINE";
- public static final String EXISTS = "EXISTS";
- public static final String EXPUNGE = "EXPUNGE";
- public static final String FETCH = "FETCH";
- public static final String FLAG_ANSWERED = "\\ANSWERED";
- public static final String FLAG_DELETED = "\\DELETED";
- public static final String FLAG_FLAGGED = "\\FLAGGED";
- public static final String FLAG_NO_SELECT = "\\NOSELECT";
- public static final String FLAG_SEEN = "\\SEEN";
- public static final String FLAGS = "FLAGS";
- public static final String FLAGS_SILENT = "FLAGS.SILENT";
- public static final String ID = "ID";
- public static final String INBOX = "INBOX";
- public static final String INTERNALDATE = "INTERNALDATE";
- public static final String LIST = "LIST";
- public static final String LOGIN = "LOGIN";
- public static final String LOGOUT = "LOGOUT";
- public static final String LSUB = "LSUB";
- public static final String NAMESPACE = "NAMESPACE";
- public static final String NO = "NO";
- public static final String NOOP = "NOOP";
- public static final String OK = "OK";
- public static final String PARSE = "PARSE";
- public static final String PERMANENTFLAGS = "PERMANENTFLAGS";
- public static final String PREAUTH = "PREAUTH";
- public static final String READ_ONLY = "READ-ONLY";
- public static final String READ_WRITE = "READ-WRITE";
- public static final String RENAME = "RENAME";
- public static final String RFC822_SIZE = "RFC822.SIZE";
- public static final String SEARCH = "SEARCH";
- public static final String SELECT = "SELECT";
- public static final String STARTTLS = "STARTTLS";
- public static final String STATUS = "STATUS";
- public static final String STORE = "STORE";
- public static final String SUBSCRIBE = "SUBSCRIBE";
- public static final String TEXT = "TEXT";
- public static final String TRYCREATE = "TRYCREATE";
- public static final String UID = "UID";
- public static final String UID_COPY = "UID COPY";
- public static final String UID_FETCH = "UID FETCH";
- public static final String UID_SEARCH = "UID SEARCH";
- public static final String UID_STORE = "UID STORE";
- public static final String UIDNEXT = "UIDNEXT";
- public static final String UIDPLUS = "UIDPLUS";
- public static final String UIDVALIDITY = "UIDVALIDITY";
- public static final String UNSEEN = "UNSEEN";
- public static final String UNSUBSCRIBE = "UNSUBSCRIBE";
- public static final String XOAUTH2 = "XOAUTH2";
- public static final String APPENDUID = "APPENDUID";
- public static final String NIL = "NIL";
-
- /**
- * NO responses
- */
- public static final String NO_COMMAND_NOT_ALLOWED = "command not allowed";
- public static final String NO_RESERVATION_FAILED = "reservation failed";
- public static final String NO_APPLICATION_ERROR = "application error";
- public static final String NO_INVALID_PARAMETER = "invalid parameter";
- public static final String NO_INVALID_COMMAND = "invalid command";
- public static final String NO_UNKNOWN_COMMAND = "unknown command";
- // AUTHENTICATE
- // The subscriber can not be located in the system.
- public static final String NO_UNKNOWN_USER = "unknown user";
- // The Client Type or Protocol Version is unknown.
- public static final String NO_UNKNOWN_CLIENT = "unknown client";
- // The password received from the client does not match the password defined in the subscriber's profile.
- public static final String NO_INVALID_PASSWORD = "invalid password";
- // The subscriber's mailbox has not yet been initialised via the TUI
- public static final String NO_MAILBOX_NOT_INITIALIZED = "mailbox not initialized";
- // The subscriber has not been provisioned for the VVM service.
- public static final String NO_SERVICE_IS_NOT_PROVISIONED =
- "service is not provisioned";
- // The subscriber is provisioned for the VVM service but the VVM service is currently not active
- public static final String NO_SERVICE_IS_NOT_ACTIVATED = "service is not activated";
- // The Voice Mail Blocked flag in the subscriber's profile is set to YES.
- public static final String NO_USER_IS_BLOCKED = "user is blocked";
-
- /**
- * extensions
- */
- public static final String GETQUOTA = "GETQUOTA";
- public static final String GETQUOTAROOT = "GETQUOTAROOT";
- public static final String QUOTAROOT = "QUOTAROOT";
- public static final String QUOTA = "QUOTA";
-
- /**
- * capabilities
- */
- public static final String CAPABILITY_AUTH_DIGEST_MD5 = "AUTH=DIGEST-MD5";
- public static final String CAPABILITY_STARTTLS = "STARTTLS";
-
- /**
- * authentication
- */
- public static final String AUTH_DIGEST_MD5 = "DIGEST-MD5";
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java b/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java
deleted file mode 100644
index 9f272e31c..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapElement.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-/**
- * Class representing "element"s in IMAP responses.
- *
- * <p>Class hierarchy:
- * <pre>
- * ImapElement
- * |
- * |-- ImapElement.NONE (for 'index out of range')
- * |
- * |-- ImapList (isList() == true)
- * | |
- * | |-- ImapList.EMPTY
- * | |
- * | --- ImapResponse
- * |
- * --- ImapString (isString() == true)
- * |
- * |-- ImapString.EMPTY
- * |
- * |-- ImapSimpleString
- * |
- * |-- ImapMemoryLiteral
- * |
- * --- ImapTempFileLiteral
- * </pre>
- */
-public abstract class ImapElement {
- /**
- * An element that is returned by {@link ImapList#getElementOrNone} to indicate an index
- * is out of range.
- */
- public static final ImapElement NONE = new ImapElement() {
- @Override public void destroy() {
- // Don't call super.destroy().
- // It's a shared object. We don't want the mDestroyed to be set on this.
- }
-
- @Override public boolean isList() {
- return false;
- }
-
- @Override public boolean isString() {
- return false;
- }
-
- @Override public String toString() {
- return "[NO ELEMENT]";
- }
-
- @Override
- public boolean equalsForTest(ImapElement that) {
- return super.equalsForTest(that);
- }
- };
-
- private boolean mDestroyed = false;
-
- public abstract boolean isList();
-
- public abstract boolean isString();
-
- protected boolean isDestroyed() {
- return mDestroyed;
- }
-
- /**
- * Clean up the resources used by the instance.
- * It's for removing a temp file used by {@link ImapTempFileLiteral}.
- */
- public void destroy() {
- mDestroyed = true;
- }
-
- /**
- * Throws {@link RuntimeException} if it's already destroyed.
- */
- protected final void checkNotDestroyed() {
- if (mDestroyed) {
- throw new RuntimeException("Already destroyed");
- }
- }
-
- /**
- * Return a string that represents this object; it's purely for the debug purpose. Don't
- * mistake it for {@link ImapString#getString}.
- *
- * Abstract to force subclasses to implement it.
- */
- @Override
- public abstract String toString();
-
- /**
- * The equals implementation that is intended to be used only for unit testing.
- * (Because it may be heavy and has a special sense of "equal" for testing.)
- */
- public boolean equalsForTest(ImapElement that) {
- if (that == null) {
- return false;
- }
- return this.getClass() == that.getClass(); // Has to be the same class.
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapList.java b/java/com/android/voicemailomtp/mail/store/imap/ImapList.java
deleted file mode 100644
index 970423cbd..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapList.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import java.util.ArrayList;
-
-/**
- * Class represents an IMAP list.
- */
-public class ImapList extends ImapElement {
- /**
- * {@link ImapList} representing an empty list.
- */
- public static final ImapList EMPTY = new ImapList() {
- @Override public void destroy() {
- // Don't call super.destroy().
- // It's a shared object. We don't want the mDestroyed to be set on this.
- }
-
- @Override void add(ImapElement e) {
- throw new RuntimeException();
- }
- };
-
- private ArrayList<ImapElement> mList = new ArrayList<ImapElement>();
-
- /* package */ void add(ImapElement e) {
- if (e == null) {
- throw new RuntimeException("Can't add null");
- }
- mList.add(e);
- }
-
- @Override
- public final boolean isString() {
- return false;
- }
-
- @Override
- public final boolean isList() {
- return true;
- }
-
- public final int size() {
- return mList.size();
- }
-
- public final boolean isEmpty() {
- return size() == 0;
- }
-
- /**
- * Return true if the element at {@code index} exists, is string, and equals to {@code s}.
- * (case insensitive)
- */
- public final boolean is(int index, String s) {
- return is(index, s, false);
- }
-
- /**
- * Same as {@link #is(int, String)}, but does the prefix match if {@code prefixMatch}.
- */
- public final boolean is(int index, String s, boolean prefixMatch) {
- if (!prefixMatch) {
- return getStringOrEmpty(index).is(s);
- } else {
- return getStringOrEmpty(index).startsWith(s);
- }
- }
-
- /**
- * Return the element at {@code index}.
- * If {@code index} is out of range, returns {@link ImapElement#NONE}.
- */
- public final ImapElement getElementOrNone(int index) {
- return (index >= mList.size()) ? ImapElement.NONE : mList.get(index);
- }
-
- /**
- * Return the element at {@code index} if it's a list.
- * If {@code index} is out of range or not a list, returns {@link ImapList#EMPTY}.
- */
- public final ImapList getListOrEmpty(int index) {
- ImapElement el = getElementOrNone(index);
- return el.isList() ? (ImapList) el : EMPTY;
- }
-
- /**
- * Return the element at {@code index} if it's a string.
- * If {@code index} is out of range or not a string, returns {@link ImapString#EMPTY}.
- */
- public final ImapString getStringOrEmpty(int index) {
- ImapElement el = getElementOrNone(index);
- return el.isString() ? (ImapString) el : ImapString.EMPTY;
- }
-
- /**
- * Return an element keyed by {@code key}. Return null if not found. {@code key} has to be
- * at an even index.
- */
- /* package */ final ImapElement getKeyedElementOrNull(String key, boolean prefixMatch) {
- for (int i = 1; i < size(); i += 2) {
- if (is(i-1, key, prefixMatch)) {
- return mList.get(i);
- }
- }
- return null;
- }
-
- /**
- * Return an {@link ImapList} keyed by {@code key}.
- * Return {@link ImapList#EMPTY} if not found.
- */
- public final ImapList getKeyedListOrEmpty(String key) {
- return getKeyedListOrEmpty(key, false);
- }
-
- /**
- * Return an {@link ImapList} keyed by {@code key}.
- * Return {@link ImapList#EMPTY} if not found.
- */
- public final ImapList getKeyedListOrEmpty(String key, boolean prefixMatch) {
- ImapElement e = getKeyedElementOrNull(key, prefixMatch);
- return (e != null) ? ((ImapList) e) : ImapList.EMPTY;
- }
-
- /**
- * Return an {@link ImapString} keyed by {@code key}.
- * Return {@link ImapString#EMPTY} if not found.
- */
- public final ImapString getKeyedStringOrEmpty(String key) {
- return getKeyedStringOrEmpty(key, false);
- }
-
- /**
- * Return an {@link ImapString} keyed by {@code key}.
- * Return {@link ImapString#EMPTY} if not found.
- */
- public final ImapString getKeyedStringOrEmpty(String key, boolean prefixMatch) {
- ImapElement e = getKeyedElementOrNull(key, prefixMatch);
- return (e != null) ? ((ImapString) e) : ImapString.EMPTY;
- }
-
- /**
- * Return true if it contains {@code s}.
- */
- public final boolean contains(String s) {
- for (int i = 0; i < size(); i++) {
- if (getStringOrEmpty(i).is(s)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void destroy() {
- if (mList != null) {
- for (ImapElement e : mList) {
- e.destroy();
- }
- mList = null;
- }
- super.destroy();
- }
-
- @Override
- public String toString() {
- return mList.toString();
- }
-
- /**
- * Return the text representations of the contents concatenated with ",".
- */
- public final String flatten() {
- return flatten(new StringBuilder()).toString();
- }
-
- /**
- * Returns text representations (i.e. getString()) of contents joined together with
- * "," as the separator.
- *
- * Only used for building the capability string passed to vendor policies.
- *
- * We can't use toString(), because it's for debugging (meaning the format may change any time),
- * and it won't expand literals.
- */
- private final StringBuilder flatten(StringBuilder sb) {
- sb.append('[');
- for (int i = 0; i < mList.size(); i++) {
- if (i > 0) {
- sb.append(',');
- }
- final ImapElement e = getElementOrNone(i);
- if (e.isList()) {
- getListOrEmpty(i).flatten(sb);
- } else if (e.isString()) {
- sb.append(getStringOrEmpty(i).getString());
- }
- }
- sb.append(']');
- return sb;
- }
-
- @Override
- public boolean equalsForTest(ImapElement that) {
- if (!super.equalsForTest(that)) {
- return false;
- }
- ImapList thatList = (ImapList) that;
- if (size() != thatList.size()) {
- return false;
- }
- for (int i = 0; i < size(); i++) {
- if (!mList.get(i).equalsForTest(thatList.getElementOrNone(i))) {
- return false;
- }
- }
- return true;
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java b/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java
deleted file mode 100644
index ad60ca7a4..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapMemoryLiteral.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.mail.FixedLengthInputStream;
-import com.android.voicemailomtp.VvmLog;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-
-/**
- * Subclass of {@link ImapString} used for literals backed by an in-memory byte array.
- */
-public class ImapMemoryLiteral extends ImapString {
- private final String TAG = "ImapMemoryLiteral";
- private byte[] mData;
-
- /* package */ ImapMemoryLiteral(FixedLengthInputStream in) throws IOException {
- // We could use ByteArrayOutputStream and IOUtils.copy, but it'd perform an unnecessary
- // copy....
- mData = new byte[in.getLength()];
- int pos = 0;
- while (pos < mData.length) {
- int read = in.read(mData, pos, mData.length - pos);
- if (read < 0) {
- break;
- }
- pos += read;
- }
- if (pos != mData.length) {
- VvmLog.w(TAG, "length mismatch");
- }
- }
-
- @Override
- public void destroy() {
- mData = null;
- super.destroy();
- }
-
- @Override
- public String getString() {
- try {
- return new String(mData, "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- VvmLog.e(TAG, "Unsupported encoding: ", e);
- }
- return null;
- }
-
- @Override
- public InputStream getAsStream() {
- return new ByteArrayInputStream(mData);
- }
-
- @Override
- public String toString() {
- return String.format("{%d byte literal(memory)}", mData.length);
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java b/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java
deleted file mode 100644
index 412f16d8a..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapResponse.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-/**
- * Class represents an IMAP response.
- */
-public class ImapResponse extends ImapList {
- private final String mTag;
- private final boolean mIsContinuationRequest;
-
- /* package */ ImapResponse(String tag, boolean isContinuationRequest) {
- mTag = tag;
- mIsContinuationRequest = isContinuationRequest;
- }
-
- /* package */ static boolean isStatusResponse(String symbol) {
- return ImapConstants.OK.equalsIgnoreCase(symbol)
- || ImapConstants.NO.equalsIgnoreCase(symbol)
- || ImapConstants.BAD.equalsIgnoreCase(symbol)
- || ImapConstants.PREAUTH.equalsIgnoreCase(symbol)
- || ImapConstants.BYE.equalsIgnoreCase(symbol);
- }
-
- /**
- * @return whether it's a tagged response.
- */
- public boolean isTagged() {
- return mTag != null;
- }
-
- /**
- * @return whether it's a continuation request.
- */
- public boolean isContinuationRequest() {
- return mIsContinuationRequest;
- }
-
- public boolean isStatusResponse() {
- return isStatusResponse(getStringOrEmpty(0).getString());
- }
-
- /**
- * @return whether it's an OK response.
- */
- public boolean isOk() {
- return is(0, ImapConstants.OK);
- }
-
- /**
- * @return whether it's an BAD response.
- */
- public boolean isBad() {
- return is(0, ImapConstants.BAD);
- }
-
- /**
- * @return whether it's an NO response.
- */
- public boolean isNo() {
- return is(0, ImapConstants.NO);
- }
-
- /**
- * @return whether it's an {@code responseType} data response. (i.e. not tagged).
- * @param index where {@code responseType} should appear. e.g. 1 for "FETCH"
- * @param responseType e.g. "FETCH"
- */
- public final boolean isDataResponse(int index, String responseType) {
- return !isTagged() && getStringOrEmpty(index).is(responseType);
- }
-
- /**
- * @return Response code (RFC 3501 7.1) if it's a status response.
- *
- * e.g. "ALERT" for "* OK [ALERT] System shutdown in 10 minutes"
- */
- public ImapString getResponseCodeOrEmpty() {
- if (!isStatusResponse()) {
- return ImapString.EMPTY; // Not a status response.
- }
- return getListOrEmpty(1).getStringOrEmpty(0);
- }
-
- /**
- * @return Alert message it it has ALERT response code.
- *
- * e.g. "System shutdown in 10 minutes" for "* OK [ALERT] System shutdown in 10 minutes"
- */
- public ImapString getAlertTextOrEmpty() {
- if (!getResponseCodeOrEmpty().is(ImapConstants.ALERT)) {
- return ImapString.EMPTY; // Not an ALERT
- }
- // The 3rd element contains all the rest of line.
- return getStringOrEmpty(2);
- }
-
- /**
- * @return Response text in a status response.
- */
- public ImapString getStatusResponseTextOrEmpty() {
- if (!isStatusResponse()) {
- return ImapString.EMPTY;
- }
- return getStringOrEmpty(getElementOrNone(1).isList() ? 2 : 1);
- }
-
- public ImapString getStatusOrEmpty() {
- if (!isStatusResponse()) {
- return ImapString.EMPTY;
- }
- return getStringOrEmpty(0);
- }
-
- @Override
- public String toString() {
- String tag = mTag;
- if (isContinuationRequest()) {
- tag = "+";
- }
- return "#" + tag + "# " + super.toString();
- }
-
- @Override
- public boolean equalsForTest(ImapElement that) {
- if (!super.equalsForTest(that)) {
- return false;
- }
- final ImapResponse thatResponse = (ImapResponse) that;
- if (mTag == null) {
- if (thatResponse.mTag != null) {
- return false;
- }
- } else {
- if (!mTag.equals(thatResponse.mTag)) {
- return false;
- }
- }
- if (mIsContinuationRequest != thatResponse.mIsContinuationRequest) {
- return false;
- }
- return true;
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java b/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java
deleted file mode 100644
index 692596f14..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapResponseParser.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.voicemailomtp.mail.FixedLengthInputStream;
-import com.android.voicemailomtp.mail.MessagingException;
-import com.android.voicemailomtp.mail.PeekableInputStream;
-import com.android.voicemailomtp.VvmLog;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-/**
- * IMAP response parser.
- */
-public class ImapResponseParser {
- private static final String TAG = "ImapResponseParser";
-
- /**
- * Literal larger than this will be stored in temp file.
- */
- public static final int LITERAL_KEEP_IN_MEMORY_THRESHOLD = 2 * 1024 * 1024;
-
- /** Input stream */
- private final PeekableInputStream mIn;
-
- private final int mLiteralKeepInMemoryThreshold;
-
- /** StringBuilder used by readUntil() */
- private final StringBuilder mBufferReadUntil = new StringBuilder();
-
- /** StringBuilder used by parseBareString() */
- private final StringBuilder mParseBareString = new StringBuilder();
-
- /**
- * We store all {@link ImapResponse} in it. {@link #destroyResponses()} must be called from
- * time to time to destroy them and clear it.
- */
- private final ArrayList<ImapResponse> mResponsesToDestroy = new ArrayList<ImapResponse>();
-
- /**
- * Exception thrown when we receive BYE. It derives from IOException, so it'll be treated
- * in the same way EOF does.
- */
- public static class ByeException extends IOException {
- public static final String MESSAGE = "Received BYE";
- public ByeException() {
- super(MESSAGE);
- }
- }
-
- /**
- * Public constructor for normal use.
- */
- public ImapResponseParser(InputStream in) {
- this(in, LITERAL_KEEP_IN_MEMORY_THRESHOLD);
- }
-
- /**
- * Constructor for testing to override the literal size threshold.
- */
- /* package for test */ ImapResponseParser(InputStream in, int literalKeepInMemoryThreshold) {
- mIn = new PeekableInputStream(in);
- mLiteralKeepInMemoryThreshold = literalKeepInMemoryThreshold;
- }
-
- private static IOException newEOSException() {
- final String message = "End of stream reached";
- VvmLog.d(TAG, message);
- return new IOException(message);
- }
-
- /**
- * Peek next one byte.
- *
- * Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
- * we shouldn't see EOF during parsing.
- */
- private int peek() throws IOException {
- final int next = mIn.peek();
- if (next == -1) {
- throw newEOSException();
- }
- return next;
- }
-
- /**
- * Read and return one byte from {@link #mIn}, and put it in {@link #mDiscourseLogger}.
- *
- * Throws IOException() if reaches EOF. As long as logical response lines end with \r\n,
- * we shouldn't see EOF during parsing.
- */
- private int readByte() throws IOException {
- int next = mIn.read();
- if (next == -1) {
- throw newEOSException();
- }
- return next;
- }
-
- /**
- * Destroy all the {@link ImapResponse}s stored in the internal storage and clear it.
- *
- * @see #readResponse()
- */
- public void destroyResponses() {
- for (ImapResponse r : mResponsesToDestroy) {
- r.destroy();
- }
- mResponsesToDestroy.clear();
- }
-
- /**
- * Reads the next response available on the stream and returns an
- * {@link ImapResponse} object that represents it.
- *
- * <p>When this method successfully returns an {@link ImapResponse}, the {@link ImapResponse}
- * is stored in the internal storage. When the {@link ImapResponse} is no longer used
- * {@link #destroyResponses} should be called to destroy all the responses in the array.
- *
- * @param byeExpected is a untagged BYE response expected? If not proper cleanup will be done
- * and {@link ByeException} will be thrown.
- * @return the parsed {@link ImapResponse} object.
- * @exception ByeException when detects BYE and <code>byeExpected</code> is false.
- */
- public ImapResponse readResponse(boolean byeExpected) throws IOException, MessagingException {
- ImapResponse response = null;
- try {
- response = parseResponse();
- } catch (RuntimeException e) {
- // Parser crash -- log network activities.
- onParseError(e);
- throw e;
- } catch (IOException e) {
- // Network error, or received an unexpected char.
- onParseError(e);
- throw e;
- }
-
- // Handle this outside of try-catch. We don't have to dump protocol log when getting BYE.
- if (!byeExpected && response.is(0, ImapConstants.BYE)) {
- Log.w(TAG, ByeException.MESSAGE);
- response.destroy();
- throw new ByeException();
- }
- mResponsesToDestroy.add(response);
- return response;
- }
-
- private void onParseError(Exception e) {
- // Read a few more bytes, so that the log will contain some more context, even if the parser
- // crashes in the middle of a response.
- // This also makes sure the byte in question will be logged, no matter where it crashes.
- // e.g. when parseAtom() peeks and finds at an unexpected char, it throws an exception
- // before actually reading it.
- // However, we don't want to read too much, because then it may get into an email message.
- try {
- for (int i = 0; i < 4; i++) {
- int b = readByte();
- if (b == -1 || b == '\n') {
- break;
- }
- }
- } catch (IOException ignore) {
- }
- VvmLog.w(TAG, "Exception detected: " + e.getMessage());
- }
-
- /**
- * Read next byte from stream and throw it away. If the byte is different from {@code expected}
- * throw {@link MessagingException}.
- */
- /* package for test */ void expect(char expected) throws IOException {
- final int next = readByte();
- if (expected != next) {
- throw new IOException(String.format("Expected %04x (%c) but got %04x (%c)",
- (int) expected, expected, next, (char) next));
- }
- }
-
- /**
- * Read bytes until we find {@code end}, and return all as string.
- * The {@code end} will be read (rather than peeked) and won't be included in the result.
- */
- /* package for test */ String readUntil(char end) throws IOException {
- mBufferReadUntil.setLength(0);
- for (;;) {
- final int ch = readByte();
- if (ch != end) {
- mBufferReadUntil.append((char) ch);
- } else {
- return mBufferReadUntil.toString();
- }
- }
- }
-
- /**
- * Read all bytes until \r\n.
- */
- /* package */ String readUntilEol() throws IOException {
- String ret = readUntil('\r');
- expect('\n'); // TODO Should this really be error?
- return ret;
- }
-
- /**
- * Parse and return the response line.
- */
- private ImapResponse parseResponse() throws IOException, MessagingException {
- // We need to destroy the response if we get an exception.
- // So, we first store the response that's being built in responseToDestroy, until it's
- // completely built, at which point we copy it into responseToReturn and null out
- // responseToDestroyt.
- // If responseToDestroy is not null in finally, we destroy it because that means
- // we got an exception somewhere.
- ImapResponse responseToDestroy = null;
- final ImapResponse responseToReturn;
-
- try {
- final int ch = peek();
- if (ch == '+') { // Continuation request
- readByte(); // skip +
- expect(' ');
- responseToDestroy = new ImapResponse(null, true);
-
- // If it's continuation request, we don't really care what's in it.
- responseToDestroy.add(new ImapSimpleString(readUntilEol()));
-
- // Response has successfully been built. Let's return it.
- responseToReturn = responseToDestroy;
- responseToDestroy = null;
- } else {
- // Status response or response data
- final String tag;
- if (ch == '*') {
- tag = null;
- readByte(); // skip *
- expect(' ');
- } else {
- tag = readUntil(' ');
- }
- responseToDestroy = new ImapResponse(tag, false);
-
- final ImapString firstString = parseBareString();
- responseToDestroy.add(firstString);
-
- // parseBareString won't eat a space after the string, so we need to skip it,
- // if exists.
- // If the next char is not ' ', it should be EOL.
- if (peek() == ' ') {
- readByte(); // skip ' '
-
- if (responseToDestroy.isStatusResponse()) { // It's a status response
-
- // Is there a response code?
- final int next = peek();
- if (next == '[') {
- responseToDestroy.add(parseList('[', ']'));
- if (peek() == ' ') { // Skip following space
- readByte();
- }
- }
-
- String rest = readUntilEol();
- if (!TextUtils.isEmpty(rest)) {
- // The rest is free-form text.
- responseToDestroy.add(new ImapSimpleString(rest));
- }
- } else { // It's a response data.
- parseElements(responseToDestroy, '\0');
- }
- } else {
- expect('\r');
- expect('\n');
- }
-
- // Response has successfully been built. Let's return it.
- responseToReturn = responseToDestroy;
- responseToDestroy = null;
- }
- } finally {
- if (responseToDestroy != null) {
- // We get an exception.
- responseToDestroy.destroy();
- }
- }
-
- return responseToReturn;
- }
-
- private ImapElement parseElement() throws IOException, MessagingException {
- final int next = peek();
- switch (next) {
- case '(':
- return parseList('(', ')');
- case '[':
- return parseList('[', ']');
- case '"':
- readByte(); // Skip "
- return new ImapSimpleString(readUntil('"'));
- case '{':
- return parseLiteral();
- case '\r': // CR
- readByte(); // Consume \r
- expect('\n'); // Should be followed by LF.
- return null;
- case '\n': // LF // There shouldn't be a bare LF, but just in case.
- readByte(); // Consume \n
- return null;
- default:
- return parseBareString();
- }
- }
-
- /**
- * Parses an atom.
- *
- * Special case: If an atom contains '[', everything until the next ']' will be considered
- * a part of the atom.
- * (e.g. "BODY[HEADER.FIELDS ("DATE" ...)]" will become a single ImapString)
- *
- * If the value is "NIL", returns an empty string.
- */
- private ImapString parseBareString() throws IOException, MessagingException {
- mParseBareString.setLength(0);
- for (;;) {
- final int ch = peek();
-
- // TODO Can we clean this up? (This condition is from the old parser.)
- if (ch == '(' || ch == ')' || ch == '{' || ch == ' ' ||
- // ']' is not part of atom (it's in resp-specials)
- 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 == '"' || (0x00 <= ch && ch <= 0x1f) || ch == 0x7f) {
- if (mParseBareString.length() == 0) {
- throw new MessagingException("Expected string, none found.");
- }
- String s = mParseBareString.toString();
-
- // NIL will be always converted into the empty string.
- if (ImapConstants.NIL.equalsIgnoreCase(s)) {
- return ImapString.EMPTY;
- }
- return new ImapSimpleString(s);
- } else if (ch == '[') {
- // Eat all until next ']'
- mParseBareString.append((char) readByte());
- mParseBareString.append(readUntil(']'));
- mParseBareString.append(']'); // readUntil won't include the end char.
- } else {
- mParseBareString.append((char) readByte());
- }
- }
- }
-
- private void parseElements(ImapList list, char end)
- throws IOException, MessagingException {
- for (;;) {
- for (;;) {
- final int next = peek();
- if (next == end) {
- return;
- }
- if (next != ' ') {
- break;
- }
- // Skip space
- readByte();
- }
- final ImapElement el = parseElement();
- if (el == null) { // EOL
- return;
- }
- list.add(el);
- }
- }
-
- private ImapList parseList(char opening, char closing)
- throws IOException, MessagingException {
- expect(opening);
- final ImapList list = new ImapList();
- parseElements(list, closing);
- expect(closing);
- return list;
- }
-
- private ImapString parseLiteral() throws IOException, MessagingException {
- expect('{');
- final int size;
- try {
- size = Integer.parseInt(readUntil('}'));
- } catch (NumberFormatException nfe) {
- throw new MessagingException("Invalid length in literal");
- }
- if (size < 0) {
- throw new MessagingException("Invalid negative length in literal");
- }
- expect('\r');
- expect('\n');
- FixedLengthInputStream in = new FixedLengthInputStream(mIn, size);
- if (size > mLiteralKeepInMemoryThreshold) {
- return new ImapTempFileLiteral(in);
- } else {
- return new ImapMemoryLiteral(in);
- }
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java b/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java
deleted file mode 100644
index 22d8141a0..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapSimpleString.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.VvmLog;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-
-/**
- * Subclass of {@link ImapString} used for non literals.
- */
-public class ImapSimpleString extends ImapString {
- private final String TAG = "ImapSimpleString";
- private String mString;
-
- /* package */ ImapSimpleString(String string) {
- mString = (string != null) ? string : "";
- }
-
- @Override
- public void destroy() {
- mString = null;
- super.destroy();
- }
-
- @Override
- public String getString() {
- return mString;
- }
-
- @Override
- public InputStream getAsStream() {
- try {
- return new ByteArrayInputStream(mString.getBytes("US-ASCII"));
- } catch (UnsupportedEncodingException e) {
- VvmLog.e(TAG, "Unsupported encoding: ", e);
- }
- return null;
- }
-
- @Override
- public String toString() {
- // Purposefully not return just mString, in order to prevent using it instead of getString.
- return "\"" + mString + "\"";
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapString.java b/java/com/android/voicemailomtp/mail/store/imap/ImapString.java
deleted file mode 100644
index 83efb6479..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapString.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.VvmLog;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-/**
- * Class represents an IMAP "element" that is not a list.
- *
- * An atom, quoted string, literal, are all represented by this. Values like OK, STATUS are too.
- * Also, this class class may contain more arbitrary value like "BODY[HEADER.FIELDS ("DATE")]".
- * See {@link ImapResponseParser}.
- */
-public abstract class ImapString extends ImapElement {
- private static final byte[] EMPTY_BYTES = new byte[0];
-
- public static final ImapString EMPTY = new ImapString() {
- @Override public void destroy() {
- // Don't call super.destroy().
- // It's a shared object. We don't want the mDestroyed to be set on this.
- }
-
- @Override public String getString() {
- return "";
- }
-
- @Override public InputStream getAsStream() {
- return new ByteArrayInputStream(EMPTY_BYTES);
- }
-
- @Override public String toString() {
- return "";
- }
- };
-
- // This is used only for parsing IMAP's FETCH ENVELOPE command, in which
- // en_US-like date format is used like "01-Jan-2009 11:20:39 -0800", so this should be
- // handled by Locale.US
- private final static SimpleDateFormat DATE_TIME_FORMAT =
- new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
-
- private boolean mIsInteger;
- private int mParsedInteger;
- private Date mParsedDate;
-
- @Override
- public final boolean isList() {
- return false;
- }
-
- @Override
- public final boolean isString() {
- return true;
- }
-
- /**
- * @return true if and only if the length of the string is larger than 0.
- *
- * Note: IMAP NIL is considered an empty string. See {@link ImapResponseParser
- * #parseBareString}.
- * On the other hand, a quoted/literal string with value NIL (i.e. "NIL" and {3}\r\nNIL) is
- * treated literally.
- */
- public final boolean isEmpty() {
- return getString().length() == 0;
- }
-
- public abstract String getString();
-
- public abstract InputStream getAsStream();
-
- /**
- * @return whether it can be parsed as a number.
- */
- public final boolean isNumber() {
- if (mIsInteger) {
- return true;
- }
- try {
- mParsedInteger = Integer.parseInt(getString());
- mIsInteger = true;
- return true;
- } catch (NumberFormatException e) {
- return false;
- }
- }
-
- /**
- * @return value parsed as a number, or 0 if the string is not a number.
- */
- public final int getNumberOrZero() {
- return getNumber(0);
- }
-
- /**
- * @return value parsed as a number, or {@code defaultValue} if the string is not a number.
- */
- public final int getNumber(int defaultValue) {
- if (!isNumber()) {
- return defaultValue;
- }
- return mParsedInteger;
- }
-
- /**
- * @return whether it can be parsed as a date using {@link #DATE_TIME_FORMAT}.
- */
- public final boolean isDate() {
- if (mParsedDate != null) {
- return true;
- }
- if (isEmpty()) {
- return false;
- }
- try {
- mParsedDate = DATE_TIME_FORMAT.parse(getString());
- return true;
- } catch (ParseException e) {
- VvmLog.w("ImapString", getString() + " can't be parsed as a date.");
- return false;
- }
- }
-
- /**
- * @return value it can be parsed as a {@link Date}, or null otherwise.
- */
- public final Date getDateOrNull() {
- if (!isDate()) {
- return null;
- }
- return mParsedDate;
- }
-
- /**
- * @return whether the value case-insensitively equals to {@code s}.
- */
- public final boolean is(String s) {
- if (s == null) {
- return false;
- }
- return getString().equalsIgnoreCase(s);
- }
-
-
- /**
- * @return whether the value case-insensitively starts with {@code s}.
- */
- public final boolean startsWith(String prefix) {
- if (prefix == null) {
- return false;
- }
- final String me = this.getString();
- if (me.length() < prefix.length()) {
- return false;
- }
- return me.substring(0, prefix.length()).equalsIgnoreCase(prefix);
- }
-
- // To force subclasses to implement it.
- @Override
- public abstract String toString();
-
- @Override
- public final boolean equalsForTest(ImapElement that) {
- if (!super.equalsForTest(that)) {
- return false;
- }
- ImapString thatString = (ImapString) that;
- return getString().equals(thatString.getString());
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java b/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java
deleted file mode 100644
index efe5c3848..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapTempFileLiteral.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.mail.FixedLengthInputStream;
-import com.android.voicemailomtp.mail.TempDirectory;
-import com.android.voicemailomtp.mail.utils.Utility;
-import com.android.voicemailomtp.mail.utils.LogUtils;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Subclass of {@link ImapString} used for literals backed by a temp file.
- */
-public class ImapTempFileLiteral extends ImapString {
- private final String TAG = "ImapTempFileLiteral";
-
- /* package for test */ final File mFile;
-
- /** Size is purely for toString() */
- private final int mSize;
-
- /* package */ ImapTempFileLiteral(FixedLengthInputStream stream) throws IOException {
- mSize = stream.getLength();
- mFile = File.createTempFile("imap", ".tmp", TempDirectory.getTempDirectory());
-
- // Unfortunately, we can't really use deleteOnExit(), because temp filenames are random
- // so it'd simply cause a memory leak.
- // deleteOnExit() simply adds filenames to a static list and the list will never shrink.
- // mFile.deleteOnExit();
- OutputStream out = new FileOutputStream(mFile);
- IOUtils.copy(stream, out);
- out.close();
- }
-
- /**
- * Make sure we delete the temp file.
- *
- * We should always be calling {@link ImapResponse#destroy()}, but it's here as a last resort.
- */
- @Override
- protected void finalize() throws Throwable {
- try {
- destroy();
- } finally {
- super.finalize();
- }
- }
-
- @Override
- public InputStream getAsStream() {
- checkNotDestroyed();
- try {
- return new FileInputStream(mFile);
- } catch (FileNotFoundException e) {
- // It's probably possible if we're low on storage and the system clears the cache dir.
- LogUtils.w(TAG, "ImapTempFileLiteral: Temp file not found");
-
- // Return 0 byte stream as a dummy...
- return new ByteArrayInputStream(new byte[0]);
- }
- }
-
- @Override
- public String getString() {
- checkNotDestroyed();
- try {
- byte[] bytes = IOUtils.toByteArray(getAsStream());
- // Prevent crash from OOM; we've seen this, but only rarely and not reproducibly
- if (bytes.length > ImapResponseParser.LITERAL_KEEP_IN_MEMORY_THRESHOLD) {
- throw new IOException();
- }
- return Utility.fromAscii(bytes);
- } catch (IOException e) {
- LogUtils.w(TAG, "ImapTempFileLiteral: Error while reading temp file", e);
- return "";
- }
- }
-
- @Override
- public void destroy() {
- try {
- if (!isDestroyed() && mFile.exists()) {
- mFile.delete();
- }
- } catch (RuntimeException re) {
- // Just log and ignore.
- LogUtils.w(TAG, "Failed to remove temp file: " + re.getMessage());
- }
- super.destroy();
- }
-
- @Override
- public String toString() {
- return String.format("{%d byte literal(file)}", mSize);
- }
-
- public boolean tempFileExistsForTest() {
- return mFile.exists();
- }
-} \ No newline at end of file
diff --git a/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java b/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java
deleted file mode 100644
index b045eb32f..000000000
--- a/java/com/android/voicemailomtp/mail/store/imap/ImapUtility.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.voicemailomtp.mail.store.imap;
-
-import com.android.voicemailomtp.mail.utils.LogUtils;
-
-import java.util.ArrayList;
-
-/**
- * Utility methods for use with IMAP.
- */
-public class ImapUtility {
- public static final String TAG = "ImapUtility";
- /**
- * Apply quoting rules per IMAP RFC,
- * quoted = DQUOTE *QUOTED-CHAR DQUOTE
- * QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
- * quoted-specials = DQUOTE / "\"
- *
- * This is used primarily for IMAP login, but might be useful elsewhere.
- *
- * NOTE: Not very efficient - you may wish to preflight this, or perhaps it should check
- * for trouble chars before calling the replace functions.
- *
- * @param s The string to be quoted.
- * @return A copy of the string, having undergone quoting as described above
- */
- public static String imapQuoted(String s) {
-
- // First, quote any backslashes by replacing \ with \\
- // regex Pattern: \\ (Java string const = \\\\)
- // Substitute: \\\\ (Java string const = \\\\\\\\)
- String result = s.replaceAll("\\\\", "\\\\\\\\");
-
- // Then, quote any double-quotes by replacing " with \"
- // regex Pattern: " (Java string const = \")
- // Substitute: \\" (Java string const = \\\\\")
- result = result.replaceAll("\"", "\\\\\"");
-
- // return string with quotes around it
- return "\"" + result + "\"";
- }
-
- /**
- * Gets all of the values in a sequence set per RFC 3501. Any ranges are expanded into a
- * list of individual numbers. If the set is invalid, an empty array is returned.
- * <pre>
- * sequence-number = nz-number / "*"
- * sequence-range = sequence-number ":" sequence-number
- * sequence-set = (sequence-number / sequence-range) *("," sequence-set)
- * </pre>
- */
- public static String[] getImapSequenceValues(String set) {
- ArrayList<String> list = new ArrayList<String>();
- if (set != null) {
- String[] setItems = set.split(",");
- for (String item : setItems) {
- if (item.indexOf(':') == -1) {
- // simple item
- try {
- Integer.parseInt(item); // Don't need the value; just ensure it's valid
- list.add(item);
- } catch (NumberFormatException e) {
- LogUtils.d(TAG, "Invalid UID value", e);
- }
- } else {
- // range
- for (String rangeItem : getImapRangeValues(item)) {
- list.add(rangeItem);
- }
- }
- }
- }
- String[] stringList = new String[list.size()];
- return list.toArray(stringList);
- }
-
- /**
- * Expand the given number range into a list of individual numbers. If the range is not valid,
- * an empty array is returned.
- * <pre>
- * sequence-number = nz-number / "*"
- * sequence-range = sequence-number ":" sequence-number
- * sequence-set = (sequence-number / sequence-range) *("," sequence-set)
- * </pre>
- */
- public static String[] getImapRangeValues(String range) {
- ArrayList<String> list = new ArrayList<String>();
- try {
- if (range != null) {
- int colonPos = range.indexOf(':');
- if (colonPos > 0) {
- int first = Integer.parseInt(range.substring(0, colonPos));
- int second = Integer.parseInt(range.substring(colonPos + 1));
- if (first < second) {
- for (int i = first; i <= second; i++) {
- list.add(Integer.toString(i));
- }
- } else {
- for (int i = first; i >= second; i--) {
- list.add(Integer.toString(i));
- }
- }
- }
- }
- } catch (NumberFormatException e) {
- LogUtils.d(TAG, "Invalid range value", e);
- }
- String[] stringList = new String[list.size()];
- return list.toArray(stringList);
- }
-} \ No newline at end of file