001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.mime4j.message;
021
022import java.io.IOException;
023import java.io.InputStream;
024
025import org.apache.james.mime4j.MimeException;
026import org.apache.james.mime4j.MimeIOException;
027import org.apache.james.mime4j.codec.DecodeMonitor;
028import org.apache.james.mime4j.dom.Body;
029import org.apache.james.mime4j.dom.Disposable;
030import org.apache.james.mime4j.dom.Entity;
031import org.apache.james.mime4j.dom.FieldParser;
032import org.apache.james.mime4j.dom.Header;
033import org.apache.james.mime4j.dom.Message;
034import org.apache.james.mime4j.dom.MessageBuilder;
035import org.apache.james.mime4j.dom.Multipart;
036import org.apache.james.mime4j.dom.SingleBody;
037import org.apache.james.mime4j.dom.field.ParsedField;
038import org.apache.james.mime4j.field.DefaultFieldParser;
039import org.apache.james.mime4j.field.LenientFieldParser;
040import org.apache.james.mime4j.parser.AbstractContentHandler;
041import org.apache.james.mime4j.parser.MimeStreamParser;
042import org.apache.james.mime4j.stream.BodyDescriptorBuilder;
043import org.apache.james.mime4j.stream.Field;
044import org.apache.james.mime4j.stream.MimeConfig;
045
046/**
047 * Default implementation of {@link MessageBuilder}.
048 */
049public class DefaultMessageBuilder implements MessageBuilder {
050
051    private FieldParser<? extends ParsedField> fieldParser = null;
052    private BodyFactory bodyFactory = null;
053    private MimeConfig config = null;
054    private BodyDescriptorBuilder bodyDescBuilder = null;
055    private boolean contentDecoding = true;
056    private boolean flatMode = false;
057    private DecodeMonitor monitor = null;
058
059    public DefaultMessageBuilder() {
060        super();
061    }
062
063    public void setFieldParser(final FieldParser<? extends ParsedField> fieldParser) {
064        this.fieldParser = fieldParser;
065    }
066
067    public void setBodyFactory(final BodyFactory bodyFactory) {
068        this.bodyFactory = bodyFactory;
069    }
070
071    public void setMimeEntityConfig(final MimeConfig config) {
072        this.config = config;
073    }
074
075    public void setBodyDescriptorBuilder(final BodyDescriptorBuilder bodyDescBuilder) {
076        this.bodyDescBuilder  = bodyDescBuilder;
077    }
078
079    public void setDecodeMonitor(final DecodeMonitor monitor) {
080        this.monitor = monitor;
081    }
082
083    public void setContentDecoding(boolean contentDecoding) {
084        this.contentDecoding = contentDecoding;
085    }
086
087    public void setFlatMode(boolean flatMode) {
088        this.flatMode = flatMode;
089    }
090
091    /**
092     * Creates a new <code>Header</code> from the specified
093     * <code>Header</code>. The <code>Header</code> instance is initialized
094     * with a copy of the list of {@link Field}s of the specified
095     * <code>Header</code>. The <code>Field</code> objects are not copied
096     * because they are immutable and can safely be shared between headers.
097     *
098     * @param other
099     *            header to copy.
100     */
101    public Header copy(Header other) {
102        HeaderImpl copy = new HeaderImpl();
103        for (Field otherField : other.getFields()) {
104            copy.addField(otherField);
105        }
106        return copy;
107    }
108
109    /**
110     * Creates a new <code>BodyPart</code> from the specified
111     * <code>Entity</code>. The <code>BodyPart</code> instance is initialized
112     * with copies of header and body of the specified <code>Entity</code>.
113     * The parent entity of the new body part is <code>null</code>.
114     *
115     * @param other
116     *            body part to copy.
117     * @throws UnsupportedOperationException
118     *             if <code>other</code> contains a {@link SingleBody} that
119     *             does not support the {@link SingleBody#copy() copy()}
120     *             operation.
121     * @throws IllegalArgumentException
122     *             if <code>other</code> contains a <code>Body</code> that
123     *             is neither a {@link Message}, {@link Multipart} or
124     *             {@link SingleBody}.
125     */
126    public BodyPart copy(Entity other) {
127        BodyPart copy = new BodyPart();
128        if (other.getHeader() != null) {
129            copy.setHeader(copy(other.getHeader()));
130        }
131        if (other.getBody() != null) {
132            copy.setBody(copy(other.getBody()));
133        }
134        return copy;
135    }
136
137    /**
138     * Creates a new <code>Multipart</code> from the specified
139     * <code>Multipart</code>. The <code>Multipart</code> instance is
140     * initialized with copies of preamble, epilogue, sub type and the list of
141     * body parts of the specified <code>Multipart</code>. The parent entity
142     * of the new multipart is <code>null</code>.
143     *
144     * @param other
145     *            multipart to copy.
146     * @throws UnsupportedOperationException
147     *             if <code>other</code> contains a {@link SingleBody} that
148     *             does not support the {@link SingleBody#copy() copy()}
149     *             operation.
150     * @throws IllegalArgumentException
151     *             if <code>other</code> contains a <code>Body</code> that
152     *             is neither a {@link Message}, {@link Multipart} or
153     *             {@link SingleBody}.
154     */
155    public Multipart copy(Multipart other) {
156        MultipartImpl copy = new MultipartImpl(other.getSubType());
157        for (Entity otherBodyPart : other.getBodyParts()) {
158            copy.addBodyPart(copy(otherBodyPart));
159        }
160        copy.setPreamble(other.getPreamble());
161        copy.setEpilogue(other.getEpilogue());
162        return copy;
163    }
164
165
166    /**
167     * Returns a copy of the given {@link Body} that can be used (and modified)
168     * independently of the original. The copy should be
169     * {@link Disposable#dispose() disposed of} when it is no longer needed.
170     * <p>
171     * The {@link Body#getParent() parent} of the returned copy is
172     * <code>null</code>, that is, the copy is detached from the parent
173     * entity of the original.
174     *
175     * @param body
176     *            body to copy.
177     * @return a copy of the given body.
178     * @throws UnsupportedOperationException
179     *             if <code>body</code> is an instance of {@link SingleBody}
180     *             that does not support the {@link SingleBody#copy() copy()}
181     *             operation (or contains such a <code>SingleBody</code>).
182     * @throws IllegalArgumentException
183     *             if <code>body</code> is <code>null</code> or
184     *             <code>body</code> is a <code>Body</code> that is neither
185     *             a {@link MessageImpl}, {@link Multipart} or {@link SingleBody}
186     *             (or contains such a <code>Body</code>).
187     */
188    public Body copy(Body body) {
189        if (body == null)
190            throw new IllegalArgumentException("Body is null");
191
192        if (body instanceof Message)
193            return copy((Message) body);
194
195        if (body instanceof Multipart)
196            return copy((Multipart) body);
197
198        if (body instanceof SingleBody)
199            return ((SingleBody) body).copy();
200
201        throw new IllegalArgumentException("Unsupported body class");
202    }
203
204    /**
205     * Creates a new <code>Message</code> from the specified
206     * <code>Message</code>. The <code>Message</code> instance is
207     * initialized with copies of header and body of the specified
208     * <code>Message</code>. The parent entity of the new message is
209     * <code>null</code>.
210     *
211     * @param other
212     *            message to copy.
213     * @throws UnsupportedOperationException
214     *             if <code>other</code> contains a {@link SingleBody} that
215     *             does not support the {@link SingleBody#copy() copy()}
216     *             operation.
217     * @throws IllegalArgumentException
218     *             if <code>other</code> contains a <code>Body</code> that
219     *             is neither a {@link MessageImpl}, {@link Multipart} or
220     *             {@link SingleBody}.
221     */
222    public Message copy(Message other) {
223        MessageImpl copy = new MessageImpl();
224        if (other.getHeader() != null) {
225            copy.setHeader(copy(other.getHeader()));
226        }
227        if (other.getBody() != null) {
228            copy.setBody(copy(other.getBody()));
229        }
230        return copy;
231    }
232
233    public Header newHeader() {
234        return new HeaderImpl();
235    }
236
237    public Header newHeader(final Header source) {
238        return copy(source);
239    }
240
241    public Multipart newMultipart(final String subType) {
242        return new MultipartImpl(subType);
243    }
244
245    public Multipart newMultipart(final Multipart source) {
246        return copy(source);
247    }
248
249    public Header parseHeader(final InputStream is) throws IOException, MimeIOException {
250        final MimeConfig cfg = config != null ? config : new MimeConfig();
251        boolean strict = cfg.isStrictParsing();
252        final DecodeMonitor mon = monitor != null ? monitor :
253            strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT;
254        final FieldParser<? extends ParsedField> fp = fieldParser != null ? fieldParser :
255            strict ? DefaultFieldParser.getParser() : LenientFieldParser.getParser();
256        final HeaderImpl header = new HeaderImpl();
257        final MimeStreamParser parser = new MimeStreamParser();
258        parser.setContentHandler(new AbstractContentHandler() {
259            @Override
260            public void endHeader() {
261                parser.stop();
262            }
263            @Override
264            public void field(Field field) throws MimeException {
265                ParsedField parsedField;
266                if (field instanceof ParsedField) {
267                    parsedField = (ParsedField) field;
268                } else {
269                    parsedField = fp.parse(field, mon);
270                }
271                header.addField(parsedField);
272            }
273        });
274        try {
275            parser.parse(is);
276        } catch (MimeException ex) {
277            throw new MimeIOException(ex);
278        }
279        return header;
280    }
281
282    public Message newMessage() {
283        return new MessageImpl();
284    }
285
286    public Message newMessage(final Message source) {
287        return copy(source);
288    }
289
290    public Message parseMessage(final InputStream is) throws IOException, MimeIOException {
291        try {
292            MessageImpl message = new MessageImpl();
293            MimeConfig cfg = config != null ? config : new MimeConfig();
294            boolean strict = cfg.isStrictParsing();
295            DecodeMonitor mon = monitor != null ? monitor :
296                strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT;
297            BodyDescriptorBuilder bdb = bodyDescBuilder != null ? bodyDescBuilder :
298                new DefaultBodyDescriptorBuilder(null, fieldParser != null ? fieldParser :
299                    strict ? DefaultFieldParser.getParser() : LenientFieldParser.getParser(), mon);
300            BodyFactory bf = bodyFactory != null ? bodyFactory : new BasicBodyFactory();
301            MimeStreamParser parser = new MimeStreamParser(cfg, mon, bdb);
302            // EntityBuilder expect the parser will send ParserFields for the well known fields
303            // It will throw exceptions, otherwise.
304            parser.setContentHandler(new EntityBuilder(message, bf));
305            parser.setContentDecoding(contentDecoding);
306            if (flatMode) {
307                parser.setFlat();
308            } else {
309                parser.setRecurse();
310            }
311            parser.parse(is);
312            return message;
313        } catch (MimeException e) {
314            throw new MimeIOException(e);
315        }
316    }
317
318}