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() {
99345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        StringBuffer sb = new StringBuffer();
100345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        sb.append("<");
101345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        for (int i = 0; i < 24; i++) {
102345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // We'll use a 5-bit range (0..31)
103345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            int value = sRandom.nextInt() & 31;
104345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            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     *
116345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param in
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
137345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        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 {
143345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        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 {
150345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        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) {
18109eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon
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) {
193345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
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 {
207345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        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 {
217345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        String contentDisposition = getFirstHeader(MimeHeader.HEADER_CONTENT_DISPOSITION);
218345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (contentDisposition == null) {
219345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return null;
220345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
221345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return contentDisposition;
222345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
223345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
224345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
225aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
226345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getContentId() throws MessagingException {
227345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        String contentId = getFirstHeader(MimeHeader.HEADER_CONTENT_ID);
228345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (contentId == null) {
229345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return null;
230345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
231345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // remove optionally surrounding brackets.
232345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return REMOVE_OPTIONAL_BRACKETS.matcher(contentId).replaceAll("$1");
233345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
234345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
235345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
236345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public boolean isComplete() {
237345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mComplete;
238345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
239345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
240aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
241345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getMimeType() throws MessagingException {
242345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return MimeUtility.getHeaderParameter(getContentType(), null);
243345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
244345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
245aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
246345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public int getSize() throws MessagingException {
247345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mSize;
248345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
249345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
250345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
251345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Returns a list of the given recipient type from this message. If no addresses are
252345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * found the method returns an empty array.
253345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
254345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
255345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getRecipients(RecipientType type) throws MessagingException {
256345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (type == RecipientType.TO) {
257345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mTo == null) {
258345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mTo = Address.parse(MimeUtility.unfold(getFirstHeader("To")));
259345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
260345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mTo;
261345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.CC) {
262345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mCc == null) {
263345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mCc = Address.parse(MimeUtility.unfold(getFirstHeader("CC")));
264345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
265345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mCc;
266345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.BCC) {
267345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mBcc == null) {
268345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mBcc = Address.parse(MimeUtility.unfold(getFirstHeader("BCC")));
269345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
270345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mBcc;
271345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
272345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new MessagingException("Unrecognized recipient type.");
273345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
274345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
275345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
276345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
277345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException {
278345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int TO_LENGTH = 4;  // "To: "
279345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int CC_LENGTH = 4;  // "Cc: "
280345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int BCC_LENGTH = 5; // "Bcc: "
281345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (type == RecipientType.TO) {
282345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
283345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("To");
284345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mTo = null;
285345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
286345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("To", MimeUtility.fold(Address.toHeader(addresses), TO_LENGTH));
287345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mTo = addresses;
288345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
289345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.CC) {
290345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
291345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("CC");
292345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mCc = null;
293345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
294345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("CC", MimeUtility.fold(Address.toHeader(addresses), CC_LENGTH));
295345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mCc = addresses;
296345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
297345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else if (type == RecipientType.BCC) {
298345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (addresses == null || addresses.length == 0) {
299345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                removeHeader("BCC");
300345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mBcc = null;
301345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
302345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                setHeader("BCC", MimeUtility.fold(Address.toHeader(addresses), BCC_LENGTH));
303345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                this.mBcc = addresses;
304345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
305345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
306345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new MessagingException("Unrecognized recipient type.");
307345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
308345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
309345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
310345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
311345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Returns the unfolded, decoded value of the Subject header.
312345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
313345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
314345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getSubject() throws MessagingException {
315345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return MimeUtility.unfoldAndDecode(getFirstHeader("Subject"));
316345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
317345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
318345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
319345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setSubject(String subject) throws MessagingException {
320345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int HEADER_NAME_LENGTH = 9;     // "Subject: "
321345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        setHeader("Subject", MimeUtility.foldAndEncode2(subject, HEADER_NAME_LENGTH));
322345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
323345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
324345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
325345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getFrom() throws MessagingException {
326345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mFrom == null) {
327345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            String list = MimeUtility.unfold(getFirstHeader("From"));
328345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (list == null || list.length() == 0) {
329345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                list = MimeUtility.unfold(getFirstHeader("Sender"));
330345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
331345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mFrom = Address.parse(list);
332345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
333345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mFrom;
334345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
335345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
336345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
337345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setFrom(Address from) throws MessagingException {
338345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int FROM_LENGTH = 6;  // "From: "
339345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (from != null) {
340345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("From", MimeUtility.fold(from.toHeader(), FROM_LENGTH));
341345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            this.mFrom = new Address[] {
342345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    from
343345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                };
344345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
345345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            this.mFrom = null;
346345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
347345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
348345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
349345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
350345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Address[] getReplyTo() throws MessagingException {
351345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mReplyTo == null) {
352345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = Address.parse(MimeUtility.unfold(getFirstHeader("Reply-to")));
353345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
354345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mReplyTo;
355345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
356345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
357345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
358345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setReplyTo(Address[] replyTo) throws MessagingException {
359345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        final int REPLY_TO_LENGTH = 10;  // "Reply-to: "
360345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (replyTo == null || replyTo.length == 0) {
361345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            removeHeader("Reply-to");
362345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = null;
363345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
364345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("Reply-to", MimeUtility.fold(Address.toHeader(replyTo), REPLY_TO_LENGTH));
365345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mReplyTo = replyTo;
366345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
367345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
368345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
369345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
370345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set the mime "Message-ID" header
371345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param messageId the new Message-ID value
372345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
373345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
374345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
375345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setMessageId(String messageId) throws MessagingException {
376345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        setHeader("Message-ID", messageId);
377345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
378345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
379345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
380345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get the mime "Message-ID" header.  This value will be preloaded with a locally-generated
381345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * random ID, if the value has not previously been set.  Local generation can be inhibited/
382345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * overridden by explicitly clearing the headers, removing the message-id header, etc.
383345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return the Message-ID header string, or null if explicitly has been set to null
384345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
385345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
386345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getMessageId() throws MessagingException {
387345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        String messageId = getFirstHeader("Message-ID");
388345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (messageId == null && !mInhibitLocalMessageId) {
389345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            messageId = generateMessageId();
390345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setMessageId(messageId);
391345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
392345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return messageId;
393345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
394345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
395345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
396345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void saveChanges() throws MessagingException {
397345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        throw new MessagingException("saveChanges not yet implemented");
398345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
399345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
400345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
401345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public Body getBody() throws MessagingException {
402345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mBody;
403345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
404345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
405345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
406345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setBody(Body body) throws MessagingException {
407345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        this.mBody = body;
408345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (body instanceof Multipart) {
409345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            Multipart multipart = ((Multipart)body);
410345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            multipart.setParent(this);
411345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TYPE, multipart.getContentType());
412345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader("MIME-Version", "1.0");
413345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
414345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        else if (body instanceof TextBody) {
415345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TYPE, String.format("%s;\n charset=utf-8",
416345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    getMimeType()));
417345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            setHeader(MimeHeader.HEADER_CONTENT_TRANSFER_ENCODING, "base64");
418345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
419345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
420345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
421345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    protected String getFirstHeader(String name) throws MessagingException {
422345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return getMimeHeaders().getFirstHeader(name);
423345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
424345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
425345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
426345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void addHeader(String name, String value) throws MessagingException {
427345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().addHeader(name, value);
428345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
429345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
430345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
431345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setHeader(String name, String value) throws MessagingException {
432345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().setHeader(name, value);
433345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
434345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
435345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
436345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String[] getHeader(String name) throws MessagingException {
437345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return getMimeHeaders().getHeader(name);
438345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
439345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
440345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    @Override
441345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void removeHeader(String name) throws MessagingException {
442345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().removeHeader(name);
443345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if ("Message-ID".equalsIgnoreCase(name)) {
444345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mInhibitLocalMessageId = true;
445345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
446345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
447345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
448345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
449345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set extended header
450345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
451345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param name Extended header name
452345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param value header value - flattened by removing CR-NL if any
453345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * remove header if value is null
454345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
455345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
456aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
457345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setExtendedHeader(String name, String value) throws MessagingException {
458345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (value == null) {
459345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (mExtendedHeader != null) {
460345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mExtendedHeader.removeHeader(name);
461345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
462345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return;
463345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
464345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader == null) {
465345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = new MimeHeader();
466345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
467345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        mExtendedHeader.setHeader(name, END_OF_LINE.matcher(value).replaceAll(""));
468345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
469345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
470345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
471345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get extended header
472345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
473345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param name Extended header name
474345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return header value - null if header does not exist
475345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
476345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
477aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
478345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getExtendedHeader(String name) throws MessagingException {
479345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader == null) {
480345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return null;
481345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
482345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return mExtendedHeader.getFirstHeader(name);
483345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
484345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
485345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
486345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Set entire extended headers from String
487345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
488345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param headers Extended header and its value - "CR-NL-separated pairs
489345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * if null or empty, remove entire extended headers
490345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @throws MessagingException
491345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
492345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void setExtendedHeaders(String headers) throws MessagingException {
493345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (TextUtils.isEmpty(headers)) {
494345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = null;
495345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        } else {
496345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mExtendedHeader = new MimeHeader();
497345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            for (String header : END_OF_LINE.split(headers)) {
498345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                String[] tokens = header.split(":", 2);
499345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                if (tokens.length != 2) {
500345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    throw new MessagingException("Illegal extended headers: " + headers);
501345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                }
502345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                mExtendedHeader.setHeader(tokens[0].trim(), tokens[1].trim());
503345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
504345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
505345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
506345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
507345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
508345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Get entire extended headers as String
509345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
510345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @return "CR-NL-separated extended headers - null if extended header does not exist
511345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
512345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public String getExtendedHeaders() {
513345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mExtendedHeader != null) {
514345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            return mExtendedHeader.writeToString();
515345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
516345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return null;
517345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
518345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
519345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    /**
520345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * Write message header and body to output stream
521345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     *
522345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     * @param out Output steam to write message header and body.
523345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein     */
524aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
525345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public void writeTo(OutputStream out) throws IOException, MessagingException {
526345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024);
527345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // Force creation of local message-id
528345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMessageId();
529345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        getMimeHeaders().writeTo(out);
530345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // mExtendedHeader will not be write out to external output stream,
531345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        // because it is intended to internal use.
532345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        writer.write("\r\n");
533345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        writer.flush();
534345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        if (mBody != null) {
535345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            mBody.writeTo(out);
536345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
537345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
538345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
539aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy    @Override
540345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    public InputStream getInputStream() throws MessagingException {
541345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        return null;
542345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
543345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
544345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    class MimeMessageBuilder implements ContentHandler {
54509eca8072a2396527baede2b312d91daf1c9c2b9Martin Hibdon        private final Stack<Object> stack = new Stack<Object>();
546345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
547345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public MimeMessageBuilder() {
548345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
549345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
5503b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy        private void expect(Class<?> c) {
551345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (!c.isInstance(stack.peek())) {
552345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new IllegalStateException("Internal stack error: " + "Expected '"
553345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                        + c.getName() + "' found '" + stack.peek().getClass().getName() + "'");
554345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
555345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
556345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
557aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
558345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startMessage() {
559345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            if (stack.isEmpty()) {
560345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(MimeMessage.this);
561345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } else {
562345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                expect(Part.class);
563345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                try {
564345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    MimeMessage m = new MimeMessage();
565345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    ((Part)stack.peek()).setBody(m);
566345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    stack.push(m);
567345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                } catch (MessagingException me) {
568345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                    throw new Error(me);
569345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                }
570345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
571345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
572345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
573aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
574345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endMessage() {
575345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMessage.class);
576345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
577345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
578345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
579aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
580345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startHeader() {
581345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
582345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
583345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
584aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
585345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void field(String fieldData) {
586345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
587345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
588345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                String[] tokens = fieldData.split(":", 2);
589345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((Part)stack.peek()).addHeader(tokens[0], tokens[1].trim());
590345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
591345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
592345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
593345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
594345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
595aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
596345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endHeader() {
597345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
598345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
599345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
600aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
601345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startMultipart(BodyDescriptor bd) {
602345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
603345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
604345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            Part e = (Part)stack.peek();
605345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
606345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                MimeMultipart multiPart = new MimeMultipart(e.getContentType());
607345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                e.setBody(multiPart);
608345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(multiPart);
609345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
610345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
611345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
612345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
613345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
614aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
615345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void body(BodyDescriptor bd, InputStream in) throws IOException {
616345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(Part.class);
617345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            Body body = MimeUtility.decodeBody(in, bd.getTransferEncoding());
618345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
619345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((Part)stack.peek()).setBody(body);
620345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
621345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
622345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
623345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
624345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
625aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
626345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endMultipart() {
627345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
628345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
629345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
630aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
631345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void startBodyPart() {
632345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
633345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
634345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
635345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                MimeBodyPart bodyPart = new MimeBodyPart();
636345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((MimeMultipart)stack.peek()).addBodyPart(bodyPart);
637345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                stack.push(bodyPart);
638345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
639345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
640345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
641345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
642345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
643aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
644345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void endBodyPart() {
645345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(BodyPart.class);
646345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            stack.pop();
647345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
648345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
649aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
650345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void epilogue(InputStream is) throws IOException {
651345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
652345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            StringBuffer sb = new StringBuffer();
653345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            int b;
654345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            while ((b = is.read()) != -1) {
655345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                sb.append((char)b);
656345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
657345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            // ((Multipart) stack.peek()).setEpilogue(sb.toString());
658345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
659345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
660aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
661345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void preamble(InputStream is) throws IOException {
662345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            expect(MimeMultipart.class);
663345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            StringBuffer sb = new StringBuffer();
664345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            int b;
665345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            while ((b = is.read()) != -1) {
666345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                sb.append((char)b);
667345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
668345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            try {
669345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                ((MimeMultipart)stack.peek()).setPreamble(sb.toString());
670345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            } catch (MessagingException me) {
671345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein                throw new Error(me);
672345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            }
673345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
674345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein
675aa27bc0e1c3bb6be4609b00007637a9d3e960f5eScott Kennedy        @Override
676345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        public void raw(InputStream is) throws IOException {
677345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein            throw new UnsupportedOperationException("Not supported");
678345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein        }
679345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein    }
680345c43e12db42f6bdc0c15ac1c96af10164a458cAndrew Sapperstein}
681