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
020 package org.apache.james.mime4j.message;
021
022 import java.io.IOException;
023 import java.io.InputStream;
024
025 import org.apache.james.mime4j.MimeException;
026 import org.apache.james.mime4j.MimeIOException;
027 import org.apache.james.mime4j.codec.DecodeMonitor;
028 import org.apache.james.mime4j.dom.Body;
029 import org.apache.james.mime4j.dom.Disposable;
030 import org.apache.james.mime4j.dom.Entity;
031 import org.apache.james.mime4j.dom.FieldParser;
032 import org.apache.james.mime4j.dom.Header;
033 import org.apache.james.mime4j.dom.Message;
034 import org.apache.james.mime4j.dom.MessageBuilder;
035 import org.apache.james.mime4j.dom.Multipart;
036 import org.apache.james.mime4j.dom.SingleBody;
037 import org.apache.james.mime4j.dom.field.ParsedField;
038 import org.apache.james.mime4j.field.DefaultFieldParser;
039 import org.apache.james.mime4j.field.LenientFieldParser;
040 import org.apache.james.mime4j.parser.AbstractContentHandler;
041 import org.apache.james.mime4j.parser.MimeStreamParser;
042 import org.apache.james.mime4j.stream.BodyDescriptorBuilder;
043 import org.apache.james.mime4j.stream.Field;
044 import org.apache.james.mime4j.stream.MimeConfig;
045
046 /**
047 * Default implementation of {@link MessageBuilder}.
048 */
049 public 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 }