Message.java revision 8978aac1977408b05e386ae846c30920c7faa0a6
1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19
20package org.apache.james.mime4j.message;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25import java.util.Stack;
26
27import org.apache.james.mime4j.BodyDescriptor;
28import org.apache.james.mime4j.ContentHandler;
29import org.apache.james.mime4j.MimeStreamParser;
30import org.apache.james.mime4j.decoder.Base64InputStream;
31import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
32import org.apache.james.mime4j.field.Field;
33import org.apache.james.mime4j.field.UnstructuredField;
34
35
36/**
37 * Represents a MIME message. The following code parses a stream into a
38 * <code>Message</code> object.
39 *
40 * <pre>
41 *      Message msg = new Message(new BufferedInputStream(
42 *                                      new FileInputStream("mime.msg")));
43 * </pre>
44 *
45 *
46 *
47 * @version $Id: Message.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
48 */
49public class Message extends Entity implements Body {
50
51    /**
52     * Creates a new empty <code>Message</code>.
53     */
54    public Message() {
55    }
56
57    /**
58     * Parses the specified MIME message stream into a <code>Message</code>
59     * instance.
60     *
61     * @param is the stream to parse.
62     * @throws IOException on I/O errors.
63     */
64    public Message(InputStream is) throws IOException {
65        MimeStreamParser parser = new MimeStreamParser();
66        parser.setContentHandler(new MessageBuilder());
67        parser.parse(is);
68    }
69
70
71    /**
72     * Gets the <code>Subject</code> field.
73     *
74     * @return the <code>Subject</code> field or <code>null</code> if it
75     *         doesn't exist.
76     */
77    public UnstructuredField getSubject() {
78        return (UnstructuredField) getHeader().getField(Field.SUBJECT);
79    }
80
81    /**
82     *
83     * @see org.apache.james.mime4j.message.Entity#writeTo(java.io.OutputStream)
84     */
85    public void writeTo(OutputStream out) throws IOException {
86        getHeader().writeTo(out);
87
88        Body body = getBody();
89        if (body instanceof Multipart) {
90            Multipart mp = (Multipart) body;
91            mp.writeTo(out);
92        } else {
93            body.writeTo(out);
94        }
95    }
96
97
98    private class MessageBuilder implements ContentHandler {
99        private Stack stack = new Stack();
100
101        public MessageBuilder() {
102        }
103
104        private void expect(Class c) {
105            if (!c.isInstance(stack.peek())) {
106                throw new IllegalStateException("Internal stack error: "
107                        + "Expected '" + c.getName() + "' found '"
108                        + stack.peek().getClass().getName() + "'");
109            }
110        }
111
112        /**
113         * @see org.apache.james.mime4j.ContentHandler#startMessage()
114         */
115        public void startMessage() {
116            if (stack.isEmpty()) {
117                stack.push(Message.this);
118            } else {
119                expect(Entity.class);
120                Message m = new Message();
121                ((Entity) stack.peek()).setBody(m);
122                stack.push(m);
123            }
124        }
125
126        /**
127         * @see org.apache.james.mime4j.ContentHandler#endMessage()
128         */
129        public void endMessage() {
130            expect(Message.class);
131            stack.pop();
132        }
133
134        /**
135         * @see org.apache.james.mime4j.ContentHandler#startHeader()
136         */
137        public void startHeader() {
138            stack.push(new Header());
139        }
140
141        /**
142         * @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
143         */
144        public void field(String fieldData) {
145            expect(Header.class);
146            ((Header) stack.peek()).addField(Field.parse(fieldData));
147        }
148
149        /**
150         * @see org.apache.james.mime4j.ContentHandler#endHeader()
151         */
152        public void endHeader() {
153            expect(Header.class);
154            Header h = (Header) stack.pop();
155            expect(Entity.class);
156            ((Entity) stack.peek()).setHeader(h);
157        }
158
159        /**
160         * @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
161         */
162        public void startMultipart(BodyDescriptor bd) {
163            expect(Entity.class);
164
165            Entity e = (Entity) stack.peek();
166            Multipart multiPart = new Multipart();
167            e.setBody(multiPart);
168            stack.push(multiPart);
169        }
170
171        /**
172         * @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
173         */
174        public void body(BodyDescriptor bd, InputStream is) throws IOException {
175            expect(Entity.class);
176
177            String enc = bd.getTransferEncoding();
178            if ("base64".equals(enc)) {
179                is = new Base64InputStream(is);
180            } else if ("quoted-printable".equals(enc)) {
181                is = new QuotedPrintableInputStream(is);
182            }
183
184            Body body = null;
185            if (bd.getMimeType().startsWith("text/")) {
186                body = new MemoryTextBody(is, bd.getCharset());
187            } else {
188                body = new MemoryBinaryBody(is);
189            }
190
191            ((Entity) stack.peek()).setBody(body);
192        }
193
194        /**
195         * @see org.apache.james.mime4j.ContentHandler#endMultipart()
196         */
197        public void endMultipart() {
198            stack.pop();
199        }
200
201        /**
202         * @see org.apache.james.mime4j.ContentHandler#startBodyPart()
203         */
204        public void startBodyPart() {
205            expect(Multipart.class);
206
207            BodyPart bodyPart = new BodyPart();
208            ((Multipart) stack.peek()).addBodyPart(bodyPart);
209            stack.push(bodyPart);
210        }
211
212        /**
213         * @see org.apache.james.mime4j.ContentHandler#endBodyPart()
214         */
215        public void endBodyPart() {
216            expect(BodyPart.class);
217            stack.pop();
218        }
219
220        /**
221         * @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
222         */
223        public void epilogue(InputStream is) throws IOException {
224            expect(Multipart.class);
225            StringBuffer sb = new StringBuffer();
226            int b;
227            while ((b = is.read()) != -1) {
228                sb.append((char) b);
229            }
230            ((Multipart) stack.peek()).setEpilogue(sb.toString());
231        }
232
233        /**
234         * @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
235         */
236        public void preamble(InputStream is) throws IOException {
237            expect(Multipart.class);
238            StringBuffer sb = new StringBuffer();
239            int b;
240            while ((b = is.read()) != -1) {
241                sb.append((char) b);
242            }
243            ((Multipart) stack.peek()).setPreamble(sb.toString());
244        }
245
246        /**
247         * TODO: Implement me
248         *
249         * @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
250         */
251        public void raw(InputStream is) throws IOException {
252            throw new UnsupportedOperationException("Not supported");
253        }
254
255    }
256}
257