1345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/*
2345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Copyright (C) 2008 The Android Open Source Project
3345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein *
4345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License");
5345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * you may not use this file except in compliance with the License.
6345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * You may obtain a copy of the License at
7345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein *
8345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein *      http://www.apache.org/licenses/LICENSE-2.0
9345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein *
10345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * Unless required by applicable law or agreed to in writing, software
11345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS,
12345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * See the License for the specific language governing permissions and
14345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * limitations under the License.
15345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */
16345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
17345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpackage com.android.emailcommon.internet;
18345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
19345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.Address;
20345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.Body;
21345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.BodyPart;
22345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.Message;
23345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.MessagingException;
24345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.Multipart;
25345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport com.android.emailcommon.mail.Part;
2609eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdonimport com.android.mail.utils.LogUtils;
27345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
28345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.BodyDescriptor;
29345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.ContentHandler;
30345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.EOLConvertingInputStream;
31345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.MimeStreamParser;
32345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.field.DateTimeField;
33345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport org.apache.james.mime4j.field.Field;
34345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
35345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport android.text.TextUtils;
36345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
37345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.io.BufferedWriter;
38345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.io.IOException;
39345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.io.InputStream;
40345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.io.OutputStream;
41345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.io.OutputStreamWriter;
42345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.text.SimpleDateFormat;
43345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.Date;
44345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.Locale;
45345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.Stack;
46345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinimport java.util.regex.Pattern;
47345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
48345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein/**
49345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * An implementation of Message that stores all of its metadata in RFC 822 and
50345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * RFC 2045 style headers.
51345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein *
52345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * NOTE:  Automatic generation of a local message-id is becoming unwieldy and should be removed.
53345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein * It would be better to simply do it explicitly on local creation of new outgoing messages.
54345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein */
55345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sappersteinpublic class MimeMessage extends Message {
56345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private MimeHeader mHeader;
57345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private MimeHeader mExtendedHeader;
58345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
5909eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon    // NOTE:  The fields here are transcribed out of headers, and values stored here will supersede
60345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // the values found in the headers.  Use caution to prevent any out-of-phase errors.  In
61345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // particular, any adds/changes/deletes here must be echoed by changes in the parse() function.
62345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Address[] mFrom;
63345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Address[] mTo;
64345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Address[] mCc;
65345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Address[] mBcc;
66345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Address[] mReplyTo;
67345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Date mSentDate;
68345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private Body mBody;
69345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    protected int mSize;
70345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private boolean mInhibitLocalMessageId = false;
71345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private boolean mComplete = true;
72345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
73345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // Shared random source for generating local message-id values
74345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private static final java.util.Random sRandom = new java.util.Random();
75345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
76345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
77345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // "Jan", not the other localized format like "Ene" (meaning January in locale es).
78345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // This conversion is used when generating outgoing MIME messages. Incoming MIME date
79345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // headers are parsed by org.apache.james.mime4j.field.DateTimeField which does not have any
80345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // localization code.
81345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private static final SimpleDateFormat DATE_FORMAT =
82345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
83345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
84345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // regex that matches content id surrounded by "<>" optionally.
85345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private static final Pattern REMOVE_OPTIONAL_BRACKETS = Pattern.compile("^<?([^>]+)>?$");
86345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    // regex that matches end of line.
87345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private static final Pattern END_OF_LINE = Pattern.compile("\r?\n");
88345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
89345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public MimeMessage() {
90345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mHeader = null;
91345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
92345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
93345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
94345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Generate a local message id.  This is only used when none has been assigned, and is
95345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * installed lazily.  Any remote (typically server-assigned) message id takes precedence.
96345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return a long, locally-generated message-ID value
97345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
983b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy    private static String generateMessageId() {
997ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final StringBuilder sb = new StringBuilder();
100345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        sb.append("<");
101345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        for (int i = 0; i < 24; i++) {
102345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // We'll use a 5-bit range (0..31)
1037ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final int value = sRandom.nextInt() & 31;
1047ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final char c = "0123456789abcdefghijklmnopqrstuv".charAt(value);
105345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            sb.append(c);
106345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
107345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        sb.append(".");
108345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        sb.append(Long.toString(System.currentTimeMillis()));
109345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        sb.append("@email.android.com>");
110345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return sb.toString();
111345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
112345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
113345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
114345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Parse the given InputStream using Apache Mime4J to build a MimeMessage.
115345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
1167ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler     * @param in InputStream providing message content
117345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws IOException
118345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
119345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
120345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public MimeMessage(InputStream in) throws IOException, MessagingException {
121345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        parse(in);
122345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
123345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
124345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private MimeStreamParser init() {
125345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // Before parsing the input stream, clear all local fields that may be superceded by
126345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // the new incoming message.
127345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().clear();
128345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mInhibitLocalMessageId = true;
129345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mFrom = null;
130345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mTo = null;
131345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mCc = null;
132345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mBcc = null;
133345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mReplyTo = null;
134345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mSentDate = null;
135345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mBody = null;
136345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
1377ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final MimeStreamParser parser = new MimeStreamParser();
138345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        parser.setContentHandler(new MimeMessageBuilder());
139345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return parser;
140345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
141345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
142345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    protected void parse(InputStream in) throws IOException, MessagingException {
1437ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final MimeStreamParser parser = init();
144345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        parser.parse(new EOLConvertingInputStream(in));
145345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mComplete = !parser.getPrematureEof();
146345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
147345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
148345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void parse(InputStream in, EOLConvertingInputStream.Callback callback)
149345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throws IOException, MessagingException {
1507ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final MimeStreamParser parser = init();
151345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        parser.parse(new EOLConvertingInputStream(in, getSize(), callback));
152345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mComplete = !parser.getPrematureEof();
153345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
154345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
155345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
156345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Return the internal mHeader value, with very lazy initialization.
157345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * The goal is to save memory by not creating the headers until needed.
158345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
159345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    private MimeHeader getMimeHeaders() {
160345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mHeader == null) {
161345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mHeader = new MimeHeader();
162345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
163345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mHeader;
164345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
165345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
166345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
167345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Date getReceivedDate() throws MessagingException {
168345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return null;
169345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
170345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
171345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
172345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Date getSentDate() throws MessagingException {
173345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mSentDate == null) {
174345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
175345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                DateTimeField field = (DateTimeField)Field.parse("Date: "
176345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                        + MimeUtility.unfoldAndDecode(getFirstHeader("Date")));
177345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mSentDate = field.getDate();
17809eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                // TODO: We should make it more clear what exceptions can be thrown here,
17909eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                // and whether they reflect a normal or error condition.
18009eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon            } catch (Exception e) {
1817ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                LogUtils.v(LogUtils.TAG, "Message missing Date header");
18209eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon            }
18309eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon        }
18409eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon        if (mSentDate == null) {
18509eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon            // If we still don't have a date, fall back to "Delivery-date"
18609eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon            try {
18709eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                DateTimeField field = (DateTimeField)Field.parse("Date: "
18809eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                        + MimeUtility.unfoldAndDecode(getFirstHeader("Delivery-date")));
18909eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                mSentDate = field.getDate();
19009eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                // TODO: We should make it more clear what exceptions can be thrown here,
19109eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon                // and whether they reflect a normal or error condition.
192345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (Exception e) {
1937ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                LogUtils.v(LogUtils.TAG, "Message also missing Delivery-Date header");
194345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
195345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
196345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mSentDate;
197345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
198345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
199345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
200345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setSentDate(Date sentDate) throws MessagingException {
201345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        setHeader("Date", DATE_FORMAT.format(sentDate));
202345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        this.mSentDate = sentDate;
203345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
204345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
205345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
206345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getContentType() throws MessagingException {
2077ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final String contentType = getFirstHeader(MimeHeader.HEADER_CONTENT_TYPE);
208345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (contentType == null) {
209345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return "text/plain";
210345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
211345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return contentType;
212345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
213345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
214345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
215aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
216345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getDisposition() throws MessagingException {
2177ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        return getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
218345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
219345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
220aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
221345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getContentId() throws MessagingException {
2227ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
223345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (contentId == null) {
224345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return null;
225345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
226345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // remove optionally surrounding brackets.
227345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
228345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
229345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
230345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
231345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public boolean isComplete() {
232345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mComplete;
233345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
234345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
235aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
236345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getMimeType() throws MessagingException {
237345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return MimeUtility.getHeaderParameter(getContentType(), null);
238345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
239345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
240aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
241345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public int getSize() throws MessagingException {
242345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mSize;
243345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
244345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
245345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
246345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Returns a list of the given recipient type from this message. If no addresses are
247345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * found the method returns an empty array.
248345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
249345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
250345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getRecipients(RecipientType type) throws MessagingException {
251345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (type == RecipientType.TO) {
252345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mTo == null) {
253345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mTo = Address.parse(MimeUtility.unfold(getFirstHeader("To")));
254345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
255345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mTo;
256345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.CC) {
257345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mCc == null) {
258345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mCc = Address.parse(MimeUtility.unfold(getFirstHeader("CC")));
259345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
260345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mCc;
261345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.BCC) {
262345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mBcc == null) {
263345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mBcc = Address.parse(MimeUtility.unfold(getFirstHeader("BCC")));
264345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
265345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mBcc;
266345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
267345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new MessagingException("Unrecognized recipient type.");
268345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
269345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
270345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
271345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
272345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException {
273345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int TO_LENGTH = 4;  // "To: "
274345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int CC_LENGTH = 4;  // "Cc: "
275345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int BCC_LENGTH = 5; // "Bcc: "
276345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (type == RecipientType.TO) {
277345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
278345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("To");
279345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mTo = null;
280345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
281345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("To", MimeUtility.fold(Address.toHeader(addresses), TO_LENGTH));
282345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mTo = addresses;
283345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
284345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.CC) {
285345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
286345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("CC");
287345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mCc = null;
288345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
289345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("CC", MimeUtility.fold(Address.toHeader(addresses), CC_LENGTH));
290345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mCc = addresses;
291345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
292345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.BCC) {
293345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
294345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("BCC");
295345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mBcc = null;
296345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
297345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("BCC", MimeUtility.fold(Address.toHeader(addresses), BCC_LENGTH));
298345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mBcc = addresses;
299345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
300345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
301345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new MessagingException("Unrecognized recipient type.");
302345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
303345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
304345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
305345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
306345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Returns the unfolded, decoded value of the Subject header.
307345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
308345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
309345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getSubject() throws MessagingException {
310345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return MimeUtility.unfoldAndDecode(getFirstHeader("Subject"));
311345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
312345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
313345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
314345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setSubject(String subject) throws MessagingException {
315345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int HEADER_NAME_LENGTH = 9;     // "Subject: "
316345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        setHeader("Subject", MimeUtility.foldAndEncode2(subject, HEADER_NAME_LENGTH));
317345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
318345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
319345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
320345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getFrom() throws MessagingException {
321345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mFrom == null) {
322345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            String list = MimeUtility.unfold(getFirstHeader("From"));
323345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (list == null || list.length() == 0) {
324345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                list = MimeUtility.unfold(getFirstHeader("Sender"));
325345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
326345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mFrom = Address.parse(list);
327345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
328345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mFrom;
329345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
330345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
331345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
332345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setFrom(Address from) throws MessagingException {
333345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int FROM_LENGTH = 6;  // "From: "
334345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (from != null) {
335345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("From", MimeUtility.fold(from.toHeader(), FROM_LENGTH));
336345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            this.mFrom = new Address[] {
337345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    from
338345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                };
339345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
340345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            this.mFrom = null;
341345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
342345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
343345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
344345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
345345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getReplyTo() throws MessagingException {
346345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mReplyTo == null) {
347345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = Address.parse(MimeUtility.unfold(getFirstHeader("Reply-to")));
348345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
349345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mReplyTo;
350345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
351345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
352345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
353345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setReplyTo(Address[] replyTo) throws MessagingException {
354345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int REPLY_TO_LENGTH = 10;  // "Reply-to: "
355345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (replyTo == null || replyTo.length == 0) {
356345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            removeHeader("Reply-to");
357345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = null;
358345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
359345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("Reply-to", MimeUtility.fold(Address.toHeader(replyTo), REPLY_TO_LENGTH));
360345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = replyTo;
361345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
362345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
363345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
364345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
365345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set the mime "Message-ID" header
366345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param messageId the new Message-ID value
367345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
368345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
369345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
370345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setMessageId(String messageId) throws MessagingException {
371345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        setHeader("Message-ID", messageId);
372345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
373345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
374345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
375345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get the mime "Message-ID" header.  This value will be preloaded with a locally-generated
376345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * random ID, if the value has not previously been set.  Local generation can be inhibited/
377345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * overridden by explicitly clearing the headers, removing the message-id header, etc.
378345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return the Message-ID header string, or null if explicitly has been set to null
379345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
380345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
381345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getMessageId() throws MessagingException {
382345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        String messageId = getFirstHeader("Message-ID");
383345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (messageId == null && !mInhibitLocalMessageId) {
384345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            messageId = generateMessageId();
385345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setMessageId(messageId);
386345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
387345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return messageId;
388345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
389345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
390345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
391345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void saveChanges() throws MessagingException {
392345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        throw new MessagingException("saveChanges not yet implemented");
393345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
394345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
395345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
396345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Body getBody() throws MessagingException {
397345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mBody;
398345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
399345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
400345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
401345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setBody(Body body) throws MessagingException {
402345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        this.mBody = body;
403345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (body instanceof Multipart) {
4047ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final Multipart multipart = ((Multipart)body);
405345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            multipart.setParent(this);
406345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
407345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("MIME-Version", "1.0");
408345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
409345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        else if (body instanceof TextBody) {
410345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8",
411345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    getMimeType()));
412345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
413345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
414345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
415345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
416345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    protected String getFirstHeader(String name) throws MessagingException {
417345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return getMimeHeaders().getFirstHeader(name);
418345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
419345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
420345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
421345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void addHeader(String name, String value) throws MessagingException {
422345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().addHeader(name, value);
423345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
424345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
425345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
426345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setHeader(String name, String value) throws MessagingException {
427345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().setHeader(name, value);
428345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
429345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
430345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
431345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String[] getHeader(String name) throws MessagingException {
432345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return getMimeHeaders().getHeader(name);
433345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
434345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
435345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
436345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void removeHeader(String name) throws MessagingException {
437345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().removeHeader(name);
438345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if ("Message-ID".equalsIgnoreCase(name)) {
439345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mInhibitLocalMessageId = true;
440345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
441345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
442345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
443345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
444345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set extended header
445345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
446345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param name Extended header name
447345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param value header value - flattened by removing CR-NL if any
448345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * remove header if value is null
449345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
450345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
451aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
452345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setExtendedHeader(String name, String value) throws MessagingException {
453345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (value == null) {
454345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mExtendedHeader != null) {
455345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mExtendedHeader.removeHeader(name);
456345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
457345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return;
458345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
459345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader == null) {
460345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = new MimeHeader();
461345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
462345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
463345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
464345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
465345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
466345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get extended header
467345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
468345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param name Extended header name
469345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return header value - null if header does not exist
470345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
471345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
472aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
473345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getExtendedHeader(String name) throws MessagingException {
474345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader == null) {
475345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return null;
476345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
477345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mExtendedHeader.getFirstHeader(name);
478345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
479345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
480345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
481345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set entire extended headers from String
482345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
483345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param headers Extended header and its value - "CR-NL-separated pairs
484345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * if null or empty, remove entire extended headers
485345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
486345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
487345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setExtendedHeaders(String headers) throws MessagingException {
488345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (TextUtils.isEmpty(headers)) {
489345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = null;
490345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
491345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = new MimeHeader();
4927ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            for (final String header : END_OF_LINE.split(headers)) {
4937ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                final String[] tokens = header.split(":", 2);
494345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                if (tokens.length != 2) {
495345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    throw new MessagingException("Illegal extended headers: " + headers);
496345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                }
497345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mExtendedHeader.setHeader(tokens[0].trim(), tokens[1].trim());
498345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
499345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
500345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
501345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
502345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
503345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get entire extended headers as String
504345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
505345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return "CR-NL-separated extended headers - null if extended header does not exist
506345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
507345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getExtendedHeaders() {
508345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader != null) {
509345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mExtendedHeader.writeToString();
510345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
511345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return null;
512345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
513345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
514345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
515345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Write message header and body to output stream
516345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
517345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param out Output steam to write message header and body.
518345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
519aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
520345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void writeTo(OutputStream out) throws IOException, MessagingException {
5217ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler        final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
522345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // Force creation of local message-id
523345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMessageId();
524345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().writeTo(out);
525345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // mExtendedHeader will not be write out to external output stream,
526345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // because it is intended to internal use.
527345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        writer.write("\r\n");
528345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        writer.flush();
529345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mBody != null) {
530345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mBody.writeTo(out);
531345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
532345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
533345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
534aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
535345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public InputStream getInputStream() throws MessagingException {
536345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return null;
537345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
538345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
539345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    class MimeMessageBuilder implements ContentHandler {
54009eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon        private final Stack<Object> stack = new Stack<Object>();
541345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
542345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public MimeMessageBuilder() {
543345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
544345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
5453b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy        private void expect(Class<?> c) {
546345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (!c.isInstance(stack.peek())) {
547345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new IllegalStateException("Internal stack error: " + "Expected '"
548345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                        + c.getName() + "' found '" + stack.peek().getClass().getName() + "'");
549345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
550345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
551345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
552aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
553345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startMessage() {
554345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (stack.isEmpty()) {
555345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(MimeMessage.this);
556345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
557345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                expect(Part.class);
558345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                try {
5597ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                    final MimeMessage m = new MimeMessage();
560345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    ((Part)stack.peek()).setBody(m);
561345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    stack.push(m);
562345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                } catch (MessagingException me) {
563345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    throw new Error(me);
564345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                }
565345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
566345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
567345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
568aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
569345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endMessage() {
570345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMessage.class);
571345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
572345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
573345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
574aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
575345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startHeader() {
576345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
577345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
578345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
579aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
580345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void field(String fieldData) {
581345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
582345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
5837ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                final String[] tokens = fieldData.split(":", 2);
584345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim());
585345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
586345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
587345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
588345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
589345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
590aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
591345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endHeader() {
592345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
593345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
594345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
595aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
596345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startMultipart(BodyDescriptor bd) {
597345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
598345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
5997ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final Part e = (Part)stack.peek();
600345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
6017ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                final MimeMultipart multiPart = new MimeMultipart(e.getContentType());
602345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                e.setBody(multiPart);
603345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(multiPart);
604345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
605345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
606345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
607345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
608345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
609aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
610345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void body(BodyDescriptor bd, InputStream in) throws IOException {
611345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
6127ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
613345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
614345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((Part)stack.peek()).setBody(body);
615345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
616345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
617345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
618345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
619345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
620aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
621345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endMultipart() {
622345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
623345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
624345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
625aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
626345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startBodyPart() {
627345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
628345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
629345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
6307ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler                final MimeBodyPart bodyPart = new MimeBodyPart();
631345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((MimeMultipart)stack.peek()).addBodyPart(bodyPart);
632345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(bodyPart);
633345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
634345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
635345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
636345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
637345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
638aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
639345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endBodyPart() {
640345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(BodyPart.class);
641345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
642345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
643345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
644aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
645345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void epilogue(InputStream is) throws IOException {
646345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
6477ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final StringBuilder sb = new StringBuilder();
648345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            int b;
649345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            while ((b = is.read()) != -1) {
650345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                sb.append((char)b);
651345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
6527ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            // TODO: why is this commented out?
653345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // ((Multipart) stack.peek()).setEpilogue(sb.toString());
654345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
655345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
656aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
657345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void preamble(InputStream is) throws IOException {
658345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
6597ddf672ebc7b43350a52573ef682e4f3f59ae2d1Tony Mantler            final StringBuilder sb = new StringBuilder();
660345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            int b;
661345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            while ((b = is.read()) != -1) {
662345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                sb.append((char)b);
663345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
664345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
665345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((MimeMultipart)stack.peek()).setPreamble(sb.toString());
666345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
667345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
668345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
669345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
670345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
671aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
672345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void raw(InputStream is) throws IOException {
673345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new UnsupportedOperationException("Not supported");
674345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
675345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
676345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein}
677