1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * https://oss.oracle.com/licenses/CDDL+GPL-1.1
12  * or LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */

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

102
103 public class MimeMessage extends Message implements MimePart {
104
105     /**
106      * The DataHandler object representing this Message's content.
107      */

108     protected DataHandler dh;
109
110     /**
111      * Byte array that holds the bytes of this Message's content.
112      */

113     protected byte[] content;
114
115     /**
116      * If the data for this message was supplied by an
117      * InputStream that implements the SharedInputStream interface,
118      * <code>contentStream</code> is another such stream representing
119      * the content of this message.  In this case, <code>content</code>
120      * will be null.
121      *
122      * @since    JavaMail 1.2
123      */

124     protected InputStream contentStream;
125
126     /**
127      * The InternetHeaders object that stores the header
128      * of this message.
129      */

130     protected InternetHeaders headers;
131
132     /**
133      * The Flags for this message. 
134      */

135     protected Flags flags;
136
137     /**
138      * A flag indicating whether the message has been modified.
139      * If the message has not been modified, any data in the
140      * <code>content</code> array is assumed to be valid and is used
141      * directly in the <code>writeTo</code> method.  This flag is
142      * set to true when an empty message is created or when the
143      * <code>saveChanges</code> method is called.
144      *
145      * @since    JavaMail 1.2
146      */

147     protected boolean modified = false;
148
149     /**
150      * Does the <code>saveChanges</code> method need to be called on
151      * this message?  This flag is set to false by the public constructor
152      * and set to true by the <code>saveChanges</code> method.  The
153      * <code>writeTo</code> method checks this flag and calls the
154      * <code>saveChanges</code> method as necessary.  This avoids the
155      * common mistake of forgetting to call the <code>saveChanges</code>
156      * method on a newly constructed message.
157      *
158      * @since    JavaMail 1.2
159      */

160     protected boolean saved = false;
161
162     /**
163      * If our content is a Multipart or Message object, we save it
164      * the first time it's created by parsing a stream so that changes
165      * to the contained objects will not be lost. <p>
166      *
167      * If this field is not null, it's return by the {@link #getContent}
168      * method.  The {@link #getContent} method sets this field if it
169      * would return a Multipart or MimeMessage object.  This field is
170      * is cleared by the {@link #setDataHandler} method.
171      *
172      * @since    JavaMail 1.5
173      */

174     protected Object cachedContent;
175
176     // Used to parse dates
177     private static final MailDateFormat mailDateFormat = new MailDateFormat();
178
179     // Should addresses in headers be parsed in "strict" mode?
180     private boolean strict = true;
181     // Is UTF-8 allowed in headers?
182     private boolean allowutf8 = false;
183
184     /**
185      * Default constructor. An empty message object is created.
186      * The <code>headers</code> field is set to an empty InternetHeaders
187      * object. The <code>flags</code> field is set to an empty Flags
188      * object. The <code>modified</code> flag is set to true.
189      *
190      * @param    session    the Sesssion
191      */

192     public MimeMessage(Session session) {
193     super(session);
194     modified = true;
195     headers = new InternetHeaders();
196     flags = new Flags();    // empty flags object
197     initStrict();
198     }
199
200     /**
201      * Constructs a MimeMessage by reading and parsing the data from the
202      * specified MIME InputStream. The InputStream will be left positioned
203      * at the end of the data for the message. Note that the input stream
204      * parse is done within this constructor itself. <p>
205      *
206      * The input stream contains an entire MIME formatted message with
207      * headers and data.
208      *
209      * @param session    Session object for this message
210      * @param is    the message input stream
211      * @exception    MessagingException for failures
212      */

213     public MimeMessage(Session session, InputStream is) 
214             throws MessagingException {
215     super(session);
216     flags = new Flags(); // empty Flags object
217     initStrict();
218     parse(is);
219     saved = true;
220     }
221
222     /**
223      * Constructs a new MimeMessage with content initialized from the
224      * <code>source</code> MimeMessage.  The new message is independent
225      * of the original. <p>
226      *
227      * Note: The current implementation is rather inefficient, copying
228      * the data more times than strictly necessary.
229      *
230      * @param    source    the message to copy content from
231      * @exception    MessagingException for failures
232      * @since        JavaMail 1.2
233      */

234     public MimeMessage(MimeMessage source) throws MessagingException {
235     super(source.session);
236     flags = source.getFlags();
237     if (flags == null)    // make sure flags is always set
238         flags = new Flags();
239     ByteArrayOutputStream bos;
240     int size = source.getSize();
241     if (size > 0)
242         bos = new ByteArrayOutputStream(size);
243     else
244         bos = new ByteArrayOutputStream();
245     try {
246         strict = source.strict;
247         source.writeTo(bos);
248         bos.close();
249         SharedByteArrayInputStream bis =
250                 new SharedByteArrayInputStream(bos.toByteArray());
251         parse(bis);
252         bis.close();
253         saved = true;
254     } catch (IOException ex) {
255         // should never happen, but just in case...
256         throw new MessagingException("IOException while copying message",
257                         ex);
258     }
259     }
260
261     /**
262      * Constructs an empty MimeMessage object with the given Folder
263      * and message number. <p>
264      *
265      * This method is for providers subclassing <code>MimeMessage</code>.
266      *
267      * @param    folder    the Folder this message is from
268      * @param    msgnum    the number of this message
269      */

270     protected MimeMessage(Folder folder, int msgnum) {
271     super(folder, msgnum);
272     flags = new Flags();  // empty Flags object
273     saved = true;
274     initStrict();
275     }
276
277     /**
278      * Constructs a MimeMessage by reading and parsing the data from the
279      * specified MIME InputStream. The InputStream will be left positioned
280      * at the end of the data for the message. Note that the input stream
281      * parse is done within this constructor itself. <p>
282      *
283      * This method is for providers subclassing <code>MimeMessage</code>.
284      *
285      * @param folder    The containing folder.
286      * @param is    the message input stream
287      * @param msgnum    Message number of this message within its folder
288      * @exception    MessagingException for failures
289      */

290     protected MimeMessage(Folder folder, InputStream is, int msgnum)
291         throws MessagingException {
292     this(folder, msgnum);
293     initStrict();
294     parse(is);
295     }
296
297     /**
298      * Constructs a MimeMessage from the given InternetHeaders object
299      * and content.
300      *
301      * This method is for providers subclassing <code>MimeMessage</code>.
302      *
303      * @param folder    The containing folder.
304      * @param headers    The headers
305      * @param content    The message content
306      * @param msgnum    Message number of this message within its folder
307      * @exception    MessagingException for failures
308      */

309     protected MimeMessage(Folder folder, InternetHeaders headers,
310         byte[] content, int msgnum) throws MessagingException {
311     this(folder, msgnum);
312     this.headers = headers;
313     this.content = content;
314     initStrict();
315     }
316
317     /**
318      * Set the strict flag based on property.
319      */

320     private void initStrict() {
321     if (session != null) {
322         strict = PropUtil.getBooleanSessionProperty(session,
323                     "mail.mime.address.strict"true);
324         allowutf8 = PropUtil.getBooleanSessionProperty(session,
325                     "mail.mime.allowutf8"false);
326     }
327     }
328
329     /**
330      * Parse the InputStream setting the <code>headers</code> and
331      * <code>content</code> fields appropriately.  Also resets the
332      * <code>modified</code> flag. <p>
333      *
334      * This method is intended for use by subclasses that need to
335      * control when the InputStream is parsed.
336      *
337      * @param is    The message input stream
338      * @exception    MessagingException for failures
339      */

340     protected void parse(InputStream is) throws MessagingException {
341
342     if (!(is instanceof ByteArrayInputStream) &&
343         !(is instanceof BufferedInputStream) &&
344         !(is instanceof SharedInputStream))
345         is = new BufferedInputStream(is);
346     
347     headers = createInternetHeaders(is);
348
349     if (is instanceof SharedInputStream) {
350         SharedInputStream sis = (SharedInputStream)is;
351         contentStream = sis.newStream(sis.getPosition(), -1);
352     } else {
353         try {
354         content = ASCIIUtility.getBytes(is);
355         } catch (IOException ioex) {
356         throw new MessagingException("IOException", ioex);
357         }
358     }
359
360     modified = false;
361     }
362
363     /** 
364      * Returns the value of the RFC 822 "From" header fields. If this 
365      * header field is absent, the "Sender" header field is used. 
366      * If the "Sender" header field is also absent, <code>null</code>
367      * is returned.<p>
368      *
369      * This implementation uses the <code>getHeader</code> method
370      * to obtain the requisite header field.
371      *
372      * @return        Address object
373      * @exception    MessagingException for failures
374      * @see    #headers
375      */

376     @Override
377     public Address[] getFrom() throws MessagingException {
378     Address[] a = getAddressHeader("From");
379     if (a == null)
380         a = getAddressHeader("Sender");
381     
382     return a;
383     }
384
385     /**
386      * Set the RFC 822 "From" header field. Any existing values are 
387      * replaced with the given address. If address is <code>null</code>,
388      * this header is removed.
389      *
390      * @param address    the sender 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 for other failures
397      */

398     @Override
399     public void setFrom(Address address) throws MessagingException {
400     if (address == null)
401         removeHeader("From");
402     else
403         setHeader("From", MimeUtility.fold(6, address.toString()));
404     }
405
406     /**
407      * Set the RFC 822 "From" header field. Any existing values are 
408      * replaced with the given addresses. If address is <code>null</code>,
409      * this header is removed.
410      *
411      * @param address    the sender(s) of this message
412      * @exception    IllegalWriteException if the underlying
413      *            implementation does not support modification
414      *            of existing values
415      * @exception    IllegalStateException if this message is
416      *            obtained from a READ_ONLY folder.
417      * @exception    MessagingException for other failures
418      * @since        JvaMail 1.5
419      */

420     public void setFrom(String address) throws MessagingException {
421     if (address == null)
422         removeHeader("From");
423     else
424         setAddressHeader("From", InternetAddress.parse(address));
425     }
426
427     /**
428      * Set the RFC 822 "From" header field using the value of the
429      * <code>InternetAddress.getLocalAddress</code> method.
430      *
431      * @exception    IllegalWriteException if the underlying
432      *            implementation does not support modification
433      *            of existing values
434      * @exception    IllegalStateException if this message is
435      *            obtained from a READ_ONLY folder.
436      * @exception    MessagingException for other failures
437      */

438     @Override
439     public void setFrom() throws MessagingException {
440     InternetAddress me = null;
441     try {
442         me = InternetAddress._getLocalAddress(session);
443     } catch (Exception ex) {
444         // if anything goes wrong (SecurityException, UnknownHostException),
445         // chain the exception
446         throw new MessagingException("No From address", ex);
447     }
448     if (me != null)
449         setFrom(me);
450     else
451         throw new MessagingException("No From address");
452     }
453
454     /**
455      * Add the specified addresses to the existing "From" field. If
456      * the "From" field does not already exist, it is created.
457      *
458      * @param addresses    the senders of this message
459      * @exception    IllegalWriteException if the underlying
460      *            implementation does not support modification
461      *            of existing values
462      * @exception    IllegalStateException if this message is
463      *            obtained from a READ_ONLY folder.
464      * @exception    MessagingException for other failures
465      */

466     @Override
467     public void addFrom(Address[] addresses) throws MessagingException {
468     addAddressHeader("From", addresses);
469     }
470
471     /** 
472      * Returns the value of the RFC 822 "Sender" header field.
473      * If the "Sender" header field is absent, <code>null</code>
474      * is returned.<p>
475      *
476      * This implementation uses the <code>getHeader</code> method
477      * to obtain the requisite header field.
478      *
479      * @return        Address object
480      * @exception    MessagingException for failures
481      * @see        #headers
482      * @since        JavaMail 1.3
483      */

484     public Address getSender() throws MessagingException {
485     Address[] a = getAddressHeader("Sender");
486     if (a == null || a.length == 0)
487         return null;
488     return a[0];    // there can be only one
489     }
490
491     /**
492      * Set the RFC 822 "Sender" header field. Any existing values are 
493      * replaced with the given address. If address is <code>null</code>,
494      * this header is removed.
495      *
496      * @param address    the sender of this message
497      * @exception    IllegalWriteException if the underlying
498      *            implementation does not support modification
499      *            of existing values
500      * @exception    IllegalStateException if this message is
501      *            obtained from a READ_ONLY folder.
502      * @exception    MessagingException for other failures
503      * @since        JavaMail 1.3
504      */

505     public void setSender(Address address) throws MessagingException {
506     if (address == null)
507         removeHeader("Sender");
508     else
509         setHeader("Sender", MimeUtility.fold(8, address.toString()));
510     }
511
512     /**
513      * This inner class extends the javax.mail.Message.RecipientType
514      * class to add additional RecipientTypes. The one additional
515      * RecipientType currently defined here is NEWSGROUPS.
516      *
517      * @see javax.mail.Message.RecipientType
518      */

519     public static class RecipientType extends Message.RecipientType {
520
521     private static final long serialVersionUID = -5468290701714395543L;
522
523     /**
524      * The "Newsgroup" (Usenet news) recipients.
525      */

526     public static final RecipientType NEWSGROUPS =
527                     new RecipientType("Newsgroups");
528     protected RecipientType(String type) {
529         super(type);
530     }
531
532     @Override
533     protected Object readResolve() throws ObjectStreamException {
534         if (type.equals("Newsgroups"))
535         return NEWSGROUPS;
536         else
537         return super.readResolve();
538     }
539     }
540
541     /**
542      * Returns the recepients specified by the type. The mapping
543      * between the type and the corresponding RFC 822 header is
544      * as follows:
545      * <pre>
546      *        Message.RecipientType.TO        "To"
547      *        Message.RecipientType.CC        "Cc"
548      *        Message.RecipientType.BCC        "Bcc"
549      *        MimeMessage.RecipientType.NEWSGROUPS    "Newsgroups"
550      * </pre><br>
551      *
552      * Returns null if the header specified by the type is not found
553      * or if its value is empty. <p>
554      *
555      * This implementation uses the <code>getHeader</code> method
556      * to obtain the requisite header field.
557      *
558      * @param           type    Type of recepient
559      * @return          array of Address objects
560      * @exception       MessagingException if header could not
561      *                  be retrieved
562      * @exception       AddressException if the header is misformatted
563      * @see        #headers
564      * @see        javax.mail.Message.RecipientType#TO
565      * @see        javax.mail.Message.RecipientType#CC
566      * @see        javax.mail.Message.RecipientType#BCC
567      * @see        javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
568      */

569     @Override
570     public Address[] getRecipients(Message.RecipientType type)
571                 throws MessagingException {
572     if (type == RecipientType.NEWSGROUPS) {
573         String s = getHeader("Newsgroups"",");
574         return (s == null) ? null : NewsAddress.parse(s);
575     } else
576         return getAddressHeader(getHeaderName(type));
577     }
578
579     /**
580      * Get all the recipient addresses for the message.
581      * Extracts the TO, CC, BCC, and NEWSGROUPS recipients.
582      *
583      * @return          array of Address objects
584      * @exception       MessagingException for failures
585      * @see        javax.mail.Message.RecipientType#TO
586      * @see        javax.mail.Message.RecipientType#CC
587      * @see        javax.mail.Message.RecipientType#BCC
588      * @see        javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
589      */

590     @Override
591     public Address[] getAllRecipients() throws MessagingException {
592     Address[] all = super.getAllRecipients();
593     Address[] ng = getRecipients(RecipientType.NEWSGROUPS);
594
595     if (ng == null)
596         return all;        // the common case
597     if (all == null)
598         return ng;        // a rare case
599
600     Address[] addresses = new Address[all.length + ng.length];
601     System.arraycopy(all, 0, addresses, 0, all.length);
602     System.arraycopy(ng, 0, addresses, all.length, ng.length);
603     return addresses;
604     }
605     
606     /**
607      * Set the specified recipient type to the given addresses.
608      * If the address parameter is <code>null</code>, the corresponding
609      * recipient field is removed.
610      *
611      * @param type    Recipient type
612      * @param addresses    Addresses
613      * @exception    IllegalWriteException if the underlying
614      *            implementation does not support modification
615      *            of existing values
616      * @exception    IllegalStateException if this message is
617      *            obtained from a READ_ONLY folder.
618      * @exception    MessagingException for other failures
619      * @see        #getRecipients
620      */

621     @Override
622     public void setRecipients(Message.RecipientType type, Address[] addresses)
623                                 throws MessagingException {
624     if (type == RecipientType.NEWSGROUPS) {
625         if (addresses == null || addresses.length == 0)
626         removeHeader("Newsgroups");
627         else
628         setHeader("Newsgroups", NewsAddress.toString(addresses));
629     } else
630         setAddressHeader(getHeaderName(type), addresses);
631     }
632
633     /**
634      * Set the specified recipient type to the given addresses.
635      * If the address parameter is <code>null</code>, the corresponding
636      * recipient field is removed.
637      *   
638      * @param type      Recipient type
639      * @param addresses Addresses
640      * @exception       AddressException if the attempt to parse the
641      *                  addresses String fails
642      * @exception       IllegalWriteException if the underlying
643      *                  implementation does not support modification
644      *                  of existing values
645      * @exception       IllegalStateException if this message is
646      *                  obtained from a READ_ONLY folder.
647      * @exception       MessagingException for other failures
648      * @see             #getRecipients
649      * @since           JavaMail 1.2
650      */

651     public void setRecipients(Message.RecipientType type, String addresses)
652                                 throws MessagingException {
653         if (type == RecipientType.NEWSGROUPS) {
654             if (addresses == null || addresses.length() == 0)
655                 removeHeader("Newsgroups");
656             else
657                 setHeader("Newsgroups", addresses);
658         } else
659             setAddressHeader(getHeaderName(type),
660         addresses == null ? null : InternetAddress.parse(addresses));
661     }
662
663     /**
664      * Add the given addresses to the specified recipient type.
665      *
666      * @param type    Recipient type
667      * @param addresses    Addresses
668      * @exception    IllegalWriteException if the underlying
669      *            implementation does not support modification
670      *            of existing values
671      * @exception    IllegalStateException if this message is
672      *            obtained from a READ_ONLY folder.
673      * @exception    MessagingException for other failures
674      */

675     @Override
676     public void addRecipients(Message.RecipientType type, Address[] addresses)
677                                 throws MessagingException {
678     if (type == RecipientType.NEWSGROUPS) {
679         String s = NewsAddress.toString(addresses);
680         if (s != null)
681         addHeader("Newsgroups", s);
682     } else
683         addAddressHeader(getHeaderName(type), addresses);
684     }
685
686     /**
687      * Add the given addresses to the specified recipient type.
688      * 
689      * @param type      Recipient type
690      * @param addresses Addresses
691      * @exception       AddressException if the attempt to parse the
692      *                  addresses String fails
693      * @exception       IllegalWriteException if the underlying
694      *                  implementation does not support modification
695      *                  of existing values
696      * @exception       IllegalStateException if this message is
697      *                  obtained from a READ_ONLY folder.
698      * @exception       MessagingException for other failures
699      * @since           JavaMail 1.2
700      */

701     public void addRecipients(Message.RecipientType type, String addresses)
702                                 throws MessagingException {
703         if (type == RecipientType.NEWSGROUPS) {
704             if (addresses != null && addresses.length() != 0)
705                 addHeader("Newsgroups", addresses);
706         } else
707             addAddressHeader(getHeaderName(type),
708             InternetAddress.parse(addresses));
709     }
710  
711     /**
712      * Return the value of the RFC 822 "Reply-To" header field. If
713      * this header is unavailable or its value is absent, then
714      * the <code>getFrom</code> method is called and its value is returned.
715      *
716      * This implementation uses the <code>getHeader</code> method
717      * to obtain the requisite header field.
718      *
719      * @exception    MessagingException for failures
720      * @see        #headers
721      */

722     @Override
723     public Address[] getReplyTo() throws MessagingException {
724     Address[] a = getAddressHeader("Reply-To");
725     if (a == null || a.length == 0)
726         a = getFrom();
727     return a;
728     }
729
730     /**
731      * Set the RFC 822 "Reply-To" header field. If the address 
732      * parameter is <code>null</code>, this header is removed.
733      *
734      * @exception    IllegalWriteException if the underlying
735      *            implementation does not support modification
736      *            of existing values
737      * @exception    IllegalStateException if this message is
738      *            obtained from a READ_ONLY folder.
739      * @exception    MessagingException for other failures
740      */

741     @Override
742     public void setReplyTo(Address[] addresses) throws MessagingException {
743     setAddressHeader("Reply-To", addresses);
744     }
745
746     // Convenience method to get addresses
747     private Address[] getAddressHeader(String name) 
748             throws MessagingException {
749     String s = getHeader(name, ",");
750     return (s == null) ? null : InternetAddress.parseHeader(s, strict);
751     }
752
753     // Convenience method to set addresses
754     private void setAddressHeader(String name, Address[] addresses)
755             throws MessagingException {
756     String s;
757     if (allowutf8)
758         s = InternetAddress.toUnicodeString(addresses, name.length() + 2);
759     else
760         s = InternetAddress.toString(addresses, name.length() + 2);
761     if (s == null)
762         removeHeader(name);
763     else
764         setHeader(name, s);
765     }
766
767     private void addAddressHeader(String name, Address[] addresses)
768             throws MessagingException {
769     if (addresses == null || addresses.length == 0)
770         return;
771     Address[] a = getAddressHeader(name);
772     Address[] anew;
773     if (a == null || a.length == 0)
774         anew = addresses;
775     else {
776         anew = new Address[a.length + addresses.length];
777         System.arraycopy(a, 0, anew, 0, a.length);
778         System.arraycopy(addresses, 0, anew, a.length, addresses.length);
779     }
780     String s;
781     if (allowutf8)
782         s = InternetAddress.toUnicodeString(anew, name.length() + 2);
783     else
784         s = InternetAddress.toString(anew, name.length() + 2);
785     if (s == null)
786         return;
787     setHeader(name, s);
788     }
789
790     /**
791      * Returns the value of the "Subject" header field. Returns null 
792      * if the subject field is unavailable or its value is absent. <p>
793      *
794      * If the subject is encoded as per RFC 2047, it is decoded and
795      * converted into Unicode. If the decoding or conversion fails, the
796      * raw data is returned as is. <p>
797      *
798      * This implementation uses the <code>getHeader</code> method
799      * to obtain the requisite header field.
800      *
801      * @return          Subject
802      * @exception    MessagingException for failures
803      * @see        #headers
804      */

805     @Override
806     public String getSubject() throws MessagingException {
807     String rawvalue = getHeader("Subject"null);
808
809     if (rawvalue == null)
810         return null;
811
812     try {
813         return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
814     } catch (UnsupportedEncodingException ex) {
815         return rawvalue;
816     }
817     }
818
819     /**
820      * Set the "Subject" header field. If the subject contains 
821      * non US-ASCII characters, it will be encoded using the 
822      * platform's default charset. If the subject contains only 
823      * US-ASCII characters, no encoding is done and it is used 
824      * as-is. If the subject is null, the existing "Subject" field
825      * is removed. <p>
826      *
827      * The application must ensure that the subject does not contain
828      * any line breaks. <p>
829      *
830      * Note that if the charset encoding process fails, a
831      * MessagingException is thrown, and an UnsupportedEncodingException
832      * is included in the chain of nested exceptions within the
833      * MessagingException.
834      *
835      * @param     subject        The subject
836      * @exception    IllegalWriteException if the underlying
837      *            implementation does not support modification
838      *            of existing values
839      * @exception    IllegalStateException if this message is
840      *            obtained from a READ_ONLY folder.
841      * @exception    MessagingException for other failures
842      */

843     @Override
844     public void setSubject(String subject) throws MessagingException {
845     setSubject(subject, null);
846     }
847
848     /**
849      * Set the "Subject" header field. If the subject contains non 
850      * US-ASCII characters, it will be encoded using the specified
851      * charset. If the subject contains only US-ASCII characters, no 
852      * encoding is done and it is used as-is. If the subject is null
853      * the existing "Subject" header field is removed. <p>
854      *
855      * The application must ensure that the subject does not contain
856      * any line breaks. <p>
857      *
858      * Note that if the charset encoding process fails, a
859      * MessagingException is thrown, and an UnsupportedEncodingException
860      * is included in the chain of nested exceptions within the
861      * MessagingException.
862      *
863      * @param    subject        The subject
864      * @param    charset        The charset 
865      * @exception        IllegalWriteException if the underlying
866      *                implementation does not support modification
867      *                of existing values
868      * @exception        IllegalStateException if this message is
869      *                obtained from a READ_ONLY folder.
870      * @exception        MessagingException for other failures
871      */

872     public void setSubject(String subject, String charset)
873             throws MessagingException {
874     if (subject == null) {
875         removeHeader("Subject");
876     } else {
877         try {
878         setHeader("Subject", MimeUtility.fold(9,
879             MimeUtility.encodeText(subject, charset, null)));
880         } catch (UnsupportedEncodingException uex) {
881         throw new MessagingException("Encoding error", uex);
882         }
883     }
884     }
885
886     /**
887      * Returns the value of the RFC 822 "Date" field. This is the date 
888      * on which this message was sent. Returns null if this field is 
889      * unavailable or its value is absent. <p>
890      * 
891      * This implementation uses the <code>getHeader</code> method
892      * to obtain the requisite header field.
893      *
894      * @return          The sent Date
895      * @exception    MessagingException for failures
896      */

897     @Override
898     public Date getSentDate() throws MessagingException {
899     String s = getHeader("Date"null);
900     if (s != null) {
901         try {
902         synchronized (mailDateFormat) {
903             return mailDateFormat.parse(s);
904         }
905         } catch (ParseException pex) {
906         return null;
907         }
908     }
909     
910     return null;
911     }
912
913     /**
914      * Set the RFC 822 "Date" header field. This is the date on which the
915      * creator of the message indicates that the message is complete
916      * and ready for delivery. If the date parameter is 
917      * <code>null</code>, the existing "Date" field is removed.
918      *
919      * @exception    IllegalWriteException if the underlying
920      *            implementation does not support modification
921      * @exception    IllegalStateException if this message is
922      *            obtained from a READ_ONLY folder.
923      * @exception    MessagingException for other failures
924      */

925     @Override
926     public void setSentDate(Date d) throws MessagingException {
927     if (d == null)
928         removeHeader("Date");
929     else {
930         synchronized (mailDateFormat) {
931         setHeader("Date", mailDateFormat.format(d));
932         }
933     }
934     }
935
936     /**
937      * Returns the Date on this message was received. Returns 
938      * <code>null</code> if this date cannot be obtained. <p>
939      *
940      * Note that RFC 822 does not define a field for the received
941      * date. Hence only implementations that can provide this date
942      * need return a valid value. <p>
943      *
944      * This implementation returns <code>null</code>.
945      *
946      * @return          the date this message was received
947      * @exception    MessagingException for failures
948      */

949     @Override
950     public Date getReceivedDate() throws MessagingException {
951     return null;    
952     }
953
954     /**
955      * Return the size of the content of this message in bytes. 
956      * Return -1 if the size cannot be determined. <p>
957      *
958      * Note that this number may not be an exact measure of the
959      * content size and may or may not account for any transfer
960      * encoding of the content. <p>
961      *
962      * This implementation returns the size of the <code>content</code>
963      * array (if not null), or, if <code>contentStream</code> is not
964      * null, and the <code>available</code> method returns a positive
965      * number, it returns that number as the size.  Otherwise, it returns
966      * -1.
967      *
968      * @return          size of content in bytes
969      * @exception    MessagingException for failures
970      */
  
971     @Override
972     public int getSize() throws MessagingException {
973     if (content != null)
974         return content.length;
975     if (contentStream != null) {
976         try {
977         int size = contentStream.available();
978         // only believe the size if it's greater than zero, since zero
979         // is the default returned by the InputStream class itself
980         if (size > 0)
981             return size;
982         } catch (IOException ex) {
983         // ignore it
984         }
985     }
986     return -1;
987     }
988
989     /**
990      * Return the number of lines for the content of this message.
991      * Return -1 if this number cannot be determined. <p>
992      *
993      * Note that this number may not be an exact measure of the 
994      * content length and may or may not account for any transfer 
995      * encoding of the content. <p>
996      *
997      * This implementation returns -1.
998      *
999      * @return          number of lines in the content.
1000      * @exception    MessagingException for failures
1001      */
  
1002     @Override
1003     public int getLineCount() throws MessagingException {
1004     return -1;
1005     }
1006
1007     /**
1008      * Returns the value of the RFC 822 "Content-Type" header field. 
1009      * This represents the content-type of the content of this 
1010      * message. This value must not be null. If this field is 
1011      * unavailable, "text/plain" should be returned. <p>
1012      *
1013      * This implementation uses the <code>getHeader</code> method
1014      * to obtain the requisite header field.
1015      *
1016      * @return          The ContentType of this part
1017      * @exception    MessagingException for failures
1018      * @see             javax.activation.DataHandler
1019      */

1020     @Override
1021     public String getContentType() throws MessagingException {
1022     String s = getHeader("Content-Type"null);
1023     s = MimeUtil.cleanContentType(this, s);
1024     if (s == null)
1025         return "text/plain";
1026     return s;
1027     }
1028
1029     /**
1030      * Is this Part of the specified MIME type?  This method
1031      * compares <strong>only the <code>primaryType</code> and 
1032      * <code>subType</code></strong>.
1033      * The parameters of the content types are ignored. <p>
1034      *
1035      * For example, this method will return <code>true</code> when
1036      * comparing a Part of content type <strong>"text/plain"</strong>
1037      * with <strong>"text/plain; charset=foobar"</strong>. <p>
1038      *
1039      * If the <code>subType</code> of <code>mimeType</code> is the
1040      * special character '*', then the subtype is ignored during the
1041      * comparison.
1042      *
1043      * @param    mimeType    the MIME type to check
1044      * @return            true if it matches the MIME type
1045      * @exception        MessagingException for failures
1046      */

1047     @Override
1048     public boolean isMimeType(String mimeType) throws MessagingException {
1049     return MimeBodyPart.isMimeType(this, mimeType);
1050     }
1051
1052     /**
1053      * Returns the disposition from the "Content-Disposition" header field.
1054      * This represents the disposition of this part. The disposition
1055      * describes how the part should be presented to the user. <p>
1056      *
1057      * If the Content-Disposition field is unavailable, 
1058      * <code>null</code> is returned. <p>
1059      *
1060      * This implementation uses the <code>getHeader</code> method
1061      * to obtain the requisite header field.
1062      *
1063      * @return          disposition of this part, or null if unknown
1064      * @exception    MessagingException for failures
1065      */

1066     @Override
1067     public String getDisposition() throws MessagingException {
1068     return MimeBodyPart.getDisposition(this);
1069     }
1070
1071     /**
1072      * Set the disposition in the "Content-Disposition" header field
1073      * of this body part.  If the disposition is null, any existing
1074      * "Content-Disposition" header field is removed.
1075      *
1076      * @exception    IllegalWriteException if the underlying
1077      *            implementation does not support modification
1078      * @exception    IllegalStateException if this message is
1079      *            obtained from a READ_ONLY folder.
1080      * @exception    MessagingException for other failures
1081      */

1082     @Override
1083     public void setDisposition(String disposition) throws MessagingException {
1084     MimeBodyPart.setDisposition(this, disposition);
1085     }
1086
1087     /**
1088      * Returns the content transfer encoding from the
1089      * "Content-Transfer-Encoding" header
1090      * field. Returns <code>null</code> if the header is unavailable
1091      * or its value is absent. <p>
1092      *
1093      * This implementation uses the <code>getHeader</code> method
1094      * to obtain the requisite header field.
1095      *
1096      * @return          content-transfer-encoding
1097      * @exception    MessagingException for failures
1098      */

1099     @Override
1100     public String getEncoding() throws MessagingException {
1101     return MimeBodyPart.getEncoding(this);
1102     }
1103
1104     /**
1105      * Returns the value of the "Content-ID" header field. Returns
1106      * <code>null</code> if the field is unavailable or its value is 
1107      * absent. <p>
1108      *
1109      * This implementation uses the <code>getHeader</code> method
1110      * to obtain the requisite header field.
1111      *
1112      * @return          content-ID
1113      * @exception    MessagingException for failures
1114      */

1115     @Override
1116     public String getContentID() throws MessagingException {
1117     return getHeader("Content-Id"null);
1118     }
1119
1120     /**
1121      * Set the "Content-ID" header field of this Message.
1122      * If the <code>cid</code> parameter is null, any existing 
1123      * "Content-ID" is removed.
1124      *
1125      * @param    cid    the content ID
1126      * @exception    IllegalWriteException if the underlying
1127      *            implementation does not support modification
1128      * @exception    IllegalStateException if this message is
1129      *            obtained from a READ_ONLY folder.
1130      * @exception    MessagingException for other failures
1131      */

1132     public void setContentID(String cid) throws MessagingException {
1133     if (cid == null)
1134         removeHeader("Content-ID");
1135     else
1136         setHeader("Content-ID", cid);
1137     }
1138
1139     /**
1140      * Return the value of the "Content-MD5" header field. Returns 
1141      * <code>null</code> if this field is unavailable or its value
1142      * is absent. <p>
1143      *
1144      * This implementation uses the <code>getHeader</code> method
1145      * to obtain the requisite header field.
1146      *
1147      * @return          content-MD5
1148      * @exception    MessagingException for failures
1149      */

1150     @Override
1151     public String getContentMD5() throws MessagingException {
1152     return getHeader("Content-MD5"null);
1153     }
1154
1155     /**
1156      * Set the "Content-MD5" header field of this Message.
1157      *
1158      * @exception    IllegalWriteException if the underlying
1159      *            implementation does not support modification
1160      * @exception    IllegalStateException if this message is
1161      *            obtained from a READ_ONLY folder.
1162      * @exception    MessagingException for other failures
1163      */

1164     @Override
1165     public void setContentMD5(String md5) throws MessagingException {
1166     setHeader("Content-MD5", md5);
1167     }
1168
1169     /**
1170      * Returns the "Content-Description" header field of this Message.
1171      * This typically associates some descriptive information with 
1172      * this part. Returns null if this field is unavailable or its
1173      * value is absent. <p>
1174      *
1175      * If the Content-Description field is encoded as per RFC 2047,
1176      * it is decoded and converted into Unicode. If the decoding or 
1177      * conversion fails, the raw data is returned as-is <p>
1178      *
1179      * This implementation uses the <code>getHeader</code> method
1180      * to obtain the requisite header field.
1181      * 
1182      * @return    content-description
1183      * @exception    MessagingException for failures
1184      */

1185     @Override
1186     public String getDescription() throws MessagingException {
1187     return MimeBodyPart.getDescription(this);
1188     }
1189
1190     /**
1191      * Set the "Content-Description" header field for this Message.
1192      * If the description parameter is <code>null</code>, then any 
1193      * existing "Content-Description" fields are removed. <p>
1194      *
1195      * If the description contains non US-ASCII characters, it will 
1196      * be encoded using the platform's default charset. If the 
1197      * description contains only US-ASCII characters, no encoding 
1198      * is done and it is used as-is. <p>
1199      *
1200      * Note that if the charset encoding process fails, a
1201      * MessagingException is thrown, and an UnsupportedEncodingException
1202      * is included in the chain of nested exceptions within the
1203      * MessagingException.
1204      * 
1205      * @param description content-description
1206      * @exception    IllegalWriteException if the underlying
1207      *            implementation does not support modification
1208      * @exception    IllegalStateException if this message is
1209      *            obtained from a READ_ONLY folder.
1210      * @exception    MessagingException An
1211      *             UnsupportedEncodingException may be included
1212      *            in the exception chain if the charset
1213      *            conversion fails.
1214      */

1215     @Override
1216     public void setDescription(String description) throws MessagingException {
1217     setDescription(description, null);
1218     }
1219
1220     /**
1221      * Set the "Content-Description" header field for this Message.
1222      * If the description parameter is <code>null</code>, then any 
1223      * existing "Content-Description" fields are removed. <p>
1224      *
1225      * If the description contains non US-ASCII characters, it will 
1226      * be encoded using the specified charset. If the description 
1227      * contains only US-ASCII characters, no encoding  is done and 
1228      * it is used as-is. <p>
1229      *
1230      * Note that if the charset encoding process fails, a
1231      * MessagingException is thrown, and an UnsupportedEncodingException
1232      * is included in the chain of nested exceptions within the
1233      * MessagingException.
1234      * 
1235      * @param    description    Description
1236      * @param    charset        Charset for encoding
1237      * @exception        IllegalWriteException if the underlying
1238      *                implementation does not support modification
1239      * @exception        IllegalStateException if this message is
1240      *                obtained from a READ_ONLY folder.
1241      * @exception        MessagingException An
1242      *                 UnsupportedEncodingException may be included
1243      *                in the exception chain if the charset
1244      *                conversion fails.
1245      */

1246     public void setDescription(String description, String charset) 
1247         throws MessagingException {
1248     MimeBodyPart.setDescription(this, description, charset);
1249     }
1250
1251     /**
1252      * Get the languages specified in the "Content-Language" header
1253      * field of this message. The Content-Language header is defined by
1254      * RFC 1766. Returns <code>null</code> if this field is unavailable
1255      * or its value is absent. <p>
1256      *
1257      * This implementation uses the <code>getHeader</code> method
1258      * to obtain the requisite header field.
1259      *
1260      * @return            value of content-language header.
1261      * @exception        MessagingException for failures
1262      */

1263     @Override
1264     public String[] getContentLanguage() throws MessagingException {
1265     return MimeBodyPart.getContentLanguage(this);
1266     }
1267
1268     /**
1269      * Set the "Content-Language" header of this MimePart. The
1270      * Content-Language header is defined by RFC 1766.
1271      *
1272      * @param languages     array of language tags
1273      * @exception        IllegalWriteException if the underlying
1274      *                implementation does not support modification
1275      * @exception        IllegalStateException if this message is
1276      *                obtained from a READ_ONLY folder.
1277      * @exception        MessagingException for other failures
1278      */

1279     @Override
1280     public void setContentLanguage(String[] languages)
1281             throws MessagingException {
1282     MimeBodyPart.setContentLanguage(this, languages);
1283     }
1284
1285     /**
1286      * Returns the value of the "Message-ID" header field. Returns
1287      * null if this field is unavailable or its value is absent. <p>
1288      *
1289      * The default implementation provided here uses the
1290      * <code>getHeader</code> method to return the value of the
1291      * "Message-ID" field.
1292      *
1293      * @return     Message-ID
1294      * @exception  MessagingException if the retrieval of this field
1295      *            causes any exception.
1296      * @see        javax.mail.search.MessageIDTerm
1297      * @since        JavaMail 1.1
1298      */

1299     public String getMessageID() throws MessagingException {
1300     return getHeader("Message-ID"null);
1301     }
1302
1303     /**
1304      * Get the filename associated with this Message. <p>
1305      *
1306      * Returns the value of the "filename" parameter from the
1307      * "Content-Disposition" header field of this message. If it's
1308      * not available, returns the value of the "name" parameter from
1309      * the "Content-Type" header field of this BodyPart.
1310      * Returns <code>null</code> if both are absent. <p>
1311      *
1312      * If the <code>mail.mime.encodefilename</code> System property
1313      * is set to true, the {@link MimeUtility#decodeText
1314      * MimeUtility.decodeText} method will be used to decode the
1315      * filename.  While such encoding is not supported by the MIME
1316      * spec, many mailers use this technique to support non-ASCII
1317      * characters in filenames.  The default value of this property
1318      * is false.
1319      *
1320      * @return    filename
1321      * @exception        MessagingException for failures
1322      */

1323     @Override
1324     public String getFileName() throws MessagingException {
1325     return MimeBodyPart.getFileName(this);
1326     }
1327
1328     /**
1329      * Set the filename associated with this part, if possible. <p>
1330      *
1331      * Sets the "filename" parameter of the "Content-Disposition"
1332      * header field of this message. <p>
1333      *
1334      * If the <code>mail.mime.encodefilename</code> System property
1335      * is set to true, the {@link MimeUtility#encodeText
1336      * MimeUtility.encodeText} method will be used to encode the
1337      * filename.  While such encoding is not supported by the MIME
1338      * spec, many mailers use this technique to support non-ASCII
1339      * characters in filenames.  The default value of this property
1340      * is false.
1341      *
1342      * @exception        IllegalWriteException if the underlying
1343      *                implementation does not support modification
1344      * @exception        IllegalStateException if this message is
1345      *                obtained from a READ_ONLY folder.
1346      * @exception        MessagingException for other failures
1347      */

1348     @Override
1349     public void setFileName(String filename) throws MessagingException {
1350     MimeBodyPart.setFileName(this, filename);    
1351     }
1352
1353     private String getHeaderName(Message.RecipientType type)
1354                 throws MessagingException {
1355     String headerName;
1356
1357     if (type == Message.RecipientType.TO)
1358         headerName = "To";
1359     else if (type == Message.RecipientType.CC)
1360         headerName = "Cc";
1361     else if (type == Message.RecipientType.BCC)
1362         headerName = "Bcc";
1363     else if (type == MimeMessage.RecipientType.NEWSGROUPS)
1364         headerName = "Newsgroups";
1365     else
1366         throw new MessagingException("Invalid Recipient Type");
1367     return headerName;
1368     }
1369
1370
1371     /**
1372      * Return a decoded input stream for this Message's "content". <p>
1373      *
1374      * This implementation obtains the input stream from the DataHandler,
1375      * that is, it invokes <code>getDataHandler().getInputStream()</code>.
1376      *
1377      * @return         an InputStream
1378      * @exception       IOException this is typically thrown by the
1379      *            DataHandler. Refer to the documentation for
1380      *            javax.activation.DataHandler for more details.
1381      * @exception    MessagingException for other failures
1382      *
1383      * @see    #getContentStream
1384      * @see     javax.activation.DataHandler#getInputStream
1385      */

1386     @Override
1387     public InputStream getInputStream() 
1388         throws IOException, MessagingException {
1389     return getDataHandler().getInputStream();
1390     }
1391
1392     /**
1393      * Produce the raw bytes of the content. This method is used during
1394      * parsing, to create a DataHandler object for the content. Subclasses
1395      * that can provide a separate input stream for just the message 
1396      * content might want to override this method. <p>
1397      *
1398      * This implementation returns a SharedInputStream, if
1399      * <code>contentStream</code> is not null.  Otherwise, it
1400      * returns a ByteArrayInputStream constructed
1401      * out of the <code>content</code> byte array.
1402      *
1403      * @return    an InputStream containing the raw bytes
1404      * @exception    MessagingException for failures
1405      * @see #content
1406      */

1407     protected InputStream getContentStream() throws MessagingException {
1408     if (contentStream != null)
1409         return ((SharedInputStream)contentStream).newStream(0, -1);
1410     if (content != null)
1411         return new SharedByteArrayInputStream(content);
1412
1413     throw new MessagingException("No MimeMessage content");
1414     }
1415
1416     /**
1417      * Return an InputStream to the raw data with any Content-Transfer-Encoding
1418      * intact.  This method is useful if the "Content-Transfer-Encoding"
1419      * header is incorrect or corrupt, which would prevent the
1420      * <code>getInputStream</code> method or <code>getContent</code> method
1421      * from returning the correct data.  In such a case the application may
1422      * use this method and attempt to decode the raw data itself. <p>
1423      *
1424      * This implementation simply calls the <code>getContentStream</code>
1425      * method.
1426      *
1427      * @return    an InputStream containing the raw bytes
1428      * @exception    MessagingException for failures
1429      * @see    #getInputStream
1430      * @see    #getContentStream
1431      * @since    JavaMail 1.2
1432      */

1433     public InputStream getRawInputStream() throws MessagingException {
1434     return getContentStream();
1435     }
1436
1437     /**                                                            
1438      * Return a DataHandler for this Message's content. <p>
1439      *
1440      * The implementation provided here works approximately as follows.
1441      * Note the use of the <code>getContentStream</code> method to 
1442      * generate the byte stream for the content. Also note that
1443      * any transfer-decoding is done automatically within this method.
1444      *
1445      * <blockquote><pre>
1446      *  getDataHandler() {
1447      *      if (dh == null) {
1448      *          dh = new DataHandler(new MimePartDataSource(this));
1449      *      }
1450      *      return dh;
1451      *  }
1452      *
1453      *  class MimePartDataSource implements DataSource {
1454      *      public getInputStream() {
1455      *          return MimeUtility.decode(
1456      *             getContentStream(), getEncoding());
1457      *      }
1458      *    
1459      *        .... &lt;other DataSource methods&gt;
1460      *  }
1461      * </pre></blockquote><p>
1462      *
1463      * @exception    MessagingException for failures
1464      */

1465     @Override
1466     public synchronized DataHandler getDataHandler() 
1467         throws MessagingException {
1468     if (dh == null)
1469         dh = new MimeBodyPart.MimePartDataHandler(this);
1470     return dh;
1471     }
1472
1473     /**
1474      * Return the content as a Java object. The type of this
1475      * object is dependent on the content itself. For 
1476      * example, the native format of a "text/plain" content
1477      * is usually a String object. The native format for a "multipart"
1478      * message is always a Multipart subclass. For content types that are
1479      * unknown to the DataHandler system, an input stream is returned
1480      * as the content. <p>
1481      *
1482      * This implementation obtains the content from the DataHandler,
1483      * that is, it invokes <code>getDataHandler().getContent()</code>.
1484      * If the content is a Multipart or Message object and was created by
1485      * parsing a stream, the object is cached and returned in subsequent
1486      * calls so that modifications to the content will not be lost.
1487      *
1488      * @return          Object
1489      * @see        javax.mail.Part
1490      * @see         javax.activation.DataHandler#getContent
1491      * @exception       IOException this is typically thrown by the
1492      *            DataHandler. Refer to the documentation for
1493      *            javax.activation.DataHandler for more details.
1494      * @exception       MessagingException for other failures
1495      */

1496     @Override
1497     public Object getContent() throws IOException, MessagingException {
1498     if (cachedContent != null)
1499         return cachedContent;
1500     Object c;
1501     try {
1502         c = getDataHandler().getContent();
1503     } catch (FolderClosedIOException fex) {
1504         throw new FolderClosedException(fex.getFolder(), fex.getMessage());
1505     } catch (MessageRemovedIOException mex) {
1506         throw new MessageRemovedException(mex.getMessage());
1507     }
1508     if (MimeBodyPart.cacheMultipart &&
1509         (c instanceof Multipart || c instanceof Message) &&
1510         (content != null || contentStream != null)) {
1511         cachedContent = c;
1512         /*
1513          * We may abandon the input stream so make sure
1514          * the MimeMultipart has consumed the stream.
1515          */

1516         if (c instanceof MimeMultipart)
1517         ((MimeMultipart)c).parse();
1518     }
1519     return c;
1520     }
1521
1522     /**
1523      * This method provides the mechanism to set this part's content.
1524      * The given DataHandler object should wrap the actual content.
1525      *
1526      * @param   dh      The DataHandler for the content.
1527      * @exception    IllegalWriteException if the underlying
1528      *            implementation does not support modification
1529      * @exception    IllegalStateException if this message is
1530      *            obtained from a READ_ONLY folder.
1531      * @exception       MessagingException for other failures
1532      */

1533     @Override
1534     public synchronized void setDataHandler(DataHandler dh) 
1535         throws MessagingException {
1536     this.dh = dh;
1537     cachedContent = null;
1538     MimeBodyPart.invalidateContentHeaders(this);
1539     }
1540
1541     /**
1542      * A convenience method for setting this Message's content. <p>
1543      *
1544      * The content is wrapped in a DataHandler object. Note that a
1545      * DataContentHandler class for the specified type should be
1546      * available to the JavaMail implementation for this to work right.
1547      * i.e., to do <code>setContent(foobar, "application/x-foobar")</code>,
1548      * a DataContentHandler for "application/x-foobar" should be installed.
1549      * Refer to the Java Activation Framework for more information.
1550      *
1551      * @param    o    the content object
1552      * @param    type    Mime type of the object
1553      * @exception       IllegalWriteException if the underlying
1554      *            implementation does not support modification of
1555      *            existing values
1556      * @exception    IllegalStateException if this message is
1557      *            obtained from a READ_ONLY folder.
1558      * @exception       MessagingException for other failures
1559      */

1560     @Override
1561     public void setContent(Object o, String type) 
1562             throws MessagingException {
1563     if (o instanceof Multipart)
1564         setContent((Multipart)o);
1565     else
1566         setDataHandler(new DataHandler(o, type));
1567     }
1568
1569     /**
1570      * Convenience method that sets the given String as this
1571      * part's content, with a MIME type of "text/plain". If the
1572      * string contains non US-ASCII characters. it will be encoded
1573      * using the platform's default charset. The charset is also
1574      * used to set the "charset" parameter.<p>
1575      *
1576      * Note that there may be a performance penalty if
1577      * <code>text</code> is large, since this method may have
1578      * to scan all the characters to determine what charset to
1579      * use. <p>
1580      *
1581      * If the charset is already known, use the
1582      * <code>setText</code> method that takes the charset parameter.
1583      *
1584      * @param    text    the text content to set
1585      * @exception    MessagingException    if an error occurs
1586      * @see    #setText(String text, String charset)
1587      */

1588     @Override
1589     public void setText(String text) throws MessagingException {
1590     setText(text, null);
1591     }
1592
1593     /**
1594      * Convenience method that sets the given String as this part's
1595      * content, with a MIME type of "text/plain" and the specified
1596      * charset. The given Unicode string will be charset-encoded
1597      * using the specified charset. The charset is also used to set
1598      * the "charset" parameter.
1599      *
1600      * @param    text    the text content to set
1601      * @param    charset    the charset to use for the text
1602      * @exception    MessagingException    if an error occurs
1603      */

1604     @Override
1605     public void setText(String text, String charset)
1606             throws MessagingException {
1607     MimeBodyPart.setText(this, text, charset, "plain");
1608     }
1609
1610     /**
1611      * Convenience method that sets the given String as this part's
1612      * content, with a primary MIME type of "text" and the specified
1613      * MIME subtype.  The given Unicode string will be charset-encoded
1614      * using the specified charset. The charset is also used to set
1615      * the "charset" parameter.
1616      *
1617      * @param    text    the text content to set
1618      * @param    charset    the charset to use for the text
1619      * @param    subtype    the MIME subtype to use (e.g., "html")
1620      * @exception    MessagingException    if an error occurs
1621      * @since    JavaMail 1.4
1622      */

1623     @Override
1624     public void setText(String text, String charset, String subtype)
1625                         throws MessagingException {
1626     MimeBodyPart.setText(this, text, charset, subtype);
1627     }
1628
1629     /**
1630      * This method sets the Message's content to a Multipart object.
1631      *
1632      * @param  mp      The multipart object that is the Message's content
1633      * @exception       IllegalWriteException if the underlying
1634      *            implementation does not support modification of
1635      *            existing values
1636      * @exception    IllegalStateException if this message is
1637      *            obtained from a READ_ONLY folder.
1638      * @exception       MessagingException for other failures
1639      */

1640     @Override
1641     public void setContent(Multipart mp) throws MessagingException {
1642     setDataHandler(new DataHandler(mp, mp.getContentType()));
1643     mp.setParent(this);
1644     }
1645
1646     /**
1647      * Get a new Message suitable for a reply to this message.
1648      * The new Message will have its attributes and headers 
1649      * set up appropriately.  Note that this new message object
1650      * will be empty, i.e., it will <strong>not</strong> have a "content".
1651      * These will have to be suitably filled in by the client. <p>
1652      *
1653      * If <code>replyToAll</code> is set, the new Message will be addressed
1654      * to all recipients of this message.  Otherwise, the reply will be
1655      * addressed to only the sender of this message (using the value
1656      * of the <code>getReplyTo</code> method).  <p>
1657      *
1658      * The "Subject" field is filled in with the original subject
1659      * prefixed with "Re:" (unless it already starts with "Re:").
1660      * The "In-Reply-To" header is set in the new message if this
1661      * message has a "Message-Id" header.  The <code>ANSWERED</code>
1662      * flag is set in this message.
1663      *
1664      * The current implementation also sets the "References" header
1665      * in the new message to include the contents of the "References"
1666      * header (or, if missing, the "In-Reply-To" header) in this message,
1667      * plus the contents of the "Message-Id" header of this message,
1668      * as described in RFC 2822.
1669      *
1670      * @param    replyToAll    reply should be sent to all recipients
1671      *                of this message
1672      * @return        the reply Message
1673      * @exception    MessagingException for failures
1674      */

1675     @Override
1676     public Message reply(boolean replyToAll) throws MessagingException {
1677     return reply(replyToAll, true);
1678     }
1679
1680     /**
1681      * Get a new Message suitable for a reply to this message.
1682      * The new Message will have its attributes and headers 
1683      * set up appropriately.  Note that this new message object
1684      * will be empty, i.e., it will <strong>not</strong> have a "content".
1685      * These will have to be suitably filled in by the client. <p>
1686      *
1687      * If <code>replyToAll</code> is set, the new Message will be addressed
1688      * to all recipients of this message.  Otherwise, the reply will be
1689      * addressed to only the sender of this message (using the value
1690      * of the <code>getReplyTo</code> method).  <p>
1691      *
1692      * If <code>setAnswered</code> is set, the
1693      * {@link javax.mail.Flags.Flag#ANSWERED ANSWERED} flag is set
1694      * in this message. <p>
1695      *
1696      * The "Subject" field is filled in with the original subject
1697      * prefixed with "Re:" (unless it already starts with "Re:").
1698      * The "In-Reply-To" header is set in the new message if this
1699      * message has a "Message-Id" header.
1700      *
1701      * The current implementation also sets the "References" header
1702      * in the new message to include the contents of the "References"
1703      * header (or, if missing, the "In-Reply-To" header) in this message,
1704      * plus the contents of the "Message-Id" header of this message,
1705      * as described in RFC 2822.
1706      *
1707      * @param    replyToAll    reply should be sent to all recipients
1708      *                of this message
1709      * @param    setAnswered    set the ANSWERED flag in this message?
1710      * @return        the reply Message
1711      * @exception    MessagingException for failures
1712      * @since        JavaMail 1.5
1713      */

1714     public Message reply(boolean replyToAll, boolean setAnswered)
1715                 throws MessagingException {
1716     MimeMessage reply = createMimeMessage(session);
1717     /*
1718      * Have to manipulate the raw Subject header so that we don't lose
1719      * any encoding information.  This is safe because "Re:" isn't
1720      * internationalized and (generally) isn't encoded.  If the entire
1721      * Subject header is encoded, prefixing it with "Re: " still leaves
1722      * a valid and correct encoded header.
1723      */

1724     String subject = getHeader("Subject"null);
1725     if (subject != null) {
1726         if (!subject.regionMatches(true, 0, "Re: ", 0, 4))
1727         subject = "Re: " + subject;
1728         reply.setHeader("Subject", subject);
1729     }
1730     Address a[] = getReplyTo();
1731     reply.setRecipients(Message.RecipientType.TO, a);
1732     if (replyToAll) {
1733         List<Address> v = new ArrayList<>();
1734         // add my own address to list
1735         InternetAddress me = InternetAddress.getLocalAddress(session);
1736         if (me != null)
1737         v.add(me);
1738         // add any alternate names I'm known by
1739         String alternates = null;
1740         if (session != null)
1741         alternates = session.getProperty("mail.alternates");
1742         if (alternates != null)
1743         eliminateDuplicates(v,
1744                 InternetAddress.parse(alternates, false));
1745         // should we Cc all other original recipients?
1746         String replyallccStr = null;
1747         boolean replyallcc = false;
1748         if (session != null)
1749         replyallcc = PropUtil.getBooleanSessionProperty(session,
1750                         "mail.replyallcc"false);
1751         // add the recipients from the To field so far
1752         eliminateDuplicates(v, a);
1753         a = getRecipients(Message.RecipientType.TO);
1754         a = eliminateDuplicates(v, a);
1755         if (a != null && a.length > 0) {
1756         if (replyallcc)
1757             reply.addRecipients(Message.RecipientType.CC, a);
1758         else
1759             reply.addRecipients(Message.RecipientType.TO, a);
1760         }
1761         a = getRecipients(Message.RecipientType.CC);
1762         a = eliminateDuplicates(v, a);
1763         if (a != null && a.length > 0)
1764         reply.addRecipients(Message.RecipientType.CC, a);
1765         // don't eliminate duplicate newsgroups
1766         a = getRecipients(RecipientType.NEWSGROUPS);
1767         if (a != null && a.length > 0)
1768         reply.setRecipients(RecipientType.NEWSGROUPS, a);
1769     }
1770
1771     String msgId = getHeader("Message-Id"null);
1772     if (msgId != null)
1773         reply.setHeader("In-Reply-To", msgId);
1774
1775     /*
1776      * Set the References header as described in RFC 2822:
1777      *
1778      * The "References:" field will contain the contents of the parent's
1779      * "References:" field (if any) followed by the contents of the parent's
1780      * "Message-ID:" field (if any).  If the parent message does not contain
1781      * a "References:" field but does have an "In-Reply-To:" field
1782      * containing a single message identifier, then the "References:" field
1783      * will contain the contents of the parent's "In-Reply-To:" field
1784      * followed by the contents of the parent's "Message-ID:" field (if
1785      * any).  If the parent has none of the "References:""In-Reply-To:",
1786      * or "Message-ID:" fields, then the new message will have no
1787      * "References:" field.
1788      */

1789     String refs = getHeader("References"" ");
1790     if (refs == null) {
1791         // XXX - should only use if it contains a single message identifier
1792         refs = getHeader("In-Reply-To"" ");
1793     }
1794     if (msgId != null) {
1795         if (refs != null)
1796         refs = MimeUtility.unfold(refs) + " " + msgId;
1797         else
1798         refs = msgId;
1799     }
1800     if (refs != null)
1801         reply.setHeader("References", MimeUtility.fold(12, refs));
1802
1803     if (setAnswered) {
1804         try {
1805         setFlags(answeredFlag, true);
1806         } catch (MessagingException mex) {
1807         // ignore it
1808         }
1809     }
1810     return reply;
1811     }
1812
1813     // used above in reply()
1814     private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);
1815
1816     /**
1817      * Check addrs for any duplicates that may already be in v.
1818      * Return a new array without the duplicates.  Add any new
1819      * addresses to v.  Note that the input array may be modified.
1820      */

1821     private Address[] eliminateDuplicates(List<Address> v, Address[] addrs) {
1822     if (addrs == null)
1823         return null;
1824     int gone = 0;
1825     for (int i = 0; i < addrs.length; i++) {
1826         boolean found = false;
1827         // search the list for this address
1828         for (int j = 0; j < v.size(); j++) {
1829         if (((InternetAddress)v.get(j)).equals(addrs[i])) {
1830             // found it; count it and remove it from the input array
1831             found = true;
1832             gone++;
1833             addrs[i] = null;
1834             break;
1835         }
1836         }
1837         if (!found)
1838         v.add(addrs[i]);    // add new address to list
1839     }
1840     // if we found any duplicates, squish the array
1841     if (gone != 0) {
1842         Address[] a;
1843         // new array should be same type as original array
1844         // XXX - there must be a better way, perhaps reflection?
1845         if (addrs instanceof InternetAddress[])
1846         a = new InternetAddress[addrs.length - gone];
1847         else
1848         a = new Address[addrs.length - gone];
1849         for (int i = 0, j = 0; i < addrs.length; i++)
1850         if (addrs[i] != null)
1851             a[j++] = addrs[i];
1852         addrs = a;
1853     }
1854     return addrs;
1855     }
1856
1857     /**
1858      * Output the message as an RFC 822 format stream. <p>
1859      *
1860      * Note that, depending on how the messag was constructed, it may
1861      * use a variety of line termination conventions.  Generally the
1862      * output should be sent through an appropriate FilterOutputStream
1863      * that converts the line terminators to the desired form, either
1864      * CRLF for MIME compatibility and for use in Internet protocols,
1865      * or the local platform's line terminator for storage in a local
1866      * text file. <p>
1867      *
1868      * This implementation calls the <code>writeTo(OutputStream,
1869      * String[])</code> method with a null ignore list.
1870      *
1871      * @exception IOException    if an error occurs writing to the stream
1872      *                or if an error is generated by the
1873      *                javax.activation layer.
1874      * @exception MessagingException for other failures
1875      * @see javax.activation.DataHandler#writeTo
1876      */

1877     @Override
1878     public void writeTo(OutputStream os)
1879                 throws IOException, MessagingException {
1880     writeTo(os, null);
1881     }
1882
1883     /**
1884      * Output the message as an RFC 822 format stream, without
1885      * specified headers.  If the <code>saved</code> flag is not set,
1886      * the <code>saveChanges</code> method is called.
1887      * If the <code>modified</code> flag is not
1888      * set and the <code>content</code> array is not null, the
1889      * <code>content</code> array is written directly, after
1890      * writing the appropriate message headers.
1891      *
1892      * @param    os        the stream to write to
1893      * @param    ignoreList    the headers to not include in the output
1894      * @exception IOException    if an error occurs writing to the stream
1895      *                or if an error is generated by the
1896      *                javax.activation layer.
1897      * @exception javax.mail.MessagingException for other failures
1898      * @see javax.activation.DataHandler#writeTo
1899      */

1900     public void writeTo(OutputStream os, String[] ignoreList)
1901                 throws IOException, MessagingException {
1902     if (!saved)
1903         saveChanges();
1904
1905     if (modified) {
1906         MimeBodyPart.writeTo(this, os, ignoreList);
1907         return;
1908     }
1909
1910     // Else, the content is untouched, so we can just output it
1911     // First, write out the header
1912     Enumeration<String> hdrLines = getNonMatchingHeaderLines(ignoreList);
1913     LineOutputStream los = new LineOutputStream(os, allowutf8);
1914     while (hdrLines.hasMoreElements())
1915         los.writeln(hdrLines.nextElement());
1916
1917     // The CRLF separator between header and content
1918     los.writeln();
1919
1920     // Finally, the content. 
1921     if (content == null) {
1922         // call getContentStream to give subclass a chance to
1923         // provide the data on demand
1924         InputStream is = null;
1925         byte[] buf = new byte[8192];
1926         try {
1927         is = getContentStream();
1928         // now copy the data to the output stream
1929         int len;
1930         while ((len = is.read(buf)) > 0)
1931             os.write(buf, 0, len);
1932         } finally {
1933         if (is != null)
1934             is.close();
1935         buf = null;
1936         }
1937     } else {
1938         os.write(content);
1939     }
1940     os.flush();
1941     }
1942
1943     /**
1944      * Get all the headers for this header_name. Note that certain
1945      * headers may be encoded as per RFC 2047 if they contain 
1946      * non US-ASCII characters and these should be decoded. <p>
1947      *
1948      * This implementation obtains the headers from the 
1949      * <code>headers</code> InternetHeaders object.
1950      *
1951      * @param    name    name of header
1952      * @return    array of headers
1953      * @exception       MessagingException for failures
1954      * @see     javax.mail.internet.MimeUtility
1955      */

1956     @Override
1957     public String[] getHeader(String name)
1958             throws MessagingException {
1959     return headers.getHeader(name);
1960     }
1961
1962     /**
1963      * Get all the headers for this header name, returned as a single
1964      * String, with headers separated by the delimiter. If the
1965      * delimiter is <code>null</code>, only the first header is 
1966      * returned.
1967      *
1968      * @param name        the name of this header
1969      * @param delimiter        separator between values
1970      * @return                  the value fields for all headers with 
1971      *                this name
1972      * @exception           MessagingException for failures
1973      */

1974     @Override
1975     public String getHeader(String name, String delimiter)
1976                 throws MessagingException {
1977     return headers.getHeader(name, delimiter);
1978     }
1979
1980     /**
1981      * Set the value for this header_name. Replaces all existing
1982      * header values with this new value. Note that RFC 822 headers
1983      * must contain only US-ASCII characters, so a header that
1984      * contains non US-ASCII characters must have been encoded by the
1985      * caller as per the rules of RFC 2047.
1986      *
1987      * @param    name     header name
1988      * @param    value    header value
1989      * @see     javax.mail.internet.MimeUtility
1990      * @exception    IllegalWriteException if the underlying
1991      *            implementation does not support modification
1992      * @exception    IllegalStateException if this message is
1993      *            obtained from a READ_ONLY folder.
1994      * @exception       MessagingException for other failures
1995      */

1996     @Override
1997     public void setHeader(String name, String value)
1998                                 throws MessagingException {
1999     headers.setHeader(name, value);    
2000     }
2001
2002     /**
2003      * Add this value to the existing values for this header_name.
2004      * Note that RFC 822 headers must contain only US-ASCII 
2005      * characters, so a header that contains non US-ASCII characters 
2006      * must have been encoded as per the rules of RFC 2047.
2007      *
2008      * @param    name     header name
2009      * @param    value    header value
2010      * @see     javax.mail.internet.MimeUtility
2011      * @exception    IllegalWriteException if the underlying
2012      *            implementation does not support modification
2013      * @exception    IllegalStateException if this message is
2014      *            obtained from a READ_ONLY folder.
2015      * @exception       MessagingException for other failures
2016      */

2017     @Override
2018     public void addHeader(String name, String value)
2019                                 throws MessagingException {
2020     headers.addHeader(name, value);
2021     }
2022
2023     /**
2024      * Remove all headers with this name.
2025      * @exception    IllegalWriteException if the underlying
2026      *            implementation does not support modification
2027      * @exception    IllegalStateException if this message is
2028      *            obtained from a READ_ONLY folder.
2029      * @exception       MessagingException for other failures
2030      */

2031     @Override
2032     public void removeHeader(String name)
2033                                 throws MessagingException {
2034     headers.removeHeader(name);
2035     }
2036
2037     /**
2038      * Return all the headers from this Message as an enumeration
2039      * of Header objects. <p>
2040      *
2041      * Note that certain headers may be encoded as per RFC 2047 
2042      * if they contain non US-ASCII characters and these should 
2043      * be decoded. <p>
2044      *
2045      * This implementation obtains the headers from the 
2046      * <code>headers</code> InternetHeaders object.
2047      *
2048      * @return    array of header objects
2049      * @exception  MessagingException for failures
2050      * @see     javax.mail.internet.MimeUtility
2051      */

2052     @Override
2053     public Enumeration<Header> getAllHeaders() throws MessagingException {
2054     return headers.getAllHeaders();    
2055     }
2056
2057     /**
2058      * Return matching headers from this Message as an Enumeration of
2059      * Header objects. This implementation obtains the headers from
2060      * the <code>headers</code> InternetHeaders object.
2061      *
2062      * @exception  MessagingException for failures
2063      */

2064     @Override
2065     public Enumeration<Header> getMatchingHeaders(String[] names)
2066             throws MessagingException {
2067     return headers.getMatchingHeaders(names);
2068     }
2069
2070     /**
2071      * Return non-matching headers from this Message as an
2072      * Enumeration of Header objects. This implementation 
2073      * obtains the header from the <code>headers</code> InternetHeaders object.
2074      *
2075      * @exception  MessagingException for failures
2076      */

2077     @Override
2078     public Enumeration<Header> getNonMatchingHeaders(String[] names)
2079             throws MessagingException {
2080     return headers.getNonMatchingHeaders(names);
2081     }
2082
2083     /**
2084      * Add a raw RFC 822 header-line. 
2085      *
2086      * @exception    IllegalWriteException if the underlying
2087      *            implementation does not support modification
2088      * @exception    IllegalStateException if this message is
2089      *            obtained from a READ_ONLY folder.
2090      * @exception      MessagingException for other failures
2091      */

2092     @Override
2093     public void addHeaderLine(String line) throws MessagingException {
2094     headers.addHeaderLine(line);
2095     }
2096
2097     /**
2098      * Get all header lines as an Enumeration of Strings. A Header
2099      * line is a raw RFC 822 header-line, containing both the "name" 
2100      * and "value" field. 
2101      *
2102      * @exception      MessagingException for failures
2103      */

2104     @Override
2105     public Enumeration<String> getAllHeaderLines() throws MessagingException {
2106     return headers.getAllHeaderLines();
2107     }
2108
2109     /**
2110      * Get matching header lines as an Enumeration of Strings. 
2111      * A Header line is a raw RFC 822 header-line, containing both 
2112      * the "name" and "value" field.
2113      *
2114      * @exception      MessagingException for failures
2115      */

2116     @Override
2117     public Enumeration<String> getMatchingHeaderLines(String[] names)
2118                                         throws MessagingException {
2119     return headers.getMatchingHeaderLines(names);
2120     }
2121
2122     /**
2123      * Get non-matching header lines as an Enumeration of Strings. 
2124      * A Header line is a raw RFC 822 header-line, containing both 
2125      * the "name" and "value" field.
2126      *
2127      * @exception      MessagingException for failures
2128      */

2129     @Override
2130     public Enumeration<String> getNonMatchingHeaderLines(String[] names)
2131                                         throws MessagingException {
2132     return headers.getNonMatchingHeaderLines(names);
2133     }
2134
2135     /**
2136      * Return a <code>Flags</code> object containing the flags for 
2137      * this message. <p>
2138      *
2139      * Note that a clone of the internal Flags object is returned, so
2140      * modifying the returned Flags object will not affect the flags
2141      * of this message.
2142      *
2143      * @return          Flags object containing the flags for this message
2144      * @exception      MessagingException for failures
2145      * @see         javax.mail.Flags
2146      */

2147     @Override
2148     public synchronized Flags getFlags() throws MessagingException {
2149     return (Flags)flags.clone();
2150     }
2151
2152     /**
2153      * Check whether the flag specified in the <code>flag</code>
2154      * argument is set in this message. <p>
2155      *
2156      * This implementation checks this message's internal 
2157      * <code>flags</code> object.
2158      *
2159      * @param flag    the flag
2160      * @return        value of the specified flag for this message
2161      * @exception       MessagingException for failures
2162      * @see         javax.mail.Flags.Flag
2163      * @see        javax.mail.Flags.Flag#ANSWERED
2164      * @see        javax.mail.Flags.Flag#DELETED
2165      * @see        javax.mail.Flags.Flag#DRAFT
2166      * @see        javax.mail.Flags.Flag#FLAGGED
2167      * @see        javax.mail.Flags.Flag#RECENT
2168      * @see        javax.mail.Flags.Flag#SEEN
2169      */

2170     @Override
2171     public synchronized boolean isSet(Flags.Flag flag)
2172                 throws MessagingException {
2173     return (flags.contains(flag));
2174     }
2175
2176     /**
2177      * Set the flags for this message. <p>
2178      *
2179      * This implementation modifies the <code>flags</code> field.
2180      *
2181      * @exception    IllegalWriteException if the underlying
2182      *            implementation does not support modification
2183      * @exception    IllegalStateException if this message is
2184      *            obtained from a READ_ONLY folder.
2185      * @exception      MessagingException for other failures
2186      */

2187     @Override
2188     public synchronized void setFlags(Flags flag, boolean set)
2189             throws MessagingException {
2190     if (set)
2191         flags.add(flag);
2192     else
2193         flags.remove(flag);
2194     }
2195
2196     /**
2197      * Updates the appropriate header fields of this message to be
2198      * consistent with the message's contents. If this message is
2199      * contained in a Folder, any changes made to this message are
2200      * committed to the containing folder. <p>
2201      *
2202      * If any part of a message's headers or contents are changed,
2203      * <code>saveChanges</code> must be called to ensure that those
2204      * changes are permanent. Otherwise, any such modifications may or 
2205      * may not be saved, depending on the folder implementation. <p>
2206      *
2207      * Messages obtained from folders opened READ_ONLY should not be
2208      * modified and saveChanges should not be called on such messages. <p>
2209      *
2210      * This method sets the <code>modified</code> flag to true, the
2211      * <code>save</code> flag to true, and then calls the
2212      * <code>updateHeaders</code> method.
2213      *
2214      * @exception    IllegalWriteException if the underlying
2215      *            implementation does not support modification
2216      * @exception    IllegalStateException if this message is
2217      *            obtained from a READ_ONLY folder.
2218      * @exception      MessagingException for other failures
2219      */

2220     @Override
2221     public void saveChanges() throws MessagingException {
2222     modified = true;
2223     saved = true;
2224     updateHeaders();
2225     }
2226
2227     /**
2228      * Update the Message-ID header.  This method is called
2229      * by the <code>updateHeaders</code> and allows a subclass
2230      * to override only the algorithm for choosing a Message-ID.
2231      *
2232      * @exception      MessagingException for failures
2233      * @since        JavaMail 1.4
2234      */

2235     protected void updateMessageID() throws MessagingException {
2236     setHeader("Message-ID"
2237           "<" + UniqueValue.getUniqueMessageIDValue(session) + ">");
2238           
2239     }    
2240
2241     /**
2242      * Called by the <code>saveChanges</code> method to actually
2243      * update the MIME headers.  The implementation here sets the
2244      * <code>Content-Transfer-Encoding</code> header (if needed
2245      * and not already set), the <code>Date</code> header (if
2246      * not already set), the <code>MIME-Version</code> header
2247      * and the <code>Message-ID</code> header. Also, if the content
2248      * of this message is a <code>MimeMultipart</code>, its
2249      * <code>updateHeaders</code> method is called. <p>
2250      *
2251      * If the {@link #cachedContent} field is not null (that is,
2252      * it references a Multipart or Message object), then
2253      * that object is used to set a new DataHandler, any
2254      * stream data used to create this object is discarded,
2255      * and the {@link #cachedContent} field is cleared.
2256      *
2257      * @exception    IllegalWriteException if the underlying
2258      *            implementation does not support modification
2259      * @exception    IllegalStateException if this message is
2260      *            obtained from a READ_ONLY folder.
2261      * @exception      MessagingException for other failures
2262      */

2263     protected synchronized void updateHeaders() throws MessagingException {
2264     MimeBodyPart.updateHeaders(this);    
2265     setHeader("MIME-Version""1.0");
2266     if (getHeader("Date") == null)
2267         setSentDate(new Date());
2268         updateMessageID();
2269
2270     if (cachedContent != null) {
2271         dh = new DataHandler(cachedContent, getContentType());
2272         cachedContent = null;
2273         content = null;
2274         if (contentStream != null) {
2275         try {
2276             contentStream.close();
2277         } catch (IOException ioex) { }    // nothing to do
2278         }
2279         contentStream = null;
2280     }
2281     }
2282
2283     /**
2284      * Create and return an InternetHeaders object that loads the
2285      * headers from the given InputStream.  Subclasses can override
2286      * this method to return a subclass of InternetHeaders, if
2287      * necessary.  This implementation simply constructs and returns
2288      * an InternetHeaders object.
2289      *
2290      * @return    an InternetHeaders object
2291      * @param    is    the InputStream to read the headers from
2292      * @exception      MessagingException for failures
2293      * @since        JavaMail 1.2
2294      */

2295     protected InternetHeaders createInternetHeaders(InputStream is)
2296                 throws MessagingException {
2297     return new InternetHeaders(is, allowutf8);
2298     }
2299
2300     /**
2301      * Create and return a MimeMessage object.  The reply method
2302      * uses this method to create the MimeMessage object that it
2303      * will return.  Subclasses can override this method to return
2304      * a subclass of MimeMessage.  This implementation simply constructs
2305      * and returns a MimeMessage object using the supplied Session.
2306      *
2307      * @param    session    the Session to use for the new message
2308      * @return        the new MimeMessage object
2309      * @exception      MessagingException for failures
2310      * @since        JavaMail 1.4
2311      */

2312     protected MimeMessage createMimeMessage(Session session)
2313                 throws MessagingException {
2314     return new MimeMessage(session);
2315     }
2316 }
2317