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 * .... <other DataSource methods>
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