From d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9 Mon Sep 17 00:00:00 2001 From: Eric Erfanian Date: Wed, 15 Mar 2017 14:41:07 -0700 Subject: Update Dialer source from latest green build. * Refactor voicemail component * Add new enriched calling components Test: treehugger, manual aosp testing Change-Id: I521a0f86327d4b42e14d93927c7d613044ed5942 --- .../mail/internet/BinaryTempFileBody.java | 91 --- .../voicemailomtp/mail/internet/MimeBodyPart.java | 207 ------- .../voicemailomtp/mail/internet/MimeHeader.java | 161 ----- .../voicemailomtp/mail/internet/MimeMessage.java | 675 --------------------- .../voicemailomtp/mail/internet/MimeMultipart.java | 112 ---- .../voicemailomtp/mail/internet/MimeUtility.java | 416 ------------- .../voicemailomtp/mail/internet/TextBody.java | 63 -- 7 files changed, 1725 deletions(-) delete mode 100644 java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/MimeHeader.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/MimeMessage.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/MimeMultipart.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/MimeUtility.java delete mode 100644 java/com/android/voicemailomtp/mail/internet/TextBody.java (limited to 'java/com/android/voicemailomtp/mail/internet') diff --git a/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java b/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java deleted file mode 100644 index 52c43de16..000000000 --- a/java/com/android/voicemailomtp/mail/internet/BinaryTempFileBody.java +++ /dev/null @@ -1,91 +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.internet; - -import com.android.voicemailomtp.mail.Body; -import com.android.voicemailomtp.mail.MessagingException; -import com.android.voicemailomtp.mail.TempDirectory; - -import org.apache.commons.io.IOUtils; - -import android.util.Base64; -import android.util.Base64OutputStream; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A Body that is backed by a temp file. The Body exposes a getOutputStream method that allows - * the user to write to the temp file. After the write the body is available via getInputStream - * and writeTo one time. After writeTo is called, or the InputStream returned from - * getInputStream is closed the file is deleted and the Body should be considered disposed of. - */ -public class BinaryTempFileBody implements Body { - private File mFile; - - /** - * An alternate way to put data into a BinaryTempFileBody is to simply supply an already- - * created file. Note that this file will be deleted after it is read. - * @param filePath The file containing the data to be stored on disk temporarily - */ - public void setFile(String filePath) { - mFile = new File(filePath); - } - - public OutputStream getOutputStream() throws IOException { - mFile = File.createTempFile("body", null, TempDirectory.getTempDirectory()); - mFile.deleteOnExit(); - return new FileOutputStream(mFile); - } - - @Override - public InputStream getInputStream() throws MessagingException { - try { - return new BinaryTempFileBodyInputStream(new FileInputStream(mFile)); - } - catch (IOException ioe) { - throw new MessagingException("Unable to open body", ioe); - } - } - - @Override - public void writeTo(OutputStream out) throws IOException, MessagingException { - InputStream in = getInputStream(); - Base64OutputStream base64Out = new Base64OutputStream( - out, Base64.CRLF | Base64.NO_CLOSE); - IOUtils.copy(in, base64Out); - base64Out.close(); - mFile.delete(); - in.close(); - } - - class BinaryTempFileBodyInputStream extends FilterInputStream { - public BinaryTempFileBodyInputStream(InputStream in) { - super(in); - } - - @Override - public void close() throws IOException { - super.close(); - mFile.delete(); - } - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java b/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java deleted file mode 100644 index 8a9c45cf9..000000000 --- a/java/com/android/voicemailomtp/mail/internet/MimeBodyPart.java +++ /dev/null @@ -1,207 +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.internet; - -import com.android.voicemailomtp.mail.Body; -import com.android.voicemailomtp.mail.BodyPart; -import com.android.voicemailomtp.mail.MessagingException; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.regex.Pattern; - -/** - * TODO this is a close approximation of Message, need to update along with - * Message. - */ -public class MimeBodyPart extends BodyPart { - protected MimeHeader mHeader = new MimeHeader(); - protected MimeHeader mExtendedHeader; - protected Body mBody; - protected int mSize; - - // regex that matches content id surrounded by "<>" optionally. - private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^]+)>?$"); - // regex that matches end of line. - private static final Pattern END_OF_LINE = Pattern.compile("\r?\n"); - - public MimeBodyPart() throws MessagingException { - this(null); - } - - public MimeBodyPart(Body body) throws MessagingException { - this(body, null); - } - - public MimeBodyPart(Body body, String mimeType) throws MessagingException { - if (mimeType != null) { - setHeader(MimeHeader.HEADER_CONTENT_TYPE, mimeType); - } - setBody(body); - } - - protected String getFirstHeader(String name) throws MessagingException { - return mHeader.getFirstHeader(name); - } - - @Override - public void addHeader(String name, String value) throws MessagingException { - mHeader.addHeader(name, value); - } - - @Override - public void setHeader(String name, String value) throws MessagingException { - mHeader.setHeader(name, value); - } - - @Override - public String[] getHeader(String name) throws MessagingException { - return mHeader.getHeader(name); - } - - @Override - public void removeHeader(String name) throws MessagingException { - mHeader.removeHeader(name); - } - - @Override - public Body getBody() throws MessagingException { - return mBody; - } - - @Override - public void setBody(Body body) throws MessagingException { - this.mBody = body; - if (body instanceof com.android.voicemailomtp.mail.Multipart) { - com.android.voicemailomtp.mail.Multipart multipart = - ((com.android.voicemailomtp.mail.Multipart)body); - multipart.setParent(this); - setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType()); - } - else if (body instanceof TextBody) { - String contentType = String.format("%s;\n charset=utf-8", getMimeType()); - String name = MimeUtility.getHeaderParameter(getContentType(), "name"); - if (name != null) { - contentType += String.format(";\n name=\"%s\"", name); - } - setHeader(MimeHeader.HEADER_CONTENT_TYPE, contentType); - setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); - } - } - - @Override - public String getContentType() throws MessagingException { - String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE); - if (contentType == null) { - return "text/plain"; - } else { - return contentType; - } - } - - @Override - public String getDisposition() throws MessagingException { - String contentDisposition = getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION); - if (contentDisposition == null) { - return null; - } else { - return contentDisposition; - } - } - - @Override - public String getContentId() throws MessagingException { - String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID); - if (contentId == null) { - return null; - } else { - // remove optionally surrounding brackets. - return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1"); - } - } - - @Override - public String getMimeType() throws MessagingException { - return MimeUtility.getHeaderParameter(getContentType(), null); - } - - @Override - public boolean isMimeType(String mimeType) throws MessagingException { - return getMimeType().equals(mimeType); - } - - public void setSize(int size) { - this.mSize = size; - } - - @Override - public int getSize() throws MessagingException { - return mSize; - } - - /** - * Set extended header - * - * @param name Extended header name - * @param value header value - flattened by removing CR-NL if any - * remove header if value is null - * @throws MessagingException - */ - @Override - public void setExtendedHeader(String name, String value) throws MessagingException { - if (value == null) { - if (mExtendedHeader != null) { - mExtendedHeader.removeHeader(name); - } - return; - } - if (mExtendedHeader == null) { - mExtendedHeader = new MimeHeader(); - } - mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll("")); - } - - /** - * Get extended header - * - * @param name Extended header name - * @return header value - null if header does not exist - * @throws MessagingException - */ - @Override - public String getExtendedHeader(String name) throws MessagingException { - if (mExtendedHeader == null) { - return null; - } - return mExtendedHeader.getFirstHeader(name); - } - - /** - * Write the MimeMessage out in MIME format. - */ - @Override - public void writeTo(OutputStream out) throws IOException, MessagingException { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); - mHeader.writeTo(out); - writer.write("\r\n"); - writer.flush(); - if (mBody != null) { - mBody.writeTo(out); - } - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/MimeHeader.java b/java/com/android/voicemailomtp/mail/internet/MimeHeader.java deleted file mode 100644 index 4b0aea749..000000000 --- a/java/com/android/voicemailomtp/mail/internet/MimeHeader.java +++ /dev/null @@ -1,161 +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.internet; - -import com.android.voicemailomtp.mail.MessagingException; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.ArrayList; - -public class MimeHeader { - /** - * Application specific header that contains Store specific information about an attachment. - * In IMAP this contains the IMAP BODYSTRUCTURE part id so that the ImapStore can later - * retrieve the attachment at will from the server. - * The info is recorded from this header on LocalStore.appendMessage and is put back - * into the MIME data by LocalStore.fetch. - */ - public static final String HEADER_ANDROID_ATTACHMENT_STORE_DATA = "X-Android-Attachment-StoreData"; - - public static final String HEADER_CONTENT_TYPE = "Content-Type"; - public static final String HEADER_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; - public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; - public static final String HEADER_CONTENT_ID = "Content-ID"; - - /** - * Fields that should be omitted when writing the header using writeTo() - */ - private static final String[] WRITE_OMIT_FIELDS = { -// HEADER_ANDROID_ATTACHMENT_DOWNLOADED, -// HEADER_ANDROID_ATTACHMENT_ID, - HEADER_ANDROID_ATTACHMENT_STORE_DATA - }; - - protected final ArrayList mFields = new ArrayList(); - - public void clear() { - mFields.clear(); - } - - public String getFirstHeader(String name) throws MessagingException { - String[] header = getHeader(name); - if (header == null) { - return null; - } - return header[0]; - } - - public void addHeader(String name, String value) throws MessagingException { - mFields.add(new Field(name, value)); - } - - public void setHeader(String name, String value) throws MessagingException { - if (name == null || value == null) { - return; - } - removeHeader(name); - addHeader(name, value); - } - - public String[] getHeader(String name) throws MessagingException { - ArrayList values = new ArrayList(); - for (Field field : mFields) { - if (field.name.equalsIgnoreCase(name)) { - values.add(field.value); - } - } - if (values.size() == 0) { - return null; - } - return values.toArray(new String[] {}); - } - - public void removeHeader(String name) throws MessagingException { - ArrayList removeFields = new ArrayList(); - for (Field field : mFields) { - if (field.name.equalsIgnoreCase(name)) { - removeFields.add(field); - } - } - mFields.removeAll(removeFields); - } - - /** - * Write header into String - * - * @return CR-NL separated header string except the headers in writeOmitFields - * null if header is empty - */ - public String writeToString() { - if (mFields.size() == 0) { - return null; - } - StringBuilder builder = new StringBuilder(); - for (Field field : mFields) { - if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) { - builder.append(field.name + ": " + field.value + "\r\n"); - } - } - return builder.toString(); - } - - public void writeTo(OutputStream out) throws IOException, MessagingException { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); - for (Field field : mFields) { - if (!arrayContains(WRITE_OMIT_FIELDS, field.name)) { - writer.write(field.name + ": " + field.value + "\r\n"); - } - } - writer.flush(); - } - - private static class Field { - final String name; - final String value; - - public Field(String name, String value) { - this.name = name; - this.value = value; - } - - @Override - public String toString() { - return name + "=" + value; - } - } - - @Override - public String toString() { - return (mFields == null) ? null : mFields.toString(); - } - - public final static boolean arrayContains(Object[] a, Object o) { - int index = arrayIndex(a, o); - return (index >= 0); - } - - public final static int arrayIndex(Object[] a, Object o) { - for (int i = 0, count = a.length; i < count; i++) { - if (a[i].equals(o)) { - return i; - } - } - return -1; - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/MimeMessage.java b/java/com/android/voicemailomtp/mail/internet/MimeMessage.java deleted file mode 100644 index a11cd6d83..000000000 --- a/java/com/android/voicemailomtp/mail/internet/MimeMessage.java +++ /dev/null @@ -1,675 +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.internet; - -import com.android.voicemailomtp.mail.Address; -import com.android.voicemailomtp.mail.Body; -import com.android.voicemailomtp.mail.BodyPart; -import com.android.voicemailomtp.mail.Message; -import com.android.voicemailomtp.mail.MessagingException; -import com.android.voicemailomtp.mail.Multipart; -import com.android.voicemailomtp.mail.Part; -import com.android.voicemailomtp.mail.utils.LogUtils; - -import org.apache.james.mime4j.BodyDescriptor; -import org.apache.james.mime4j.ContentHandler; -import org.apache.james.mime4j.EOLConvertingInputStream; -import org.apache.james.mime4j.MimeStreamParser; -import org.apache.james.mime4j.field.DateTimeField; -import org.apache.james.mime4j.field.Field; - -import android.text.TextUtils; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Stack; -import java.util.regex.Pattern; - -/** - * An implementation of Message that stores all of its metadata in RFC 822 and - * RFC 2045 style headers. - * - * NOTE: Automatic generation of a local message-id is becoming unwieldy and should be removed. - * It would be better to simply do it explicitly on local creation of new outgoing messages. - */ -public class MimeMessage extends Message { - private MimeHeader mHeader; - private MimeHeader mExtendedHeader; - - // NOTE: The fields here are transcribed out of headers, and values stored here will supersede - // the values found in the headers. Use caution to prevent any out-of-phase errors. In - // particular, any adds/changes/deletes here must be echoed by changes in the parse() function. - private Address[] mFrom; - private Address[] mTo; - private Address[] mCc; - private Address[] mBcc; - private Address[] mReplyTo; - private Date mSentDate; - private Body mBody; - protected int mSize; - private boolean mInhibitLocalMessageId = false; - private boolean mComplete = true; - - // Shared random source for generating local message-id values - private static final java.util.Random sRandom = new java.util.Random(); - - // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to - // "Jan", not the other localized format like "Ene" (meaning January in locale es). - // This conversion is used when generating outgoing MIME messages. Incoming MIME date - // headers are parsed by org.apache.james.mime4j.field.DateTimeField which does not have any - // localization code. - private static final SimpleDateFormat DATE_FORMAT = - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); - - // regex that matches content id surrounded by "<>" optionally. - private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^]+)>?$"); - // regex that matches end of line. - private static final Pattern END_OF_LINE = Pattern.compile("\r?\n"); - - public MimeMessage() { - mHeader = null; - } - - /** - * Generate a local message id. This is only used when none has been assigned, and is - * installed lazily. Any remote (typically server-assigned) message id takes precedence. - * @return a long, locally-generated message-ID value - */ - private static String generateMessageId() { - final StringBuilder sb = new StringBuilder(); - sb.append("<"); - for (int i = 0; i < 24; i++) { - // We'll use a 5-bit range (0..31) - final int value = sRandom.nextInt() & 31; - final char c = "0123456789abcdefghijklmnopqrstuv".charAt(value); - sb.append(c); - } - sb.append("."); - sb.append(Long.toString(System.currentTimeMillis())); - sb.append("@email.android.com>"); - return sb.toString(); - } - - /** - * Parse the given InputStream using Apache Mime4J to build a MimeMessage. - * - * @param in InputStream providing message content - * @throws IOException - * @throws MessagingException - */ - public MimeMessage(InputStream in) throws IOException, MessagingException { - parse(in); - } - - private MimeStreamParser init() { - // Before parsing the input stream, clear all local fields that may be superceded by - // the new incoming message. - getMimeHeaders().clear(); - mInhibitLocalMessageId = true; - mFrom = null; - mTo = null; - mCc = null; - mBcc = null; - mReplyTo = null; - mSentDate = null; - mBody = null; - - final MimeStreamParser parser = new MimeStreamParser(); - parser.setContentHandler(new MimeMessageBuilder()); - return parser; - } - - protected void parse(InputStream in) throws IOException, MessagingException { - final MimeStreamParser parser = init(); - parser.parse(new EOLConvertingInputStream(in)); - mComplete = !parser.getPrematureEof(); - } - - public void parse(InputStream in, EOLConvertingInputStream.Callback callback) - throws IOException, MessagingException { - final MimeStreamParser parser = init(); - parser.parse(new EOLConvertingInputStream(in, getSize(), callback)); - mComplete = !parser.getPrematureEof(); - } - - /** - * Return the internal mHeader value, with very lazy initialization. - * The goal is to save memory by not creating the headers until needed. - */ - private MimeHeader getMimeHeaders() { - if (mHeader == null) { - mHeader = new MimeHeader(); - } - return mHeader; - } - - @Override - public Date getReceivedDate() throws MessagingException { - return null; - } - - @Override - public Date getSentDate() throws MessagingException { - if (mSentDate == null) { - try { - DateTimeField field = (DateTimeField)Field.parse("Date: " - + MimeUtility.unfoldAndDecode(getFirstHeader("Date"))); - mSentDate = field.getDate(); - // TODO: We should make it more clear what exceptions can be thrown here, - // and whether they reflect a normal or error condition. - } catch (Exception e) { - LogUtils.v(LogUtils.TAG, "Message missing Date header"); - } - } - if (mSentDate == null) { - // If we still don't have a date, fall back to "Delivery-date" - try { - DateTimeField field = (DateTimeField)Field.parse("Date: " - + MimeUtility.unfoldAndDecode(getFirstHeader("Delivery-date"))); - mSentDate = field.getDate(); - // TODO: We should make it more clear what exceptions can be thrown here, - // and whether they reflect a normal or error condition. - } catch (Exception e) { - LogUtils.v(LogUtils.TAG, "Message also missing Delivery-Date header"); - } - } - return mSentDate; - } - - @Override - public void setSentDate(Date sentDate) throws MessagingException { - setHeader("Date", DATE_FORMAT.format(sentDate)); - this.mSentDate = sentDate; - } - - @Override - public String getContentType() throws MessagingException { - final String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE); - if (contentType == null) { - return "text/plain"; - } else { - return contentType; - } - } - - @Override - public String getDisposition() throws MessagingException { - return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION); - } - - @Override - public String getContentId() throws MessagingException { - final String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID); - if (contentId == null) { - return null; - } else { - // remove optionally surrounding brackets. - return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1"); - } - } - - public boolean isComplete() { - return mComplete; - } - - @Override - public String getMimeType() throws MessagingException { - return MimeUtility.getHeaderParameter(getContentType(), null); - } - - @Override - public int getSize() throws MessagingException { - return mSize; - } - - /** - * Returns a list of the given recipient type from this message. If no addresses are - * found the method returns an empty array. - */ - @Override - public Address[] getRecipients(String type) throws MessagingException { - if (type == RECIPIENT_TYPE_TO) { - if (mTo == null) { - mTo = Address.parse(MimeUtility.unfold(getFirstHeader("To"))); - } - return mTo; - } else if (type == RECIPIENT_TYPE_CC) { - if (mCc == null) { - mCc = Address.parse(MimeUtility.unfold(getFirstHeader("CC"))); - } - return mCc; - } else if (type == RECIPIENT_TYPE_BCC) { - if (mBcc == null) { - mBcc = Address.parse(MimeUtility.unfold(getFirstHeader("BCC"))); - } - return mBcc; - } else { - throw new MessagingException("Unrecognized recipient type."); - } - } - - @Override - public void setRecipients(String type, Address[] addresses) throws MessagingException { - final int TO_LENGTH = 4; // "To: " - final int CC_LENGTH = 4; // "Cc: " - final int BCC_LENGTH = 5; // "Bcc: " - if (type == RECIPIENT_TYPE_TO) { - if (addresses == null || addresses.length == 0) { - removeHeader("To"); - this.mTo = null; - } else { - setHeader("To", MimeUtility.fold(Address.toHeader(addresses), TO_LENGTH)); - this.mTo = addresses; - } - } else if (type == RECIPIENT_TYPE_CC) { - if (addresses == null || addresses.length == 0) { - removeHeader("CC"); - this.mCc = null; - } else { - setHeader("CC", MimeUtility.fold(Address.toHeader(addresses), CC_LENGTH)); - this.mCc = addresses; - } - } else if (type == RECIPIENT_TYPE_BCC) { - if (addresses == null || addresses.length == 0) { - removeHeader("BCC"); - this.mBcc = null; - } else { - setHeader("BCC", MimeUtility.fold(Address.toHeader(addresses), BCC_LENGTH)); - this.mBcc = addresses; - } - } else { - throw new MessagingException("Unrecognized recipient type."); - } - } - - /** - * Returns the unfolded, decoded value of the Subject header. - */ - @Override - public String getSubject() throws MessagingException { - return MimeUtility.unfoldAndDecode(getFirstHeader("Subject")); - } - - @Override - public void setSubject(String subject) throws MessagingException { - final int HEADER_NAME_LENGTH = 9; // "Subject: " - setHeader("Subject", MimeUtility.foldAndEncode2(subject, HEADER_NAME_LENGTH)); - } - - @Override - public Address[] getFrom() throws MessagingException { - if (mFrom == null) { - String list = MimeUtility.unfold(getFirstHeader("From")); - if (list == null || list.length() == 0) { - list = MimeUtility.unfold(getFirstHeader("Sender")); - } - mFrom = Address.parse(list); - } - return mFrom; - } - - @Override - public void setFrom(Address from) throws MessagingException { - final int FROM_LENGTH = 6; // "From: " - if (from != null) { - setHeader("From", MimeUtility.fold(from.toHeader(), FROM_LENGTH)); - this.mFrom = new Address[] { - from - }; - } else { - this.mFrom = null; - } - } - - @Override - public Address[] getReplyTo() throws MessagingException { - if (mReplyTo == null) { - mReplyTo = Address.parse(MimeUtility.unfold(getFirstHeader("Reply-to"))); - } - return mReplyTo; - } - - @Override - public void setReplyTo(Address[] replyTo) throws MessagingException { - final int REPLY_TO_LENGTH = 10; // "Reply-to: " - if (replyTo == null || replyTo.length == 0) { - removeHeader("Reply-to"); - mReplyTo = null; - } else { - setHeader("Reply-to", MimeUtility.fold(Address.toHeader(replyTo), REPLY_TO_LENGTH)); - mReplyTo = replyTo; - } - } - - /** - * Set the mime "Message-ID" header - * @param messageId the new Message-ID value - * @throws MessagingException - */ - @Override - public void setMessageId(String messageId) throws MessagingException { - setHeader("Message-ID", messageId); - } - - /** - * Get the mime "Message-ID" header. This value will be preloaded with a locally-generated - * random ID, if the value has not previously been set. Local generation can be inhibited/ - * overridden by explicitly clearing the headers, removing the message-id header, etc. - * @return the Message-ID header string, or null if explicitly has been set to null - */ - @Override - public String getMessageId() throws MessagingException { - String messageId = getFirstHeader("Message-ID"); - if (messageId == null && !mInhibitLocalMessageId) { - messageId = generateMessageId(); - setMessageId(messageId); - } - return messageId; - } - - @Override - public void saveChanges() throws MessagingException { - throw new MessagingException("saveChanges not yet implemented"); - } - - @Override - public Body getBody() throws MessagingException { - return mBody; - } - - @Override - public void setBody(Body body) throws MessagingException { - this.mBody = body; - if (body instanceof Multipart) { - final Multipart multipart = ((Multipart)body); - multipart.setParent(this); - setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType()); - setHeader("MIME-Version", "1.0"); - } - else if (body instanceof TextBody) { - setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8", - getMimeType())); - setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64"); - } - } - - protected String getFirstHeader(String name) throws MessagingException { - return getMimeHeaders().getFirstHeader(name); - } - - @Override - public void addHeader(String name, String value) throws MessagingException { - getMimeHeaders().addHeader(name, value); - } - - @Override - public void setHeader(String name, String value) throws MessagingException { - getMimeHeaders().setHeader(name, value); - } - - @Override - public String[] getHeader(String name) throws MessagingException { - return getMimeHeaders().getHeader(name); - } - - @Override - public void removeHeader(String name) throws MessagingException { - getMimeHeaders().removeHeader(name); - if ("Message-ID".equalsIgnoreCase(name)) { - mInhibitLocalMessageId = true; - } - } - - /** - * Set extended header - * - * @param name Extended header name - * @param value header value - flattened by removing CR-NL if any - * remove header if value is null - * @throws MessagingException - */ - @Override - public void setExtendedHeader(String name, String value) throws MessagingException { - if (value == null) { - if (mExtendedHeader != null) { - mExtendedHeader.removeHeader(name); - } - return; - } - if (mExtendedHeader == null) { - mExtendedHeader = new MimeHeader(); - } - mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll("")); - } - - /** - * Get extended header - * - * @param name Extended header name - * @return header value - null if header does not exist - * @throws MessagingException - */ - @Override - public String getExtendedHeader(String name) throws MessagingException { - if (mExtendedHeader == null) { - return null; - } - return mExtendedHeader.getFirstHeader(name); - } - - /** - * Set entire extended headers from String - * - * @param headers Extended header and its value - "CR-NL-separated pairs - * if null or empty, remove entire extended headers - * @throws MessagingException - */ - public void setExtendedHeaders(String headers) throws MessagingException { - if (TextUtils.isEmpty(headers)) { - mExtendedHeader = null; - } else { - mExtendedHeader = new MimeHeader(); - for (final String header : END_OF_LINE.split(headers)) { - final String[] tokens = header.split(":", 2); - if (tokens.length != 2) { - throw new MessagingException("Illegal extended headers: " + headers); - } - mExtendedHeader.setHeader(tokens[0].trim(), tokens[1].trim()); - } - } - } - - /** - * Get entire extended headers as String - * - * @return "CR-NL-separated extended headers - null if extended header does not exist - */ - public String getExtendedHeaders() { - if (mExtendedHeader != null) { - return mExtendedHeader.writeToString(); - } - return null; - } - - /** - * Write message header and body to output stream - * - * @param out Output steam to write message header and body. - */ - @Override - public void writeTo(OutputStream out) throws IOException, MessagingException { - final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); - // Force creation of local message-id - getMessageId(); - getMimeHeaders().writeTo(out); - // mExtendedHeader will not be write out to external output stream, - // because it is intended to internal use. - writer.write("\r\n"); - writer.flush(); - if (mBody != null) { - mBody.writeTo(out); - } - } - - @Override - public InputStream getInputStream() throws MessagingException { - return null; - } - - class MimeMessageBuilder implements ContentHandler { - private final Stack stack = new Stack(); - - public MimeMessageBuilder() { - } - - private void expect(Class c) { - if (!c.isInstance(stack.peek())) { - throw new IllegalStateException("Internal stack error: " + "Expected '" - + c.getName() + "' found '" + stack.peek().getClass().getName() + "'"); - } - } - - @Override - public void startMessage() { - if (stack.isEmpty()) { - stack.push(MimeMessage.this); - } else { - expect(Part.class); - try { - final MimeMessage m = new MimeMessage(); - ((Part)stack.peek()).setBody(m); - stack.push(m); - } catch (MessagingException me) { - throw new Error(me); - } - } - } - - @Override - public void endMessage() { - expect(MimeMessage.class); - stack.pop(); - } - - @Override - public void startHeader() { - expect(Part.class); - } - - @Override - public void field(String fieldData) { - expect(Part.class); - try { - final String[] tokens = fieldData.split(":", 2); - ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim()); - } catch (MessagingException me) { - throw new Error(me); - } - } - - @Override - public void endHeader() { - expect(Part.class); - } - - @Override - public void startMultipart(BodyDescriptor bd) { - expect(Part.class); - - final Part e = (Part)stack.peek(); - try { - final MimeMultipart multiPart = new MimeMultipart(e.getContentType()); - e.setBody(multiPart); - stack.push(multiPart); - } catch (MessagingException me) { - throw new Error(me); - } - } - - @Override - public void body(BodyDescriptor bd, InputStream in) throws IOException { - expect(Part.class); - final Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding()); - try { - ((Part)stack.peek()).setBody(body); - } catch (MessagingException me) { - throw new Error(me); - } - } - - @Override - public void endMultipart() { - stack.pop(); - } - - @Override - public void startBodyPart() { - expect(MimeMultipart.class); - - try { - final MimeBodyPart bodyPart = new MimeBodyPart(); - ((MimeMultipart)stack.peek()).addBodyPart(bodyPart); - stack.push(bodyPart); - } catch (MessagingException me) { - throw new Error(me); - } - } - - @Override - public void endBodyPart() { - expect(BodyPart.class); - stack.pop(); - } - - @Override - public void epilogue(InputStream is) throws IOException { - expect(MimeMultipart.class); - final StringBuilder sb = new StringBuilder(); - int b; - while ((b = is.read()) != -1) { - sb.append((char)b); - } - // TODO: why is this commented out? - // ((Multipart) stack.peek()).setEpilogue(sb.toString()); - } - - @Override - public void preamble(InputStream is) throws IOException { - expect(MimeMultipart.class); - final StringBuilder sb = new StringBuilder(); - int b; - while ((b = is.read()) != -1) { - sb.append((char)b); - } - try { - ((MimeMultipart)stack.peek()).setPreamble(sb.toString()); - } catch (MessagingException me) { - throw new Error(me); - } - } - - @Override - public void raw(InputStream is) throws IOException { - throw new UnsupportedOperationException("Not supported"); - } - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java b/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java deleted file mode 100644 index 111924336..000000000 --- a/java/com/android/voicemailomtp/mail/internet/MimeMultipart.java +++ /dev/null @@ -1,112 +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.internet; - -import com.android.voicemailomtp.mail.BodyPart; -import com.android.voicemailomtp.mail.MessagingException; -import com.android.voicemailomtp.mail.Multipart; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; - -public class MimeMultipart extends Multipart { - protected String mPreamble; - - protected String mContentType; - - protected String mBoundary; - - protected String mSubType; - - public MimeMultipart() throws MessagingException { - mBoundary = generateBoundary(); - setSubType("mixed"); - } - - public MimeMultipart(String contentType) throws MessagingException { - this.mContentType = contentType; - try { - mSubType = MimeUtility.getHeaderParameter(contentType, null).split("/")[1]; - mBoundary = MimeUtility.getHeaderParameter(contentType, "boundary"); - if (mBoundary == null) { - throw new MessagingException("MultiPart does not contain boundary: " + contentType); - } - } catch (Exception e) { - throw new MessagingException( - "Invalid MultiPart Content-Type; must contain subtype and boundary. (" - + contentType + ")", e); - } - } - - public String generateBoundary() { - StringBuffer sb = new StringBuffer(); - sb.append("----"); - for (int i = 0; i < 30; i++) { - sb.append(Integer.toString((int)(Math.random() * 35), 36)); - } - return sb.toString().toUpperCase(); - } - - public String getPreamble() throws MessagingException { - return mPreamble; - } - - public void setPreamble(String preamble) throws MessagingException { - this.mPreamble = preamble; - } - - @Override - public String getContentType() throws MessagingException { - return mContentType; - } - - public void setSubType(String subType) throws MessagingException { - this.mSubType = subType; - mContentType = String.format("multipart/%s; boundary=\"%s\"", subType, mBoundary); - } - - @Override - public void writeTo(OutputStream out) throws IOException, MessagingException { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); - - if (mPreamble != null) { - writer.write(mPreamble + "\r\n"); - } - - for (int i = 0, count = mParts.size(); i < count; i++) { - BodyPart bodyPart = mParts.get(i); - writer.write("--" + mBoundary + "\r\n"); - writer.flush(); - bodyPart.writeTo(out); - writer.write("\r\n"); - } - - writer.write("--" + mBoundary + "--\r\n"); - writer.flush(); - } - - @Override - public InputStream getInputStream() throws MessagingException { - return null; - } - - public String getSubTypeForTest() { - return mSubType; - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/MimeUtility.java b/java/com/android/voicemailomtp/mail/internet/MimeUtility.java deleted file mode 100644 index 4d310b0f5..000000000 --- a/java/com/android/voicemailomtp/mail/internet/MimeUtility.java +++ /dev/null @@ -1,416 +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.internet; - -import android.text.TextUtils; -import android.util.Base64; -import android.util.Base64DataException; -import android.util.Base64InputStream; - -import com.android.voicemailomtp.mail.Body; -import com.android.voicemailomtp.mail.BodyPart; -import com.android.voicemailomtp.mail.Message; -import com.android.voicemailomtp.mail.MessagingException; -import com.android.voicemailomtp.mail.Multipart; -import com.android.voicemailomtp.mail.Part; -import com.android.voicemailomtp.VvmLog; - -import org.apache.commons.io.IOUtils; -import org.apache.james.mime4j.codec.EncoderUtil; -import org.apache.james.mime4j.decoder.DecoderUtil; -import org.apache.james.mime4j.decoder.QuotedPrintableInputStream; -import org.apache.james.mime4j.util.CharsetUtil; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class MimeUtility { - private static final String LOG_TAG = "Email"; - - public static final String MIME_TYPE_RFC822 = "message/rfc822"; - private final static Pattern PATTERN_CR_OR_LF = Pattern.compile("\r|\n"); - - /** - * Replace sequences of CRLF+WSP with WSP. Tries to preserve original string - * object whenever possible. - */ - public static String unfold(String s) { - if (s == null) { - return null; - } - Matcher patternMatcher = PATTERN_CR_OR_LF.matcher(s); - if (patternMatcher.find()) { - patternMatcher.reset(); - s = patternMatcher.replaceAll(""); - } - return s; - } - - public static String decode(String s) { - if (s == null) { - return null; - } - return DecoderUtil.decodeEncodedWords(s); - } - - public static String unfoldAndDecode(String s) { - return decode(unfold(s)); - } - - // TODO implement proper foldAndEncode - // NOTE: When this really works, we *must* remove all calls to foldAndEncode2() to prevent - // duplication of encoding. - public static String foldAndEncode(String s) { - return s; - } - - /** - * INTERIM version of foldAndEncode that will be used only by Subject: headers. - * This is safer than implementing foldAndEncode() (see above) and risking unknown damage - * to other headers. - * - * TODO: Copy this code to foldAndEncode(), get rid of this function, confirm all working OK. - * - * @param s original string to encode and fold - * @param usedCharacters number of characters already used up by header name - - * @return the String ready to be transmitted - */ - public static String foldAndEncode2(String s, int usedCharacters) { - // james.mime4j.codec.EncoderUtil.java - // encode: encodeIfNecessary(text, usage, numUsedInHeaderName) - // Usage.TEXT_TOKENlooks like the right thing for subjects - // use WORD_ENTITY for address/names - - String encoded = EncoderUtil.encodeIfNecessary(s, EncoderUtil.Usage.TEXT_TOKEN, - usedCharacters); - - return fold(encoded, usedCharacters); - } - - /** - * INTERIM: From newer version of org.apache.james (but we don't want to import - * the entire MimeUtil class). - * - * Splits the specified string into a multiple-line representation with - * lines no longer than 76 characters (because the line might contain - * encoded words; see RFC - * 2047 section 2). If the string contains non-whitespace sequences - * longer than 76 characters a line break is inserted at the whitespace - * character following the sequence resulting in a line longer than 76 - * characters. - * - * @param s - * string to split. - * @param usedCharacters - * number of characters already used up. Usually the number of - * characters for header field name plus colon and one space. - * @return a multiple-line representation of the given string. - */ - public static String fold(String s, int usedCharacters) { - final int maxCharacters = 76; - - final int length = s.length(); - if (usedCharacters + length <= maxCharacters) - return s; - - StringBuilder sb = new StringBuilder(); - - int lastLineBreak = -usedCharacters; - int wspIdx = indexOfWsp(s, 0); - while (true) { - if (wspIdx == length) { - sb.append(s.substring(Math.max(0, lastLineBreak))); - return sb.toString(); - } - - int nextWspIdx = indexOfWsp(s, wspIdx + 1); - - if (nextWspIdx - lastLineBreak > maxCharacters) { - sb.append(s.substring(Math.max(0, lastLineBreak), wspIdx)); - sb.append("\r\n"); - lastLineBreak = wspIdx; - } - - wspIdx = nextWspIdx; - } - } - - /** - * INTERIM: From newer version of org.apache.james (but we don't want to import - * the entire MimeUtil class). - * - * Search for whitespace. - */ - private static int indexOfWsp(String s, int fromIndex) { - final int len = s.length(); - for (int index = fromIndex; index < len; index++) { - char c = s.charAt(index); - if (c == ' ' || c == '\t') - return index; - } - return len; - } - - /** - * Returns the named parameter of a header field. If name is null the first - * parameter is returned, or if there are no additional parameters in the - * field the entire field is returned. Otherwise the named parameter is - * searched for in a case insensitive fashion and returned. If the parameter - * cannot be found the method returns null. - * - * TODO: quite inefficient with the inner trimming & splitting. - * TODO: Also has a latent bug: uses "startsWith" to match the name, which can false-positive. - * TODO: The doc says that for a null name you get the first param, but you get the header. - * Should probably just fix the doc, but if other code assumes that behavior, fix the code. - * TODO: Need to decode %-escaped strings, as in: filename="ab%22d". - * ('+' -> ' ' conversion too? check RFC) - * - * @param header - * @param name - * @return the entire header (if name=null), the found parameter, or null - */ - public static String getHeaderParameter(String header, String name) { - if (header == null) { - return null; - } - String[] parts = unfold(header).split(";"); - if (name == null) { - return parts[0].trim(); - } - String lowerCaseName = name.toLowerCase(); - for (String part : parts) { - if (part.trim().toLowerCase().startsWith(lowerCaseName)) { - String[] parameterParts = part.split("=", 2); - if (parameterParts.length < 2) { - return null; - } - String parameter = parameterParts[1].trim(); - if (parameter.startsWith("\"") && parameter.endsWith("\"")) { - return parameter.substring(1, parameter.length() - 1); - } else { - return parameter; - } - } - } - return null; - } - - /** - * Reads the Part's body and returns a String based on any charset conversion that needed - * to be done. - * @param part The part containing a body - * @return a String containing the converted text in the body, or null if there was no text - * or an error during conversion. - */ - public static String getTextFromPart(Part part) { - try { - if (part != null && part.getBody() != null) { - InputStream in = part.getBody().getInputStream(); - String mimeType = part.getMimeType(); - if (mimeType != null && MimeUtility.mimeTypeMatches(mimeType, "text/*")) { - /* - * Now we read the part into a buffer for further processing. Because - * the stream is now wrapped we'll remove any transfer encoding at this point. - */ - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(in, out); - in.close(); - in = null; // we want all of our memory back, and close might not release - - /* - * We've got a text part, so let's see if it needs to be processed further. - */ - String charset = getHeaderParameter(part.getContentType(), "charset"); - if (charset != null) { - /* - * See if there is conversion from the MIME charset to the Java one. - */ - charset = CharsetUtil.toJavaCharset(charset); - } - /* - * No encoding, so use us-ascii, which is the standard. - */ - if (charset == null) { - charset = "ASCII"; - } - /* - * Convert and return as new String - */ - String result = out.toString(charset); - out.close(); - return result; - } - } - - } - catch (OutOfMemoryError oom) { - /* - * If we are not able to process the body there's nothing we can do about it. Return - * null and let the upper layers handle the missing content. - */ - VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + oom.toString()); - } - catch (Exception e) { - /* - * If we are not able to process the body there's nothing we can do about it. Return - * null and let the upper layers handle the missing content. - */ - VvmLog.e(LOG_TAG, "Unable to getTextFromPart " + e.toString()); - } - return null; - } - - /** - * Returns true if the given mimeType matches the matchAgainst specification. The comparison - * ignores case and the matchAgainst string may include "*" for a wildcard (e.g. "image/*"). - * - * @param mimeType A MIME type to check. - * @param matchAgainst A MIME type to check against. May include wildcards. - * @return true if the mimeType matches - */ - public static boolean mimeTypeMatches(String mimeType, String matchAgainst) { - Pattern p = Pattern.compile(matchAgainst.replaceAll("\\*", "\\.\\*"), - Pattern.CASE_INSENSITIVE); - return p.matcher(mimeType).matches(); - } - - /** - * Returns true if the given mimeType matches any of the matchAgainst specifications. The - * comparison ignores case and the matchAgainst strings may include "*" for a wildcard - * (e.g. "image/*"). - * - * @param mimeType A MIME type to check. - * @param matchAgainst An array of MIME types to check against. May include wildcards. - * @return true if the mimeType matches any of the matchAgainst strings - */ - public static boolean mimeTypeMatches(String mimeType, String[] matchAgainst) { - for (String matchType : matchAgainst) { - if (mimeTypeMatches(mimeType, matchType)) { - return true; - } - } - return false; - } - - /** - * Given an input stream and a transfer encoding, return a wrapped input stream for that - * encoding (or the original if none is required) - * @param in the input stream - * @param contentTransferEncoding the content transfer encoding - * @return a properly wrapped stream - */ - public static InputStream getInputStreamForContentTransferEncoding(InputStream in, - String contentTransferEncoding) { - if (contentTransferEncoding != null) { - contentTransferEncoding = - MimeUtility.getHeaderParameter(contentTransferEncoding, null); - if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) { - in = new QuotedPrintableInputStream(in); - } - else if ("base64".equalsIgnoreCase(contentTransferEncoding)) { - in = new Base64InputStream(in, Base64.DEFAULT); - } - } - return in; - } - - /** - * Removes any content transfer encoding from the stream and returns a Body. - */ - public static Body decodeBody(InputStream in, String contentTransferEncoding) - throws IOException { - /* - * We'll remove any transfer encoding by wrapping the stream. - */ - in = getInputStreamForContentTransferEncoding(in, contentTransferEncoding); - BinaryTempFileBody tempBody = new BinaryTempFileBody(); - OutputStream out = tempBody.getOutputStream(); - try { - IOUtils.copy(in, out); - } catch (Base64DataException bde) { - // TODO Need to fix this somehow - //String warning = "\n\n" + Email.getMessageDecodeErrorString(); - //out.write(warning.getBytes()); - } finally { - out.close(); - } - return tempBody; - } - - /** - * Recursively scan a Part (usually a Message) and sort out which of its children will be - * "viewable" and which will be attachments. - * - * @param part The part to be broken down - * @param viewables This arraylist will be populated with all parts that appear to be - * the "message" (e.g. text/plain & text/html) - * @param attachments This arraylist will be populated with all parts that appear to be - * attachments (including inlines) - * @throws MessagingException - */ - public static void collectParts(Part part, ArrayList viewables, - ArrayList attachments) throws MessagingException { - String disposition = part.getDisposition(); - String dispositionType = MimeUtility.getHeaderParameter(disposition, null); - // If a disposition is not specified, default to "inline" - boolean inline = - TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType); - // The lower-case mime type - String mimeType = part.getMimeType().toLowerCase(); - - if (part.getBody() instanceof Multipart) { - // If the part is Multipart but not alternative it's either mixed or - // something we don't know about, which means we treat it as mixed - // per the spec. We just process its pieces recursively. - MimeMultipart mp = (MimeMultipart)part.getBody(); - boolean foundHtml = false; - if (mp.getSubTypeForTest().equals("alternative")) { - for (int i = 0; i < mp.getCount(); i++) { - if (mp.getBodyPart(i).isMimeType("text/html")) { - foundHtml = true; - break; - } - } - } - for (int i = 0; i < mp.getCount(); i++) { - // See if we have text and html - BodyPart bp = mp.getBodyPart(i); - // If there's html, don't bother loading text - if (foundHtml && bp.isMimeType("text/plain")) { - continue; - } - collectParts(bp, viewables, attachments); - } - } else if (part.getBody() instanceof Message) { - // If the part is an embedded message we just continue to process - // it, pulling any viewables or attachments into the running list. - Message message = (Message)part.getBody(); - collectParts(message, viewables, attachments); - } else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) { - // We'll treat text and images as viewables - viewables.add(part); - } else { - // Everything else is an attachment. - attachments.add(part); - } - } -} diff --git a/java/com/android/voicemailomtp/mail/internet/TextBody.java b/java/com/android/voicemailomtp/mail/internet/TextBody.java deleted file mode 100644 index 578193eff..000000000 --- a/java/com/android/voicemailomtp/mail/internet/TextBody.java +++ /dev/null @@ -1,63 +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.internet; - -import android.util.Base64; - -import com.android.voicemailomtp.mail.Body; -import com.android.voicemailomtp.mail.MessagingException; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; - -public class TextBody implements Body { - String mBody; - - public TextBody(String body) { - this.mBody = body; - } - - @Override - public void writeTo(OutputStream out) throws IOException, MessagingException { - byte[] bytes = mBody.getBytes("UTF-8"); - out.write(Base64.encode(bytes, Base64.CRLF)); - } - - /** - * Get the text of the body in it's unencoded format. - * @return - */ - public String getText() { - return mBody; - } - - /** - * Returns an InputStream that reads this body's text in UTF-8 format. - */ - @Override - public InputStream getInputStream() throws MessagingException { - try { - byte[] b = mBody.getBytes("UTF-8"); - return new ByteArrayInputStream(b); - } - catch (UnsupportedEncodingException usee) { - return null; - } - } -} -- cgit v1.2.3