View Javadoc

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  
20  package org.apache.james.mime4j.message;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.Date;
29  import java.util.TimeZone;
30  
31  import org.apache.james.mime4j.MimeException;
32  import org.apache.james.mime4j.MimeIOException;
33  import org.apache.james.mime4j.field.AddressListField;
34  import org.apache.james.mime4j.field.DateTimeField;
35  import org.apache.james.mime4j.field.FieldName;
36  import org.apache.james.mime4j.field.Fields;
37  import org.apache.james.mime4j.field.MailboxField;
38  import org.apache.james.mime4j.field.MailboxListField;
39  import org.apache.james.mime4j.field.UnstructuredField;
40  import org.apache.james.mime4j.field.address.Address;
41  import org.apache.james.mime4j.field.address.AddressList;
42  import org.apache.james.mime4j.field.address.Mailbox;
43  import org.apache.james.mime4j.field.address.MailboxList;
44  import org.apache.james.mime4j.parser.Field;
45  import org.apache.james.mime4j.parser.MimeEntityConfig;
46  import org.apache.james.mime4j.parser.MimeStreamParser;
47  import org.apache.james.mime4j.storage.DefaultStorageProvider;
48  import org.apache.james.mime4j.storage.StorageProvider;
49  
50  /**
51   * Represents a MIME message. The following code parses a stream into a
52   * <code>Message</code> object.
53   * 
54   * <pre>
55   * Message msg = new Message(new FileInputStream(&quot;mime.msg&quot;));
56   * </pre>
57   */
58  public class Message extends Entity implements Body {
59  
60      /**
61       * Creates a new empty <code>Message</code>.
62       */
63      public Message() {
64      }
65  
66      /**
67       * Creates a new <code>Message</code> from the specified
68       * <code>Message</code>. The <code>Message</code> instance is
69       * initialized with copies of header and body of the specified
70       * <code>Message</code>. The parent entity of the new message is
71       * <code>null</code>.
72       * 
73       * @param other
74       *            message to copy.
75       * @throws UnsupportedOperationException
76       *             if <code>other</code> contains a {@link SingleBody} that
77       *             does not support the {@link SingleBody#copy() copy()}
78       *             operation.
79       * @throws IllegalArgumentException
80       *             if <code>other</code> contains a <code>Body</code> that
81       *             is neither a {@link Message}, {@link Multipart} or
82       *             {@link SingleBody}.
83       */
84      public Message(Message other) {
85          super(other);
86      }
87  
88      /**
89       * Parses the specified MIME message stream into a <code>Message</code>
90       * instance.
91       * 
92       * @param is
93       *            the stream to parse.
94       * @throws IOException
95       *             on I/O errors.
96       * @throws MimeIOException
97       *             on MIME protocol violations.
98       */
99      public Message(InputStream is) throws IOException, MimeIOException {
100         this(is, null, DefaultStorageProvider.getInstance());
101     }
102 
103     /**
104      * Parses the specified MIME message stream into a <code>Message</code>
105      * instance using given {@link MimeEntityConfig}.
106      * 
107      * @param is
108      *            the stream to parse.
109      * @throws IOException
110      *             on I/O errors.
111      * @throws MimeIOException
112      *             on MIME protocol violations.
113      */
114     public Message(InputStream is, MimeEntityConfig config) throws IOException,
115             MimeIOException {
116         this(is, config, DefaultStorageProvider.getInstance());
117     }
118 
119     /**
120      * Parses the specified MIME message stream into a <code>Message</code>
121      * instance using given {@link MimeEntityConfig} and {@link StorageProvider}.
122      * 
123      * @param is
124      *            the stream to parse.
125      * @param config
126      *            {@link MimeEntityConfig} to use.
127      * @param storageProvider
128      *            {@link StorageProvider} to use for storing text and binary
129      *            message bodies.
130      * @throws IOException
131      *             on I/O errors.
132      * @throws MimeIOException
133      *             on MIME protocol violations.
134      */
135     public Message(InputStream is, MimeEntityConfig config,
136             StorageProvider storageProvider) throws IOException,
137             MimeIOException {
138         try {
139             MimeStreamParser parser = new MimeStreamParser(config);
140             parser.setContentHandler(new MessageBuilder(this, storageProvider));
141             parser.parse(is);
142         } catch (MimeException e) {
143             throw new MimeIOException(e);
144         }
145     }
146 
147     /**
148      * Write the content to the given output stream using the
149      * {@link MessageWriter#DEFAULT default} message writer.
150      * 
151      * @param out
152      *            the output stream to write to.
153      * @throws IOException
154      *             in case of an I/O error
155      * @see MessageWriter
156      */
157     public void writeTo(OutputStream out) throws IOException {
158         MessageWriter.DEFAULT.writeEntity(this, out);
159     }
160 
161     /**
162      * Returns the value of the <i>Message-ID</i> header field of this message
163      * or <code>null</code> if it is not present.
164      * 
165      * @return the identifier of this message.
166      */
167     public String getMessageId() {
168         Field field = obtainField(FieldName.MESSAGE_ID);
169         if (field == null)
170             return null;
171 
172         return field.getBody();
173     }
174 
175     /**
176      * Creates and sets a new <i>Message-ID</i> header field for this message.
177      * A <code>Header</code> is created if this message does not already have
178      * one.
179      * 
180      * @param hostname
181      *            host name to be included in the identifier or
182      *            <code>null</code> if no host name should be included.
183      */
184     public void createMessageId(String hostname) {
185         Header header = obtainHeader();
186 
187         header.setField(Fields.messageId(hostname));
188     }
189 
190     /**
191      * Returns the (decoded) value of the <i>Subject</i> header field of this
192      * message or <code>null</code> if it is not present.
193      * 
194      * @return the subject of this message.
195      */
196     public String getSubject() {
197         UnstructuredField field = obtainField(FieldName.SUBJECT);
198         if (field == null)
199             return null;
200 
201         return field.getValue();
202     }
203 
204     /**
205      * Sets the <i>Subject</i> header field for this message. The specified
206      * string may contain non-ASCII characters, in which case it gets encoded as
207      * an 'encoded-word' automatically. A <code>Header</code> is created if
208      * this message does not already have one.
209      * 
210      * @param subject
211      *            subject to set or <code>null</code> to remove the subject
212      *            header field.
213      */
214     public void setSubject(String subject) {
215         Header header = obtainHeader();
216 
217         if (subject == null) {
218             header.removeFields(FieldName.SUBJECT);
219         } else {
220             header.setField(Fields.subject(subject));
221         }
222     }
223 
224     /**
225      * Returns the value of the <i>Date</i> header field of this message as
226      * <code>Date</code> object or <code>null</code> if it is not present.
227      * 
228      * @return the date of this message.
229      */
230     public Date getDate() {
231         DateTimeField dateField = obtainField(FieldName.DATE);
232         if (dateField == null)
233             return null;
234 
235         return dateField.getDate();
236     }
237 
238     /**
239      * Sets the <i>Date</i> header field for this message. This method uses the
240      * default <code>TimeZone</code> of this host to encode the specified
241      * <code>Date</code> object into a string.
242      * 
243      * @param date
244      *            date to set or <code>null</code> to remove the date header
245      *            field.
246      */
247     public void setDate(Date date) {
248         setDate(date, null);
249     }
250 
251     /**
252      * Sets the <i>Date</i> header field for this message. The specified
253      * <code>TimeZone</code> is used to encode the specified <code>Date</code>
254      * object into a string.
255      * 
256      * @param date
257      *            date to set or <code>null</code> to remove the date header
258      *            field.
259      * @param zone
260      *            a time zone.
261      */
262     public void setDate(Date date, TimeZone zone) {
263         Header header = obtainHeader();
264 
265         if (date == null) {
266             header.removeFields(FieldName.DATE);
267         } else {
268             header.setField(Fields.date(FieldName.DATE, date, zone));
269         }
270     }
271 
272     /**
273      * Returns the value of the <i>Sender</i> header field of this message as
274      * <code>Mailbox</code> object or <code>null</code> if it is not
275      * present.
276      * 
277      * @return the sender of this message.
278      */
279     public Mailbox getSender() {
280         return getMailbox(FieldName.SENDER);
281     }
282 
283     /**
284      * Sets the <i>Sender</i> header field of this message to the specified
285      * mailbox address.
286      * 
287      * @param sender
288      *            address to set or <code>null</code> to remove the header
289      *            field.
290      */
291     public void setSender(Mailbox sender) {
292         setMailbox(FieldName.SENDER, sender);
293     }
294 
295     /**
296      * Returns the value of the <i>From</i> header field of this message as
297      * <code>MailboxList</code> object or <code>null</code> if it is not
298      * present.
299      * 
300      * @return value of the from field of this message.
301      */
302     public MailboxList getFrom() {
303         return getMailboxList(FieldName.FROM);
304     }
305 
306     /**
307      * Sets the <i>From</i> header field of this message to the specified
308      * mailbox address.
309      * 
310      * @param from
311      *            address to set or <code>null</code> to remove the header
312      *            field.
313      */
314     public void setFrom(Mailbox from) {
315         setMailboxList(FieldName.FROM, from);
316     }
317 
318     /**
319      * Sets the <i>From</i> header field of this message to the specified
320      * mailbox addresses.
321      * 
322      * @param from
323      *            addresses to set or <code>null</code> or no arguments to
324      *            remove the header field.
325      */
326     public void setFrom(Mailbox... from) {
327         setMailboxList(FieldName.FROM, from);
328     }
329 
330     /**
331      * Sets the <i>From</i> header field of this message to the specified
332      * mailbox addresses.
333      * 
334      * @param from
335      *            addresses to set or <code>null</code> or an empty collection
336      *            to remove the header field.
337      */
338     public void setFrom(Collection<Mailbox> from) {
339         setMailboxList(FieldName.FROM, from);
340     }
341 
342     /**
343      * Returns the value of the <i>To</i> header field of this message as
344      * <code>AddressList</code> object or <code>null</code> if it is not
345      * present.
346      * 
347      * @return value of the to field of this message.
348      */
349     public AddressList getTo() {
350         return getAddressList(FieldName.TO);
351     }
352 
353     /**
354      * Sets the <i>To</i> header field of this message to the specified
355      * address.
356      * 
357      * @param to
358      *            address to set or <code>null</code> to remove the header
359      *            field.
360      */
361     public void setTo(Address to) {
362         setAddressList(FieldName.TO, to);
363     }
364 
365     /**
366      * Sets the <i>To</i> header field of this message to the specified
367      * addresses.
368      * 
369      * @param to
370      *            addresses to set or <code>null</code> or no arguments to
371      *            remove the header field.
372      */
373     public void setTo(Address... to) {
374         setAddressList(FieldName.TO, to);
375     }
376 
377     /**
378      * Sets the <i>To</i> header field of this message to the specified
379      * addresses.
380      * 
381      * @param to
382      *            addresses to set or <code>null</code> or an empty collection
383      *            to remove the header field.
384      */
385     public void setTo(Collection<Address> to) {
386         setAddressList(FieldName.TO, to);
387     }
388 
389     /**
390      * Returns the value of the <i>Cc</i> header field of this message as
391      * <code>AddressList</code> object or <code>null</code> if it is not
392      * present.
393      * 
394      * @return value of the cc field of this message.
395      */
396     public AddressList getCc() {
397         return getAddressList(FieldName.CC);
398     }
399 
400     /**
401      * Sets the <i>Cc</i> header field of this message to the specified
402      * address.
403      * 
404      * @param cc
405      *            address to set or <code>null</code> to remove the header
406      *            field.
407      */
408     public void setCc(Address cc) {
409         setAddressList(FieldName.CC, cc);
410     }
411 
412     /**
413      * Sets the <i>Cc</i> header field of this message to the specified
414      * addresses.
415      * 
416      * @param cc
417      *            addresses to set or <code>null</code> or no arguments to
418      *            remove the header field.
419      */
420     public void setCc(Address... cc) {
421         setAddressList(FieldName.CC, cc);
422     }
423 
424     /**
425      * Sets the <i>Cc</i> header field of this message to the specified
426      * addresses.
427      * 
428      * @param cc
429      *            addresses to set or <code>null</code> or an empty collection
430      *            to remove the header field.
431      */
432     public void setCc(Collection<Address> cc) {
433         setAddressList(FieldName.CC, cc);
434     }
435 
436     /**
437      * Returns the value of the <i>Bcc</i> header field of this message as
438      * <code>AddressList</code> object or <code>null</code> if it is not
439      * present.
440      * 
441      * @return value of the bcc field of this message.
442      */
443     public AddressList getBcc() {
444         return getAddressList(FieldName.BCC);
445     }
446 
447     /**
448      * Sets the <i>Bcc</i> header field of this message to the specified
449      * address.
450      * 
451      * @param bcc
452      *            address to set or <code>null</code> to remove the header
453      *            field.
454      */
455     public void setBcc(Address bcc) {
456         setAddressList(FieldName.BCC, bcc);
457     }
458 
459     /**
460      * Sets the <i>Bcc</i> header field of this message to the specified
461      * addresses.
462      * 
463      * @param bcc
464      *            addresses to set or <code>null</code> or no arguments to
465      *            remove the header field.
466      */
467     public void setBcc(Address... bcc) {
468         setAddressList(FieldName.BCC, bcc);
469     }
470 
471     /**
472      * Sets the <i>Bcc</i> header field of this message to the specified
473      * addresses.
474      * 
475      * @param bcc
476      *            addresses to set or <code>null</code> or an empty collection
477      *            to remove the header field.
478      */
479     public void setBcc(Collection<Address> bcc) {
480         setAddressList(FieldName.BCC, bcc);
481     }
482 
483     /**
484      * Returns the value of the <i>Reply-To</i> header field of this message as
485      * <code>AddressList</code> object or <code>null</code> if it is not
486      * present.
487      * 
488      * @return value of the reply to field of this message.
489      */
490     public AddressList getReplyTo() {
491         return getAddressList(FieldName.REPLY_TO);
492     }
493 
494     /**
495      * Sets the <i>Reply-To</i> header field of this message to the specified
496      * address.
497      * 
498      * @param replyTo
499      *            address to set or <code>null</code> to remove the header
500      *            field.
501      */
502     public void setReplyTo(Address replyTo) {
503         setAddressList(FieldName.REPLY_TO, replyTo);
504     }
505 
506     /**
507      * Sets the <i>Reply-To</i> header field of this message to the specified
508      * addresses.
509      * 
510      * @param replyTo
511      *            addresses to set or <code>null</code> or no arguments to
512      *            remove the header field.
513      */
514     public void setReplyTo(Address... replyTo) {
515         setAddressList(FieldName.REPLY_TO, replyTo);
516     }
517 
518     /**
519      * Sets the <i>Reply-To</i> header field of this message to the specified
520      * addresses.
521      * 
522      * @param replyTo
523      *            addresses to set or <code>null</code> or an empty collection
524      *            to remove the header field.
525      */
526     public void setReplyTo(Collection<Address> replyTo) {
527         setAddressList(FieldName.REPLY_TO, replyTo);
528     }
529 
530     private Mailbox getMailbox(String fieldName) {
531         MailboxField field = obtainField(fieldName);
532         if (field == null)
533             return null;
534 
535         return field.getMailbox();
536     }
537 
538     private void setMailbox(String fieldName, Mailbox mailbox) {
539         Header header = obtainHeader();
540 
541         if (mailbox == null) {
542             header.removeFields(fieldName);
543         } else {
544             header.setField(Fields.mailbox(fieldName, mailbox));
545         }
546     }
547 
548     private MailboxList getMailboxList(String fieldName) {
549         MailboxListField field = obtainField(fieldName);
550         if (field == null)
551             return null;
552 
553         return field.getMailboxList();
554     }
555 
556     private void setMailboxList(String fieldName, Mailbox mailbox) {
557         setMailboxList(fieldName, mailbox == null ? null : Collections
558                 .singleton(mailbox));
559     }
560 
561     private void setMailboxList(String fieldName, Mailbox... mailboxes) {
562         setMailboxList(fieldName, mailboxes == null ? null : Arrays
563                 .asList(mailboxes));
564     }
565 
566     private void setMailboxList(String fieldName, Collection<Mailbox> mailboxes) {
567         Header header = obtainHeader();
568 
569         if (mailboxes == null || mailboxes.isEmpty()) {
570             header.removeFields(fieldName);
571         } else {
572             header.setField(Fields.mailboxList(fieldName, mailboxes));
573         }
574     }
575 
576     private AddressList getAddressList(String fieldName) {
577         AddressListField field = obtainField(fieldName);
578         if (field == null)
579             return null;
580 
581         return field.getAddressList();
582     }
583 
584     private void setAddressList(String fieldName, Address address) {
585         setAddressList(fieldName, address == null ? null : Collections
586                 .singleton(address));
587     }
588 
589     private void setAddressList(String fieldName, Address... addresses) {
590         setAddressList(fieldName, addresses == null ? null : Arrays
591                 .asList(addresses));
592     }
593 
594     private void setAddressList(String fieldName, Collection<Address> addresses) {
595         Header header = obtainHeader();
596 
597         if (addresses == null || addresses.isEmpty()) {
598             header.removeFields(fieldName);
599         } else {
600             header.setField(Fields.addressList(fieldName, addresses));
601         }
602     }
603 
604 }