KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > mail > internet > MimeMessage


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 /*
23  * @(#)MimeMessage.java 1.89 06/03/02
24  *
25  * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
26  */

27
28 package javax.mail.internet;
29
30 import javax.mail.*;
31 import javax.activation.*;
32 import java.lang.*;
33 import java.io.*;
34 import java.util.*;
35 import java.text.ParseException JavaDoc;
36 import com.sun.mail.util.ASCIIUtility;
37 import com.sun.mail.util.LineOutputStream;
38 import javax.mail.util.SharedByteArrayInputStream;
39
40 /**
41  * This class represents a MIME style email message. It implements
42  * the <code>Message</code> abstract class and the <code>MimePart</code>
43  * interface. <p>
44  *
45  * Clients wanting to create new MIME style messages will instantiate
46  * an empty MimeMessage object and then fill it with appropriate
47  * attributes and content. <p>
48  *
49  * Service providers that implement MIME compliant backend stores may
50  * want to subclass MimeMessage and override certain methods to provide
51  * specific implementations. The simplest case is probably a provider
52  * that generates a MIME style input stream and leaves the parsing of
53  * the stream to this class. <p>
54  *
55  * MimeMessage uses the <code>InternetHeaders</code> class to parse and
56  * store the top level RFC 822 headers of a message. <p>
57  *
58  * The <code>mail.mime.address.strict</code> session property controls
59  * the parsing of address headers. By default, strict parsing of address
60  * headers is done. If this property is set to <code>"false"</code>,
61  * strict parsing is not done and many illegal addresses that sometimes
62  * occur in real messages are allowed. See the <code>InternetAddress</code>
63  * class for details. <p>
64  *
65  * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
66  *
67  * RFC 822 header fields <strong>must</strong> contain only
68  * US-ASCII characters. MIME allows non ASCII characters to be present
69  * in certain portions of certain headers, by encoding those characters.
70  * RFC 2047 specifies the rules for doing this. The MimeUtility
71  * class provided in this package can be used to to achieve this.
72  * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
73  * <code>addHeaderLine</code> methods are responsible for enforcing
74  * the MIME requirements for the specified headers. In addition, these
75  * header fields must be folded (wrapped) before being sent if they
76  * exceed the line length limitation for the transport (1000 bytes for
77  * SMTP). Received headers may have been folded. The application is
78  * responsible for folding and unfolding headers as appropriate. <p>
79  *
80  * @author John Mani
81  * @author Bill Shannon
82  * @author Max Spivak
83  * @author Kanwar Oberoi
84  * @see javax.mail.internet.MimeUtility
85  * @see javax.mail.Part
86  * @see javax.mail.Message
87  * @see javax.mail.internet.MimePart
88  * @see javax.mail.internet.InternetAddress
89  */

90
91 public class MimeMessage extends Message implements MimePart JavaDoc {
92
93     /**
94      * The DataHandler object representing this Message's content.
95      */

96     protected DataHandler dh;
97
98     /**
99      * Byte array that holds the bytes of this Message's content.
100      */

101     protected byte[] content;
102
103     /**
104      * If the data for this message was supplied by an
105      * InputStream that implements the SharedInputStream interface,
106      * <code>contentStream</code> is another such stream representing
107      * the content of this message. In this case, <code>content</code>
108      * will be null.
109      *
110      * @since JavaMail 1.2
111      */

112     protected InputStream contentStream;
113
114     /**
115      * The InternetHeaders object that stores the header
116      * of this message.
117      */

118     protected InternetHeaders JavaDoc headers;
119
120     /**
121      * The Flags for this message.
122      */

123     protected Flags flags;
124
125     /**
126      * A flag indicating whether the message has been modified.
127      * If the message has not been modified, any data in the
128      * <code>content</code> array is assumed to be valid and is used
129      * directly in the <code>writeTo</code> method. This flag is
130      * set to true when an empty message is created or when the
131      * <code>saveChanges</code> method is called.
132      *
133      * @since JavaMail 1.2
134      */

135     protected boolean modified = false;
136
137     /**
138      * Does the <code>saveChanges</code> method need to be called on
139      * this message? This flag is set to false by the public constructor
140      * and set to true by the <code>saveChanges</code> method. The
141      * <code>writeTo</code> method checks this flag and calls the
142      * <code>saveChanges</code> method as necessary. This avoids the
143      * common mistake of forgetting to call the <code>saveChanges</code>
144      * method on a newly constructed message.
145      *
146      * @since JavaMail 1.2
147      */

148     protected boolean saved = false;
149
150     // Used to parse dates
151
private static MailDateFormat JavaDoc mailDateFormat = new MailDateFormat JavaDoc();
152
153     // Should addresses in headers be parsed in "strict" mode?
154
private boolean strict = true;
155
156     /**
157      * Default constructor. An empty message object is created.
158      * The <code>headers</code> field is set to an empty InternetHeaders
159      * object. The <code>flags</code> field is set to an empty Flags
160      * object. The <code>modified</code> flag is set to true.
161      */

162     public MimeMessage(Session session) {
163     super(session);
164     modified = true;
165     headers = new InternetHeaders JavaDoc();
166     flags = new Flags(); // empty flags object
167
initStrict();
168     }
169
170     /**
171      * Constructs a MimeMessage by reading and parsing the data from the
172      * specified MIME InputStream. The InputStream will be left positioned
173      * at the end of the data for the message. Note that the input stream
174      * parse is done within this constructor itself. <p>
175      *
176      * The input stream contains an entire MIME formatted message with
177      * headers and data.
178      *
179      * @param session Session object for this message
180      * @param is the message input stream
181      * @exception MessagingException
182      */

183     public MimeMessage(Session session, InputStream is)
184             throws MessagingException {
185     super(session);
186     flags = new Flags(); // empty Flags object
187
initStrict();
188     parse(is);
189     saved = true;
190     }
191
192     /**
193      * Constructs a new MimeMessage with content initialized from the
194      * <code>source</code> MimeMessage. The new message is independent
195      * of the original. <p>
196      *
197      * Note: The current implementation is rather inefficient, copying
198      * the data more times than strictly necessary.
199      *
200      * @param source the message to copy content from
201      * @exception MessagingException
202      * @since JavaMail 1.2
203      */

204     public MimeMessage(MimeMessage JavaDoc source) throws MessagingException {
205     super(source.session);
206     flags = source.getFlags();
207     ByteArrayOutputStream bos;
208     int size = source.getSize();
209     if (size > 0)
210         bos = new ByteArrayOutputStream(size);
211     else
212         bos = new ByteArrayOutputStream();
213     try {
214         strict = source.strict;
215         source.writeTo(bos);
216         bos.close();
217         SharedByteArrayInputStream bis =
218                 new SharedByteArrayInputStream(bos.toByteArray());
219         parse(bis);
220         bis.close();
221         saved = true;
222     } catch (IOException ex) {
223         // should never happen, but just in case...
224
throw new MessagingException("IOException while copying message",
225                         ex);
226     }
227     }
228
229     /**
230      * Constructs an empty MimeMessage object with the given Folder
231      * and message number. <p>
232      *
233      * This method is for providers subclassing <code>MimeMessage</code>.
234      */

235     protected MimeMessage(Folder folder, int msgnum) {
236     super(folder, msgnum);
237     flags = new Flags(); // empty Flags object
238
saved = true;
239     initStrict();
240     }
241
242     /**
243      * Constructs a MimeMessage by reading and parsing the data from the
244      * specified MIME InputStream. The InputStream will be left positioned
245      * at the end of the data for the message. Note that the input stream
246      * parse is done within this constructor itself. <p>
247      *
248      * This method is for providers subclassing <code>MimeMessage</code>.
249      *
250      * @param folder The containing folder.
251      * @param is the message input stream
252      * @param msgnum Message number of this message within its folder
253      * @exception MessagingException
254      */

255     protected MimeMessage(Folder folder, InputStream is, int msgnum)
256         throws MessagingException {
257     this(folder, msgnum);
258     initStrict();
259     parse(is);
260     }
261
262     /**
263      * Constructs a MimeMessage from the given InternetHeaders object
264      * and content.
265      *
266      * This method is for providers subclassing <code>MimeMessage</code>.
267      *
268      * @param folder The containing folder.
269      * @param headers The headers
270      * @param content The message content
271      * @param msgnum Message number of this message within its folder
272      * @exception MessagingException
273      */

274     protected MimeMessage(Folder folder, InternetHeaders JavaDoc headers,
275         byte[] content, int msgnum) throws MessagingException {
276     this(folder, msgnum);
277     this.headers = headers;
278     this.content = content;
279     initStrict();
280     }
281
282     /**
283      * Set the strict flag based on property.
284      */

285     private void initStrict() {
286     if (session != null) {
287         String JavaDoc s = session.getProperty("mail.mime.address.strict");
288         strict = s == null || !s.equalsIgnoreCase("false");
289     }
290     }
291
292     /**
293      * Parse the InputStream setting the <code>headers</code> and
294      * <code>content</code> fields appropriately. Also resets the
295      * <code>modified</code> flag. <p>
296      *
297      * This method is intended for use by subclasses that need to
298      * control when the InputStream is parsed.
299      *
300      * @param is The message input stream
301      * @exception MessagingException
302      */

303     protected void parse(InputStream is) throws MessagingException {
304
305     if (!(is instanceof ByteArrayInputStream) &&
306         !(is instanceof BufferedInputStream) &&
307         !(is instanceof SharedInputStream JavaDoc))
308         is = new BufferedInputStream(is);
309     
310     headers = createInternetHeaders(is);
311
312     if (is instanceof SharedInputStream JavaDoc) {
313         SharedInputStream JavaDoc sis = (SharedInputStream JavaDoc)is;
314         contentStream = sis.newStream(sis.getPosition(), -1);
315     } else {
316         try {
317         content = ASCIIUtility.getBytes(is);
318         } catch (IOException ioex) {
319         throw new MessagingException("IOException", ioex);
320         }
321     }
322
323     modified = false;
324     }
325
326     /**
327      * Returns the value of the RFC 822 "From" header fields. If this
328      * header field is absent, the "Sender" header field is used.
329      * If the "Sender" header field is also absent, <code>null</code>
330      * is returned.<p>
331      *
332      * This implementation uses the <code>getHeader</code> method
333      * to obtain the requisite header field.
334      *
335      * @return Address object
336      * @exception MessagingException
337      * @see #headers
338      */

339     public Address[] getFrom() throws MessagingException {
340     Address[] a = getAddressHeader("From");
341     if (a == null)
342         a = getAddressHeader("Sender");
343     
344     return a;
345     }
346
347     /**
348      * Set the RFC 822 "From" header field. Any existing values are
349      * replaced with the given address. If address is <code>null</code>,
350      * this header is removed.
351      *
352      * @param address the sender of this message
353      * @exception IllegalWriteException if the underlying
354      * implementation does not support modification
355      * of existing values
356      * @exception IllegalStateException if this message is
357      * obtained from a READ_ONLY folder.
358      * @exception MessagingException
359      */

360     public void setFrom(Address address) throws MessagingException {
361     if (address == null)
362         removeHeader("From");
363     else
364         setHeader("From", address.toString());
365     }
366
367     /**
368      * Set the RFC 822 "From" header field using the value of the
369      * <code>InternetAddress.getLocalAddress</code> method.
370      *
371      * @exception IllegalWriteException if the underlying
372      * implementation does not support modification
373      * of existing values
374      * @exception IllegalStateException if this message is
375      * obtained from a READ_ONLY folder.
376      * @exception MessagingException
377      */

378     public void setFrom() throws MessagingException {
379     InternetAddress JavaDoc me = InternetAddress.getLocalAddress(session);
380     if (me != null)
381         setFrom(me);
382     else
383         throw new MessagingException("No From address");
384     }
385
386     /**
387      * Add the specified addresses to the existing "From" field. If
388      * the "From" field does not already exist, it is created.
389      *
390      * @param addresses the senders of this message
391      * @exception IllegalWriteException if the underlying
392      * implementation does not support modification
393      * of existing values
394      * @exception IllegalStateException if this message is
395      * obtained from a READ_ONLY folder.
396      * @exception MessagingException
397      */

398     public void addFrom(Address[] addresses) throws MessagingException {
399     addAddressHeader("From", addresses);
400     }
401
402     /**
403      * Returns the value of the RFC 822 "Sender" header field.
404      * If the "Sender" header field is absent, <code>null</code>
405      * is returned.<p>
406      *
407      * This implementation uses the <code>getHeader</code> method
408      * to obtain the requisite header field.
409      *
410      * @return Address object
411      * @exception MessagingException
412      * @see #headers
413      * @since JavaMail 1.3
414      */

415     public Address getSender() throws MessagingException {
416     Address[] a = getAddressHeader("Sender");
417     if (a == null || a.length == 0)
418         return null;
419     return a[0]; // there can be only one
420
}
421
422     /**
423      * Set the RFC 822 "Sender" header field. Any existing values are
424      * replaced with the given address. If address is <code>null</code>,
425      * this header is removed.
426      *
427      * @param address the sender of this message
428      * @exception IllegalWriteException if the underlying
429      * implementation does not support modification
430      * of existing values
431      * @exception IllegalStateException if this message is
432      * obtained from a READ_ONLY folder.
433      * @exception MessagingException
434      * @since JavaMail 1.3
435      */

436     public void setSender(Address address) throws MessagingException {
437     if (address == null)
438         removeHeader("Sender");
439     else
440         setHeader("Sender", address.toString());
441     }
442
443     /**
444      * This inner class extends the javax.mail.Message.RecipientType
445      * class to add additional RecipientTypes. The one additional
446      * RecipientType currently defined here is NEWSGROUPS.
447      *
448      * @see javax.mail.Message.RecipientType
449      */

450     public static class RecipientType extends Message.RecipientType {
451
452     private static final long serialVersionUID = -5468290701714395543L;
453
454     /**
455      * The "Newsgroup" (Usenet news) recipients.
456      */

457     public static final RecipientType NEWSGROUPS =
458                     new RecipientType("Newsgroups");
459     protected RecipientType(String JavaDoc type) {
460         super(type);
461     }
462
463     protected Object JavaDoc readResolve() throws ObjectStreamException {
464         if (type.equals("Newsgroups"))
465         return NEWSGROUPS;
466         else
467         return super.readResolve();
468     }
469     }
470
471     /**
472      * Returns the recepients specified by the type. The mapping
473      * between the type and the corresponding RFC 822 header is
474      * as follows:
475      * <pre>
476      * Message.RecipientType.TO "To"
477      * Message.RecipientType.CC "Cc"
478      * Message.RecipientType.BCC "Bcc"
479      * MimeMessage.RecipientType.NEWSGROUPS "Newsgroups"
480      * </pre><br>
481      *
482      * Returns null if the header specified by the type is not found
483      * or if its value is empty. <p>
484      *
485      * This implementation uses the <code>getHeader</code> method
486      * to obtain the requisite header field.
487      *
488      * @param type Type of recepient
489      * @return array of Address objects
490      * @exception MessagingException if header could not
491      * be retrieved
492      * @exception AddressException if the header is misformatted
493      * @see #headers
494      * @see javax.mail.Message.RecipientType#TO
495      * @see javax.mail.Message.RecipientType#CC
496      * @see javax.mail.Message.RecipientType#BCC
497      * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
498      */

499     public Address[] getRecipients(Message.RecipientType type)
500                 throws MessagingException {
501     if (type == RecipientType.NEWSGROUPS) {
502         String JavaDoc s = getHeader("Newsgroups", ",");
503         return (s == null) ? null : NewsAddress.parse(s);
504     } else
505         return getAddressHeader(getHeaderName(type));
506     }
507
508     /**
509      * Get all the recipient addresses for the message.
510      * Extracts the TO, CC, BCC, and NEWSGROUPS recipients.
511      *
512      * @return array of Address objects
513      * @exception MessagingException
514      * @see javax.mail.Message.RecipientType#TO
515      * @see javax.mail.Message.RecipientType#CC
516      * @see javax.mail.Message.RecipientType#BCC
517      * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
518      */

519     public Address[] getAllRecipients() throws MessagingException {
520     Address[] all = super.getAllRecipients();
521     Address[] ng = getRecipients(RecipientType.NEWSGROUPS);
522
523     if (ng == null)
524         return all; // the common case
525

526     int numRecip =
527         (all != null ? all.length : 0) +
528         (ng != null ? ng.length : 0);
529     Address[] addresses = new Address[numRecip];
530     int pos = 0;
531     if (all != null) {
532         System.arraycopy(all, 0, addresses, pos, all.length);
533         pos += all.length;
534     }
535     if (ng != null) {
536         System.arraycopy(ng, 0, addresses, pos, ng.length);
537         pos += ng.length;
538     }
539     return addresses;
540     }
541     
542     /**
543      * Set the specified recipient type to the given addresses.
544      * If the address parameter is <code>null</code>, the corresponding
545      * recipient field is removed.
546      *
547      * @param type Recipient type
548      * @param addresses Addresses
549      * @exception IllegalWriteException if the underlying
550      * implementation does not support modification
551      * of existing values
552      * @exception IllegalStateException if this message is
553      * obtained from a READ_ONLY folder.
554      * @exception MessagingException
555      * @see #getRecipients
556      */

557     public void setRecipients(Message.RecipientType type, Address[] addresses)
558                                 throws MessagingException {
559     if (type == RecipientType.NEWSGROUPS) {
560         if (addresses == null || addresses.length == 0)
561         removeHeader("Newsgroups");
562         else
563         setHeader("Newsgroups", NewsAddress.toString(addresses));
564     } else
565         setAddressHeader(getHeaderName(type), addresses);
566     }
567
568     /**
569      * Set the specified recipient type to the given addresses.
570      * If the address parameter is <code>null</code>, the corresponding
571      * recipient field is removed.
572      *
573      * @param type Recipient type
574      * @param addresses Addresses
575      * @exception AddressException if the attempt to parse the
576      * addresses String fails
577      * @exception IllegalWriteException if the underlying
578      * implementation does not support modification
579      * of existing values
580      * @exception IllegalStateException if this message is
581      * obtained from a READ_ONLY folder.
582      * @exception MessagingException
583      * @see #getRecipients
584      * @since JavaMail 1.2
585      */

586     public void setRecipients(Message.RecipientType type, String JavaDoc addresses)
587                                 throws MessagingException {
588         if (type == RecipientType.NEWSGROUPS) {
589             if (addresses == null || addresses.length() == 0)
590                 removeHeader("Newsgroups");
591             else
592                 setHeader("Newsgroups", addresses);
593         } else
594             setAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
595     }
596
597     /**
598      * Add the given addresses to the specified recipient type.
599      *
600      * @param type Recipient type
601      * @param addresses Addresses
602      * @exception IllegalWriteException if the underlying
603      * implementation does not support modification
604      * of existing values
605      * @exception IllegalStateException if this message is
606      * obtained from a READ_ONLY folder.
607      * @exception MessagingException
608      */

609     public void addRecipients(Message.RecipientType type, Address[] addresses)
610                                 throws MessagingException {
611     if (type == RecipientType.NEWSGROUPS) {
612         String JavaDoc s = NewsAddress.toString(addresses);
613         if (s != null)
614         addHeader("Newsgroups", s);
615     } else
616         addAddressHeader(getHeaderName(type), addresses);
617     }
618
619     /**
620      * Add the given addresses to the specified recipient type.
621      *
622      * @param type Recipient type
623      * @param addresses Addresses
624      * @exception AddressException if the attempt to parse the
625      * addresses String fails
626      * @exception IllegalWriteException if the underlying
627      * implementation does not support modification
628      * of existing values
629      * @exception IllegalStateException if this message is
630      * obtained from a READ_ONLY folder.
631      * @exception MessagingException
632      * @since JavaMail 1.2
633      */

634     public void addRecipients(Message.RecipientType type, String JavaDoc addresses)
635                                 throws MessagingException {
636         if (type == RecipientType.NEWSGROUPS) {
637             if (addresses != null && addresses.length() != 0)
638                 addHeader("Newsgroups", addresses);
639         } else
640             addAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
641     }
642  
643     /**
644      * Return the value of the RFC 822 "Reply-To" header field. If
645      * this header is unavailable or its value is absent, then
646      * the <code>getFrom</code> method is called and its value is returned.
647      *
648      * This implementation uses the <code>getHeader</code> method
649      * to obtain the requisite header field.
650      *
651      * @exception MessagingException
652      * @see #headers
653      */

654     public Address[] getReplyTo() throws MessagingException {
655     Address[] a = getAddressHeader("Reply-To");
656     if (a == null)
657         a = getFrom();
658     return a;
659     }
660
661     /**
662      * Set the RFC 822 "Reply-To" header field. If the address
663      * parameter is <code>null</code>, this header is removed.
664      *
665      * @exception IllegalWriteException if the underlying
666      * implementation does not support modification
667      * of existing values
668      * @exception IllegalStateException if this message is
669      * obtained from a READ_ONLY folder.
670      * @exception MessagingException
671      */

672     public void setReplyTo(Address[] addresses) throws MessagingException {
673     setAddressHeader("Reply-To", addresses);
674     }
675
676     // Convenience method to get addresses
677
private Address[] getAddressHeader(String JavaDoc name)
678             throws MessagingException {
679     String JavaDoc s = getHeader(name, ",");
680     return (s == null) ? null : InternetAddress.parseHeader(s, strict);
681     }
682
683     // Convenience method to set addresses
684
private void setAddressHeader(String JavaDoc name, Address[] addresses)
685             throws MessagingException {
686     String JavaDoc s = InternetAddress.toString(addresses);
687     if (s == null)
688         removeHeader(name);
689     else
690         setHeader(name, s);
691     }
692
693     private void addAddressHeader(String JavaDoc name, Address[] addresses)
694             throws MessagingException {
695     String JavaDoc s = InternetAddress.toString(addresses);
696     if (s == null)
697         return;
698     addHeader(name, s);
699     }
700
701     /**
702      * Returns the value of the "Subject" header field. Returns null
703      * if the subject field is unavailable or its value is absent. <p>
704      *
705      * If the subject is encoded as per RFC 2047, it is decoded and
706      * converted into Unicode. If the decoding or conversion fails, the
707      * raw data is returned as is. <p>
708      *
709      * This implementation uses the <code>getHeader</code> method
710      * to obtain the requisite header field.
711      *
712      * @return Subject
713      * @exception MessagingException
714      * @see #headers
715      */

716     public String JavaDoc getSubject() throws MessagingException {
717     String JavaDoc rawvalue = getHeader("Subject", null);
718
719     if (rawvalue == null)
720         return null;
721
722     try {
723         return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
724     } catch (UnsupportedEncodingException ex) {
725         return rawvalue;
726     }
727     }
728
729     /**
730      * Set the "Subject" header field. If the subject contains
731      * non US-ASCII characters, it will be encoded using the
732      * platform's default charset. If the subject contains only
733      * US-ASCII characters, no encoding is done and it is used
734      * as-is. If the subject is null, the existing "Subject" field
735      * is removed. <p>
736      *
737      * The application must ensure that the subject does not contain
738      * any line breaks. <p>
739      *
740      * Note that if the charset encoding process fails, a
741      * MessagingException is thrown, and an UnsupportedEncodingException
742      * is included in the chain of nested exceptions within the
743      * MessagingException.
744      *
745      * @param subject The subject
746      * @exception IllegalWriteException if the underlying
747      * implementation does not support modification
748      * of existing values
749      * @exception IllegalStateException if this message is
750      * obtained from a READ_ONLY folder.
751      * @exception MessagingException. An
752      * UnsupportedEncodingException may be included
753      * in the exception chain if the charset
754      * conversion fails.
755      */

756     public void setSubject(String JavaDoc subject) throws MessagingException {
757     setSubject(subject, null);
758     }
759
760     /**
761      * Set the "Subject" header field. If the subject contains non
762      * US-ASCII characters, it will be encoded using the specified
763      * charset. If the subject contains only US-ASCII characters, no
764      * encoding is done and it is used as-is. If the subject is null,
765      * the existing "Subject" header field is removed. <p>
766      *
767      * The application must ensure that the subject does not contain
768      * any line breaks. <p>
769      *
770      * Note that if the charset encoding process fails, a
771      * MessagingException is thrown, and an UnsupportedEncodingException
772      * is included in the chain of nested exceptions within the
773      * MessagingException.
774      *
775      * @param subject The subject
776      * @param charset The charset
777      * @exception IllegalWriteException if the underlying
778      * implementation does not support modification
779      * of existing values
780      * @exception IllegalStateException if this message is
781      * obtained from a READ_ONLY folder.
782      * @exception MessagingException. An
783      * UnsupportedEncodingException may be included
784      * in the exception chain if the charset
785      * conversion fails.
786      */

787     public void setSubject(String JavaDoc subject, String JavaDoc charset)
788             throws MessagingException {
789     if (subject == null)
790         removeHeader("Subject");
791
792     try {
793         setHeader("Subject", MimeUtility.fold(9,
794         MimeUtility.encodeText(subject, charset, null)));
795     } catch (UnsupportedEncodingException uex) {
796         throw new MessagingException("Encoding error", uex);
797     }
798     }
799
800     /**
801      * Returns the value of the RFC 822 "Date" field. This is the date
802      * on which this message was sent. Returns null if this field is
803      * unavailable or its value is absent. <p>
804      *
805      * This implementation uses the <code>getHeader</code> method
806      * to obtain the requisite header field.
807      *
808      * @return The sent Date
809      * @exception MessagingException
810      */

811     public Date getSentDate() throws MessagingException {
812     String JavaDoc s = getHeader("Date", null);
813     if (s != null) {
814         try {
815         synchronized (mailDateFormat) {
816             return mailDateFormat.parse(s);
817         }
818         } catch (ParseException JavaDoc pex) {
819         return null;
820         }
821     }
822     
823     return null;
824     }
825
826     /**
827      * Set the RFC 822 "Date" header field. This is the date on which the
828      * creator of the message indicates that the message is complete
829      * and ready for delivery. If the date parameter is
830      * <code>null</code>, the existing "Date" field is removed.
831      *
832      * @exception IllegalWriteException if the underlying
833      * implementation does not support modification
834      * @exception IllegalStateException if this message is
835      * obtained from a READ_ONLY folder.
836      * @exception MessagingException
837      */

838     public void setSentDate(Date d) throws MessagingException {
839     if (d == null)
840         removeHeader("Date");
841     else {
842         synchronized (mailDateFormat) {
843         setHeader("Date", mailDateFormat.format(d));
844         }
845     }
846     }
847
848     /**
849      * Returns the Date on this message was received. Returns
850      * <code>null</code> if this date cannot be obtained. <p>
851      *
852      * Note that RFC 822 does not define a field for the received
853      * date. Hence only implementations that can provide this date
854      * need return a valid value. <p>
855      *
856      * This implementation returns <code>null</code>.
857      *
858      * @return the date this message was received
859      * @exception MessagingException
860      */

861     public Date getReceivedDate() throws MessagingException {
862     return null;
863     }
864
865     /**
866      * Return the size of the content of this message in bytes.
867      * Return -1 if the size cannot be determined. <p>
868      *
869      * Note that this number may not be an exact measure of the
870      * content size and may or may not account for any transfer
871      * encoding of the content. <p>
872      *
873      * This implementation returns the size of the <code>content</code>
874      * array (if not null), or, if <code>contentStream</code> is not
875      * null, and the <code>available</code> method returns a positive
876      * number, it returns that number as the size. Otherwise, it returns
877      * -1.
878      *
879      * @return size of content in bytes
880      * @exception MessagingException
881      */

882     public int getSize() throws MessagingException {
883     if (content != null)
884         return content.length;
885     if (contentStream != null) {
886         try {
887         int size = contentStream.available();
888         // only believe the size if it's greater than zero, since zero
889
// is the default returned by the InputStream class itself
890
if (size > 0)
891             return size;
892         } catch (IOException ex) {
893         // ignore it
894
}
895     }
896     return -1;
897     }
898
899     /**
900      * Return the number of lines for the content of this message.
901      * Return -1 if this number cannot be determined. <p>
902      *
903      * Note that this number may not be an exact measure of the
904      * content length and may or may not account for any transfer
905      * encoding of the content. <p>
906      *
907      * This implementation returns -1.
908      *
909      * @return number of lines in the content.
910      * @exception MessagingException
911      */

912      public int getLineCount() throws MessagingException {
913     return -1;
914      }
915
916     /**
917      * Returns the value of the RFC 822 "Content-Type" header field.
918      * This represents the content-type of the content of this
919      * message. This value must not be null. If this field is
920      * unavailable, "text/plain" should be returned. <p>
921      *
922      * This implementation uses the <code>getHeader</code> method
923      * to obtain the requisite header field.
924      *
925      * @return The ContentType of this part
926      * @exception MessagingException
927      * @see javax.activation.DataHandler
928      */

929     public String JavaDoc getContentType() throws MessagingException {
930     String JavaDoc s = getHeader("Content-Type", null);
931     if (s == null)
932         return "text/plain";
933     return s;
934     }
935
936     /**
937      * Is this Part of the specified MIME type? This method
938      * compares <strong>only the <code>primaryType</code> and
939      * <code>subType</code></strong>.
940      * The parameters of the content types are ignored. <p>
941      *
942      * For example, this method will return <code>true</code> when
943      * comparing a Part of content type <strong>"text/plain"</strong>
944      * with <strong>"text/plain; charset=foobar"</strong>. <p>
945      *
946      * If the <code>subType</code> of <code>mimeType</code> is the
947      * special character '*', then the subtype is ignored during the
948      * comparison.
949      */

950     public boolean isMimeType(String JavaDoc mimeType) throws MessagingException {
951     return MimeBodyPart.isMimeType(this, mimeType);
952     }
953
954     /**
955      * Returns the value of the "Content-Disposition" header field.
956      * This represents the disposition of this part. The disposition
957      * describes how the part should be presented to the user. <p>
958      *
959      * If the Content-Disposition field is unavailable,
960      * <code>null</code> is returned. <p>
961      *
962      * This implementation uses the <code>getHeader</code> method
963      * to obtain the requisite header field.
964      *
965      * @return disposition of this part, or null if unknown
966      * @exception MessagingException
967      */

968     public String JavaDoc getDisposition() throws MessagingException {
969     return MimeBodyPart.getDisposition(this);
970     }
971
972     /**
973      * Set the "Content-Disposition" header field of this Message.
974      * If <code>disposition</code> is null, any existing "Content-Disposition"
975      * header field is removed.
976      *
977      * @exception IllegalWriteException if the underlying
978      * implementation does not support modification
979      * @exception IllegalStateException if this message is
980      * obtained from a READ_ONLY folder.
981      * @exception MessagingException
982      */

983     public void setDisposition(String JavaDoc disposition) throws MessagingException {
984     MimeBodyPart.setDisposition(this, disposition);
985     }
986
987     /**
988      * Returns the content transfer encoding from the
989      * "Content-Transfer-Encoding" header
990      * field. Returns <code>null</code> if the header is unavailable
991      * or its value is absent. <p>
992      *
993      * This implementation uses the <code>getHeader</code> method
994      * to obtain the requisite header field.
995      *
996      * @return content-transfer-encoding
997      * @exception MessagingException
998      */

999     public String JavaDoc getEncoding() throws MessagingException {
1000    return MimeBodyPart.getEncoding(this);
1001    }
1002
1003    /**
1004     * Returns the value of the "Content-ID" header field. Returns
1005     * <code>null</code> if the field is unavailable or its value is
1006     * absent. <p>
1007     *
1008     * This implementation uses the <code>getHeader</code> method
1009     * to obtain the requisite header field.
1010     *
1011     * @return content-ID
1012     * @exception MessagingException
1013     */

1014    public String JavaDoc getContentID() throws MessagingException {
1015    return getHeader("Content-Id", null);
1016    }
1017
1018    /**
1019     * Set the "Content-ID" header field of this Message.
1020     * If the <code>cid</code> parameter is null, any existing
1021     * "Content-ID" is removed.
1022     *
1023     * @exception IllegalWriteException if the underlying
1024     * implementation does not support modification
1025     * @exception IllegalStateException if this message is
1026     * obtained from a READ_ONLY folder.
1027     * @exception MessagingException
1028     */

1029    public void setContentID(String JavaDoc cid) throws MessagingException {
1030    if (cid == null)
1031        removeHeader("Content-ID");
1032    else
1033        setHeader("Content-ID", cid);
1034    }
1035
1036    /**
1037     * Return the value of the "Content-MD5" header field. Returns
1038     * <code>null</code> if this field is unavailable or its value
1039     * is absent. <p>
1040     *
1041     * This implementation uses the <code>getHeader</code> method
1042     * to obtain the requisite header field.
1043     *
1044     * @return content-MD5
1045     * @exception MessagingException
1046     */

1047    public String JavaDoc getContentMD5() throws MessagingException {
1048    return getHeader("Content-MD5", null);
1049    }
1050
1051    /**
1052     * Set the "Content-MD5" header field of this Message.
1053     *
1054     * @exception IllegalWriteException if the underlying
1055     * implementation does not support modification
1056     * @exception IllegalStateException if this message is
1057     * obtained from a READ_ONLY folder.
1058     * @exception MessagingException
1059     */

1060    public void setContentMD5(String JavaDoc md5) throws MessagingException {
1061    setHeader("Content-MD5", md5);
1062    }
1063
1064    /**
1065     * Returns the "Content-Description" header field of this Message.
1066     * This typically associates some descriptive information with
1067     * this part. Returns null if this field is unavailable or its
1068     * value is absent. <p>
1069     *
1070     * If the Content-Description field is encoded as per RFC 2047,
1071     * it is decoded and converted into Unicode. If the decoding or
1072     * conversion fails, the raw data is returned as-is <p>
1073     *
1074     * This implementation uses the <code>getHeader</code> method
1075     * to obtain the requisite header field.
1076     *
1077     * @return content-description
1078     * @exception MessagingException
1079     */

1080    public String JavaDoc getDescription() throws MessagingException {
1081    return MimeBodyPart.getDescription(this);
1082    }
1083
1084    /**
1085     * Set the "Content-Description" header field for this Message.
1086     * If the description parameter is <code>null</code>, then any
1087     * existing "Content-Description" fields are removed. <p>
1088     *
1089     * If the description contains non US-ASCII characters, it will
1090     * be encoded using the platform's default charset. If the
1091     * description contains only US-ASCII characters, no encoding
1092     * is done and it is used as-is. <p>
1093     *
1094     * Note that if the charset encoding process fails, a
1095     * MessagingException is thrown, and an UnsupportedEncodingException
1096     * is included in the chain of nested exceptions within the
1097     * MessagingException.
1098     *
1099     * @param description content-description
1100     * @exception IllegalWriteException if the underlying
1101     * implementation does not support modification
1102     * @exception IllegalStateException if this message is
1103     * obtained from a READ_ONLY folder.
1104     * @exception MessagingException. An
1105     * UnsupportedEncodingException may be included
1106     * in the exception chain if the charset
1107     * conversion fails.
1108     */

1109    public void setDescription(String JavaDoc description) throws MessagingException {
1110    setDescription(description, null);
1111    }
1112
1113    /**
1114     * Set the "Content-Description" header field for this Message.
1115     * If the description parameter is <code>null</code>, then any
1116     * existing "Content-Description" fields are removed. <p>
1117     *
1118     * If the description contains non US-ASCII characters, it will
1119     * be encoded using the specified charset. If the description
1120     * contains only US-ASCII characters, no encoding is done and
1121     * it is used as-is. <p>
1122     *
1123     * Note that if the charset encoding process fails, a
1124     * MessagingException is thrown, and an UnsupportedEncodingException
1125     * is included in the chain of nested exceptions within the
1126     * MessagingException.
1127     *
1128     * @param description Description
1129     * @param charset Charset for encoding
1130     * @exception IllegalWriteException if the underlying
1131     * implementation does not support modification
1132     * @exception IllegalStateException if this message is
1133     * obtained from a READ_ONLY folder.
1134     * @exception MessagingException. An
1135     * UnsupportedEncodingException may be included
1136     * in the exception chain if the charset
1137     * conversion fails.
1138     */

1139    public void setDescription(String JavaDoc description, String JavaDoc charset)
1140        throws MessagingException {
1141    MimeBodyPart.setDescription(this, description, charset);
1142    }
1143
1144    /**
1145     * Get the languages specified in the "Content-Language" header
1146     * field of this message. The Content-Language header is defined by
1147     * RFC 1766. Returns <code>null</code> if this field is unavailable
1148     * or its value is absent. <p>
1149     *
1150     * This implementation uses the <code>getHeader</code> method
1151     * to obtain the requisite header field.
1152     *
1153     * @return value of content-language header.
1154     * @exception MessagingException
1155     */

1156    public String JavaDoc[] getContentLanguage() throws MessagingException {
1157    return MimeBodyPart.getContentLanguage(this);
1158    }
1159
1160    /**
1161     * Set the "Content-Language" header of this MimePart. The
1162     * Content-Language header is defined by RFC 1766.
1163     *
1164     * @param languages array of language tags
1165     * @exception IllegalWriteException if the underlying
1166     * implementation does not support modification
1167     * @exception IllegalStateException if this message is
1168     * obtained from a READ_ONLY folder.
1169     * @exception MessagingException
1170     */

1171    public void setContentLanguage(String JavaDoc[] languages)
1172            throws MessagingException {
1173    MimeBodyPart.setContentLanguage(this, languages);
1174    }
1175
1176    /**
1177     * Returns the value of the "Message-ID" header field. Returns
1178     * null if this field is unavailable or its value is absent. <p>
1179     *
1180     * The default implementation provided here uses the
1181     * <code>getHeader</code> method to return the value of the
1182     * "Message-ID" field.
1183     *
1184     * @return Message-ID
1185     * @exception MessagingException if the retrieval of this field
1186     * causes any exception.
1187     * @see javax.mail.search.MessageIDTerm
1188     * @since JavaMail 1.1
1189     */

1190    public String JavaDoc getMessageID() throws MessagingException {
1191    return getHeader("Message-ID", null);
1192    }
1193
1194    /**
1195     * Get the filename associated with this Message. <p>
1196     *
1197     * Returns the value of the "filename" parameter from the
1198     * "Content-Disposition" header field of this message. If it's
1199     * not available, returns the value of the "name" parameter from
1200     * the "Content-Type" header field of this BodyPart.
1201     * Returns <code>null</code> if both are absent. <p>
1202     *
1203     * If the <code>mail.mime.encodefilename</code> System property
1204     * is set to true, the {@link MimeUtility#decodeText
1205     * MimeUtility.decodeText} method will be used to decode the
1206     * filename. While such encoding is not supported by the MIME
1207     * spec, many mailers use this technique to support non-ASCII
1208     * characters in filenames. The default value of this property
1209     * is false.
1210     *
1211     * @return filename
1212     * @exception MessagingException
1213     */

1214    public String JavaDoc getFileName() throws MessagingException {
1215    return MimeBodyPart.getFileName(this);
1216    }
1217
1218    /**
1219     * Set the filename associated with this part, if possible. <p>
1220     *
1221     * Sets the "filename" parameter of the "Content-Disposition"
1222     * header field of this message. <p>
1223     *
1224     * If the <code>mail.mime.encodefilename</code> System property
1225     * is set to true, the {@link MimeUtility#encodeText
1226     * MimeUtility.encodeText} method will be used to encode the
1227     * filename. While such encoding is not supported by the MIME
1228     * spec, many mailers use this technique to support non-ASCII
1229     * characters in filenames. The default value of this property
1230     * is false.
1231     *
1232     * @exception IllegalWriteException if the underlying
1233     * implementation does not support modification
1234     * @exception IllegalStateException if this message is
1235     * obtained from a READ_ONLY folder.
1236     * @exception MessagingException
1237     */

1238    public void setFileName(String JavaDoc filename) throws MessagingException {
1239    MimeBodyPart.setFileName(this, filename);
1240    }
1241
1242    private String JavaDoc getHeaderName(Message.RecipientType type)
1243                throws MessagingException {
1244    String JavaDoc headerName;
1245
1246    if (type == Message.RecipientType.TO)
1247        headerName = "To";
1248    else if (type == Message.RecipientType.CC)
1249        headerName = "Cc";
1250    else if (type == Message.RecipientType.BCC)
1251        headerName = "Bcc";
1252    else if (type == MimeMessage.RecipientType.NEWSGROUPS)
1253        headerName = "Newsgroups";
1254    else
1255        throw new MessagingException("Invalid Recipient Type");
1256    return headerName;
1257    }
1258
1259
1260    /**
1261     * Return a decoded input stream for this Message's "content". <p>
1262     *
1263     * This implementation obtains the input stream from the DataHandler,
1264     * that is, it invokes <code>getDataHandler().getInputStream()</code>.
1265     *
1266     * @return an InputStream
1267     * @exception MessagingException
1268     * @exception IOException this is typically thrown by the
1269     * DataHandler. Refer to the documentation for
1270     * javax.activation.DataHandler for more details.
1271     *
1272     * @see #getContentStream
1273     * @see javax.activation.DataHandler#getInputStream
1274     */

1275    public InputStream getInputStream()
1276        throws IOException, MessagingException {
1277    return getDataHandler().getInputStream();
1278    }
1279
1280    /**
1281     * Produce the raw bytes of the content. This method is used during
1282     * parsing, to create a DataHandler object for the content. Subclasses
1283     * that can provide a separate input stream for just the message
1284     * content might want to override this method. <p>
1285     *
1286     * This implementation returns a SharedInputStream, if
1287     * <code>contentStream</code> is not null. Otherwise, it
1288     * returns a ByteArrayInputStream constructed
1289     * out of the <code>content</code> byte array.
1290     *
1291     * @see #content
1292     */

1293    protected InputStream getContentStream() throws MessagingException {
1294    if (contentStream != null)
1295        return ((SharedInputStream JavaDoc)contentStream).newStream(0, -1);
1296    if (content != null)
1297        return new SharedByteArrayInputStream(content);
1298
1299    throw new MessagingException("No content");
1300    }
1301
1302    /**
1303     * Return an InputStream to the raw data with any Content-Transfer-Encoding
1304     * intact. This method is useful if the "Content-Transfer-Encoding"
1305     * header is incorrect or corrupt, which would prevent the
1306     * <code>getInputStream</code> method or <code>getContent</code> method
1307     * from returning the correct data. In such a case the application may
1308     * use this method and attempt to decode the raw data itself. <p>
1309     *
1310     * This implementation simply calls the <code>getContentStream</code>
1311     * method.
1312     *
1313     * @see #getInputStream
1314     * @see #getContentStream
1315     * @since JavaMail 1.2
1316     */

1317    public InputStream getRawInputStream() throws MessagingException {
1318    return getContentStream();
1319    }
1320
1321    /**
1322     * Return a DataHandler for this Message's content. <p>
1323     *
1324     * The implementation provided here works as follows. Note the use of
1325     * the <code>getContentStream</code> method to
1326     * generate the byte stream for the content. Also note that
1327     * any transfer-decoding is done automatically within this method.<p>
1328     *
1329     * <blockquote><pre>
1330     * getDataHandler() {
1331     * if (dh == null) {
1332     * dh = new DataHandler(new MimePartDataSource(this));
1333     * }
1334     * return dh;
1335     * }
1336     * <p>
1337     * class MimePartDataSource implements DataSource {
1338     * public getInputStream() {
1339     * return MimeUtility.decode(
1340     * getContentStream(), getEncoding());
1341     * }
1342     *
1343     * .... <other DataSource methods>
1344     * }
1345     * </pre></blockquote><p>
1346     *
1347     * @exception MessagingException
1348     */

1349    public synchronized DataHandler getDataHandler()
1350        throws MessagingException {
1351    if (dh == null)
1352        dh = new DataHandler(new MimePartDataSource JavaDoc(this));
1353    return dh;
1354    }
1355
1356    /**
1357     * Return the content as a Java object. The type of this
1358     * object is dependent on the content itself. For
1359     * example, the native format of a "text/plain" content
1360     * is usually a String object. The native format for a "multipart"
1361     * message is always a Multipart subclass. For content types that are
1362     * unknown to the DataHandler system, an input stream is returned
1363     * as the content. <p>
1364     *
1365     * This implementation obtains the content from the DataHandler,
1366     * that is, it invokes <code>getDataHandler().getContent()</code>.
1367     * If the content is a Multipart or Message object and was created by
1368     * parsing a stream, the object is cached and returned in subsequent
1369     * calls so that modifications to the content will not be lost.
1370     *
1371     * @return Object
1372     * @see javax.mail.Part
1373     * @see javax.activation.DataHandler#getContent
1374     * @exception MessagingException
1375     * @exception IOException this is typically thrown by the
1376     * DataHandler. Refer to the documentation for
1377     * javax.activation.DataHandler for more details.
1378     */

1379    public Object JavaDoc getContent() throws IOException, MessagingException {
1380    Object JavaDoc c = getDataHandler().getContent();
1381    if (MimeBodyPart.cacheMultipart &&
1382        (c instanceof Multipart || c instanceof Message) &&
1383        !(dh instanceof CachedDataHandler) &&
1384        (content != null || contentStream != null)) {
1385        dh = MimeBodyPart.createCachedDataHandler(c, getContentType());
1386    }
1387    return c;
1388    }
1389
1390    /**
1391     * This method provides the mechanism to set this part's content.
1392     * The given DataHandler object should wrap the actual content.
1393     *
1394     * @param dh The DataHandler for the content.
1395     * @exception IllegalWriteException if the underlying
1396     * implementation does not support modification
1397     * @exception IllegalStateException if this message is
1398     * obtained from a READ_ONLY folder.
1399     * @exception MessagingException
1400     */

1401    public synchronized void setDataHandler(DataHandler dh)
1402        throws MessagingException {
1403    this.dh = dh;
1404    MimeBodyPart.invalidateContentHeaders(this);
1405    }
1406
1407    /**
1408     * A convenience method for setting this Message's content. <p>
1409     *
1410     * The content is wrapped in a DataHandler object. Note that a
1411     * DataContentHandler class for the specified type should be
1412     * available to the JavaMail implementation for this to work right.
1413     * i.e., to do <code>setContent(foobar, "application/x-foobar")</code>,
1414     * a DataContentHandler for "application/x-foobar" should be installed.
1415     * Refer to the Java Activation Framework for more information.
1416     *
1417     * @param o the content object
1418     * @param type Mime type of the object
1419     * @exception IllegalWriteException if the underlying
1420     * implementation does not support modification of
1421     * existing values
1422     * @exception IllegalStateException if this message is
1423     * obtained from a READ_ONLY folder.
1424     * @exception MessagingException
1425     */

1426    public void setContent(Object JavaDoc o, String JavaDoc type)
1427            throws MessagingException {
1428    setDataHandler(new DataHandler(o, type));
1429    }
1430
1431    /**
1432     * Convenience method that sets the given String as this
1433     * part's content, with a MIME type of "text/plain". If the
1434     * string contains non US-ASCII characters. it will be encoded
1435     * using the platform's default charset. The charset is also
1436     * used to set the "charset" parameter.<p>
1437     *
1438     * Note that there may be a performance penalty if
1439     * <code>text</code> is large, since this method may have
1440     * to scan all the characters to determine what charset to
1441     * use. <p>
1442     *
1443     * If the charset is already known, use the
1444     * <code>setText</code> method that takes the charset parameter.
1445     *
1446     * @param text the text content to set
1447     * @exception MessagingException if an error occurs
1448     * @see #setText(String text, String charset)
1449     */

1450    public void setText(String JavaDoc text) throws MessagingException {
1451    setText(text, null);
1452    }
1453
1454    /**
1455     * Convenience method that sets the given String as this part's
1456     * content, with a MIME type of "text/plain" and the specified
1457     * charset. The given Unicode string will be charset-encoded
1458     * using the specified charset. The charset is also used to set
1459     * the "charset" parameter.
1460     *
1461     * @param text the text content to set
1462     * @param charset the charset to use for the text
1463     * @exception MessagingException if an error occurs
1464     */

1465    public void setText(String JavaDoc text, String JavaDoc charset)
1466            throws MessagingException {
1467    MimeBodyPart.setText(this, text, charset, "plain");
1468    }
1469
1470    /**
1471     * Convenience method that sets the given String as this part's
1472     * content, with a primary MIME type of "text" and the specified
1473     * MIME subtype. The given Unicode string will be charset-encoded
1474     * using the specified charset. The charset is also used to set
1475     * the "charset" parameter.
1476     *
1477     * @param text the text content to set
1478     * @param charset the charset to use for the text
1479     * @param subtype the MIME subtype to use (e.g., "html")
1480     * @exception MessagingException if an error occurs
1481     * @since JavaMail 1.4
1482     */

1483    public void setText(String JavaDoc text, String JavaDoc charset, String JavaDoc subtype)
1484                        throws MessagingException {
1485    MimeBodyPart.setText(this, text, charset, subtype);
1486    }
1487
1488    /**
1489     * This method sets the Message's content to a Multipart object.
1490     *
1491     * @param mp The multipart object that is the Message's content
1492     * @exception IllegalWriteException if the underlying
1493     * implementation does not support modification of
1494     * existing values
1495     * @exception IllegalStateException if this message is
1496     * obtained from a READ_ONLY folder.
1497     * @exception MessagingException
1498     */

1499    public void setContent(Multipart mp) throws MessagingException {
1500    setDataHandler(new DataHandler(mp, mp.getContentType()));
1501    mp.setParent(this);
1502    }
1503
1504    /**
1505     * Get a new Message suitable for a reply to this message.
1506     * The new Message will have its attributes and headers
1507     * set up appropriately. Note that this new message object
1508     * will be empty, i.e., it will <strong>not</strong> have a "content".
1509     * These will have to be suitably filled in by the client. <p>
1510     *
1511     * If <code>replyToAll</code> is set, the new Message will be addressed
1512     * to all recipients of this message. Otherwise, the reply will be
1513     * addressed to only the sender of this message (using the value
1514     * of the <code>getReplyTo</code> method). <p>
1515     *
1516     * The "Subject" field is filled in with the original subject
1517     * prefixed with "Re:" (unless it already starts with "Re:").
1518     * The "In-Reply-To" header is set in the new message if this
1519     * message has a "Message-Id" header. The <code>ANSWERED</code>
1520     * flag is set in this message.
1521     *
1522     * @param replyToAll reply should be sent to all recipients
1523     * of this message
1524     * @return the reply Message
1525     * @exception MessagingException
1526     */

1527    public Message reply(boolean replyToAll) throws MessagingException {
1528    MimeMessage JavaDoc reply = createMimeMessage(session);
1529    /*
1530     * Have to manipulate the raw Subject header so that we don't lose
1531     * any encoding information. This is safe because "Re:" isn't
1532     * internationalized and (generally) isn't encoded. If the entire
1533     * Subject header is encoded, prefixing it with "Re: " still leaves
1534     * a valid and correct encoded header.
1535     */

1536    String JavaDoc subject = getHeader("Subject", null);
1537    if (subject != null) {
1538        if (!subject.regionMatches(true, 0, "Re: ", 0, 4))
1539        subject = "Re: " + subject;
1540        reply.setHeader("Subject", subject);
1541    }
1542    Address a[] = getReplyTo();
1543    reply.setRecipients(Message.RecipientType.TO, a);
1544    if (replyToAll) {
1545        Vector v = new Vector();
1546        // add my own address to list
1547
InternetAddress JavaDoc me = InternetAddress.getLocalAddress(session);
1548        if (me != null)
1549        v.addElement(me);
1550        // add any alternate names I'm known by
1551
String JavaDoc alternates = null;
1552        if (session != null)
1553        alternates = session.getProperty("mail.alternates");
1554        if (alternates != null)
1555        eliminateDuplicates(v,
1556                InternetAddress.parse(alternates, false));
1557        // should we Cc all other original recipients?
1558
String JavaDoc replyallccStr = null;
1559        if (session != null)
1560        replyallccStr = session.getProperty("mail.replyallcc");
1561        boolean replyallcc =
1562        replyallccStr != null && replyallccStr.equalsIgnoreCase("true");
1563        // add the recipients from the To field so far
1564
eliminateDuplicates(v, a);
1565        a = getRecipients(Message.RecipientType.TO);
1566        a = eliminateDuplicates(v, a);
1567        if (a != null && a.length > 0) {
1568        if (replyallcc)
1569            reply.addRecipients(Message.RecipientType.CC, a);
1570        else
1571            reply.addRecipients(Message.RecipientType.TO, a);
1572        }
1573        a = getRecipients(Message.RecipientType.CC);
1574        a = eliminateDuplicates(v, a);
1575        if (a != null && a.length > 0)
1576        reply.addRecipients(Message.RecipientType.CC, a);
1577        // don't eliminate duplicate newsgroups
1578
a = getRecipients(RecipientType.NEWSGROUPS);
1579        if (a != null && a.length > 0)
1580        reply.setRecipients(RecipientType.NEWSGROUPS, a);
1581    }
1582    String JavaDoc msgId = getHeader("Message-Id", null);
1583    if (msgId != null)
1584        reply.setHeader("In-Reply-To", msgId);
1585    try {
1586        setFlags(answeredFlag, true);
1587    } catch (MessagingException mex) {
1588        // ignore it
1589
}
1590    return reply;
1591    }
1592
1593    // used above in reply()
1594
private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);
1595
1596    /**
1597     * Check addrs for any duplicates that may already be in v.
1598     * Return a new array without the duplicates. Add any new
1599     * addresses to v. Note that the input array may be modified.
1600     */

1601    private Address[] eliminateDuplicates(Vector v, Address[] addrs) {
1602    if (addrs == null)
1603        return null;
1604    int gone = 0;
1605    for (int i = 0; i < addrs.length; i++) {
1606        boolean found = false;
1607        // search the vector for this address
1608
for (int j = 0; j < v.size(); j++) {
1609        if (((InternetAddress JavaDoc)v.elementAt(j)).equals(addrs[i])) {
1610            // found it; count it and remove it from the input array
1611
found = true;
1612            gone++;
1613            addrs[i] = null;
1614            break;
1615        }
1616        }
1617        if (!found)
1618        v.addElement(addrs[i]); // add new address to vector
1619
}
1620    // if we found any duplicates, squish the array
1621
if (gone != 0) {
1622        Address[] a;
1623        // new array should be same type as original array
1624
// XXX - there must be a better way, perhaps reflection?
1625
if (addrs instanceof InternetAddress JavaDoc[])
1626        a = new InternetAddress JavaDoc[addrs.length - gone];
1627        else
1628        a = new Address[addrs.length - gone];
1629        for (int i = 0, j = 0; i < addrs.length; i++)
1630        if (addrs[i] != null)
1631            a[j++] = addrs[i];
1632        addrs = a;
1633    }
1634    return addrs;
1635    }
1636
1637    /**
1638     * Output the message as an RFC 822 format stream. <p>
1639     *
1640     * Note that, depending on how the messag was constructed, it may
1641     * use a variety of line termination conventions. Generally the
1642     * output should be sent through an appropriate FilterOutputStream
1643     * that converts the line terminators to the desired form, either
1644     * CRLF for MIME compatibility and for use in Internet protocols,
1645     * or the local platform's line terminator for storage in a local
1646     * text file. <p>
1647     *
1648     * This implementation calls the <code>writeTo(OutputStream,
1649     * String[])</code> method with a null ignore list.
1650     *
1651     * @exception IOException if an error occurs writing to the stream
1652     * or if an error is generated by the
1653     * javax.activation layer.
1654     * @exception MessagingException
1655     * @see javax.activation.DataHandler#writeTo
1656     */

1657    public void writeTo(OutputStream os)
1658                throws IOException, MessagingException {
1659    writeTo(os, null);
1660    }
1661
1662    /**
1663     * Output the message as an RFC 822 format stream, without
1664     * specified headers. If the <code>saved</code> flag is not set,
1665     * the <code>saveChanges</code> method is called.
1666     * If the <code>modified</code> flag is not
1667     * set and the <code>content</code> array is not null, the
1668     * <code>content</code> array is written directly, after
1669     * writing the appropriate message headers.
1670     *
1671     * @exception javax.mail.MessagingException
1672     * @exception IOException if an error occurs writing to the stream
1673     * or if an error is generated by the
1674     * javax.activation layer.
1675     * @see javax.activation.DataHandler#writeTo
1676     */

1677    public void writeTo(OutputStream os, String JavaDoc[] ignoreList)
1678                throws IOException, MessagingException {
1679    if (!saved)
1680        saveChanges();
1681
1682    if (modified) {
1683        MimeBodyPart.writeTo(this, os, ignoreList);
1684        return;
1685    }
1686
1687    // Else, the content is untouched, so we can just output it
1688
// First, write out the header
1689
Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList);
1690    LineOutputStream los = new LineOutputStream(os);
1691    while (hdrLines.hasMoreElements())
1692        los.writeln((String JavaDoc)hdrLines.nextElement());
1693
1694    // The CRLF separator between header and content
1695
los.writeln();
1696
1697    // Finally, the content.
1698
if (content == null) {
1699        // call getContentStream to give subclass a chance to
1700
// provide the data on demand
1701
InputStream is = getContentStream();
1702        // now copy the data to the output stream
1703
byte[] buf = new byte[8192];
1704        int len;
1705        while ((len = is.read(buf)) > 0)
1706        os.write(buf, 0, len);
1707        is.close();
1708        buf = null;
1709    } else {
1710        os.write(content);
1711    }
1712    os.flush();
1713    }
1714
1715    /**
1716     * Get all the headers for this header_name. Note that certain
1717     * headers may be encoded as per RFC 2047 if they contain
1718     * non US-ASCII characters and these should be decoded. <p>
1719     *
1720     * This implementation obtains the headers from the
1721     * <code>headers</code> InternetHeaders object.
1722     *
1723     * @param name name of header
1724     * @return array of headers
1725     * @exception MessagingException
1726     * @see javax.mail.internet.MimeUtility
1727     */

1728    public String JavaDoc[] getHeader(String JavaDoc name)
1729            throws MessagingException {
1730    return headers.getHeader(name);
1731    }
1732
1733    /**
1734     * Get all the headers for this header name, returned as a single
1735     * String, with headers separated by the delimiter. If the
1736     * delimiter is <code>null</code>, only the first header is
1737     * returned.
1738     *
1739     * @param name the name of this header
1740     * @param delimiter separator between values
1741     * @return the value fields for all headers with
1742     * this name
1743     * @exception MessagingException
1744     */

1745    public String JavaDoc getHeader(String JavaDoc name, String JavaDoc delimiter)
1746                throws MessagingException {
1747    return headers.getHeader(name, delimiter);
1748    }
1749
1750    /**
1751     * Set the value for this header_name. Replaces all existing
1752     * header values with this new value. Note that RFC 822 headers
1753     * must contain only US-ASCII characters, so a header that
1754     * contains non US-ASCII characters must have been encoded by the
1755     * caller as per the rules of RFC 2047.
1756     *
1757     * @param name header name
1758     * @param value header value
1759     * @see javax.mail.internet.MimeUtility
1760     * @exception IllegalWriteException if the underlying
1761     * implementation does not support modification
1762     * @exception IllegalStateException if this message is
1763     * obtained from a READ_ONLY folder.
1764     * @exception MessagingException
1765     */

1766    public void setHeader(String JavaDoc name, String JavaDoc value)
1767                                throws MessagingException {
1768    headers.setHeader(name, value);
1769    }
1770
1771    /**
1772     * Add this value to the existing values for this header_name.
1773     * Note that RFC 822 headers must contain only US-ASCII
1774     * characters, so a header that contains non US-ASCII characters
1775     * must have been encoded as per the rules of RFC 2047.
1776     *
1777     * @param name header name
1778     * @param value header value
1779     * @see javax.mail.internet.MimeUtility
1780     * @exception IllegalWriteException if the underlying
1781     * implementation does not support modification
1782     * @exception IllegalStateException if this message is
1783     * obtained from a READ_ONLY folder.
1784     * @exception MessagingException
1785     */

1786    public void addHeader(String JavaDoc name, String JavaDoc value)
1787                                throws MessagingException {
1788    headers.addHeader(name, value);
1789    }
1790
1791    /**
1792     * Remove all headers with this name.
1793     * @exception IllegalWriteException if the underlying
1794     * implementation does not support modification
1795     * @exception IllegalStateException if this message is
1796     * obtained from a READ_ONLY folder.
1797     * @exception MessagingException
1798     */

1799    public void removeHeader(String JavaDoc name)
1800                                throws MessagingException {
1801    headers.removeHeader(name);
1802    }
1803
1804    /**
1805     * Return all the headers from this Message as an enumeration
1806     * of Header objects. <p>
1807     *
1808     * Note that certain headers may be encoded as per RFC 2047
1809     * if they contain non US-ASCII characters and these should
1810     * be decoded. <p>
1811     *
1812     * This implementation obtains the headers from the
1813     * <code>headers</code> InternetHeaders object.
1814     *
1815     * @return array of header objects
1816     * @exception MessagingException
1817     * @see javax.mail.internet.MimeUtility
1818     */

1819    public Enumeration getAllHeaders() throws MessagingException {
1820    return headers.getAllHeaders();
1821    }
1822
1823    /**
1824     * Return matching headers from this Message as an Enumeration of
1825     * Header objects. This implementation obtains the headers from
1826     * the <code>headers</code> InternetHeaders object.
1827     *
1828     * @exception MessagingException
1829     */

1830    public Enumeration getMatchingHeaders(String JavaDoc[] names)
1831            throws MessagingException {
1832    return headers.getMatchingHeaders(names);
1833    }
1834
1835    /**
1836     * Return non-matching headers from this Message as an
1837     * Enumeration of Header objects. This implementation
1838     * obtains the header from the <code>headers</code> InternetHeaders object.
1839     *
1840     * @exception MessagingException
1841     */

1842    public Enumeration getNonMatchingHeaders(String JavaDoc[] names)
1843            throws MessagingException {
1844    return headers.getNonMatchingHeaders(names);
1845    }
1846
1847    /**
1848     * Add a raw RFC 822 header-line.
1849     *
1850     * @exception IllegalWriteException if the underlying
1851     * implementation does not support modification
1852     * @exception IllegalStateException if this message is
1853     * obtained from a READ_ONLY folder.
1854     * @exception MessagingException
1855     */

1856    public void addHeaderLine(String JavaDoc line) throws MessagingException {
1857    headers.addHeaderLine(line);
1858    }
1859
1860    /**
1861     * Get all header lines as an Enumeration of Strings. A Header
1862     * line is a raw RFC 822 header-line, containing both the "name"
1863     * and "value" field.
1864     *
1865     * @exception MessagingException
1866     */

1867    public Enumeration getAllHeaderLines() throws MessagingException {
1868    return headers.getAllHeaderLines();
1869    }
1870
1871    /**
1872     * Get matching header lines as an Enumeration of Strings.
1873     * A Header line is a raw RFC 822 header-line, containing both
1874     * the "name" and "value" field.
1875     *
1876     * @exception MessagingException
1877     */

1878    public Enumeration getMatchingHeaderLines(String JavaDoc[] names)
1879                                        throws MessagingException {
1880    return headers.getMatchingHeaderLines(names);
1881    }
1882
1883    /**
1884     * Get non-matching header lines as an Enumeration of Strings.
1885     * A Header line is a raw RFC 822 header-line, containing both
1886     * the "name" and "value" field.
1887     *
1888     * @exception MessagingException
1889     */

1890    public Enumeration getNonMatchingHeaderLines(String JavaDoc[] names)
1891                                        throws MessagingException {
1892    return headers.getNonMatchingHeaderLines(names);
1893    }
1894
1895    /**
1896     * Return a <code>Flags</code> object containing the flags for
1897     * this message. <p>
1898     *
1899     * Note that a clone of the internal Flags object is returned, so
1900     * modifying the returned Flags object will not affect the flags
1901     * of this message.
1902     *
1903     * @return Flags object containing the flags for this message
1904     * @exception MessagingException
1905     * @see javax.mail.Flags
1906     */

1907    public synchronized Flags getFlags() throws MessagingException {
1908    return (Flags)flags.clone();
1909    }
1910
1911    /**
1912     * Check whether the flag specified in the <code>flag</code>
1913     * argument is set in this message. <p>
1914     *
1915     * This implementation checks this message's internal
1916     * <code>flags</code> object.
1917     *
1918     * @param flag the flag
1919     * @return value of the specified flag for this message
1920     * @see javax.mail.Flags.Flag
1921     * @see javax.mail.Flags.Flag#ANSWERED
1922     * @see javax.mail.Flags.Flag#DELETED
1923     * @see javax.mail.Flags.Flag#DRAFT
1924     * @see javax.mail.Flags.Flag#FLAGGED
1925     * @see javax.mail.Flags.Flag#RECENT
1926     * @see javax.mail.Flags.Flag#SEEN
1927     * @exception MessagingException
1928     */

1929    public synchronized boolean isSet(Flags.Flag flag)
1930                throws MessagingException {
1931    return (flags.contains(flag));
1932    }
1933
1934    /**
1935     * Set the flags for this message. <p>
1936     *
1937     * This implementation modifies the <code>flags</code> field.
1938     *
1939     * @exception IllegalWriteException if the underlying
1940     * implementation does not support modification
1941     * @exception IllegalStateException if this message is
1942     * obtained from a READ_ONLY folder.
1943     * @exception MessagingException
1944     */

1945    public synchronized void setFlags(Flags flag, boolean set)
1946            throws MessagingException {
1947    if (set)
1948        flags.add(flag);
1949    else
1950        flags.remove(flag);
1951    }
1952
1953    /**
1954     * Updates the appropriate header fields of this message to be
1955     * consistent with the message's contents. If this message is
1956     * contained in a Folder, any changes made to this message are
1957     * committed to the containing folder. <p>
1958     *
1959     * If any part of a message's headers or contents are changed,
1960     * <code>saveChanges</code> must be called to ensure that those
1961     * changes are permanent. Otherwise, any such modifications may or
1962     * may not be saved, depending on the folder implementation. <p>
1963     *
1964     * Messages obtained from folders opened READ_ONLY should not be
1965     * modified and saveChanges should not be called on such messages. <p>
1966     *
1967     * This method sets the <code>modified</code> flag to true, the
1968     * <code>save</code> flag to true, and then calls the
1969     * <code>updateHeaders<code> method.
1970     *
1971     * @exception IllegalWriteException if the underlying
1972     * implementation does not support modification
1973     * @exception IllegalStateException if this message is
1974     * obtained from a READ_ONLY folder.
1975     * @exception MessagingException
1976     */

1977    public void saveChanges() throws MessagingException {
1978    modified = true;
1979    saved = true;
1980    updateHeaders();
1981    }
1982
1983    /**
1984     * Update the Message-ID header. This method is called
1985     * by the <code>updateHeaders</code> and allows a subclass
1986     * to override only the algorithm for choosing a Message-ID.
1987     *
1988     * @since JavaMail 1.4
1989     */

1990    protected void updateMessageID() throws MessagingException {
1991    setHeader("Message-ID",
1992          "<" + UniqueValue.getUniqueMessageIDValue(session) + ">");
1993          
1994    }
1995
1996    /**
1997     * Called by the <code>saveChanges</code> method to actually
1998     * update the MIME headers. The implementation here sets the
1999     * <code>Content-Transfer-Encoding</code> header (if needed
2000     * and not already set), the <code>MIME-Version</code> header
2001     * and the <code>Message-ID</code> header. Also, if the content
2002     * of this message is a <code>MimeMultipart</code>, it's
2003     * <code>updateHeaders</code> method is called.
2004     *
2005     * @exception IllegalWriteException if the underlying
2006     * implementation does not support modification
2007     * @exception IllegalStateException if this message is
2008     * obtained from a READ_ONLY folder.
2009     * @exception MessagingException
2010     */

2011    protected void updateHeaders() throws MessagingException {
2012    MimeBodyPart.updateHeaders(this);
2013    setHeader("MIME-Version", "1.0");
2014        updateMessageID();
2015    }
2016
2017    /**
2018     * Create and return an InternetHeaders object that loads the
2019     * headers from the given InputStream. Subclasses can override
2020     * this method to return a subclass of InternetHeaders, if
2021     * necessary. This implementation simply constructs and returns
2022     * an InternetHeaders object.
2023     *
2024     * @param is the InputStream to read the headers from
2025     * @exception MessagingException
2026     * @since JavaMail 1.2
2027     */

2028    protected InternetHeaders JavaDoc createInternetHeaders(InputStream is)
2029                throws MessagingException {
2030    return new InternetHeaders JavaDoc(is);
2031    }
2032
2033    /**
2034     * Create and return a MimeMessage object. The reply method
2035     * uses this method to create the MimeMessage object that it
2036     * will return. Subclasses can override this method to return
2037     * a subclass of MimeMessage. This implementation simply constructs
2038     * and returns a MimeMessage object using the supplied Session.
2039     *
2040     * @param session the Session to use for the new message
2041     * @return the new MimeMessage object
2042     * @since JavaMail 1.4
2043     */

2044    protected MimeMessage JavaDoc createMimeMessage(Session session)
2045                throws MessagingException {
2046    return new MimeMessage JavaDoc(session);
2047    }
2048}
2049
Popular Tags