1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 1997-2007 Sun Microsystems, Inc. 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 obtain
10 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
11 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
12 * language governing permissions and limitations under the License.
13 *
14 * When distributing the software, include this License Header Notice in each
15 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
16 * Sun designates this particular file as subject to the "Classpath" exception
17 * as provided by Sun in the GPL Version 2 section of the License file that
18 * accompanied this code. If applicable, add the following below the License
19 * Header, with the fields enclosed by brackets [] replaced by your own
20 * identifying information: "Portions Copyrighted [year]
21 * [name of copyright owner]"
22 *
23 * Contributor(s):
24 *
25 * If you wish your version of this file to be governed by only the CDDL or
26 * only the GPL Version 2, indicate your decision by adding "[Contributor]
27 * elects to include this software in this distribution under the [CDDL or GPL
28 * Version 2] license." If you don't indicate a single choice of license, a
29 * recipient has the option to distribute your version of this file under
30 * either the CDDL, the GPL Version 2 or to extend the choice of license to
31 * its licensees as provided above. However, if you add GPL Version 2 code
32 * and therefore, elected the GPL Version 2 license, then the option applies
33 * only if the new code is made subject to such option by the copyright
34 * holder.
35 */
36
37 /*
38 * @(#)MimeMessage.java 1.95 07/05/14
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. <p>
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 of 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.
166 *
167 * XXX - must have package access for MimeBodyPart.updateHeaders
168 */
169 Object cachedContent;
170
171 // Used to parse dates
172 private static MailDateFormat mailDateFormat = new MailDateFormat();
173
174 // Should addresses in headers be parsed in "strict" mode?
175 private boolean strict = true;
176
177 /**
178 * Default constructor. An empty message object is created.
179 * The <code>headers</code> field is set to an empty InternetHeaders
180 * object. The <code>flags</code> field is set to an empty Flags
181 * object. The <code>modified</code> flag is set to true.
182 */
183 public MimeMessage(Session session) {
184 super(session);
185 modified = true;
186 headers = new InternetHeaders();
187 flags = new Flags(); // empty flags object
188 initStrict();
189 }
190
191 /**
192 * Constructs a MimeMessage by reading and parsing the data from the
193 * specified MIME InputStream. The InputStream will be left positioned
194 * at the end of the data for the message. Note that the input stream
195 * parse is done within this constructor itself. <p>
196 *
197 * The input stream contains an entire MIME formatted message with
198 * headers and data.
199 *
200 * @param session Session object for this message
201 * @param is the message input stream
202 * @exception MessagingException
203 */
204 public MimeMessage(Session session, InputStream is)
205 throws MessagingException {
206 super(session);
207 flags = new Flags(); // empty Flags object
208 initStrict();
209 parse(is);
210 saved = true;
211 }
212
213 /**
214 * Constructs a new MimeMessage with content initialized from the
215 * <code>source</code> MimeMessage. The new message is independent
216 * of the original. <p>
217 *
218 * Note: The current implementation is rather inefficient, copying
219 * the data more times than strictly necessary.
220 *
221 * @param source the message to copy content from
222 * @exception MessagingException
223 * @since JavaMail 1.2
224 */
225 public MimeMessage(MimeMessage source) throws MessagingException {
226 super(source.session);
227 flags = source.getFlags();
228 ByteArrayOutputStream bos;
229 int size = source.getSize();
230 if (size > 0)
231 bos = new ByteArrayOutputStream(size);
232 else
233 bos = new ByteArrayOutputStream();
234 try {
235 strict = source.strict;
236 source.writeTo(bos);
237 bos.close();
238 SharedByteArrayInputStream bis =
239 new SharedByteArrayInputStream(bos.toByteArray());
240 parse(bis);
241 bis.close();
242 saved = true;
243 } catch (IOException ex) {
244 // should never happen, but just in case...
245 throw new MessagingException("IOException while copying message",
246 ex);
247 }
248 }
249
250 /**
251 * Constructs an empty MimeMessage object with the given Folder
252 * and message number. <p>
253 *
254 * This method is for providers subclassing <code>MimeMessage</code>.
255 */
256 protected MimeMessage(Folder folder, int msgnum) {
257 super(folder, msgnum);
258 flags = new Flags(); // empty Flags object
259 saved = true;
260 initStrict();
261 }
262
263 /**
264 * Constructs a MimeMessage by reading and parsing the data from the
265 * specified MIME InputStream. The InputStream will be left positioned
266 * at the end of the data for the message. Note that the input stream
267 * parse is done within this constructor itself. <p>
268 *
269 * This method is for providers subclassing <code>MimeMessage</code>.
270 *
271 * @param folder The containing folder.
272 * @param is the message input stream
273 * @param msgnum Message number of this message within its folder
274 * @exception MessagingException
275 */
276 protected MimeMessage(Folder folder, InputStream is, int msgnum)
277 throws MessagingException {
278 this(folder, msgnum);
279 initStrict();
280 parse(is);
281 }
282
283 /**
284 * Constructs a MimeMessage from the given InternetHeaders object
285 * and content.
286 *
287 * This method is for providers subclassing <code>MimeMessage</code>.
288 *
289 * @param folder The containing folder.
290 * @param headers The headers
291 * @param content The message content
292 * @param msgnum Message number of this message within its folder
293 * @exception MessagingException
294 */
295 protected MimeMessage(Folder folder, InternetHeaders headers,
296 byte[] content, int msgnum) throws MessagingException {
297 this(folder, msgnum);
298 this.headers = headers;
299 this.content = content;
300 initStrict();
301 }
302
303 /**
304 * Set the strict flag based on property.
305 */
306 private void initStrict() {
307 if (session != null) {
308 String s = session.getProperty("mail.mime.address.strict");
309 strict = s == null || !s.equalsIgnoreCase("false");
310 }
311 }
312
313 /**
314 * Parse the InputStream setting the <code>headers</code> and
315 * <code>content</code> fields appropriately. Also resets the
316 * <code>modified</code> flag. <p>
317 *
318 * This method is intended for use by subclasses that need to
319 * control when the InputStream is parsed.
320 *
321 * @param is The message input stream
322 * @exception MessagingException
323 */
324 protected void parse(InputStream is) throws MessagingException {
325
326 if (!(is instanceof ByteArrayInputStream) &&
327 !(is instanceof BufferedInputStream) &&
328 !(is instanceof SharedInputStream))
329 is = new BufferedInputStream(is);
330
331 headers = createInternetHeaders(is);
332
333 if (is instanceof SharedInputStream) {
334 SharedInputStream sis = (SharedInputStream)is;
335 contentStream = sis.newStream(sis.getPosition(), -1);
336 } else {
337 try {
338 content = ASCIIUtility.getBytes(is);
339 } catch (IOException ioex) {
340 throw new MessagingException("IOException", ioex);
341 }
342 }
343
344 modified = false;
345 }
346
347 /**
348 * Returns the value of the RFC 822 "From" header fields. If this
349 * header field is absent, the "Sender" header field is used.
350 * If the "Sender" header field is also absent, <code>null</code>
351 * is returned.<p>
352 *
353 * This implementation uses the <code>getHeader</code> method
354 * to obtain the requisite header field.
355 *
356 * @return Address object
357 * @exception MessagingException
358 * @see #headers
359 */
360 public Address[] getFrom() throws MessagingException {
361 Address[] a = getAddressHeader("From");
362 if (a == null)
363 a = getAddressHeader("Sender");
364
365 return a;
366 }
367
368 /**
369 * Set the RFC 822 "From" header field. Any existing values are
370 * replaced with the given address. If address is <code>null</code>,
371 * this header is removed.
372 *
373 * @param address the sender of this message
374 * @exception IllegalWriteException if the underlying
375 * implementation does not support modification
376 * of existing values
377 * @exception IllegalStateException if this message is
378 * obtained from a READ_ONLY folder.
379 * @exception MessagingException
380 */
381 public void setFrom(Address address) throws MessagingException {
382 if (address == null)
383 removeHeader("From");
384 else
385 setHeader("From", address.toString());
386 }
387
388 /**
389 * Set the RFC 822 "From" header field using the value of the
390 * <code>InternetAddress.getLocalAddress</code> method.
391 *
392 * @exception IllegalWriteException if the underlying
393 * implementation does not support modification
394 * of existing values
395 * @exception IllegalStateException if this message is
396 * obtained from a READ_ONLY folder.
397 * @exception MessagingException
398 */
399 public void setFrom() throws MessagingException {
400 InternetAddress me = InternetAddress.getLocalAddress(session);
401 if (me != null)
402 setFrom(me);
403 else
404 throw new MessagingException("No From address");
405 }
406
407 /**
408 * Add the specified addresses to the existing "From" field. If
409 * the "From" field does not already exist, it is created.
410 *
411 * @param addresses the senders 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
418 */
419 public void addFrom(Address[] addresses) throws MessagingException {
420 addAddressHeader("From", addresses);
421 }
422
423 /**
424 * Returns the value of the RFC 822 "Sender" header field.
425 * If the "Sender" header field is absent, <code>null</code>
426 * is returned.<p>
427 *
428 * This implementation uses the <code>getHeader</code> method
429 * to obtain the requisite header field.
430 *
431 * @return Address object
432 * @exception MessagingException
433 * @see #headers
434 * @since JavaMail 1.3
435 */
436 public Address getSender() throws MessagingException {
437 Address[] a = getAddressHeader("Sender");
438 if (a == null || a.length == 0)
439 return null;
440 return a[0]; // there can be only one
441 }
442
443 /**
444 * Set the RFC 822 "Sender" header field. Any existing values are
445 * replaced with the given address. If address is <code>null</code>,
446 * this header is removed.
447 *
448 * @param address the sender of this message
449 * @exception IllegalWriteException if the underlying
450 * implementation does not support modification
451 * of existing values
452 * @exception IllegalStateException if this message is
453 * obtained from a READ_ONLY folder.
454 * @exception MessagingException
455 * @since JavaMail 1.3
456 */
457 public void setSender(Address address) throws MessagingException {
458 if (address == null)
459 removeHeader("Sender");
460 else
461 setHeader("Sender", address.toString());
462 }
463
464 /**
465 * This inner class extends the javax.mail.Message.RecipientType
466 * class to add additional RecipientTypes. The one additional
467 * RecipientType currently defined here is NEWSGROUPS.
468 *
469 * @see javax.mail.Message.RecipientType
470 */
471 public static class RecipientType extends Message.RecipientType {
472
473 private static final long serialVersionUID = -5468290701714395543L;
474
475 /**
476 * The "Newsgroup" (Usenet news) recipients.
477 */
478 public static final RecipientType NEWSGROUPS =
479 new RecipientType("Newsgroups");
480 protected RecipientType(String type) {
481 super(type);
482 }
483
484 protected Object readResolve() throws ObjectStreamException {
485 if (type.equals("Newsgroups"))
486 return NEWSGROUPS;
487 else
488 return super.readResolve();
489 }
490 }
491
492 /**
493 * Returns the recepients specified by the type. The mapping
494 * between the type and the corresponding RFC 822 header is
495 * as follows:
496 * <pre>
497 * Message.RecipientType.TO "To"
498 * Message.RecipientType.CC "Cc"
499 * Message.RecipientType.BCC "Bcc"
500 * MimeMessage.RecipientType.NEWSGROUPS "Newsgroups"
501 * </pre><br>
502 *
503 * Returns null if the header specified by the type is not found
504 * or if its value is empty. <p>
505 *
506 * This implementation uses the <code>getHeader</code> method
507 * to obtain the requisite header field.
508 *
509 * @param type Type of recepient
510 * @return array of Address objects
511 * @exception MessagingException if header could not
512 * be retrieved
513 * @exception AddressException if the header is misformatted
514 * @see #headers
515 * @see javax.mail.Message.RecipientType#TO
516 * @see javax.mail.Message.RecipientType#CC
517 * @see javax.mail.Message.RecipientType#BCC
518 * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
519 */
520 public Address[] getRecipients(Message.RecipientType type)
521 throws MessagingException {
522 if (type == RecipientType.NEWSGROUPS) {
523 String s = getHeader("Newsgroups", ",");
524 return (s == null) ? null : NewsAddress.parse(s);
525 } else
526 return getAddressHeader(getHeaderName(type));
527 }
528
529 /**
530 * Get all the recipient addresses for the message.
531 * Extracts the TO, CC, BCC, and NEWSGROUPS recipients.
532 *
533 * @return array of Address objects
534 * @exception MessagingException
535 * @see javax.mail.Message.RecipientType#TO
536 * @see javax.mail.Message.RecipientType#CC
537 * @see javax.mail.Message.RecipientType#BCC
538 * @see javax.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
539 */
540 public Address[] getAllRecipients() throws MessagingException {
541 Address[] all = super.getAllRecipients();
542 Address[] ng = getRecipients(RecipientType.NEWSGROUPS);
543
544 if (ng == null)
545 return all; // the common case
546 if (all == null)
547 return ng; // a rare case
548
549 Address[] addresses = new Address[all.length + ng.length];
550 System.arraycopy(all, 0, addresses, 0, all.length);
551 System.arraycopy(ng, 0, addresses, all.length, ng.length);
552 return addresses;
553 }
554
555 /**
556 * Set the specified recipient type to the given addresses.
557 * If the address parameter is <code>null</code>, the corresponding
558 * recipient field is removed.
559 *
560 * @param type Recipient type
561 * @param addresses Addresses
562 * @exception IllegalWriteException if the underlying
563 * implementation does not support modification
564 * of existing values
565 * @exception IllegalStateException if this message is
566 * obtained from a READ_ONLY folder.
567 * @exception MessagingException
568 * @see #getRecipients
569 */
570 public void setRecipients(Message.RecipientType type, Address[] addresses)
571 throws MessagingException {
572 if (type == RecipientType.NEWSGROUPS) {
573 if (addresses == null || addresses.length == 0)
574 removeHeader("Newsgroups");
575 else
576 setHeader("Newsgroups", NewsAddress.toString(addresses));
577 } else
578 setAddressHeader(getHeaderName(type), addresses);
579 }
580
581 /**
582 * Set the specified recipient type to the given addresses.
583 * If the address parameter is <code>null</code>, the corresponding
584 * recipient field is removed.
585 *
586 * @param type Recipient type
587 * @param addresses Addresses
588 * @exception AddressException if the attempt to parse the
589 * addresses String fails
590 * @exception IllegalWriteException if the underlying
591 * implementation does not support modification
592 * of existing values
593 * @exception IllegalStateException if this message is
594 * obtained from a READ_ONLY folder.
595 * @exception MessagingException
596 * @see #getRecipients
597 * @since JavaMail 1.2
598 */
599 public void setRecipients(Message.RecipientType type, String addresses)
600 throws MessagingException {
601 if (type == RecipientType.NEWSGROUPS) {
602 if (addresses == null || addresses.length() == 0)
603 removeHeader("Newsgroups");
604 else
605 setHeader("Newsgroups", addresses);
606 } else
607 setAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
608 }
609
610 /**
611 * Add the given addresses to the specified recipient type.
612 *
613 * @param type Recipient type
614 * @param addresses Addresses
615 * @exception IllegalWriteException if the underlying
616 * implementation does not support modification
617 * of existing values
618 * @exception IllegalStateException if this message is
619 * obtained from a READ_ONLY folder.
620 * @exception MessagingException
621 */
622 public void addRecipients(Message.RecipientType type, Address[] addresses)
623 throws MessagingException {
624 if (type == RecipientType.NEWSGROUPS) {
625 String s = NewsAddress.toString(addresses);
626 if (s != null)
627 addHeader("Newsgroups", s);
628 } else
629 addAddressHeader(getHeaderName(type), addresses);
630 }
631
632 /**
633 * Add the given addresses to the specified recipient type.
634 *
635 * @param type Recipient type
636 * @param addresses Addresses
637 * @exception AddressException if the attempt to parse the
638 * addresses String fails
639 * @exception IllegalWriteException if the underlying
640 * implementation does not support modification
641 * of existing values
642 * @exception IllegalStateException if this message is
643 * obtained from a READ_ONLY folder.
644 * @exception MessagingException
645 * @since JavaMail 1.2
646 */
647 public void addRecipients(Message.RecipientType type, String addresses)
648 throws MessagingException {
649 if (type == RecipientType.NEWSGROUPS) {
650 if (addresses != null && addresses.length() != 0)
651 addHeader("Newsgroups", addresses);
652 } else
653 addAddressHeader(getHeaderName(type), InternetAddress.parse(addresses));
654 }
655
656 /**
657 * Return the value of the RFC 822 "Reply-To" header field. If
658 * this header is unavailable or its value is absent, then
659 * the <code>getFrom</code> method is called and its value is returned.
660 *
661 * This implementation uses the <code>getHeader</code> method
662 * to obtain the requisite header field.
663 *
664 * @exception MessagingException
665 * @see #headers
666 */
667 public Address[] getReplyTo() throws MessagingException {
668 Address[] a = getAddressHeader("Reply-To");
669 if (a == null)
670 a = getFrom();
671 return a;
672 }
673
674 /**
675 * Set the RFC 822 "Reply-To" header field. If the address
676 * parameter is <code>null</code>, this header is removed.
677 *
678 * @exception IllegalWriteException if the underlying
679 * implementation does not support modification
680 * of existing values
681 * @exception IllegalStateException if this message is
682 * obtained from a READ_ONLY folder.
683 * @exception MessagingException
684 */
685 public void setReplyTo(Address[] addresses) throws MessagingException {
686 setAddressHeader("Reply-To", addresses);
687 }
688
689 // Convenience method to get addresses
690 private Address[] getAddressHeader(String name)
691 throws MessagingException {
692 String s = getHeader(name, ",");
693 return (s == null) ? null : InternetAddress.parseHeader(s, strict);
694 }
695
696 // Convenience method to set addresses
697 private void setAddressHeader(String name, Address[] addresses)
698 throws MessagingException {
699 String s = InternetAddress.toString(addresses);
700 if (s == null)
701 removeHeader(name);
702 else
703 setHeader(name, s);
704 }
705
706 private void addAddressHeader(String name, Address[] addresses)
707 throws MessagingException {
708 String s = InternetAddress.toString(addresses);
709 if (s == null)
710 return;
711 addHeader(name, s);
712 }
713
714 /**
715 * Returns the value of the "Subject" header field. Returns null
716 * if the subject field is unavailable or its value is absent. <p>
717 *
718 * If the subject is encoded as per RFC 2047, it is decoded and
719 * converted into Unicode. If the decoding or conversion fails, the
720 * raw data is returned as is. <p>
721 *
722 * This implementation uses the <code>getHeader</code> method
723 * to obtain the requisite header field.
724 *
725 * @return Subject
726 * @exception MessagingException
727 * @see #headers
728 */
729 public String getSubject() throws MessagingException {
730 String rawvalue = getHeader("Subject", null);
731
732 if (rawvalue == null)
733 return null;
734
735 try {
736 return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
737 } catch (UnsupportedEncodingException ex) {
738 return rawvalue;
739 }
740 }
741
742 /**
743 * Set the "Subject" header field. If the subject contains
744 * non US-ASCII characters, it will be encoded using the
745 * platform's default charset. If the subject contains only
746 * US-ASCII characters, no encoding is done and it is used
747 * as-is. If the subject is null, the existing "Subject" field
748 * is removed. <p>
749 *
750 * The application must ensure that the subject does not contain
751 * any line breaks. <p>
752 *
753 * Note that if the charset encoding process fails, a
754 * MessagingException is thrown, and an UnsupportedEncodingException
755 * is included in the chain of nested exceptions within the
756 * MessagingException.
757 *
758 * @param subject The subject
759 * @exception IllegalWriteException if the underlying
760 * implementation does not support modification
761 * of existing values
762 * @exception IllegalStateException if this message is
763 * obtained from a READ_ONLY folder.
764 * @exception MessagingException. An
765 * UnsupportedEncodingException may be included
766 * in the exception chain if the charset
767 * conversion fails.
768 */
769 public void setSubject(String subject) throws MessagingException {
770 setSubject(subject, null);
771 }
772
773 /**
774 * Set the "Subject" header field. If the subject contains non
775 * US-ASCII characters, it will be encoded using the specified
776 * charset. If the subject contains only US-ASCII characters, no
777 * encoding is done and it is used as-is. If the subject is null,
778 * the existing "Subject" header field is removed. <p>
779 *
780 * The application must ensure that the subject does not contain
781 * any line breaks. <p>
782 *
783 * Note that if the charset encoding process fails, a
784 * MessagingException is thrown, and an UnsupportedEncodingException
785 * is included in the chain of nested exceptions within the
786 * MessagingException.
787 *
788 * @param subject The subject
789 * @param charset The charset
790 * @exception IllegalWriteException if the underlying
791 * implementation does not support modification
792 * of existing values
793 * @exception IllegalStateException if this message is
794 * obtained from a READ_ONLY folder.
795 * @exception MessagingException. An
796 * UnsupportedEncodingException may be included
797 * in the exception chain if the charset
798 * conversion fails.
799 */
800 public void setSubject(String subject, String charset)
801 throws MessagingException {
802 if (subject == null) {
803 removeHeader("Subject");
804 } else {
805 try {
806 setHeader("Subject", MimeUtility.fold(9,
807 MimeUtility.encodeText(subject, charset, null)));
808 } catch (UnsupportedEncodingException uex) {
809 throw new MessagingException("Encoding error", uex);
810 }
811 }
812 }
813
814 /**
815 * Returns the value of the RFC 822 "Date" field. This is the date
816 * on which this message was sent. Returns null if this field is
817 * unavailable or its value is absent. <p>
818 *
819 * This implementation uses the <code>getHeader</code> method
820 * to obtain the requisite header field.
821 *
822 * @return The sent Date
823 * @exception MessagingException
824 */
825 public Date getSentDate() throws MessagingException {
826 String s = getHeader("Date", null);
827 if (s != null) {
828 try {
829 synchronized (mailDateFormat) {
830 return mailDateFormat.parse(s);
831 }
832 } catch (ParseException pex) {
833 return null;
834 }
835 }
836
837 return null;
838 }
839
840 /**
841 * Set the RFC 822 "Date" header field. This is the date on which the
842 * creator of the message indicates that the message is complete
843 * and ready for delivery. If the date parameter is
844 * <code>null</code>, the existing "Date" field is removed.
845 *
846 * @exception IllegalWriteException if the underlying
847 * implementation does not support modification
848 * @exception IllegalStateException if this message is
849 * obtained from a READ_ONLY folder.
850 * @exception MessagingException
851 */
852 public void setSentDate(Date d) throws MessagingException {
853 if (d == null)
854 removeHeader("Date");
855 else {
856 synchronized (mailDateFormat) {
857 setHeader("Date", mailDateFormat.format(d));
858 }
859 }
860 }
861
862 /**
863 * Returns the Date on this message was received. Returns
864 * <code>null</code> if this date cannot be obtained. <p>
865 *
866 * Note that RFC 822 does not define a field for the received
867 * date. Hence only implementations that can provide this date
868 * need return a valid value. <p>
869 *
870 * This implementation returns <code>null</code>.
871 *
872 * @return the date this message was received
873 * @exception MessagingException
874 */
875 public Date getReceivedDate() throws MessagingException {
876 return null;
877 }
878
879 /**
880 * Return the size of the content of this message in bytes.
881 * Return -1 if the size cannot be determined. <p>
882 *
883 * Note that this number may not be an exact measure of the
884 * content size and may or may not account for any transfer
885 * encoding of the content. <p>
886 *
887 * This implementation returns the size of the <code>content</code>
888 * array (if not null), or, if <code>contentStream</code> is not
889 * null, and the <code>available</code> method returns a positive
890 * number, it returns that number as the size. Otherwise, it returns
891 * -1.
892 *
893 * @return size of content in bytes
894 * @exception MessagingException
895 */
896 public int getSize() throws MessagingException {
897 if (content != null)
898 return content.length;
899 if (contentStream != null) {
900 try {
901 int size = contentStream.available();
902 // only believe the size if it's greater than zero, since zero
903 // is the default returned by the InputStream class itself
904 if (size > 0)
905 return size;
906 } catch (IOException ex) {
907 // ignore it
908 }
909 }
910 return -1;
911 }
912
913 /**
914 * Return the number of lines for the content of this message.
915 * Return -1 if this number cannot be determined. <p>
916 *
917 * Note that this number may not be an exact measure of the
918 * content length and may or may not account for any transfer
919 * encoding of the content. <p>
920 *
921 * This implementation returns -1.
922 *
923 * @return number of lines in the content.
924 * @exception MessagingException
925 */
926 public int getLineCount() throws MessagingException {
927 return -1;
928 }
929
930 /**
931 * Returns the value of the RFC 822 "Content-Type" header field.
932 * This represents the content-type of the content of this
933 * message. This value must not be null. If this field is
934 * unavailable, "text/plain" should be returned. <p>
935 *
936 * This implementation uses the <code>getHeader</code> method
937 * to obtain the requisite header field.
938 *
939 * @return The ContentType of this part
940 * @exception MessagingException
941 * @see javax.activation.DataHandler
942 */
943 public String getContentType() throws MessagingException {
944 String s = getHeader("Content-Type", null);
945 if (s == null)
946 return "text/plain";
947 return s;
948 }
949
950 /**
951 * Is this Part of the specified MIME type? This method
952 * compares <strong>only the <code>primaryType</code> and
953 * <code>subType</code></strong>.
954 * The parameters of the content types are ignored. <p>
955 *
956 * For example, this method will return <code>true</code> when
957 * comparing a Part of content type <strong>"text/plain"</strong>
958 * with <strong>"text/plain; charset=foobar"</strong>. <p>
959 *
960 * If the <code>subType</code> of <code>mimeType</code> is the
961 * special character '*', then the subtype is ignored during the
962 * comparison.
963 */
964 public boolean isMimeType(String mimeType) throws MessagingException {
965 return MimeBodyPart.isMimeType(this, mimeType);
966 }
967
968 /**
969 * Returns the value of the "Content-Disposition" header field.
970 * This represents the disposition of this part. The disposition
971 * describes how the part should be presented to the user. <p>
972 *
973 * If the Content-Disposition field is unavailable,
974 * <code>null</code> is returned. <p>
975 *
976 * This implementation uses the <code>getHeader</code> method
977 * to obtain the requisite header field.
978 *
979 * @return disposition of this part, or null if unknown
980 * @exception MessagingException
981 */
982 public String getDisposition() throws MessagingException {
983 return MimeBodyPart.getDisposition(this);
984 }
985
986 /**
987 * Set the "Content-Disposition" header field of this Message.
988 * If <code>disposition</code> is null, any existing "Content-Disposition"
989 * header field is removed.
990 *
991 * @exception IllegalWriteException if the underlying
992 * implementation does not support modification
993 * @exception IllegalStateException if this message is
994 * obtained from a READ_ONLY folder.
995 * @exception MessagingException
996 */
997 public void setDisposition(String disposition) throws MessagingException {
998 MimeBodyPart.setDisposition(this, disposition);
999 }
1000
1001 /**
1002 * Returns the content transfer encoding from the
1003 * "Content-Transfer-Encoding" header
1004 * field. Returns <code>null</code> if the header is unavailable
1005 * or its value is absent. <p>
1006 *
1007 * This implementation uses the <code>getHeader</code> method
1008 * to obtain the requisite header field.
1009 *
1010 * @return content-transfer-encoding
1011 * @exception MessagingException
1012 */
1013 public String getEncoding() throws MessagingException {
1014 return MimeBodyPart.getEncoding(this);
1015 }
1016
1017 /**
1018 * Returns the value of the "Content-ID" header field. Returns
1019 * <code>null</code> if the field is unavailable or its value is
1020 * absent. <p>
1021 *
1022 * This implementation uses the <code>getHeader</code> method
1023 * to obtain the requisite header field.
1024 *
1025 * @return content-ID
1026 * @exception MessagingException
1027 */
1028 public String getContentID() throws MessagingException {
1029 return getHeader("Content-Id", null);
1030 }
1031
1032 /**
1033 * Set the "Content-ID" header field of this Message.
1034 * If the <code>cid</code> parameter is null, any existing
1035 * "Content-ID" is removed.
1036 *
1037 * @exception IllegalWriteException if the underlying
1038 * implementation does not support modification
1039 * @exception IllegalStateException if this message is
1040 * obtained from a READ_ONLY folder.
1041 * @exception MessagingException
1042 */
1043 public void setContentID(String cid) throws MessagingException {
1044 if (cid == null)
1045 removeHeader("Content-ID");
1046 else
1047 setHeader("Content-ID", cid);
1048 }
1049
1050 /**
1051 * Return the value of the "Content-MD5" header field. Returns
1052 * <code>null</code> if this field is unavailable or its value
1053 * is absent. <p>
1054 *
1055 * This implementation uses the <code>getHeader</code> method
1056 * to obtain the requisite header field.
1057 *
1058 * @return content-MD5
1059 * @exception MessagingException
1060 */
1061 public String getContentMD5() throws MessagingException {
1062 return getHeader("Content-MD5", null);
1063 }
1064
1065 /**
1066 * Set the "Content-MD5" header field of this Message.
1067 *
1068 * @exception IllegalWriteException if the underlying
1069 * implementation does not support modification
1070 * @exception IllegalStateException if this message is
1071 * obtained from a READ_ONLY folder.
1072 * @exception MessagingException
1073 */
1074 public void setContentMD5(String md5) throws MessagingException {
1075 setHeader("Content-MD5", md5);
1076 }
1077
1078 /**
1079 * Returns the "Content-Description" header field of this Message.
1080 * This typically associates some descriptive information with
1081 * this part. Returns null if this field is unavailable or its
1082 * value is absent. <p>
1083 *
1084 * If the Content-Description field is encoded as per RFC 2047,
1085 * it is decoded and converted into Unicode. If the decoding or
1086 * conversion fails, the raw data is returned as-is <p>
1087 *
1088 * This implementation uses the <code>getHeader</code> method
1089 * to obtain the requisite header field.
1090 *
1091 * @return content-description
1092 * @exception MessagingException
1093 */
1094 public String getDescription() throws MessagingException {
1095 return MimeBodyPart.getDescription(this);
1096 }
1097
1098 /**
1099 * Set the "Content-Description" header field for this Message.
1100 * If the description parameter is <code>null</code>, then any
1101 * existing "Content-Description" fields are removed. <p>
1102 *
1103 * If the description contains non US-ASCII characters, it will
1104 * be encoded using the platform's default charset. If the
1105 * description contains only US-ASCII characters, no encoding
1106 * is done and it is used as-is. <p>
1107 *
1108 * Note that if the charset encoding process fails, a
1109 * MessagingException is thrown, and an UnsupportedEncodingException
1110 * is included in the chain of nested exceptions within the
1111 * MessagingException.
1112 *
1113 * @param description content-description
1114 * @exception IllegalWriteException if the underlying
1115 * implementation does not support modification
1116 * @exception IllegalStateException if this message is
1117 * obtained from a READ_ONLY folder.
1118 * @exception MessagingException. An
1119 * UnsupportedEncodingException may be included
1120 * in the exception chain if the charset
1121 * conversion fails.
1122 */
1123 public void setDescription(String description) throws MessagingException {
1124 setDescription(description, null);
1125 }
1126
1127 /**
1128 * Set the "Content-Description" header field for this Message.
1129 * If the description parameter is <code>null</code>, then any
1130 * existing "Content-Description" fields are removed. <p>
1131 *
1132 * If the description contains non US-ASCII characters, it will
1133 * be encoded using the specified charset. If the description
1134 * contains only US-ASCII characters, no encoding is done and
1135 * it is used as-is. <p>
1136 *
1137 * Note that if the charset encoding process fails, a
1138 * MessagingException is thrown, and an UnsupportedEncodingException
1139 * is included in the chain of nested exceptions within the
1140 * MessagingException.
1141 *
1142 * @param description Description
1143 * @param charset Charset for encoding
1144 * @exception IllegalWriteException if the underlying
1145 * implementation does not support modification
1146 * @exception IllegalStateException if this message is
1147 * obtained from a READ_ONLY folder.
1148 * @exception MessagingException. An
1149 * UnsupportedEncodingException may be included
1150 * in the exception chain if the charset
1151 * conversion fails.
1152 */
1153 public void setDescription(String description, String charset)
1154 throws MessagingException {
1155 MimeBodyPart.setDescription(this, description, charset);
1156 }
1157
1158 /**
1159 * Get the languages specified in the "Content-Language" header
1160 * field of this message. The Content-Language header is defined by
1161 * RFC 1766. Returns <code>null</code> if this field is unavailable
1162 * or its value is absent. <p>
1163 *
1164 * This implementation uses the <code>getHeader</code> method
1165 * to obtain the requisite header field.
1166 *
1167 * @return value of content-language header.
1168 * @exception MessagingException
1169 */
1170 public String[] getContentLanguage() throws MessagingException {
1171 return MimeBodyPart.getContentLanguage(this);
1172 }
1173
1174 /**
1175 * Set the "Content-Language" header of this MimePart. The
1176 * Content-Language header is defined by RFC 1766.
1177 *
1178 * @param languages array of language tags
1179 * @exception IllegalWriteException if the underlying
1180 * implementation does not support modification
1181 * @exception IllegalStateException if this message is
1182 * obtained from a READ_ONLY folder.
1183 * @exception MessagingException
1184 */
1185 public void setContentLanguage(String[] languages)
1186 throws MessagingException {
1187 MimeBodyPart.setContentLanguage(this, languages);
1188 }
1189
1190 /**
1191 * Returns the value of the "Message-ID" header field. Returns
1192 * null if this field is unavailable or its value is absent. <p>
1193 *
1194 * The default implementation provided here uses the
1195 * <code>getHeader</code> method to return the value of the
1196 * "Message-ID" field.
1197 *
1198 * @return Message-ID
1199 * @exception MessagingException if the retrieval of this field
1200 * causes any exception.
1201 * @see javax.mail.search.MessageIDTerm
1202 * @since JavaMail 1.1
1203 */
1204 public String getMessageID() throws MessagingException {
1205 return getHeader("Message-ID", null);
1206 }
1207
1208 /**
1209 * Get the filename associated with this Message. <p>
1210 *
1211 * Returns the value of the "filename" parameter from the
1212 * "Content-Disposition" header field of this message. If it's
1213 * not available, returns the value of the "name" parameter from
1214 * the "Content-Type" header field of this BodyPart.
1215 * Returns <code>null</code> if both are absent. <p>
1216 *
1217 * If the <code>mail.mime.encodefilename</code> System property
1218 * is set to true, the {@link MimeUtility#decodeText
1219 * MimeUtility.decodeText} method will be used to decode the
1220 * filename. While such encoding is not supported by the MIME
1221 * spec, many mailers use this technique to support non-ASCII
1222 * characters in filenames. The default value of this property
1223 * is false.
1224 *
1225 * @return filename
1226 * @exception MessagingException
1227 */
1228 public String getFileName() throws MessagingException {
1229 return MimeBodyPart.getFileName(this);
1230 }
1231
1232 /**
1233 * Set the filename associated with this part, if possible. <p>
1234 *
1235 * Sets the "filename" parameter of the "Content-Disposition"
1236 * header field of this message. <p>
1237 *
1238 * If the <code>mail.mime.encodefilename</code> System property
1239 * is set to true, the {@link MimeUtility#encodeText
1240 * MimeUtility.encodeText} method will be used to encode the
1241 * filename. While such encoding is not supported by the MIME
1242 * spec, many mailers use this technique to support non-ASCII
1243 * characters in filenames. The default value of this property
1244 * is false.
1245 *
1246 * @exception IllegalWriteException if the underlying
1247 * implementation does not support modification
1248 * @exception IllegalStateException if this message is
1249 * obtained from a READ_ONLY folder.
1250 * @exception MessagingException
1251 */
1252 public void setFileName(String filename) throws MessagingException {
1253 MimeBodyPart.setFileName(this, filename);
1254 }
1255
1256 private String getHeaderName(Message.RecipientType type)
1257 throws MessagingException {
1258 String headerName;
1259
1260 if (type == Message.RecipientType.TO)
1261 headerName = "To";
1262 else if (type == Message.RecipientType.CC)
1263 headerName = "Cc";
1264 else if (type == Message.RecipientType.BCC)
1265 headerName = "Bcc";
1266 else if (type == MimeMessage.RecipientType.NEWSGROUPS)
1267 headerName = "Newsgroups";
1268 else
1269 throw new MessagingException("Invalid Recipient Type");
1270 return headerName;
1271 }
1272
1273
1274 /**
1275 * Return a decoded input stream for this Message's "content". <p>
1276 *
1277 * This implementation obtains the input stream from the DataHandler,
1278 * that is, it invokes <code>getDataHandler().getInputStream()</code>.
1279 *
1280 * @return an InputStream
1281 * @exception MessagingException
1282 * @exception IOException this is typically thrown by the
1283 * DataHandler. Refer to the documentation for
1284 * javax.activation.DataHandler for more details.
1285 *
1286 * @see #getContentStream
1287 * @see javax.activation.DataHandler#getInputStream
1288 */
1289 public InputStream getInputStream()
1290 throws IOException, MessagingException {
1291 return getDataHandler().getInputStream();
1292 }
1293
1294 /**
1295 * Produce the raw bytes of the content. This method is used during
1296 * parsing, to create a DataHandler object for the content. Subclasses
1297 * that can provide a separate input stream for just the message
1298 * content might want to override this method. <p>
1299 *
1300 * This implementation returns a SharedInputStream, if
1301 * <code>contentStream</code> is not null. Otherwise, it
1302 * returns a ByteArrayInputStream constructed
1303 * out of the <code>content</code> byte array.
1304 *
1305 * @see #content
1306 */
1307 protected InputStream getContentStream() throws MessagingException {
1308 if (contentStream != null)
1309 return ((SharedInputStream)contentStream).newStream(0, -1);
1310 if (content != null)
1311 return new SharedByteArrayInputStream(content);
1312
1313 throw new MessagingException("No content");
1314 }
1315
1316 /**
1317 * Return an InputStream to the raw data with any Content-Transfer-Encoding
1318 * intact. This method is useful if the "Content-Transfer-Encoding"
1319 * header is incorrect or corrupt, which would prevent the
1320 * <code>getInputStream</code> method or <code>getContent</code> method
1321 * from returning the correct data. In such a case the application may
1322 * use this method and attempt to decode the raw data itself. <p>
1323 *
1324 * This implementation simply calls the <code>getContentStream</code>
1325 * method.
1326 *
1327 * @see #getInputStream
1328 * @see #getContentStream
1329 * @since JavaMail 1.2
1330 */
1331 public InputStream getRawInputStream() throws MessagingException {
1332 return getContentStream();
1333 }
1334
1335 /**
1336 * Return a DataHandler for this Message's content. <p>
1337 *
1338 * The implementation provided here works as follows. Note the use of
1339 * the <code>getContentStream</code> method to
1340 * generate the byte stream for the content. Also note that
1341 * any transfer-decoding is done automatically within this method.<p>
1342 *
1343 * <blockquote><pre>
1344 * getDataHandler() {
1345 * if (dh == null) {
1346 * dh = new DataHandler(new MimePartDataSource(this));
1347 * }
1348 * return dh;
1349 * }
1350 * <p>
1351 * class MimePartDataSource implements DataSource {
1352 * public getInputStream() {
1353 * return MimeUtility.decode(
1354 * getContentStream(), getEncoding());
1355 * }
1356 *
1357 * .... <other DataSource methods>
1358 * }
1359 * </pre></blockquote><p>
1360 *
1361 * @exception MessagingException
1362 */
1363 public synchronized DataHandler getDataHandler()
1364 throws MessagingException {
1365 if (dh == null)
1366 dh = new DataHandler(new MimePartDataSource(this));
1367 return dh;
1368 }
1369
1370 /**
1371 * Return the content as a Java object. The type of this
1372 * object is dependent on the content itself. For
1373 * example, the native format of a "text/plain" content
1374 * is usually a String object. The native format for a "multipart"
1375 * message is always a Multipart subclass. For content types that are
1376 * unknown to the DataHandler system, an input stream is returned
1377 * as the content. <p>
1378 *
1379 * This implementation obtains the content from the DataHandler,
1380 * that is, it invokes <code>getDataHandler().getContent()</code>.
1381 * If the content is a Multipart or Message object and was created by
1382 * parsing a stream, the object is cached and returned in subsequent
1383 * calls so that modifications to the content will not be lost.
1384 *
1385 * @return Object
1386 * @see javax.mail.Part
1387 * @see javax.activation.DataHandler#getContent
1388 * @exception MessagingException
1389 * @exception IOException this is typically thrown by the
1390 * DataHandler. Refer to the documentation for
1391 * javax.activation.DataHandler for more details.
1392 */
1393 public Object getContent() throws IOException, MessagingException {
1394 if (cachedContent != null)
1395 return cachedContent;
1396 Object c;
1397 try {
1398 c = getDataHandler().getContent();
1399 } catch (FolderClosedIOException fex) {
1400 throw new FolderClosedException(fex.getFolder(), fex.getMessage());
1401 } catch (MessageRemovedIOException mex) {
1402 throw new MessageRemovedException(mex.getMessage());
1403 }
1404 if (MimeBodyPart.cacheMultipart &&
1405 (c instanceof Multipart || c instanceof Message) &&
1406 (content != null || contentStream != null)) {
1407 cachedContent = c;
1408 }
1409 return c;
1410 }
1411
1412 /**
1413 * This method provides the mechanism to set this part's content.
1414 * The given DataHandler object should wrap the actual content.
1415 *
1416 * @param dh The DataHandler for the content.
1417 * @exception IllegalWriteException if the underlying
1418 * implementation does not support modification
1419 * @exception IllegalStateException if this message is
1420 * obtained from a READ_ONLY folder.
1421 * @exception MessagingException
1422 */
1423 public synchronized void setDataHandler(DataHandler dh)
1424 throws MessagingException {
1425 this.dh = dh;
1426 cachedContent = null;
1427 MimeBodyPart.invalidateContentHeaders(this);
1428 }
1429
1430 /**
1431 * A convenience method for setting this Message's content. <p>
1432 *
1433 * The content is wrapped in a DataHandler object. Note that a
1434 * DataContentHandler class for the specified type should be
1435 * available to the JavaMail implementation for this to work right.
1436 * i.e., to do <code>setContent(foobar, "application/x-foobar")</code>,
1437 * a DataContentHandler for "application/x-foobar" should be installed.
1438 * Refer to the Java Activation Framework for more information.
1439 *
1440 * @param o the content object
1441 * @param type Mime type of the object
1442 * @exception IllegalWriteException if the underlying
1443 * implementation does not support modification of
1444 * existing values
1445 * @exception IllegalStateException if this message is
1446 * obtained from a READ_ONLY folder.
1447 * @exception MessagingException
1448 */
1449 public void setContent(Object o, String type)
1450 throws MessagingException {
1451 if (o instanceof Multipart)
1452 setContent((Multipart)o);
1453 else
1454 setDataHandler(new DataHandler(o, type));
1455 }
1456
1457 /**
1458 * Convenience method that sets the given String as this
1459 * part's content, with a MIME type of "text/plain". If the
1460 * string contains non US-ASCII characters. it will be encoded
1461 * using the platform's default charset. The charset is also
1462 * used to set the "charset" parameter.<p>
1463 *
1464 * Note that there may be a performance penalty if
1465 * <code>text</code> is large, since this method may have
1466 * to scan all the characters to determine what charset to
1467 * use. <p>
1468 *
1469 * If the charset is already known, use the
1470 * <code>setText</code> method that takes the charset parameter.
1471 *
1472 * @param text the text content to set
1473 * @exception MessagingException if an error occurs
1474 * @see #setText(String text, String charset)
1475 */
1476 public void setText(String text) throws MessagingException {
1477 setText(text, null);
1478 }
1479
1480 /**
1481 * Convenience method that sets the given String as this part's
1482 * content, with a MIME type of "text/plain" and the specified
1483 * charset. The given Unicode string will be charset-encoded
1484 * using the specified charset. The charset is also used to set
1485 * the "charset" parameter.
1486 *
1487 * @param text the text content to set
1488 * @param charset the charset to use for the text
1489 * @exception MessagingException if an error occurs
1490 */
1491 public void setText(String text, String charset)
1492 throws MessagingException {
1493 MimeBodyPart.setText(this, text, charset, "plain");
1494 }
1495
1496 /**
1497 * Convenience method that sets the given String as this part's
1498 * content, with a primary MIME type of "text" and the specified
1499 * MIME subtype. The given Unicode string will be charset-encoded
1500 * using the specified charset. The charset is also used to set
1501 * the "charset" parameter.
1502 *
1503 * @param text the text content to set
1504 * @param charset the charset to use for the text
1505 * @param subtype the MIME subtype to use (e.g., "html")
1506 * @exception MessagingException if an error occurs
1507 * @since JavaMail 1.4
1508 */
1509 public void setText(String text, String charset, String subtype)
1510 throws MessagingException {
1511 MimeBodyPart.setText(this, text, charset, subtype);
1512 }
1513
1514 /**
1515 * This method sets the Message's content to a Multipart object.
1516 *
1517 * @param mp The multipart object that is the Message's content
1518 * @exception IllegalWriteException if the underlying
1519 * implementation does not support modification of
1520 * existing values
1521 * @exception IllegalStateException if this message is
1522 * obtained from a READ_ONLY folder.
1523 * @exception MessagingException
1524 */
1525 public void setContent(Multipart mp) throws MessagingException {
1526 setDataHandler(new DataHandler(mp, mp.getContentType()));
1527 mp.setParent(this);
1528 }
1529
1530 /**
1531 * Get a new Message suitable for a reply to this message.
1532 * The new Message will have its attributes and headers
1533 * set up appropriately. Note that this new message object
1534 * will be empty, i.e., it will <strong>not</strong> have a "content".
1535 * These will have to be suitably filled in by the client. <p>
1536 *
1537 * If <code>replyToAll</code> is set, the new Message will be addressed
1538 * to all recipients of this message. Otherwise, the reply will be
1539 * addressed to only the sender of this message (using the value
1540 * of the <code>getReplyTo</code> method). <p>
1541 *
1542 * The "Subject" field is filled in with the original subject
1543 * prefixed with "Re:" (unless it already starts with "Re:").
1544 * The "In-Reply-To" header is set in the new message if this
1545 * message has a "Message-Id" header. The <code>ANSWERED</code>
1546 * flag is set in this message.
1547 *
1548 * The current implementation also sets the "References" header
1549 * in the new message to include the contents of the "References"
1550 * header (or, if missing, the "In-Reply-To" header) in this message,
1551 * plus the contents of the "Message-Id" header of this message,
1552 * as described in RFC 2822.
1553 *
1554 * @param replyToAll reply should be sent to all recipients
1555 * of this message
1556 * @return the reply Message
1557 * @exception MessagingException
1558 */
1559 public Message reply(boolean replyToAll) throws MessagingException {
1560 MimeMessage reply = createMimeMessage(session);
1561 /*
1562 * Have to manipulate the raw Subject header so that we don't lose
1563 * any encoding information. This is safe because "Re:" isn't
1564 * internationalized and (generally) isn't encoded. If the entire
1565 * Subject header is encoded, prefixing it with "Re: " still leaves
1566 * a valid and correct encoded header.
1567 */
1568 String subject = getHeader("Subject", null);
1569 if (subject != null) {
1570 if (!subject.regionMatches(true, 0, "Re: ", 0, 4))
1571 subject = "Re: " + subject;
1572 reply.setHeader("Subject", subject);
1573 }
1574 Address a[] = getReplyTo();
1575 reply.setRecipients(Message.RecipientType.TO, a);
1576 if (replyToAll) {
1577 Vector v = new Vector();
1578 // add my own address to list
1579 InternetAddress me = InternetAddress.getLocalAddress(session);
1580 if (me != null)
1581 v.addElement(me);
1582 // add any alternate names I'm known by
1583 String alternates = null;
1584 if (session != null)
1585 alternates = session.getProperty("mail.alternates");
1586 if (alternates != null)
1587 eliminateDuplicates(v,
1588 InternetAddress.parse(alternates, false));
1589 // should we Cc all other original recipients?
1590 String replyallccStr = null;
1591 if (session != null)
1592 replyallccStr = session.getProperty("mail.replyallcc");
1593 boolean replyallcc =
1594 replyallccStr != null && replyallccStr.equalsIgnoreCase("true");
1595 // add the recipients from the To field so far
1596 eliminateDuplicates(v, a);
1597 a = getRecipients(Message.RecipientType.TO);
1598 a = eliminateDuplicates(v, a);
1599 if (a != null && a.length > 0) {
1600 if (replyallcc)
1601 reply.addRecipients(Message.RecipientType.CC, a);
1602 else
1603 reply.addRecipients(Message.RecipientType.TO, a);
1604 }
1605 a = getRecipients(Message.RecipientType.CC);
1606 a = eliminateDuplicates(v, a);
1607 if (a != null && a.length > 0)
1608 reply.addRecipients(Message.RecipientType.CC, a);
1609 // don't eliminate duplicate newsgroups
1610 a = getRecipients(RecipientType.NEWSGROUPS);
1611 if (a != null && a.length > 0)
1612 reply.setRecipients(RecipientType.NEWSGROUPS, a);
1613 }
1614
1615 String msgId = getHeader("Message-Id", null);
1616 if (msgId != null)
1617 reply.setHeader("In-Reply-To", msgId);
1618
1619 /*
1620 * Set the References header as described in RFC 2822:
1621 *
1622 * The "References:" field will contain the contents of the parent's
1623 * "References:" field (if any) followed by the contents of the parent's
1624 * "Message-ID:" field (if any). If the parent message does not contain
1625 * a "References:" field but does have an "In-Reply-To:" field
1626 * containing a single message identifier, then the "References:" field
1627 * will contain the contents of the parent's "In-Reply-To:" field
1628 * followed by the contents of the parent's "Message-ID:" field (if
1629 * any). If the parent has none of the "References:", "In-Reply-To:",
1630 * or "Message-ID:" fields, then the new message will have no
1631 * "References:" field.
1632 */
1633 String refs = getHeader("References", " ");
1634 if (refs == null) {
1635 // XXX - should only use if it contains a single message identifier
1636 refs = getHeader("In-Reply-To", " ");
1637 }
1638 if (msgId != null) {
1639 if (refs != null)
1640 refs = MimeUtility.unfold(refs) + " " + msgId;
1641 else
1642 refs = msgId;
1643 }
1644 if (refs != null)
1645 reply.setHeader("References", MimeUtility.fold(12, refs));
1646
1647 try {
1648 setFlags(answeredFlag, true);
1649 } catch (MessagingException mex) {
1650 // ignore it
1651 }
1652 return reply;
1653 }
1654
1655 // used above in reply()
1656 private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);
1657
1658 /**
1659 * Check addrs for any duplicates that may already be in v.
1660 * Return a new array without the duplicates. Add any new
1661 * addresses to v. Note that the input array may be modified.
1662 */
1663 private Address[] eliminateDuplicates(Vector v, Address[] addrs) {
1664 if (addrs == null)
1665 return null;
1666 int gone = 0;
1667 for (int i = 0; i < addrs.length; i++) {
1668 boolean found = false;
1669 // search the vector for this address
1670 for (int j = 0; j < v.size(); j++) {
1671 if (((InternetAddress)v.elementAt(j)).equals(addrs[i])) {
1672 // found it; count it and remove it from the input array
1673 found = true;
1674 gone++;
1675 addrs[i] = null;
1676 break;
1677 }
1678 }
1679 if (!found)
1680 v.addElement(addrs[i]); // add new address to vector
1681 }
1682 // if we found any duplicates, squish the array
1683 if (gone != 0) {
1684 Address[] a;
1685 // new array should be same type as original array
1686 // XXX - there must be a better way, perhaps reflection?
1687 if (addrs instanceof InternetAddress[])
1688 a = new InternetAddress[addrs.length - gone];
1689 else
1690 a = new Address[addrs.length - gone];
1691 for (int i = 0, j = 0; i < addrs.length; i++)
1692 if (addrs[i] != null)
1693 a[j++] = addrs[i];
1694 addrs = a;
1695 }
1696 return addrs;
1697 }
1698
1699 /**
1700 * Output the message as an RFC 822 format stream. <p>
1701 *
1702 * Note that, depending on how the messag was constructed, it may
1703 * use a variety of line termination conventions. Generally the
1704 * output should be sent through an appropriate FilterOutputStream
1705 * that converts the line terminators to the desired form, either
1706 * CRLF for MIME compatibility and for use in Internet protocols,
1707 * or the local platform's line terminator for storage in a local
1708 * text file. <p>
1709 *
1710 * This implementation calls the <code>writeTo(OutputStream,
1711 * String[])</code> method with a null ignore list.
1712 *
1713 * @exception IOException if an error occurs writing to the stream
1714 * or if an error is generated by the
1715 * javax.activation layer.
1716 * @exception MessagingException
1717 * @see javax.activation.DataHandler#writeTo
1718 */
1719 public void writeTo(OutputStream os)
1720 throws IOException, MessagingException {
1721 writeTo(os, null);
1722 }
1723
1724 /**
1725 * Output the message as an RFC 822 format stream, without
1726 * specified headers. If the <code>saved</code> flag is not set,
1727 * the <code>saveChanges</code> method is called.
1728 * If the <code>modified</code> flag is not
1729 * set and the <code>content</code> array is not null, the
1730 * <code>content</code> array is written directly, after
1731 * writing the appropriate message headers.
1732 *
1733 * @exception javax.mail.MessagingException
1734 * @exception IOException if an error occurs writing to the stream
1735 * or if an error is generated by the
1736 * javax.activation layer.
1737 * @see javax.activation.DataHandler#writeTo
1738 */
1739 public void writeTo(OutputStream os, String[] ignoreList)
1740 throws IOException, MessagingException {
1741 if (!saved)
1742 saveChanges();
1743
1744 if (modified) {
1745 MimeBodyPart.writeTo(this, os, ignoreList);
1746 return;
1747 }
1748
1749 // Else, the content is untouched, so we can just output it
1750 // First, write out the header
1751 Enumeration hdrLines = getNonMatchingHeaderLines(ignoreList);
1752 LineOutputStream los = new LineOutputStream(os);
1753 while (hdrLines.hasMoreElements())
1754 los.writeln((String)hdrLines.nextElement());
1755
1756 // The CRLF separator between header and content
1757 los.writeln();
1758
1759 // Finally, the content.
1760 if (content == null) {
1761 // call getContentStream to give subclass a chance to
1762 // provide the data on demand
1763 InputStream is = getContentStream();
1764 // now copy the data to the output stream
1765 byte[] buf = new byte[8192];
1766 int len;
1767 while ((len = is.read(buf)) > 0)
1768 os.write(buf, 0, len);
1769 is.close();
1770 buf = null;
1771 } else {
1772 os.write(content);
1773 }
1774 os.flush();
1775 }
1776
1777 /**
1778 * Get all the headers for this header_name. Note that certain
1779 * headers may be encoded as per RFC 2047 if they contain
1780 * non US-ASCII characters and these should be decoded. <p>
1781 *
1782 * This implementation obtains the headers from the
1783 * <code>headers</code> InternetHeaders object.
1784 *
1785 * @param name name of header
1786 * @return array of headers
1787 * @exception MessagingException
1788 * @see javax.mail.internet.MimeUtility
1789 */
1790 public String[] getHeader(String name)
1791 throws MessagingException {
1792 return headers.getHeader(name);
1793 }
1794
1795 /**
1796 * Get all the headers for this header name, returned as a single
1797 * String, with headers separated by the delimiter. If the
1798 * delimiter is <code>null</code>, only the first header is
1799 * returned.
1800 *
1801 * @param name the name of this header
1802 * @param delimiter separator between values
1803 * @return the value fields for all headers with
1804 * this name
1805 * @exception MessagingException
1806 */
1807 public String getHeader(String name, String delimiter)
1808 throws MessagingException {
1809 return headers.getHeader(name, delimiter);
1810 }
1811
1812 /**
1813 * Set the value for this header_name. Replaces all existing
1814 * header values with this new value. Note that RFC 822 headers
1815 * must contain only US-ASCII characters, so a header that
1816 * contains non US-ASCII characters must have been encoded by the
1817 * caller as per the rules of RFC 2047.
1818 *
1819 * @param name header name
1820 * @param value header value
1821 * @see javax.mail.internet.MimeUtility
1822 * @exception IllegalWriteException if the underlying
1823 * implementation does not support modification
1824 * @exception IllegalStateException if this message is
1825 * obtained from a READ_ONLY folder.
1826 * @exception MessagingException
1827 */
1828 public void setHeader(String name, String value)
1829 throws MessagingException {
1830 headers.setHeader(name, value);
1831 }
1832
1833 /**
1834 * Add this value to the existing values for this header_name.
1835 * Note that RFC 822 headers must contain only US-ASCII
1836 * characters, so a header that contains non US-ASCII characters
1837 * must have been encoded as per the rules of RFC 2047.
1838 *
1839 * @param name header name
1840 * @param value header value
1841 * @see javax.mail.internet.MimeUtility
1842 * @exception IllegalWriteException if the underlying
1843 * implementation does not support modification
1844 * @exception IllegalStateException if this message is
1845 * obtained from a READ_ONLY folder.
1846 * @exception MessagingException
1847 */
1848 public void addHeader(String name, String value)
1849 throws MessagingException {
1850 headers.addHeader(name, value);
1851 }
1852
1853 /**
1854 * Remove all headers with this name.
1855 * @exception IllegalWriteException if the underlying
1856 * implementation does not support modification
1857 * @exception IllegalStateException if this message is
1858 * obtained from a READ_ONLY folder.
1859 * @exception MessagingException
1860 */
1861 public void removeHeader(String name)
1862 throws MessagingException {
1863 headers.removeHeader(name);
1864 }
1865
1866 /**
1867 * Return all the headers from this Message as an enumeration
1868 * of Header objects. <p>
1869 *
1870 * Note that certain headers may be encoded as per RFC 2047
1871 * if they contain non US-ASCII characters and these should
1872 * be decoded. <p>
1873 *
1874 * This implementation obtains the headers from the
1875 * <code>headers</code> InternetHeaders object.
1876 *
1877 * @return array of header objects
1878 * @exception MessagingException
1879 * @see javax.mail.internet.MimeUtility
1880 */
1881 public Enumeration getAllHeaders() throws MessagingException {
1882 return headers.getAllHeaders();
1883 }
1884
1885 /**
1886 * Return matching headers from this Message as an Enumeration of
1887 * Header objects. This implementation obtains the headers from
1888 * the <code>headers</code> InternetHeaders object.
1889 *
1890 * @exception MessagingException
1891 */
1892 public Enumeration getMatchingHeaders(String[] names)
1893 throws MessagingException {
1894 return headers.getMatchingHeaders(names);
1895 }
1896
1897 /**
1898 * Return non-matching headers from this Message as an
1899 * Enumeration of Header objects. This implementation
1900 * obtains the header from the <code>headers</code> InternetHeaders object.
1901 *
1902 * @exception MessagingException
1903 */
1904 public Enumeration getNonMatchingHeaders(String[] names)
1905 throws MessagingException {
1906 return headers.getNonMatchingHeaders(names);
1907 }
1908
1909 /**
1910 * Add a raw RFC 822 header-line.
1911 *
1912 * @exception IllegalWriteException if the underlying
1913 * implementation does not support modification
1914 * @exception IllegalStateException if this message is
1915 * obtained from a READ_ONLY folder.
1916 * @exception MessagingException
1917 */
1918 public void addHeaderLine(String line) throws MessagingException {
1919 headers.addHeaderLine(line);
1920 }
1921
1922 /**
1923 * Get all header lines as an Enumeration of Strings. A Header
1924 * line is a raw RFC 822 header-line, containing both the "name"
1925 * and "value" field.
1926 *
1927 * @exception MessagingException
1928 */
1929 public Enumeration getAllHeaderLines() throws MessagingException {
1930 return headers.getAllHeaderLines();
1931 }
1932
1933 /**
1934 * Get matching header lines as an Enumeration of Strings.
1935 * A Header line is a raw RFC 822 header-line, containing both
1936 * the "name" and "value" field.
1937 *
1938 * @exception MessagingException
1939 */
1940 public Enumeration getMatchingHeaderLines(String[] names)
1941 throws MessagingException {
1942 return headers.getMatchingHeaderLines(names);
1943 }
1944
1945 /**
1946 * Get non-matching header lines as an Enumeration of Strings.
1947 * A Header line is a raw RFC 822 header-line, containing both
1948 * the "name" and "value" field.
1949 *
1950 * @exception MessagingException
1951 */
1952 public Enumeration getNonMatchingHeaderLines(String[] names)
1953 throws MessagingException {
1954 return headers.getNonMatchingHeaderLines(names);
1955 }
1956
1957 /**
1958 * Return a <code>Flags</code> object containing the flags for
1959 * this message. <p>
1960 *
1961 * Note that a clone of the internal Flags object is returned, so
1962 * modifying the returned Flags object will not affect the flags
1963 * of this message.
1964 *
1965 * @return Flags object containing the flags for this message
1966 * @exception MessagingException
1967 * @see javax.mail.Flags
1968 */
1969 public synchronized Flags getFlags() throws MessagingException {
1970 return (Flags)flags.clone();
1971 }
1972
1973 /**
1974 * Check whether the flag specified in the <code>flag</code>
1975 * argument is set in this message. <p>
1976 *
1977 * This implementation checks this message's internal
1978 * <code>flags</code> object.
1979 *
1980 * @param flag the flag
1981 * @return value of the specified flag for this message
1982 * @see javax.mail.Flags.Flag
1983 * @see javax.mail.Flags.Flag#ANSWERED
1984 * @see javax.mail.Flags.Flag#DELETED
1985 * @see javax.mail.Flags.Flag#DRAFT
1986 * @see javax.mail.Flags.Flag#FLAGGED
1987 * @see javax.mail.Flags.Flag#RECENT
1988 * @see javax.mail.Flags.Flag#SEEN
1989 * @exception MessagingException
1990 */
1991 public synchronized boolean isSet(Flags.Flag flag)
1992 throws MessagingException {
1993 return (flags.contains(flag));
1994 }
1995
1996 /**
1997 * Set the flags for this message. <p>
1998 *
1999 * This implementation modifies the <code>flags</code> field.
2000 *
2001 * @exception IllegalWriteException if the underlying
2002 * implementation does not support modification
2003 * @exception IllegalStateException if this message is
2004 * obtained from a READ_ONLY folder.
2005 * @exception MessagingException
2006 */
2007 public synchronized void setFlags(Flags flag, boolean set)
2008 throws MessagingException {
2009 if (set)
2010 flags.add(flag);
2011 else
2012 flags.remove(flag);
2013 }
2014
2015 /**
2016 * Updates the appropriate header fields of this message to be
2017 * consistent with the message's contents. If this message is
2018 * contained in a Folder, any changes made to this message are
2019 * committed to the containing folder. <p>
2020 *
2021 * If any part of a message's headers or contents are changed,
2022 * <code>saveChanges</code> must be called to ensure that those
2023 * changes are permanent. Otherwise, any such modifications may or
2024 * may not be saved, depending on the folder implementation. <p>
2025 *
2026 * Messages obtained from folders opened READ_ONLY should not be
2027 * modified and saveChanges should not be called on such messages. <p>
2028 *
2029 * This method sets the <code>modified</code> flag to true, the
2030 * <code>save</code> flag to true, and then calls the
2031 * <code>updateHeaders<code> method.
2032 *
2033 * @exception IllegalWriteException if the underlying
2034 * implementation does not support modification
2035 * @exception IllegalStateException if this message is
2036 * obtained from a READ_ONLY folder.
2037 * @exception MessagingException
2038 */
2039 public void saveChanges() throws MessagingException {
2040 modified = true;
2041 saved = true;
2042 updateHeaders();
2043 }
2044
2045 /**
2046 * Update the Message-ID header. This method is called
2047 * by the <code>updateHeaders</code> and allows a subclass
2048 * to override only the algorithm for choosing a Message-ID.
2049 *
2050 * @since JavaMail 1.4
2051 */
2052 protected void updateMessageID() throws MessagingException {
2053 setHeader("Message-ID",
2054 "<" + UniqueValue.getUniqueMessageIDValue(session) + ">");
2055
2056 }
2057
2058 /**
2059 * Called by the <code>saveChanges</code> method to actually
2060 * update the MIME headers. The implementation here sets the
2061 * <code>Content-Transfer-Encoding</code> header (if needed
2062 * and not already set), the <code>MIME-Version</code> header
2063 * and the <code>Message-ID</code> header. Also, if the content
2064 * of this message is a <code>MimeMultipart</code>, it's
2065 * <code>updateHeaders</code> method is called.
2066 *
2067 * @exception IllegalWriteException if the underlying
2068 * implementation does not support modification
2069 * @exception IllegalStateException if this message is
2070 * obtained from a READ_ONLY folder.
2071 * @exception MessagingException
2072 */
2073 protected void updateHeaders() throws MessagingException {
2074 MimeBodyPart.updateHeaders(this);
2075 setHeader("MIME-Version", "1.0");
2076 updateMessageID();
2077
2078 /*
2079 * If we've cached a Multipart or Message object then
2080 * we're now committed to using this instance of the
2081 * object and we discard any stream data used to create
2082 * this object.
2083 */
2084 if (cachedContent != null) {
2085 dh = new DataHandler(cachedContent, getContentType());
2086 cachedContent = null;
2087 content = null;
2088 if (contentStream != null) {
2089 try {
2090 contentStream.close();
2091 } catch (IOException ioex) { } // nothing to do
2092 }
2093 contentStream = null;
2094 }
2095 }
2096
2097 /**
2098 * Create and return an InternetHeaders object that loads the
2099 * headers from the given InputStream. Subclasses can override
2100 * this method to return a subclass of InternetHeaders, if
2101 * necessary. This implementation simply constructs and returns
2102 * an InternetHeaders object.
2103 *
2104 * @param is the InputStream to read the headers from
2105 * @exception MessagingException
2106 * @since JavaMail 1.2
2107 */
2108 protected InternetHeaders createInternetHeaders(InputStream is)
2109 throws MessagingException {
2110 return new InternetHeaders(is);
2111 }
2112
2113 /**
2114 * Create and return a MimeMessage object. The reply method
2115 * uses this method to create the MimeMessage object that it
2116 * will return. Subclasses can override this method to return
2117 * a subclass of MimeMessage. This implementation simply constructs
2118 * and returns a MimeMessage object using the supplied Session.
2119 *
2120 * @param session the Session to use for the new message
2121 * @return the new MimeMessage object
2122 * @since JavaMail 1.4
2123 */
2124 protected MimeMessage createMimeMessage(Session session)
2125 throws MessagingException {
2126 return new MimeMessage(session);
2127 }
2128 }