13f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/*
23f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Copyright (C) 2015 The Android Open Source Project
33f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
43f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Licensed under the Apache License, Version 2.0 (the "License");
53f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * you may not use this file except in compliance with the License.
63f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * You may obtain a copy of the License at
73f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
83f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *      http://www.apache.org/licenses/LICENSE-2.0
93f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * Unless required by applicable law or agreed to in writing, software
113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * distributed under the License is distributed on an "AS IS" BASIS,
123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * See the License for the specific language governing permissions and
143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * limitations under the License.
153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */
163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpackage com.android.phone.common.mail.internet;
173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.Address;
193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.Body;
203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.BodyPart;
213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.Message;
223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.MessagingException;
233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.Multipart;
243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.Part;
253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport com.android.phone.common.mail.utils.LogUtils;
263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.BodyDescriptor;
283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.ContentHandler;
293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.EOLConvertingInputStream;
303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.MimeStreamParser;
313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.field.DateTimeField;
323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport org.apache.james.mime4j.field.Field;
333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport android.text.TextUtils;
353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.io.BufferedWriter;
373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.io.IOException;
383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.io.InputStream;
393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.io.OutputStream;
403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.io.OutputStreamWriter;
413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.text.SimpleDateFormat;
423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.Date;
433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.Locale;
443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.Stack;
453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenimport java.util.regex.Pattern;
463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen/**
483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * An implementation of Message that stores all of its metadata in RFC 822 and
493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * RFC 2045 style headers.
503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen *
513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * NOTE:  Automatic generation of a local message-id is becoming unwieldy and should be removed.
523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen * It would be better to simply do it explicitly on local creation of new outgoing messages.
533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen */
543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chenpublic class MimeMessage extends Message {
553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private MimeHeader mHeader;
563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private MimeHeader mExtendedHeader;
573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // NOTE:  The fields here are transcribed out of headers, and values stored here will supersede
593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // the values found in the headers.  Use caution to prevent any out-of-phase errors.  In
603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // particular, any adds/changes/deletes here must be echoed by changes in the parse() function.
613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Address[] mFrom;
623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Address[] mTo;
633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Address[] mCc;
643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Address[] mBcc;
653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Address[] mReplyTo;
663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Date mSentDate;
673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private Body mBody;
683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    protected int mSize;
693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private boolean mInhibitLocalMessageId = false;
703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private boolean mComplete = true;
713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // Shared random source for generating local message-id values
733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final java.util.Random sRandom = new java.util.Random();
743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // "Jan", not the other localized format like "Ene" (meaning January in locale es).
773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // This conversion is used when generating outgoing MIME messages. Incoming MIME date
783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // headers are parsed by org.apache.james.mime4j.field.DateTimeField which does not have any
793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // localization code.
803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final SimpleDateFormat DATE_FORMAT =
813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // regex that matches content id surrounded by "<>" optionally.
843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    // regex that matches end of line.
863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static final Pattern END_OF_LINE = Pattern.compile("\r?\n");
873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public MimeMessage() {
893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mHeader = null;
903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Generate a local message id.  This is only used when none has been assigned, and is
943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * installed lazily.  Any remote (typically server-assigned) message id takes precedence.
953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return a long, locally-generated message-ID value
963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private static String generateMessageId() {
983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final StringBuilder sb = new StringBuilder();
993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        sb.append("<");
1003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        for (int i = 0; i < 24; i++) {
1013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // We'll use a 5-bit range (0..31)
1023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final int value = sRandom.nextInt() & 31;
1033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final char c = "0123456789abcdefghijklmnopqrstuv".charAt(value);
1043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            sb.append(c);
1053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        sb.append(".");
1073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        sb.append(Long.toString(System.currentTimeMillis()));
1083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        sb.append("@email.android.com>");
1093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return sb.toString();
1103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Parse the given InputStream using Apache Mime4J to build a MimeMessage.
1143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
1153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param in InputStream providing message content
1163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws IOException
1173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws MessagingException
1183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public MimeMessage(InputStream in) throws IOException, MessagingException {
1203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        parse(in);
1213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private MimeStreamParser init() {
1243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // Before parsing the input stream, clear all local fields that may be superceded by
1253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // the new incoming message.
1263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMimeHeaders().clear();
1273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mInhibitLocalMessageId = true;
1283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mFrom = null;
1293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mTo = null;
1303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mCc = null;
1313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mBcc = null;
1323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mReplyTo = null;
1333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mSentDate = null;
1343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mBody = null;
1353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final MimeStreamParser parser = new MimeStreamParser();
1373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        parser.setContentHandler(new MimeMessageBuilder());
1383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return parser;
1393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    protected void parse(InputStream in) throws IOException, MessagingException {
1423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final MimeStreamParser parser = init();
1433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        parser.parse(new EOLConvertingInputStream(in));
1443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mComplete = !parser.getPrematureEof();
1453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void parse(InputStream in, EOLConvertingInputStream.Callback callback)
1483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            throws IOException, MessagingException {
1493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final MimeStreamParser parser = init();
1503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        parser.parse(new EOLConvertingInputStream(in, getSize(), callback));
1513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mComplete = !parser.getPrematureEof();
1523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
1553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Return the internal mHeader value, with very lazy initialization.
1563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * The goal is to save memory by not creating the headers until needed.
1573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
1583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    private MimeHeader getMimeHeaders() {
1593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mHeader == null) {
1603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mHeader = new MimeHeader();
1613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mHeader;
1633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
1663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Date getReceivedDate() throws MessagingException {
1673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return null;
1683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
1713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Date getSentDate() throws MessagingException {
1723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mSentDate == null) {
1733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
1743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                DateTimeField field = (DateTimeField)Field.parse("Date: "
1753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                        + MimeUtility.unfoldAndDecode(getFirstHeader("Date")));
1763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mSentDate = field.getDate();
1773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // TODO: We should make it more clear what exceptions can be thrown here,
1783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // and whether they reflect a normal or error condition.
1793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (Exception e) {
1803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                LogUtils.v(LogUtils.TAG, "Message missing Date header");
1813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
1823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mSentDate == null) {
1843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // If we still don't have a date, fall back to "Delivery-date"
1853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
1863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                DateTimeField field = (DateTimeField)Field.parse("Date: "
1873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                        + MimeUtility.unfoldAndDecode(getFirstHeader("Delivery-date")));
1883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mSentDate = field.getDate();
1893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // TODO: We should make it more clear what exceptions can be thrown here,
1903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                // and whether they reflect a normal or error condition.
1913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (Exception e) {
1923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                LogUtils.v(LogUtils.TAG, "Message also missing Delivery-Date header");
1933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
1943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
1953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mSentDate;
1963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
1973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
1983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
1993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setSentDate(Date sentDate) throws MessagingException {
2003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setHeader("Date", DATE_FORMAT.format(sentDate));
2013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        this.mSentDate = sentDate;
2023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getContentType() throws MessagingException {
2063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
2073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (contentType == null) {
2083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return "text/plain";
2093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
2103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return contentType;
2113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getDisposition() throws MessagingException {
2163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
2173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getContentId() throws MessagingException {
2213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
2223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (contentId == null) {
2233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
2243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
2253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // remove optionally surrounding brackets.
2263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
2273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public boolean isComplete() {
2313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mComplete;
2323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getMimeType() throws MessagingException {
2363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return MimeUtility.getHeaderParameter(getContentType(), null);
2373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public int getSize() throws MessagingException {
2413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mSize;
2423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
2453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns a list of the given recipient type from this message. If no addresses are
2463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * found the method returns an empty array.
2473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
2483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address[] getRecipients(String type) throws MessagingException {
2503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (type == RECIPIENT_TYPE_TO) {
2513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (mTo == null) {
2523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mTo = Address.parse(MimeUtility.unfold(getFirstHeader("To")));
2533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mTo;
2553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else if (type == RECIPIENT_TYPE_CC) {
2563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (mCc == null) {
2573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mCc = Address.parse(MimeUtility.unfold(getFirstHeader("CC")));
2583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mCc;
2603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else if (type == RECIPIENT_TYPE_BCC) {
2613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (mBcc == null) {
2623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mBcc = Address.parse(MimeUtility.unfold(getFirstHeader("BCC")));
2633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mBcc;
2653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
2663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            throw new MessagingException("Unrecognized recipient type.");
2673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
2683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
2693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
2703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
2713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setRecipients(String type, Address[] addresses) throws MessagingException {
2723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int TO_LENGTH = 4;  // "To: "
2733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int CC_LENGTH = 4;  // "Cc: "
2743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int BCC_LENGTH = 5; // "Bcc: "
2753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (type == RECIPIENT_TYPE_TO) {
2763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (addresses == null || addresses.length == 0) {
2773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                removeHeader("To");
2783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mTo = null;
2793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
2803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                setHeader("To", MimeUtility.fold(Address.toHeader(addresses), TO_LENGTH));
2813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mTo = addresses;
2823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else if (type == RECIPIENT_TYPE_CC) {
2843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (addresses == null || addresses.length == 0) {
2853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                removeHeader("CC");
2863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mCc = null;
2873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
2883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                setHeader("CC", MimeUtility.fold(Address.toHeader(addresses), CC_LENGTH));
2893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mCc = addresses;
2903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else if (type == RECIPIENT_TYPE_BCC) {
2923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (addresses == null || addresses.length == 0) {
2933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                removeHeader("BCC");
2943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mBcc = null;
2953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
2963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                setHeader("BCC", MimeUtility.fold(Address.toHeader(addresses), BCC_LENGTH));
2973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                this.mBcc = addresses;
2983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
2993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
3003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            throw new MessagingException("Unrecognized recipient type.");
3013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Returns the unfolded, decoded value of the Subject header.
3063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getSubject() throws MessagingException {
3093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return MimeUtility.unfoldAndDecode(getFirstHeader("Subject"));
3103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setSubject(String subject) throws MessagingException {
3143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int HEADER_NAME_LENGTH = 9;     // "Subject: "
3153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setHeader("Subject", MimeUtility.foldAndEncode2(subject, HEADER_NAME_LENGTH));
3163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address[] getFrom() throws MessagingException {
3203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mFrom == null) {
3213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            String list = MimeUtility.unfold(getFirstHeader("From"));
3223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (list == null || list.length() == 0) {
3233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                list = MimeUtility.unfold(getFirstHeader("Sender"));
3243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
3253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mFrom = Address.parse(list);
3263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mFrom;
3283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setFrom(Address from) throws MessagingException {
3323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int FROM_LENGTH = 6;  // "From: "
3333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (from != null) {
3343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader("From", MimeUtility.fold(from.toHeader(), FROM_LENGTH));
3353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            this.mFrom = new Address[] {
3363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    from
3373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                };
3383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
3393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            this.mFrom = null;
3403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Address[] getReplyTo() throws MessagingException {
3453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mReplyTo == null) {
3463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mReplyTo = Address.parse(MimeUtility.unfold(getFirstHeader("Reply-to")));
3473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mReplyTo;
3493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setReplyTo(Address[] replyTo) throws MessagingException {
3533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final int REPLY_TO_LENGTH = 10;  // "Reply-to: "
3543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (replyTo == null || replyTo.length == 0) {
3553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            removeHeader("Reply-to");
3563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mReplyTo = null;
3573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
3583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader("Reply-to", MimeUtility.fold(Address.toHeader(replyTo), REPLY_TO_LENGTH));
3593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mReplyTo = replyTo;
3603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Set the mime "Message-ID" header
3653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param messageId the new Message-ID value
3663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws MessagingException
3673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setMessageId(String messageId) throws MessagingException {
3703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        setHeader("Message-ID", messageId);
3713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
3743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get the mime "Message-ID" header.  This value will be preloaded with a locally-generated
3753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * random ID, if the value has not previously been set.  Local generation can be inhibited/
3763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * overridden by explicitly clearing the headers, removing the message-id header, etc.
3773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return the Message-ID header string, or null if explicitly has been set to null
3783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
3793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getMessageId() throws MessagingException {
3813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        String messageId = getFirstHeader("Message-ID");
3823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (messageId == null && !mInhibitLocalMessageId) {
3833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            messageId = generateMessageId();
3843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setMessageId(messageId);
3853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
3863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return messageId;
3873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void saveChanges() throws MessagingException {
3913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        throw new MessagingException("saveChanges not yet implemented");
3923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
3953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public Body getBody() throws MessagingException {
3963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mBody;
3973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
3983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
3993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setBody(Body body) throws MessagingException {
4013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        this.mBody = body;
4023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (body instanceof Multipart) {
4033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final Multipart multipart = ((Multipart)body);
4043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            multipart.setParent(this);
4053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
4063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader("MIME-Version", "1.0");
4073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        else if (body instanceof TextBody) {
4093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8",
4103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    getMimeType()));
4113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
4123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    protected String getFirstHeader(String name) throws MessagingException {
4163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return getMimeHeaders().getFirstHeader(name);
4173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void addHeader(String name, String value) throws MessagingException {
4213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMimeHeaders().addHeader(name, value);
4223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setHeader(String name, String value) throws MessagingException {
4263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMimeHeaders().setHeader(name, value);
4273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String[] getHeader(String name) throws MessagingException {
4313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return getMimeHeaders().getHeader(name);
4323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void removeHeader(String name) throws MessagingException {
4363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMimeHeaders().removeHeader(name);
4373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if ("Message-ID".equalsIgnoreCase(name)) {
4383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mInhibitLocalMessageId = true;
4393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Set extended header
4443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
4453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param name Extended header name
4463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param value header value - flattened by removing CR-NL if any
4473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * remove header if value is null
4483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws MessagingException
4493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setExtendedHeader(String name, String value) throws MessagingException {
4523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (value == null) {
4533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (mExtendedHeader != null) {
4543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mExtendedHeader.removeHeader(name);
4553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
4563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return;
4573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mExtendedHeader == null) {
4593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mExtendedHeader = new MimeHeader();
4603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
4623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get extended header
4663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
4673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param name Extended header name
4683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return header value - null if header does not exist
4693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws MessagingException
4703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
4723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getExtendedHeader(String name) throws MessagingException {
4733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mExtendedHeader == null) {
4743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return null;
4753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return mExtendedHeader.getFirstHeader(name);
4773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
4783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
4793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
4803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Set entire extended headers from String
4813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
4823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param headers Extended header and its value - "CR-NL-separated pairs
4833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * if null or empty, remove entire extended headers
4843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @throws MessagingException
4853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
4863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void setExtendedHeaders(String headers) throws MessagingException {
4873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (TextUtils.isEmpty(headers)) {
4883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mExtendedHeader = null;
4893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        } else {
4903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mExtendedHeader = new MimeHeader();
4913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            for (final String header : END_OF_LINE.split(headers)) {
4923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                final String[] tokens = header.split(":", 2);
4933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                if (tokens.length != 2) {
4943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    throw new MessagingException("Illegal extended headers: " + headers);
4953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                }
4963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                mExtendedHeader.setHeader(tokens[0].trim(), tokens[1].trim());
4973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
4983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
4993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
5023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Get entire extended headers as String
5033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
5043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @return "CR-NL-separated extended headers - null if extended header does not exist
5053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
5063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public String getExtendedHeaders() {
5073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mExtendedHeader != null) {
5083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            return mExtendedHeader.writeToString();
5093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return null;
5113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    /**
5143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * Write message header and body to output stream
5153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     *
5163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     * @param out Output steam to write message header and body.
5173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen     */
5183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
5193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public void writeTo(OutputStream out) throws IOException, MessagingException {
5203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
5213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // Force creation of local message-id
5223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMessageId();
5233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        getMimeHeaders().writeTo(out);
5243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // mExtendedHeader will not be write out to external output stream,
5253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        // because it is intended to internal use.
5263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        writer.write("\r\n");
5273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        writer.flush();
5283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        if (mBody != null) {
5293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            mBody.writeTo(out);
5303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    @Override
5343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    public InputStream getInputStream() throws MessagingException {
5353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        return null;
5363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
5373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    class MimeMessageBuilder implements ContentHandler {
5393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        private final Stack<Object> stack = new Stack<Object>();
5403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public MimeMessageBuilder() {
5423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        private void expect(Class<?> c) {
5453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (!c.isInstance(stack.peek())) {
5463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new IllegalStateException("Internal stack error: " + "Expected '"
5473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                        + c.getName() + "' found '" + stack.peek().getClass().getName() + "'");
5483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
5493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void startMessage() {
5533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            if (stack.isEmpty()) {
5543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                stack.push(MimeMessage.this);
5553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } else {
5563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                expect(Part.class);
5573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                try {
5583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    final MimeMessage m = new MimeMessage();
5593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    ((Part)stack.peek()).setBody(m);
5603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    stack.push(m);
5613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                } catch (MessagingException me) {
5623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                    throw new Error(me);
5633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                }
5643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
5653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void endMessage() {
5693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(MimeMessage.class);
5703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            stack.pop();
5713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void startHeader() {
5753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(Part.class);
5763f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5773f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5783f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5793f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void field(String fieldData) {
5803f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(Part.class);
5813f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
5823f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                final String[] tokens = fieldData.split(":", 2);
5833f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim());
5843f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (MessagingException me) {
5853f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new Error(me);
5863f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
5873f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5883f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5893f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5903f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void endHeader() {
5913f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(Part.class);
5923f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
5933f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5943f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
5953f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void startMultipart(BodyDescriptor bd) {
5963f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(Part.class);
5973f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
5983f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final Part e = (Part)stack.peek();
5993f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
6003f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                final MimeMultipart multiPart = new MimeMultipart(e.getContentType());
6013f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                e.setBody(multiPart);
6023f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                stack.push(multiPart);
6033f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (MessagingException me) {
6043f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new Error(me);
6053f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6063f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6073f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6083f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6093f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void body(BodyDescriptor bd, InputStream in) throws IOException {
6103f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(Part.class);
6113f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
6123f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
6133f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                ((Part)stack.peek()).setBody(body);
6143f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (MessagingException me) {
6153f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new Error(me);
6163f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6173f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6183f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6193f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6203f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void endMultipart() {
6213f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            stack.pop();
6223f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6233f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6243f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6253f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void startBodyPart() {
6263f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(MimeMultipart.class);
6273f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6283f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
6293f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                final MimeBodyPart bodyPart = new MimeBodyPart();
6303f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                ((MimeMultipart)stack.peek()).addBodyPart(bodyPart);
6313f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                stack.push(bodyPart);
6323f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (MessagingException me) {
6333f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new Error(me);
6343f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6353f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6363f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6373f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6383f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void endBodyPart() {
6393f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(BodyPart.class);
6403f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            stack.pop();
6413f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6423f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6433f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6443f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void epilogue(InputStream is) throws IOException {
6453f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(MimeMultipart.class);
6463f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final StringBuilder sb = new StringBuilder();
6473f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            int b;
6483f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            while ((b = is.read()) != -1) {
6493f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                sb.append((char)b);
6503f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6513f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // TODO: why is this commented out?
6523f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            // ((Multipart) stack.peek()).setEpilogue(sb.toString());
6533f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6543f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6553f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6563f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void preamble(InputStream is) throws IOException {
6573f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            expect(MimeMultipart.class);
6583f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            final StringBuilder sb = new StringBuilder();
6593f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            int b;
6603f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            while ((b = is.read()) != -1) {
6613f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                sb.append((char)b);
6623f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6633f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            try {
6643f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                ((MimeMultipart)stack.peek()).setPreamble(sb.toString());
6653f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            } catch (MessagingException me) {
6663f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen                throw new Error(me);
6673f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            }
6683f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6693f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen
6703f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        @Override
6713f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        public void raw(InputStream is) throws IOException {
6723f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen            throw new UnsupportedOperationException("Not supported");
6733f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen        }
6743f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen    }
6753f51d09afa61649a1bcf02599bc1df5aafccf088Nancy Chen}
676