Skip to content

Package: MimeMessage$RecipientType

MimeMessage$RecipientType

nameinstructionbranchcomplexitylinemethod
MimeMessage.RecipientType(String)
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
readResolve()
M: 3 C: 7
70%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 2
67%
M: 0 C: 1
100%
static {...}
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail.internet;
18:
19: import jakarta.activation.DataHandler;
20: import jakarta.mail.Address;
21: import jakarta.mail.Flags;
22: import jakarta.mail.Folder;
23: import jakarta.mail.FolderClosedException;
24: import jakarta.mail.Header;
25: import jakarta.mail.IllegalWriteException;
26: import jakarta.mail.Message;
27: import jakarta.mail.MessageRemovedException;
28: import jakarta.mail.MessagingException;
29: import jakarta.mail.Multipart;
30: import jakarta.mail.Session;
31: import jakarta.mail.util.LineOutputStream;
32:
33: import java.io.BufferedInputStream;
34: import java.io.ByteArrayInputStream;
35: import java.io.ByteArrayOutputStream;
36: import java.io.IOException;
37: import java.io.InputStream;
38: import java.io.ObjectStreamException;
39: import java.io.OutputStream;
40: import java.io.UnsupportedEncodingException;
41: import java.text.ParseException;
42: import java.util.ArrayList;
43: import java.util.Date;
44: import java.util.Enumeration;
45: import java.util.List;
46: import java.util.Properties;
47:
48:
49: /**
50: * This class represents a MIME style email message. It implements
51: * the <code>Message</code> abstract class and the <code>MimePart</code>
52: * interface. <p>
53: *
54: * Clients wanting to create new MIME style messages will instantiate
55: * an empty MimeMessage object and then fill it with appropriate
56: * attributes and content. <p>
57: *
58: * Service providers that implement MIME compliant backend stores may
59: * want to subclass MimeMessage and override certain methods to provide
60: * specific implementations. The simplest case is probably a provider
61: * that generates a MIME style input stream and leaves the parsing of
62: * the stream to this class. <p>
63: *
64: * MimeMessage uses the <code>InternetHeaders</code> class to parse and
65: * store the top level RFC 822 headers of a message. <p>
66: *
67: * The <code>mail.mime.address.strict</code> session property controls
68: * the parsing of address headers. By default, strict parsing of address
69: * headers is done. If this property is set to <code>"false"</code>,
70: * strict parsing is not done and many illegal addresses that sometimes
71: * occur in real messages are allowed. See the <code>InternetAddress</code>
72: * class for details.
73: *
74: * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
75: *
76: * RFC 822 header fields <strong>must</strong> contain only
77: * US-ASCII characters. MIME allows non ASCII characters to be present
78: * in certain portions of certain headers, by encoding those characters.
79: * RFC 2047 specifies the rules for doing this. The MimeUtility
80: * class provided in this package can be used to to achieve this.
81: * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
82: * <code>addHeaderLine</code> methods are responsible for enforcing
83: * the MIME requirements for the specified headers. In addition, these
84: * header fields must be folded (wrapped) before being sent if they
85: * exceed the line length limitation for the transport (1000 bytes for
86: * SMTP). Received headers may have been folded. The application is
87: * responsible for folding and unfolding headers as appropriate.
88: *
89: * @author John Mani
90: * @author Bill Shannon
91: * @author Max Spivak
92: * @author Kanwar Oberoi
93: * @see jakarta.mail.internet.MimeUtility
94: * @see jakarta.mail.Part
95: * @see jakarta.mail.Message
96: * @see jakarta.mail.internet.MimePart
97: * @see jakarta.mail.internet.InternetAddress
98: */
99:
100: public class MimeMessage extends Message implements MimePart {
101:
102: /**
103: * The DataHandler object representing this Message's content.
104: */
105: protected DataHandler dh;
106:
107: /**
108: * Byte array that holds the bytes of this Message's content.
109: */
110: protected byte[] content;
111:
112: /**
113: * If the data for this message was supplied by an
114: * InputStream that implements the SharedInputStream interface,
115: * <code>contentStream</code> is another such stream representing
116: * the content of this message. In this case, <code>content</code>
117: * will be null.
118: *
119: * @since JavaMail 1.2
120: */
121: protected InputStream contentStream;
122:
123: /**
124: * The InternetHeaders object that stores the header
125: * of this message.
126: */
127: protected InternetHeaders headers;
128:
129: /**
130: * The Flags for this message.
131: */
132: protected Flags flags;
133:
134: /**
135: * A flag indicating whether the message has been modified.
136: * If the message has not been modified, any data in the
137: * <code>content</code> array is assumed to be valid and is used
138: * directly in the <code>writeTo</code> method. This flag is
139: * set to true when an empty message is created or when the
140: * <code>saveChanges</code> method is called.
141: *
142: * @since JavaMail 1.2
143: */
144: protected boolean modified = false;
145:
146: /**
147: * Does the <code>saveChanges</code> method need to be called on
148: * this message? This flag is set to false by the public constructor
149: * and set to true by the <code>saveChanges</code> method. The
150: * <code>writeTo</code> method checks this flag and calls the
151: * <code>saveChanges</code> method as necessary. This avoids the
152: * common mistake of forgetting to call the <code>saveChanges</code>
153: * method on a newly constructed message.
154: *
155: * @since JavaMail 1.2
156: */
157: protected boolean saved = false;
158:
159: /**
160: * If our content is a Multipart or Message object, we save it
161: * the first time it's created by parsing a stream so that changes
162: * to the contained objects will not be lost. <p>
163: *
164: * If this field is not null, it's return by the {@link #getContent}
165: * method. The {@link #getContent} method sets this field if it
166: * would return a Multipart or MimeMessage object. This field is
167: * is cleared by the {@link #setDataHandler} method.
168: *
169: * @since JavaMail 1.5
170: */
171: protected Object cachedContent;
172:
173: // Used to parse dates
174: private static final MailDateFormat mailDateFormat = new MailDateFormat();
175:
176: // Should addresses in headers be parsed in "strict" mode?
177: private boolean strict = true;
178: // Is UTF-8 allowed in headers?
179: private boolean allowutf8 = false;
180:
181: /**
182: * Default constructor. An empty message object is created.
183: * The <code>headers</code> field is set to an empty InternetHeaders
184: * object. The <code>flags</code> field is set to an empty Flags
185: * object. The <code>modified</code> flag is set to true.
186: *
187: * @param session the Sesssion
188: */
189: public MimeMessage(Session session) {
190: super(session);
191: modified = true;
192: headers = new InternetHeaders();
193: flags = new Flags(); // empty flags object
194: initStrict();
195: }
196:
197: /**
198: * Constructs a MimeMessage by reading and parsing the data from the
199: * specified MIME InputStream. The InputStream will be left positioned
200: * at the end of the data for the message. Note that the input stream
201: * parse is done within this constructor itself. <p>
202: *
203: * The input stream contains an entire MIME formatted message with
204: * headers and data.
205: *
206: * @param session Session object for this message
207: * @param is the message input stream
208: * @throws MessagingException for failures
209: */
210: public MimeMessage(Session session, InputStream is)
211: throws MessagingException {
212: super(session);
213: flags = new Flags(); // empty Flags object
214: initStrict();
215: parse(is);
216: saved = true;
217: }
218:
219: /**
220: * Constructs a new MimeMessage with content initialized from the
221: * <code>source</code> MimeMessage. The new message is independent
222: * of the original. <p>
223: *
224: * Note: The current implementation is rather inefficient, copying
225: * the data more times than strictly necessary.
226: *
227: * @param source the message to copy content from
228: * @throws MessagingException for failures
229: * @since JavaMail 1.2
230: */
231: public MimeMessage(MimeMessage source) throws MessagingException {
232: super(source.session);
233: flags = source.getFlags();
234: if (flags == null) // make sure flags is always set
235: flags = new Flags();
236: ByteArrayOutputStream bos;
237: int size = source.getSize();
238: if (size > 0)
239: bos = new ByteArrayOutputStream(size);
240: else
241: bos = new ByteArrayOutputStream();
242: try {
243: strict = source.strict;
244: source.writeTo(bos);
245: bos.close();
246: InputStream bis = session.getStreamProvider().inputSharedByteArray(bos.toByteArray());
247: parse(bis);
248: bis.close();
249: saved = true;
250: } catch (IOException ex) {
251: // should never happen, but just in case...
252: throw new MessagingException("IOException while copying message",
253: ex);
254: }
255: }
256:
257: /**
258: * Constructs an empty MimeMessage object with the given Folder
259: * and message number. <p>
260: *
261: * This method is for providers subclassing <code>MimeMessage</code>.
262: *
263: * @param folder the Folder this message is from
264: * @param msgnum the number of this message
265: */
266: protected MimeMessage(Folder folder, int msgnum) {
267: super(folder, msgnum);
268: flags = new Flags(); // empty Flags object
269: saved = true;
270: initStrict();
271: }
272:
273: /**
274: * Constructs a MimeMessage by reading and parsing the data from the
275: * specified MIME InputStream. The InputStream will be left positioned
276: * at the end of the data for the message. Note that the input stream
277: * parse is done within this constructor itself. <p>
278: *
279: * This method is for providers subclassing <code>MimeMessage</code>.
280: *
281: * @param folder The containing folder.
282: * @param is the message input stream
283: * @param msgnum Message number of this message within its folder
284: * @throws MessagingException for failures
285: */
286: protected MimeMessage(Folder folder, InputStream is, int msgnum)
287: throws MessagingException {
288: this(folder, msgnum);
289: initStrict();
290: parse(is);
291: }
292:
293: /**
294: * Constructs a MimeMessage from the given InternetHeaders object
295: * and content.
296: *
297: * This method is for providers subclassing <code>MimeMessage</code>.
298: *
299: * @param folder The containing folder.
300: * @param headers The headers
301: * @param content The message content
302: * @param msgnum Message number of this message within its folder
303: * @throws MessagingException for failures
304: */
305: protected MimeMessage(Folder folder, InternetHeaders headers,
306: byte[] content, int msgnum) throws MessagingException {
307: this(folder, msgnum);
308: this.headers = headers;
309: this.content = content;
310: initStrict();
311: }
312:
313: /**
314: * Set the strict flag based on property.
315: */
316: private void initStrict() {
317: if (session != null) {
318: Properties props = session.getProperties();
319: strict = MimeUtility.getBooleanProperty(props, "mail.mime.address.strict", true);
320: allowutf8 = MimeUtility.getBooleanProperty(props, "mail.mime.allowutf8", false);
321: }
322: }
323:
324: /**
325: * Parse the InputStream setting the <code>headers</code> and
326: * <code>content</code> fields appropriately. Also resets the
327: * <code>modified</code> flag. <p>
328: *
329: * This method is intended for use by subclasses that need to
330: * control when the InputStream is parsed.
331: *
332: * @param is The message input stream
333: * @throws MessagingException for failures
334: */
335: protected void parse(InputStream is) throws MessagingException {
336:
337: if (!(is instanceof ByteArrayInputStream) &&
338: !(is instanceof BufferedInputStream) &&
339: !(is instanceof SharedInputStream))
340: is = new BufferedInputStream(is);
341:
342: headers = createInternetHeaders(is);
343:
344: if (is instanceof SharedInputStream) {
345: SharedInputStream sis = (SharedInputStream) is;
346: contentStream = sis.newStream(sis.getPosition(), -1);
347: } else {
348: try {
349: content = MimeUtility.getBytes(is);
350: } catch (IOException ioex) {
351: throw new MessagingException("IOException", ioex);
352: }
353: }
354:
355: modified = false;
356: }
357:
358: /**
359: * Returns the value of the RFC 822 "From" header fields. If this
360: * header field is absent, the "Sender" header field is used.
361: * If the "Sender" header field is also absent, <code>null</code>
362: * is returned.<p>
363: *
364: * This implementation uses the <code>getHeader</code> method
365: * to obtain the requisite header field.
366: *
367: * @return Address object
368: * @throws MessagingException for failures
369: * @see #headers
370: */
371: @Override
372: public Address[] getFrom() throws MessagingException {
373: Address[] a = getAddressHeader("From");
374: if (a == null)
375: a = getAddressHeader("Sender");
376:
377: return a;
378: }
379:
380: /**
381: * Set the RFC 822 "From" header field. Any existing values are
382: * replaced with the given address. If address is <code>null</code>,
383: * this header is removed.
384: *
385: * @param address the sender of this message
386: * @throws IllegalWriteException if the underlying
387: * implementation does not support modification
388: * of existing values
389: * @throws IllegalStateException if this message is
390: * obtained from a READ_ONLY folder.
391: * @throws MessagingException for other failures
392: */
393: @Override
394: public void setFrom(Address address) throws MessagingException {
395: if (address == null)
396: removeHeader("From");
397: else
398: setAddressHeader("From", new Address[]{address});
399: }
400:
401: /**
402: * Set the RFC 822 "From" header field. Any existing values are
403: * replaced with the given addresses. If address is <code>null</code>,
404: * this header is removed.
405: *
406: * @param address the sender(s) of this message
407: * @throws IllegalWriteException if the underlying
408: * implementation does not support modification
409: * of existing values
410: * @throws IllegalStateException if this message is
411: * obtained from a READ_ONLY folder.
412: * @throws MessagingException for other failures
413: * @since JvaMail 1.5
414: */
415: public void setFrom(String address) throws MessagingException {
416: if (address == null)
417: removeHeader("From");
418: else
419: setAddressHeader("From", InternetAddress.parse(address));
420: }
421:
422: /**
423: * Set the RFC 822 "From" header field using the value of the
424: * <code>InternetAddress.getLocalAddress</code> method.
425: *
426: * @throws IllegalWriteException if the underlying
427: * implementation does not support modification
428: * of existing values
429: * @throws IllegalStateException if this message is
430: * obtained from a READ_ONLY folder.
431: * @throws MessagingException for other failures
432: */
433: @Override
434: public void setFrom() throws MessagingException {
435: InternetAddress me = null;
436: try {
437: me = InternetAddress._getLocalAddress(session);
438: } catch (Exception ex) {
439: // if anything goes wrong (SecurityException, UnknownHostException),
440: // chain the exception
441: throw new MessagingException("No From address", ex);
442: }
443: if (me != null)
444: setFrom(me);
445: else
446: throw new MessagingException("No From address");
447: }
448:
449: /**
450: * Add the specified addresses to the existing "From" field. If
451: * the "From" field does not already exist, it is created.
452: *
453: * @param addresses the senders of this message
454: * @throws IllegalWriteException if the underlying
455: * implementation does not support modification
456: * of existing values
457: * @throws IllegalStateException if this message is
458: * obtained from a READ_ONLY folder.
459: * @throws MessagingException for other failures
460: */
461: @Override
462: public void addFrom(Address[] addresses) throws MessagingException {
463: addAddressHeader("From", addresses);
464: }
465:
466: /**
467: * Returns the value of the RFC 822 "Sender" header field.
468: * If the "Sender" header field is absent, <code>null</code>
469: * is returned.<p>
470: *
471: * This implementation uses the <code>getHeader</code> method
472: * to obtain the requisite header field.
473: *
474: * @return Address object
475: * @throws MessagingException for failures
476: * @see #headers
477: * @since JavaMail 1.3
478: */
479: public Address getSender() throws MessagingException {
480: Address[] a = getAddressHeader("Sender");
481: if (a == null || a.length == 0)
482: return null;
483: return a[0]; // there can be only one
484: }
485:
486: /**
487: * Set the RFC 822 "Sender" header field. Any existing values are
488: * replaced with the given address. If address is <code>null</code>,
489: * this header is removed.
490: *
491: * @param address the sender of this message
492: * @throws IllegalWriteException if the underlying
493: * implementation does not support modification
494: * of existing values
495: * @throws IllegalStateException if this message is
496: * obtained from a READ_ONLY folder.
497: * @throws MessagingException for other failures
498: * @since JavaMail 1.3
499: */
500: public void setSender(Address address) throws MessagingException {
501: if (address == null)
502: removeHeader("Sender");
503: else
504: setAddressHeader("Sender", new Address[]{address});
505: }
506:
507: /**
508: * This inner class extends the jakarta.mail.Message.RecipientType
509: * class to add additional RecipientTypes. The one additional
510: * RecipientType currently defined here is NEWSGROUPS.
511: *
512: * @see jakarta.mail.Message.RecipientType
513: */
514: public static class RecipientType extends Message.RecipientType {
515:
516: private static final long serialVersionUID = -5468290701714395543L;
517:
518: /**
519: * The "Newsgroup" (Usenet news) recipients.
520: */
521: public static final RecipientType NEWSGROUPS =
522: new RecipientType("Newsgroups");
523:
524: /**
525: * Constructor for use by subclasses.
526: *
527: * @param type the recipient type
528: */
529: protected RecipientType(String type) {
530: super(type);
531: }
532:
533: @Override
534: protected Object readResolve() throws ObjectStreamException {
535:• if (type.equals("Newsgroups"))
536: return NEWSGROUPS;
537: else
538: return super.readResolve();
539: }
540: }
541:
542: /**
543: * Returns the recepients specified by the type. The mapping
544: * between the type and the corresponding RFC 822 header is
545: * as follows:
546: * <pre>
547: *                 Message.RecipientType.TO                "To"
548: *                 Message.RecipientType.CC                "Cc"
549: *                 Message.RecipientType.BCC                "Bcc"
550: *                 MimeMessage.RecipientType.NEWSGROUPS        "Newsgroups"
551: * </pre><br>
552: *
553: * Returns null if the header specified by the type is not found
554: * or if its value is empty. <p>
555: *
556: * This implementation uses the <code>getHeader</code> method
557: * to obtain the requisite header field.
558: *
559: * @param type Type of recepient
560: * @return array of Address objects
561: * @throws MessagingException if header could not
562: * be retrieved
563: * @throws AddressException if the header is misformatted
564: * @see #headers
565: * @see jakarta.mail.Message.RecipientType#TO
566: * @see jakarta.mail.Message.RecipientType#CC
567: * @see jakarta.mail.Message.RecipientType#BCC
568: * @see jakarta.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
569: */
570: @Override
571: public Address[] getRecipients(Message.RecipientType type)
572: throws MessagingException {
573: if (type == RecipientType.NEWSGROUPS) {
574: String s = getHeader("Newsgroups", ",");
575: return (s == null) ? null : NewsAddress.parse(s);
576: } else
577: return getAddressHeader(getHeaderName(type));
578: }
579:
580: /**
581: * Get all the recipient addresses for the message.
582: * Extracts the TO, CC, BCC, and NEWSGROUPS recipients.
583: *
584: * @return array of Address objects
585: * @throws MessagingException for failures
586: * @see jakarta.mail.Message.RecipientType#TO
587: * @see jakarta.mail.Message.RecipientType#CC
588: * @see jakarta.mail.Message.RecipientType#BCC
589: * @see jakarta.mail.internet.MimeMessage.RecipientType#NEWSGROUPS
590: */
591: @Override
592: public Address[] getAllRecipients() throws MessagingException {
593: Address[] all = super.getAllRecipients();
594: Address[] ng = getRecipients(RecipientType.NEWSGROUPS);
595:
596: if (ng == null)
597: return all; // the common case
598: if (all == null)
599: return ng; // a rare case
600:
601: Address[] addresses = new Address[all.length + ng.length];
602: System.arraycopy(all, 0, addresses, 0, all.length);
603: System.arraycopy(ng, 0, addresses, all.length, ng.length);
604: return addresses;
605: }
606:
607: /**
608: * Set the specified recipient type to the given addresses.
609: * If the address parameter is <code>null</code>, the corresponding
610: * recipient field is removed.
611: *
612: * @param type Recipient type
613: * @param addresses Addresses
614: * @throws IllegalWriteException if the underlying
615: * implementation does not support modification
616: * of existing values
617: * @throws IllegalStateException if this message is
618: * obtained from a READ_ONLY folder.
619: * @throws MessagingException for other failures
620: * @see #getRecipients
621: */
622: @Override
623: public void setRecipients(Message.RecipientType type, Address[] addresses)
624: throws MessagingException {
625: if (type == RecipientType.NEWSGROUPS) {
626: if (addresses == null || addresses.length == 0)
627: removeHeader("Newsgroups");
628: else
629: setHeader("Newsgroups", NewsAddress.toString(addresses));
630: } else
631: setAddressHeader(getHeaderName(type), addresses);
632: }
633:
634: /**
635: * Set the specified recipient type to the given addresses.
636: * If the address parameter is <code>null</code>, the corresponding
637: * recipient field is removed.
638: *
639: * @param type Recipient type
640: * @param addresses Addresses
641: * @throws AddressException if the attempt to parse the
642: * addresses String fails
643: * @throws IllegalWriteException if the underlying
644: * implementation does not support modification
645: * of existing values
646: * @throws IllegalStateException if this message is
647: * obtained from a READ_ONLY folder.
648: * @throws MessagingException for other failures
649: * @see #getRecipients
650: * @since JavaMail 1.2
651: */
652: public void setRecipients(Message.RecipientType type, String addresses)
653: throws MessagingException {
654: if (type == RecipientType.NEWSGROUPS) {
655: if (addresses == null || addresses.length() == 0)
656: removeHeader("Newsgroups");
657: else
658: setHeader("Newsgroups", addresses);
659: } else
660: setAddressHeader(getHeaderName(type),
661: addresses == null ? null : InternetAddress.parse(addresses));
662: }
663:
664: /**
665: * Add the given addresses to the specified recipient type.
666: *
667: * @param type Recipient type
668: * @param addresses Addresses
669: * @throws IllegalWriteException if the underlying
670: * implementation does not support modification
671: * of existing values
672: * @throws IllegalStateException if this message is
673: * obtained from a READ_ONLY folder.
674: * @throws MessagingException for other failures
675: */
676: @Override
677: public void addRecipients(Message.RecipientType type, Address[] addresses)
678: throws MessagingException {
679: if (type == RecipientType.NEWSGROUPS) {
680: String s = NewsAddress.toString(addresses);
681: if (s != null)
682: addHeader("Newsgroups", s);
683: } else
684: addAddressHeader(getHeaderName(type), addresses);
685: }
686:
687: /**
688: * Add the given addresses to the specified recipient type.
689: *
690: * @param type Recipient type
691: * @param addresses Addresses
692: * @throws AddressException if the attempt to parse the
693: * addresses String fails
694: * @throws IllegalWriteException if the underlying
695: * implementation does not support modification
696: * of existing values
697: * @throws IllegalStateException if this message is
698: * obtained from a READ_ONLY folder.
699: * @throws MessagingException for other failures
700: * @since JavaMail 1.2
701: */
702: public void addRecipients(Message.RecipientType type, String addresses)
703: throws MessagingException {
704: if (type == RecipientType.NEWSGROUPS) {
705: if (addresses != null && addresses.length() != 0)
706: addHeader("Newsgroups", addresses);
707: } else
708: addAddressHeader(getHeaderName(type),
709: InternetAddress.parse(addresses));
710: }
711:
712: /**
713: * Return the value of the RFC 822 "Reply-To" header field. If
714: * this header is unavailable or its value is absent, then
715: * the <code>getFrom</code> method is called and its value is returned.
716: *
717: * This implementation uses the <code>getHeader</code> method
718: * to obtain the requisite header field.
719: *
720: * @throws MessagingException for failures
721: * @see #headers
722: */
723: @Override
724: public Address[] getReplyTo() throws MessagingException {
725: Address[] a = getAddressHeader("Reply-To");
726: if (a == null || a.length == 0)
727: a = getFrom();
728: return a;
729: }
730:
731: /**
732: * Set the RFC 822 "Reply-To" header field. If the address
733: * parameter is <code>null</code>, this header is removed.
734: *
735: * @throws IllegalWriteException if the underlying
736: * implementation does not support modification
737: * of existing values
738: * @throws IllegalStateException if this message is
739: * obtained from a READ_ONLY folder.
740: * @throws MessagingException for other failures
741: */
742: @Override
743: public void setReplyTo(Address[] addresses) throws MessagingException {
744: setAddressHeader("Reply-To", addresses);
745: }
746:
747: // Convenience method to get addresses
748: private Address[] getAddressHeader(String name)
749: throws MessagingException {
750: String s = getHeader(name, ",");
751: return (s == null) ? null : InternetAddress.parseHeader(s, strict);
752: }
753:
754: // Convenience method to set addresses
755: private void setAddressHeader(String name, Address[] addresses)
756: throws MessagingException {
757: String s;
758: if (allowutf8)
759: s = InternetAddress.toUnicodeString(addresses, name.length() + 2);
760: else
761: s = InternetAddress.toString(addresses, name.length() + 2);
762: if (s == null)
763: removeHeader(name);
764: else
765: setHeader(name, s);
766: }
767:
768: private void addAddressHeader(String name, Address[] addresses)
769: throws MessagingException {
770: if (addresses == null || addresses.length == 0)
771: return;
772: Address[] a = getAddressHeader(name);
773: Address[] anew;
774: if (a == null || a.length == 0)
775: anew = addresses;
776: else {
777: anew = new Address[a.length + addresses.length];
778: System.arraycopy(a, 0, anew, 0, a.length);
779: System.arraycopy(addresses, 0, anew, a.length, addresses.length);
780: }
781: String s;
782: if (allowutf8)
783: s = InternetAddress.toUnicodeString(anew, name.length() + 2);
784: else
785: s = InternetAddress.toString(anew, name.length() + 2);
786: if (s == null)
787: return;
788: setHeader(name, s);
789: }
790:
791: /**
792: * Returns the value of the "Subject" header field. Returns null
793: * if the subject field is unavailable or its value is absent. <p>
794: *
795: * If the subject is encoded as per RFC 2047, it is decoded and
796: * converted into Unicode. If the decoding or conversion fails, the
797: * raw data is returned as is. <p>
798: *
799: * This implementation uses the <code>getHeader</code> method
800: * to obtain the requisite header field.
801: *
802: * @return Subject
803: * @throws MessagingException for failures
804: * @see #headers
805: */
806: @Override
807: public String getSubject() throws MessagingException {
808: String rawvalue = getHeader("Subject", null);
809:
810: if (rawvalue == null)
811: return null;
812:
813: try {
814: return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
815: } catch (UnsupportedEncodingException ex) {
816: return rawvalue;
817: }
818: }
819:
820: /**
821: * Set the "Subject" header field. If the subject contains
822: * non US-ASCII characters, it will be encoded using the
823: * platform's default charset. If the subject contains only
824: * US-ASCII characters, no encoding is done and it is used
825: * as-is. If the subject is null, the existing "Subject" field
826: * is removed. <p>
827: *
828: * The application must ensure that the subject does not contain
829: * any line breaks. <p>
830: *
831: * Note that if the charset encoding process fails, a
832: * MessagingException is thrown, and an UnsupportedEncodingException
833: * is included in the chain of nested exceptions within the
834: * MessagingException.
835: *
836: * @param subject The subject
837: * @throws IllegalWriteException if the underlying
838: * implementation does not support modification
839: * of existing values
840: * @throws IllegalStateException if this message is
841: * obtained from a READ_ONLY folder.
842: * @throws MessagingException for other failures
843: */
844: @Override
845: public void setSubject(String subject) throws MessagingException {
846: setSubject(subject, null);
847: }
848:
849: /**
850: * Set the "Subject" header field. If the subject contains non
851: * US-ASCII characters, it will be encoded using the specified
852: * charset. If the subject contains only US-ASCII characters, no
853: * encoding is done and it is used as-is. If the subject is null,
854: * the existing "Subject" header field is removed. <p>
855: *
856: * The application must ensure that the subject does not contain
857: * any line breaks. <p>
858: *
859: * Note that if the charset encoding process fails, a
860: * MessagingException is thrown, and an UnsupportedEncodingException
861: * is included in the chain of nested exceptions within the
862: * MessagingException.
863: *
864: * @param subject The subject
865: * @param charset The charset
866: * @throws IllegalWriteException if the underlying
867: * implementation does not support modification
868: * of existing values
869: * @throws IllegalStateException if this message is
870: * obtained from a READ_ONLY folder.
871: * @throws MessagingException for other failures
872: */
873: public void setSubject(String subject, String charset)
874: throws MessagingException {
875: if (subject == null) {
876: removeHeader("Subject");
877: } else {
878: try {
879: setHeader("Subject", MimeUtility.fold(9,
880: MimeUtility.encodeText(subject, charset, null)));
881: } catch (UnsupportedEncodingException uex) {
882: throw new MessagingException("Encoding error", uex);
883: }
884: }
885: }
886:
887: /**
888: * Returns the value of the RFC 822 "Date" field. This is the date
889: * on which this message was sent. Returns null if this field is
890: * unavailable or its value is absent. <p>
891: *
892: * This implementation uses the <code>getHeader</code> method
893: * to obtain the requisite header field.
894: *
895: * @return The sent Date
896: * @throws MessagingException for failures
897: */
898: @Override
899: public Date getSentDate() throws MessagingException {
900: String s = getHeader("Date", null);
901: if (s != null) {
902: try {
903: synchronized (mailDateFormat) {
904: return mailDateFormat.parse(s);
905: }
906: } catch (ParseException pex) {
907: return null;
908: }
909: }
910:
911: return null;
912: }
913:
914: /**
915: * Set the RFC 822 "Date" header field. This is the date on which the
916: * creator of the message indicates that the message is complete
917: * and ready for delivery. If the date parameter is
918: * <code>null</code>, the existing "Date" field is removed.
919: *
920: * @throws IllegalWriteException if the underlying
921: * implementation does not support modification
922: * @throws IllegalStateException if this message is
923: * obtained from a READ_ONLY folder.
924: * @throws MessagingException for other failures
925: */
926: @Override
927: public void setSentDate(Date d) throws MessagingException {
928: if (d == null)
929: removeHeader("Date");
930: else {
931: synchronized (mailDateFormat) {
932: setHeader("Date", mailDateFormat.format(d));
933: }
934: }
935: }
936:
937: /**
938: * Returns the Date on this message was received. Returns
939: * <code>null</code> if this date cannot be obtained. <p>
940: *
941: * Note that RFC 822 does not define a field for the received
942: * date. Hence only implementations that can provide this date
943: * need return a valid value. <p>
944: *
945: * This implementation returns <code>null</code>.
946: *
947: * @return the date this message was received
948: * @throws MessagingException for failures
949: */
950: @Override
951: public Date getReceivedDate() throws MessagingException {
952: return null;
953: }
954:
955: /**
956: * Return the size of the content of this message in bytes.
957: * Return -1 if the size cannot be determined. <p>
958: *
959: * Note that this number may not be an exact measure of the
960: * content size and may or may not account for any transfer
961: * encoding of the content. <p>
962: *
963: * This implementation returns the size of the <code>content</code>
964: * array (if not null), or, if <code>contentStream</code> is not
965: * null, and the <code>available</code> method returns a positive
966: * number, it returns that number as the size. Otherwise, it returns
967: * -1.
968: *
969: * @return size of content in bytes
970: * @throws MessagingException for failures
971: */
972: @Override
973: public int getSize() throws MessagingException {
974: if (content != null)
975: return content.length;
976: if (contentStream != null) {
977: try {
978: int size = contentStream.available();
979: // only believe the size if it's greater than zero, since zero
980: // is the default returned by the InputStream class itself
981: if (size > 0)
982: return size;
983: } catch (IOException ex) {
984: // ignore it
985: }
986: }
987: return -1;
988: }
989:
990: /**
991: * Return the number of lines for the content of this message.
992: * Return -1 if this number cannot be determined. <p>
993: *
994: * Note that this number may not be an exact measure of the
995: * content length and may or may not account for any transfer
996: * encoding of the content. <p>
997: *
998: * This implementation returns -1.
999: *
1000: * @return number of lines in the content.
1001: * @throws MessagingException for failures
1002: */
1003: @Override
1004: public int getLineCount() throws MessagingException {
1005: return -1;
1006: }
1007:
1008: /**
1009: * Returns the value of the RFC 822 "Content-Type" header field.
1010: * This represents the content-type of the content of this
1011: * message. This value must not be null. If this field is
1012: * unavailable, "text/plain" should be returned. <p>
1013: *
1014: * This implementation uses the <code>getHeader</code> method
1015: * to obtain the requisite header field.
1016: *
1017: * @return The ContentType of this part
1018: * @throws MessagingException for failures
1019: * @see jakarta.activation.DataHandler
1020: */
1021: @Override
1022: public String getContentType() throws MessagingException {
1023: String s = getHeader("Content-Type", null);
1024: s = MimeUtil.cleanContentType(this, s);
1025: if (s == null)
1026: return "text/plain";
1027: return s;
1028: }
1029:
1030: /**
1031: * Is this Part of the specified MIME type? This method
1032: * compares <strong>only the <code>primaryType</code> and
1033: * <code>subType</code></strong>.
1034: * The parameters of the content types are ignored. <p>
1035: *
1036: * For example, this method will return <code>true</code> when
1037: * comparing a Part of content type <strong>"text/plain"</strong>
1038: * with <strong>"text/plain; charset=foobar"</strong>. <p>
1039: *
1040: * If the <code>subType</code> of <code>mimeType</code> is the
1041: * special character '*', then the subtype is ignored during the
1042: * comparison.
1043: *
1044: * @param mimeType the MIME type to check
1045: * @return true if it matches the MIME type
1046: * @throws MessagingException for failures
1047: */
1048: @Override
1049: public boolean isMimeType(String mimeType) throws MessagingException {
1050: return MimeBodyPart.isMimeType(this, mimeType);
1051: }
1052:
1053: /**
1054: * Returns the disposition from the "Content-Disposition" header field.
1055: * This represents the disposition of this part. The disposition
1056: * describes how the part should be presented to the user. <p>
1057: *
1058: * If the Content-Disposition field is unavailable,
1059: * <code>null</code> is returned. <p>
1060: *
1061: * This implementation uses the <code>getHeader</code> method
1062: * to obtain the requisite header field.
1063: *
1064: * @return disposition of this part, or null if unknown
1065: * @throws MessagingException for failures
1066: */
1067: @Override
1068: public String getDisposition() throws MessagingException {
1069: return MimeBodyPart.getDisposition(this);
1070: }
1071:
1072: /**
1073: * Set the disposition in the "Content-Disposition" header field
1074: * of this body part. If the disposition is null, any existing
1075: * "Content-Disposition" header field is removed.
1076: *
1077: * @throws IllegalWriteException if the underlying
1078: * implementation does not support modification
1079: * @throws IllegalStateException if this message is
1080: * obtained from a READ_ONLY folder.
1081: * @throws MessagingException for other failures
1082: */
1083: @Override
1084: public void setDisposition(String disposition) throws MessagingException {
1085: MimeBodyPart.setDisposition(this, disposition);
1086: }
1087:
1088: /**
1089: * Returns the content transfer encoding from the
1090: * "Content-Transfer-Encoding" header
1091: * field. Returns <code>null</code> if the header is unavailable
1092: * or its value is absent. <p>
1093: *
1094: * This implementation uses the <code>getHeader</code> method
1095: * to obtain the requisite header field.
1096: *
1097: * @return content-transfer-encoding
1098: * @throws MessagingException for failures
1099: */
1100: @Override
1101: public String getEncoding() throws MessagingException {
1102: return MimeBodyPart.getEncoding(this);
1103: }
1104:
1105: /**
1106: * Returns the value of the "Content-ID" header field. Returns
1107: * <code>null</code> if the field is unavailable or its value is
1108: * absent. <p>
1109: *
1110: * This implementation uses the <code>getHeader</code> method
1111: * to obtain the requisite header field.
1112: *
1113: * @return content-ID
1114: * @throws MessagingException for failures
1115: */
1116: @Override
1117: public String getContentID() throws MessagingException {
1118: return getHeader("Content-Id", null);
1119: }
1120:
1121: /**
1122: * Set the "Content-ID" header field of this Message.
1123: * If the <code>cid</code> parameter is null, any existing
1124: * "Content-ID" is removed.
1125: *
1126: * @param cid the content ID
1127: * @throws IllegalWriteException if the underlying
1128: * implementation does not support modification
1129: * @throws IllegalStateException if this message is
1130: * obtained from a READ_ONLY folder.
1131: * @throws MessagingException for other failures
1132: */
1133: public void setContentID(String cid) throws MessagingException {
1134: if (cid == null)
1135: removeHeader("Content-ID");
1136: else
1137: setHeader("Content-ID", cid);
1138: }
1139:
1140: /**
1141: * Return the value of the "Content-MD5" header field. Returns
1142: * <code>null</code> if this field is unavailable or its value
1143: * is absent. <p>
1144: *
1145: * This implementation uses the <code>getHeader</code> method
1146: * to obtain the requisite header field.
1147: *
1148: * @return content-MD5
1149: * @throws MessagingException for failures
1150: */
1151: @Override
1152: public String getContentMD5() throws MessagingException {
1153: return getHeader("Content-MD5", null);
1154: }
1155:
1156: /**
1157: * Set the "Content-MD5" header field of this Message.
1158: *
1159: * @throws IllegalWriteException if the underlying
1160: * implementation does not support modification
1161: * @throws IllegalStateException if this message is
1162: * obtained from a READ_ONLY folder.
1163: * @throws MessagingException for other failures
1164: */
1165: @Override
1166: public void setContentMD5(String md5) throws MessagingException {
1167: setHeader("Content-MD5", md5);
1168: }
1169:
1170: /**
1171: * Returns the "Content-Description" header field of this Message.
1172: * This typically associates some descriptive information with
1173: * this part. Returns null if this field is unavailable or its
1174: * value is absent. <p>
1175: *
1176: * If the Content-Description field is encoded as per RFC 2047,
1177: * it is decoded and converted into Unicode. If the decoding or
1178: * conversion fails, the raw data is returned as-is <p>
1179: *
1180: * This implementation uses the <code>getHeader</code> method
1181: * to obtain the requisite header field.
1182: *
1183: * @return content-description
1184: * @throws MessagingException for failures
1185: */
1186: @Override
1187: public String getDescription() throws MessagingException {
1188: return MimeBodyPart.getDescription(this);
1189: }
1190:
1191: /**
1192: * Set the "Content-Description" header field for this Message.
1193: * If the description parameter is <code>null</code>, then any
1194: * existing "Content-Description" fields are removed. <p>
1195: *
1196: * If the description contains non US-ASCII characters, it will
1197: * be encoded using the platform's default charset. If the
1198: * description contains only US-ASCII characters, no encoding
1199: * is done and it is used as-is. <p>
1200: *
1201: * Note that if the charset encoding process fails, a
1202: * MessagingException is thrown, and an UnsupportedEncodingException
1203: * is included in the chain of nested exceptions within the
1204: * MessagingException.
1205: *
1206: * @param description content-description
1207: * @throws IllegalWriteException if the underlying
1208: * implementation does not support modification
1209: * @throws IllegalStateException if this message is
1210: * obtained from a READ_ONLY folder.
1211: * @throws MessagingException An
1212: * UnsupportedEncodingException may be included
1213: * in the exception chain if the charset
1214: * conversion fails.
1215: */
1216: @Override
1217: public void setDescription(String description) throws MessagingException {
1218: setDescription(description, null);
1219: }
1220:
1221: /**
1222: * Set the "Content-Description" header field for this Message.
1223: * If the description parameter is <code>null</code>, then any
1224: * existing "Content-Description" fields are removed. <p>
1225: *
1226: * If the description contains non US-ASCII characters, it will
1227: * be encoded using the specified charset. If the description
1228: * contains only US-ASCII characters, no encoding is done and
1229: * it is used as-is. <p>
1230: *
1231: * Note that if the charset encoding process fails, a
1232: * MessagingException is thrown, and an UnsupportedEncodingException
1233: * is included in the chain of nested exceptions within the
1234: * MessagingException.
1235: *
1236: * @param description Description
1237: * @param charset Charset for encoding
1238: * @throws IllegalWriteException if the underlying
1239: * implementation does not support modification
1240: * @throws IllegalStateException if this message is
1241: * obtained from a READ_ONLY folder.
1242: * @throws MessagingException An
1243: * UnsupportedEncodingException may be included
1244: * in the exception chain if the charset
1245: * conversion fails.
1246: */
1247: public void setDescription(String description, String charset)
1248: throws MessagingException {
1249: MimeBodyPart.setDescription(this, description, charset);
1250: }
1251:
1252: /**
1253: * Get the languages specified in the "Content-Language" header
1254: * field of this message. The Content-Language header is defined by
1255: * RFC 1766. Returns <code>null</code> if this field is unavailable
1256: * or its value is absent. <p>
1257: *
1258: * This implementation uses the <code>getHeader</code> method
1259: * to obtain the requisite header field.
1260: *
1261: * @return value of content-language header.
1262: * @throws MessagingException for failures
1263: */
1264: @Override
1265: public String[] getContentLanguage() throws MessagingException {
1266: return MimeBodyPart.getContentLanguage(this);
1267: }
1268:
1269: /**
1270: * Set the "Content-Language" header of this MimePart. The
1271: * Content-Language header is defined by RFC 1766.
1272: *
1273: * @param languages array of language tags
1274: * @throws IllegalWriteException if the underlying
1275: * implementation does not support modification
1276: * @throws IllegalStateException if this message is
1277: * obtained from a READ_ONLY folder.
1278: * @throws MessagingException for other failures
1279: */
1280: @Override
1281: public void setContentLanguage(String[] languages)
1282: throws MessagingException {
1283: MimeBodyPart.setContentLanguage(this, languages);
1284: }
1285:
1286: /**
1287: * Returns the value of the "Message-ID" header field. Returns
1288: * null if this field is unavailable or its value is absent. <p>
1289: *
1290: * The default implementation provided here uses the
1291: * <code>getHeader</code> method to return the value of the
1292: * "Message-ID" field.
1293: *
1294: * @return Message-ID
1295: * @throws MessagingException if the retrieval of this field
1296: * causes any exception.
1297: * @see jakarta.mail.search.MessageIDTerm
1298: * @since JavaMail 1.1
1299: */
1300: public String getMessageID() throws MessagingException {
1301: return getHeader("Message-ID", null);
1302: }
1303:
1304: /**
1305: * Get the filename associated with this Message. <p>
1306: *
1307: * Returns the value of the "filename" parameter from the
1308: * "Content-Disposition" header field of this message. If it's
1309: * not available, returns the value of the "name" parameter from
1310: * the "Content-Type" header field of this BodyPart.
1311: * Returns <code>null</code> if both are absent. <p>
1312: *
1313: * If the <code>mail.mime.encodefilename</code> System property
1314: * is set to true, the {@link MimeUtility#decodeText
1315: * MimeUtility.decodeText} method will be used to decode the
1316: * filename. While such encoding is not supported by the MIME
1317: * spec, many mailers use this technique to support non-ASCII
1318: * characters in filenames. The default value of this property
1319: * is false.
1320: *
1321: * @return filename
1322: * @throws MessagingException for failures
1323: */
1324: @Override
1325: public String getFileName() throws MessagingException {
1326: return MimeBodyPart.getFileName(this);
1327: }
1328:
1329: /**
1330: * Set the filename associated with this part, if possible. <p>
1331: *
1332: * Sets the "filename" parameter of the "Content-Disposition"
1333: * header field of this message. <p>
1334: *
1335: * If the <code>mail.mime.encodefilename</code> System property
1336: * is set to true, the {@link MimeUtility#encodeText
1337: * MimeUtility.encodeText} method will be used to encode the
1338: * filename. While such encoding is not supported by the MIME
1339: * spec, many mailers use this technique to support non-ASCII
1340: * characters in filenames. The default value of this property
1341: * is false.
1342: *
1343: * @throws IllegalWriteException if the underlying
1344: * implementation does not support modification
1345: * @throws IllegalStateException if this message is
1346: * obtained from a READ_ONLY folder.
1347: * @throws MessagingException for other failures
1348: */
1349: @Override
1350: public void setFileName(String filename) throws MessagingException {
1351: MimeBodyPart.setFileName(this, filename);
1352: }
1353:
1354: private String getHeaderName(Message.RecipientType type)
1355: throws MessagingException {
1356: String headerName;
1357:
1358: if (type == Message.RecipientType.TO)
1359: headerName = "To";
1360: else if (type == Message.RecipientType.CC)
1361: headerName = "Cc";
1362: else if (type == Message.RecipientType.BCC)
1363: headerName = "Bcc";
1364: else if (type == MimeMessage.RecipientType.NEWSGROUPS)
1365: headerName = "Newsgroups";
1366: else
1367: throw new MessagingException("Invalid Recipient Type");
1368: return headerName;
1369: }
1370:
1371:
1372: /**
1373: * Return a decoded input stream for this Message's "content". <p>
1374: *
1375: * This implementation obtains the input stream from the DataHandler,
1376: * that is, it invokes <code>getDataHandler().getInputStream()</code>.
1377: *
1378: * @return an InputStream
1379: * @throws IOException this is typically thrown by the
1380: * DataHandler. Refer to the documentation for
1381: * jakarta.activation.DataHandler for more details.
1382: * @throws MessagingException for other failures
1383: * @see #getContentStream
1384: * @see jakarta.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: * @throws 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 session.getStreamProvider().inputSharedByteArray(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: * @throws 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>
1462: *
1463: * @throws 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: * @throws IOException this is typically thrown by the
1490: * DataHandler. Refer to the documentation for
1491: * jakarta.activation.DataHandler for more details.
1492: * @throws MessagingException for other failures
1493: * @see jakarta.mail.Part
1494: * @see jakarta.activation.DataHandler#getContent
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 (IOException e) {
1504: if (e.getCause() instanceof FolderClosedException) {
1505: FolderClosedException fce = (FolderClosedException) e.getCause();
1506: throw new FolderClosedException(fce.getFolder(), e.getMessage(), e);
1507: } else if (e.getCause() instanceof MessagingException) {
1508: throw new MessageRemovedException(e.getMessage(), e);
1509: } else {
1510: throw e;
1511: }
1512: }
1513: if (MimeBodyPart.cacheMultipart &&
1514: (c instanceof Multipart || c instanceof Message) &&
1515: (content != null || contentStream != null)) {
1516: cachedContent = c;
1517: /*
1518: * We may abandon the input stream so make sure
1519: * the MimeMultipart has consumed the stream.
1520: */
1521: if (c instanceof MimeMultipart)
1522: ((MimeMultipart) c).parse();
1523: }
1524: return c;
1525: }
1526:
1527: /**
1528: * This method provides the mechanism to set this part's content.
1529: * The given DataHandler object should wrap the actual content.
1530: *
1531: * @param dh The DataHandler for the content.
1532: * @throws IllegalWriteException if the underlying
1533: * implementation does not support modification
1534: * @throws IllegalStateException if this message is
1535: * obtained from a READ_ONLY folder.
1536: * @throws MessagingException for other failures
1537: */
1538: @Override
1539: public synchronized void setDataHandler(DataHandler dh)
1540: throws MessagingException {
1541: this.dh = dh;
1542: cachedContent = null;
1543: MimeBodyPart.invalidateContentHeaders(this);
1544: }
1545:
1546: /**
1547: * A convenience method for setting this Message's content. <p>
1548: *
1549: * The content is wrapped in a DataHandler object. Note that a
1550: * DataContentHandler class for the specified type should be
1551: * available to the JavaMail implementation for this to work right.
1552: * i.e., to do <code>setContent(foobar, "application/x-foobar")</code>,
1553: * a DataContentHandler for "application/x-foobar" should be installed.
1554: * Refer to the Java Activation Framework for more information.
1555: *
1556: * @param o the content object
1557: * @param type Mime type of the object
1558: * @throws IllegalWriteException if the underlying
1559: * implementation does not support modification of
1560: * existing values
1561: * @throws IllegalStateException if this message is
1562: * obtained from a READ_ONLY folder.
1563: * @throws MessagingException for other failures
1564: */
1565: @Override
1566: public void setContent(Object o, String type)
1567: throws MessagingException {
1568: if (o instanceof Multipart)
1569: setContent((Multipart) o);
1570: else
1571: setDataHandler(new DataHandler(o, type));
1572: }
1573:
1574: /**
1575: * Convenience method that sets the given String as this
1576: * part's content, with a MIME type of "text/plain". If the
1577: * string contains non US-ASCII characters. it will be encoded
1578: * using the platform's default charset. The charset is also
1579: * used to set the "charset" parameter.<p>
1580: *
1581: * Note that there may be a performance penalty if
1582: * <code>text</code> is large, since this method may have
1583: * to scan all the characters to determine what charset to
1584: * use. <p>
1585: *
1586: * If the charset is already known, use the
1587: * <code>setText</code> method that takes the charset parameter.
1588: *
1589: * @param text the text content to set
1590: * @throws MessagingException if an error occurs
1591: * @see #setText(String text, String charset)
1592: */
1593: @Override
1594: public void setText(String text) throws MessagingException {
1595: setText(text, null);
1596: }
1597:
1598: /**
1599: * Convenience method that sets the given String as this part's
1600: * content, with a MIME type of "text/plain" and the specified
1601: * charset. The given Unicode string will be charset-encoded
1602: * using the specified charset. The charset is also used to set
1603: * the "charset" parameter.
1604: *
1605: * @param text the text content to set
1606: * @param charset the charset to use for the text
1607: * @throws MessagingException if an error occurs
1608: */
1609: @Override
1610: public void setText(String text, String charset)
1611: throws MessagingException {
1612: MimeBodyPart.setText(this, text, charset, "plain");
1613: }
1614:
1615: /**
1616: * Convenience method that sets the given String as this part's
1617: * content, with a primary MIME type of "text" and the specified
1618: * MIME subtype. The given Unicode string will be charset-encoded
1619: * using the specified charset. The charset is also used to set
1620: * the "charset" parameter.
1621: *
1622: * @param text the text content to set
1623: * @param charset the charset to use for the text
1624: * @param subtype the MIME subtype to use (e.g., "html")
1625: * @throws MessagingException if an error occurs
1626: * @since JavaMail 1.4
1627: */
1628: @Override
1629: public void setText(String text, String charset, String subtype)
1630: throws MessagingException {
1631: MimeBodyPart.setText(this, text, charset, subtype);
1632: }
1633:
1634: /**
1635: * This method sets the Message's content to a Multipart object.
1636: *
1637: * @param mp The multipart object that is the Message's content
1638: * @throws IllegalWriteException if the underlying
1639: * implementation does not support modification of
1640: * existing values
1641: * @throws IllegalStateException if this message is
1642: * obtained from a READ_ONLY folder.
1643: * @throws MessagingException for other failures
1644: */
1645: @Override
1646: public void setContent(Multipart mp) throws MessagingException {
1647: setDataHandler(new DataHandler(mp, mp.getContentType()));
1648: mp.setParent(this);
1649: }
1650:
1651: /**
1652: * Get a new Message suitable for a reply to this message.
1653: * The new Message will have its attributes and headers
1654: * set up appropriately. Note that this new message object
1655: * will be empty, i.e., it will <strong>not</strong> have a "content".
1656: * These will have to be suitably filled in by the client. <p>
1657: *
1658: * If <code>replyToAll</code> is set, the new Message will be addressed
1659: * to all recipients of this message. Otherwise, the reply will be
1660: * addressed to only the sender of this message (using the value
1661: * of the <code>getReplyTo</code> method). <p>
1662: *
1663: * The "Subject" field is filled in with the original subject
1664: * prefixed with "Re:" (unless it already starts with "Re:").
1665: * The "In-Reply-To" header is set in the new message if this
1666: * message has a "Message-Id" header. The <code>ANSWERED</code>
1667: * flag is set in this message.
1668: *
1669: * The current implementation also sets the "References" header
1670: * in the new message to include the contents of the "References"
1671: * header (or, if missing, the "In-Reply-To" header) in this message,
1672: * plus the contents of the "Message-Id" header of this message,
1673: * as described in RFC 2822.
1674: *
1675: * @param replyToAll reply should be sent to all recipients
1676: * of this message
1677: * @return the reply Message
1678: * @throws MessagingException for failures
1679: */
1680: @Override
1681: public Message reply(boolean replyToAll) throws MessagingException {
1682: return reply(replyToAll, true);
1683: }
1684:
1685: /**
1686: * Get a new Message suitable for a reply to this message.
1687: * The new Message will have its attributes and headers
1688: * set up appropriately. Note that this new message object
1689: * will be empty, i.e., it will <strong>not</strong> have a "content".
1690: * These will have to be suitably filled in by the client. <p>
1691: *
1692: * If <code>replyToAll</code> is set, the new Message will be addressed
1693: * to all recipients of this message. Otherwise, the reply will be
1694: * addressed to only the sender of this message (using the value
1695: * of the <code>getReplyTo</code> method). <p>
1696: *
1697: * If <code>setAnswered</code> is set, the
1698: * {@link jakarta.mail.Flags.Flag#ANSWERED ANSWERED} flag is set
1699: * in this message. <p>
1700: *
1701: * The "Subject" field is filled in with the original subject
1702: * prefixed with "Re:" (unless it already starts with "Re:").
1703: * The "In-Reply-To" header is set in the new message if this
1704: * message has a "Message-Id" header.
1705: *
1706: * The current implementation also sets the "References" header
1707: * in the new message to include the contents of the "References"
1708: * header (or, if missing, the "In-Reply-To" header) in this message,
1709: * plus the contents of the "Message-Id" header of this message,
1710: * as described in RFC 2822.
1711: *
1712: * @param replyToAll reply should be sent to all recipients
1713: * of this message
1714: * @param setAnswered set the ANSWERED flag in this message?
1715: * @return the reply Message
1716: * @throws MessagingException for failures
1717: * @since JavaMail 1.5
1718: */
1719: public Message reply(boolean replyToAll, boolean setAnswered)
1720: throws MessagingException {
1721: MimeMessage reply = createMimeMessage(session);
1722: /*
1723: * Have to manipulate the raw Subject header so that we don't lose
1724: * any encoding information. This is safe because "Re:" isn't
1725: * internationalized and (generally) isn't encoded. If the entire
1726: * Subject header is encoded, prefixing it with "Re: " still leaves
1727: * a valid and correct encoded header.
1728: */
1729: String subject = getHeader("Subject", null);
1730: if (subject != null) {
1731: if (!subject.regionMatches(true, 0, "Re: ", 0, 4))
1732: subject = "Re: " + subject;
1733: reply.setHeader("Subject", subject);
1734: }
1735: Address[] a = getReplyTo();
1736: reply.setRecipients(Message.RecipientType.TO, a);
1737: if (replyToAll) {
1738: List<Address> v = new ArrayList<>();
1739: // add my own address to list
1740: InternetAddress me = InternetAddress.getLocalAddress(session);
1741: if (me != null)
1742: v.add(me);
1743: // add any alternate names I'm known by
1744: String alternates = null;
1745: if (session != null)
1746: alternates = session.getProperty("mail.alternates");
1747: if (alternates != null)
1748: eliminateDuplicates(v,
1749: InternetAddress.parse(alternates, false));
1750: // should we Cc all other original recipients?
1751: boolean replyallcc = false;
1752: if (session != null)
1753: replyallcc = MimeUtility.getBooleanProperty(
1754: session.getProperties(),
1755: "mail.replyallcc", false);
1756: // add the recipients from the To field so far
1757: eliminateDuplicates(v, a);
1758: a = getRecipients(Message.RecipientType.TO);
1759: a = eliminateDuplicates(v, a);
1760: if (a != null && a.length > 0) {
1761: if (replyallcc)
1762: reply.addRecipients(Message.RecipientType.CC, a);
1763: else
1764: reply.addRecipients(Message.RecipientType.TO, a);
1765: }
1766: a = getRecipients(Message.RecipientType.CC);
1767: a = eliminateDuplicates(v, a);
1768: if (a != null && a.length > 0)
1769: reply.addRecipients(Message.RecipientType.CC, a);
1770: // don't eliminate duplicate newsgroups
1771: a = getRecipients(RecipientType.NEWSGROUPS);
1772: if (a != null && a.length > 0)
1773: reply.setRecipients(RecipientType.NEWSGROUPS, a);
1774: }
1775:
1776: String msgId = getHeader("Message-Id", null);
1777: if (msgId != null)
1778: reply.setHeader("In-Reply-To", msgId);
1779:
1780: /*
1781: * Set the References header as described in RFC 2822:
1782: *
1783: * The "References:" field will contain the contents of the parent's
1784: * "References:" field (if any) followed by the contents of the parent's
1785: * "Message-ID:" field (if any). If the parent message does not contain
1786: * a "References:" field but does have an "In-Reply-To:" field
1787: * containing a single message identifier, then the "References:" field
1788: * will contain the contents of the parent's "In-Reply-To:" field
1789: * followed by the contents of the parent's "Message-ID:" field (if
1790: * any). If the parent has none of the "References:", "In-Reply-To:",
1791: * or "Message-ID:" fields, then the new message will have no
1792: * "References:" field.
1793: */
1794: String refs = getHeader("References", " ");
1795: if (refs == null) {
1796: // XXX - should only use if it contains a single message identifier
1797: refs = getHeader("In-Reply-To", " ");
1798: }
1799: if (msgId != null) {
1800: if (refs != null)
1801: refs = MimeUtility.unfold(refs) + " " + msgId;
1802: else
1803: refs = msgId;
1804: }
1805: if (refs != null)
1806: reply.setHeader("References", MimeUtility.fold(12, refs));
1807:
1808: if (setAnswered) {
1809: try {
1810: setFlags(answeredFlag, true);
1811: } catch (MessagingException mex) {
1812: // ignore it
1813: }
1814: }
1815: return reply;
1816: }
1817:
1818: // used above in reply()
1819: private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);
1820:
1821: /**
1822: * Check addrs for any duplicates that may already be in v.
1823: * Return a new array without the duplicates. Add any new
1824: * addresses to v. Note that the input array may be modified.
1825: */
1826: private Address[] eliminateDuplicates(List<Address> v, Address[] addrs) {
1827: if (addrs == null)
1828: return null;
1829: int gone = 0;
1830: for (int i = 0; i < addrs.length; i++) {
1831: boolean found = false;
1832: // search the list for this address
1833: for (int j = 0; j < v.size(); j++) {
1834: if (v.get(j).equals(addrs[i])) {
1835: // found it; count it and remove it from the input array
1836: found = true;
1837: gone++;
1838: addrs[i] = null;
1839: break;
1840: }
1841: }
1842: if (!found)
1843: v.add(addrs[i]); // add new address to list
1844: }
1845: // if we found any duplicates, squish the array
1846: if (gone != 0) {
1847: Address[] a;
1848: // new array should be same type as original array
1849: // XXX - there must be a better way, perhaps reflection?
1850: if (addrs instanceof InternetAddress[])
1851: a = new InternetAddress[addrs.length - gone];
1852: else
1853: a = new Address[addrs.length - gone];
1854: for (int i = 0, j = 0; i < addrs.length; i++)
1855: if (addrs[i] != null)
1856: a[j++] = addrs[i];
1857: addrs = a;
1858: }
1859: return addrs;
1860: }
1861:
1862: /**
1863: * Output the message as an RFC 822 format stream. <p>
1864: *
1865: * Note that, depending on how the messag was constructed, it may
1866: * use a variety of line termination conventions. Generally the
1867: * output should be sent through an appropriate FilterOutputStream
1868: * that converts the line terminators to the desired form, either
1869: * CRLF for MIME compatibility and for use in Internet protocols,
1870: * or the local platform's line terminator for storage in a local
1871: * text file. <p>
1872: *
1873: * This implementation calls the <code>writeTo(OutputStream,
1874: * String[])</code> method with a null ignore list.
1875: *
1876: * @throws IOException if an error occurs writing to the stream
1877: * or if an error is generated by the
1878: * jakarta.activation layer.
1879: * @throws MessagingException for other failures
1880: * @see jakarta.activation.DataHandler#writeTo
1881: */
1882: @Override
1883: public void writeTo(OutputStream os)
1884: throws IOException, MessagingException {
1885: writeTo(os, null);
1886: }
1887:
1888: /**
1889: * Output the message as an RFC 822 format stream, without
1890: * specified headers. If the <code>saved</code> flag is not set,
1891: * the <code>saveChanges</code> method is called.
1892: * If the <code>modified</code> flag is not
1893: * set and the <code>content</code> array is not null, the
1894: * <code>content</code> array is written directly, after
1895: * writing the appropriate message headers.
1896: *
1897: * @param os the stream to write to
1898: * @param ignoreList the headers to not include in the output
1899: * @throws IOException if an error occurs writing to the stream
1900: * or if an error is generated by the
1901: * jakarta.activation layer.
1902: * @throws jakarta.mail.MessagingException for other failures
1903: * @see jakarta.activation.DataHandler#writeTo
1904: */
1905: public void writeTo(OutputStream os, String[] ignoreList)
1906: throws IOException, MessagingException {
1907: if (!saved)
1908: saveChanges();
1909:
1910: if (modified) {
1911: MimeBodyPart.writeTo(this, os, ignoreList);
1912: return;
1913: }
1914:
1915: // Else, the content is untouched, so we can just output it
1916: // First, write out the header
1917: Enumeration<String> hdrLines = getNonMatchingHeaderLines(ignoreList);
1918: LineOutputStream los = session.getStreamProvider().outputLineStream(os, allowutf8);
1919: while (hdrLines.hasMoreElements())
1920: los.writeln(hdrLines.nextElement());
1921:
1922: // The CRLF separator between header and content
1923: los.writeln();
1924:
1925: // Finally, the content.
1926: if (content == null) {
1927: // call getContentStream to give subclass a chance to
1928: // provide the data on demand
1929: InputStream is = null;
1930: byte[] buf = new byte[8192];
1931: try {
1932: is = getContentStream();
1933: // now copy the data to the output stream
1934: int len;
1935: while ((len = is.read(buf)) > 0)
1936: os.write(buf, 0, len);
1937: } finally {
1938: if (is != null)
1939: is.close();
1940: buf = null;
1941: }
1942: } else {
1943: os.write(content);
1944: }
1945: os.flush();
1946: }
1947:
1948: /**
1949: * Get all the headers for this header_name. Note that certain
1950: * headers may be encoded as per RFC 2047 if they contain
1951: * non US-ASCII characters and these should be decoded. <p>
1952: *
1953: * This implementation obtains the headers from the
1954: * <code>headers</code> InternetHeaders object.
1955: *
1956: * @param name name of header
1957: * @return array of headers
1958: * @throws MessagingException for failures
1959: * @see jakarta.mail.internet.MimeUtility
1960: */
1961: @Override
1962: public String[] getHeader(String name)
1963: throws MessagingException {
1964: return headers.getHeader(name);
1965: }
1966:
1967: /**
1968: * Get all the headers for this header name, returned as a single
1969: * String, with headers separated by the delimiter. If the
1970: * delimiter is <code>null</code>, only the first header is
1971: * returned.
1972: *
1973: * @param name the name of this header
1974: * @param delimiter separator between values
1975: * @return the value fields for all headers with
1976: * this name
1977: * @throws MessagingException for failures
1978: */
1979: @Override
1980: public String getHeader(String name, String delimiter)
1981: throws MessagingException {
1982: return headers.getHeader(name, delimiter);
1983: }
1984:
1985: /**
1986: * Set the value for this header_name. Replaces all existing
1987: * header values with this new value. Note that RFC 822 headers
1988: * must contain only US-ASCII characters, so a header that
1989: * contains non US-ASCII characters must have been encoded by the
1990: * caller as per the rules of RFC 2047.
1991: *
1992: * @param name header name
1993: * @param value header value
1994: * @throws IllegalWriteException if the underlying
1995: * implementation does not support modification
1996: * @throws IllegalStateException if this message is
1997: * obtained from a READ_ONLY folder.
1998: * @throws MessagingException for other failures
1999: * @see jakarta.mail.internet.MimeUtility
2000: */
2001: @Override
2002: public void setHeader(String name, String value)
2003: throws MessagingException {
2004: headers.setHeader(name, value);
2005: }
2006:
2007: /**
2008: * Add this value to the existing values for this header_name.
2009: * Note that RFC 822 headers must contain only US-ASCII
2010: * characters, so a header that contains non US-ASCII characters
2011: * must have been encoded as per the rules of RFC 2047.
2012: *
2013: * @param name header name
2014: * @param value header value
2015: * @throws IllegalWriteException if the underlying
2016: * implementation does not support modification
2017: * @throws IllegalStateException if this message is
2018: * obtained from a READ_ONLY folder.
2019: * @throws MessagingException for other failures
2020: * @see jakarta.mail.internet.MimeUtility
2021: */
2022: @Override
2023: public void addHeader(String name, String value)
2024: throws MessagingException {
2025: headers.addHeader(name, value);
2026: }
2027:
2028: /**
2029: * Remove all headers with this name.
2030: *
2031: * @throws IllegalWriteException if the underlying
2032: * implementation does not support modification
2033: * @throws IllegalStateException if this message is
2034: * obtained from a READ_ONLY folder.
2035: * @throws MessagingException for other failures
2036: */
2037: @Override
2038: public void removeHeader(String name)
2039: throws MessagingException {
2040: headers.removeHeader(name);
2041: }
2042:
2043: /**
2044: * Return all the headers from this Message as an enumeration
2045: * of Header objects. <p>
2046: *
2047: * Note that certain headers may be encoded as per RFC 2047
2048: * if they contain non US-ASCII characters and these should
2049: * be decoded. <p>
2050: *
2051: * This implementation obtains the headers from the
2052: * <code>headers</code> InternetHeaders object.
2053: *
2054: * @return array of header objects
2055: * @throws MessagingException for failures
2056: * @see jakarta.mail.internet.MimeUtility
2057: */
2058: @Override
2059: public Enumeration<Header> getAllHeaders() throws MessagingException {
2060: return headers.getAllHeaders();
2061: }
2062:
2063: /**
2064: * Return matching headers from this Message as an Enumeration of
2065: * Header objects. This implementation obtains the headers from
2066: * the <code>headers</code> InternetHeaders object.
2067: *
2068: * @throws MessagingException for failures
2069: */
2070: @Override
2071: public Enumeration<Header> getMatchingHeaders(String[] names)
2072: throws MessagingException {
2073: return headers.getMatchingHeaders(names);
2074: }
2075:
2076: /**
2077: * Return non-matching headers from this Message as an
2078: * Enumeration of Header objects. This implementation
2079: * obtains the header from the <code>headers</code> InternetHeaders object.
2080: *
2081: * @throws MessagingException for failures
2082: */
2083: @Override
2084: public Enumeration<Header> getNonMatchingHeaders(String[] names)
2085: throws MessagingException {
2086: return headers.getNonMatchingHeaders(names);
2087: }
2088:
2089: /**
2090: * Add a raw RFC 822 header-line.
2091: *
2092: * @throws IllegalWriteException if the underlying
2093: * implementation does not support modification
2094: * @throws IllegalStateException if this message is
2095: * obtained from a READ_ONLY folder.
2096: * @throws MessagingException for other failures
2097: */
2098: @Override
2099: public void addHeaderLine(String line) throws MessagingException {
2100: headers.addHeaderLine(line);
2101: }
2102:
2103: /**
2104: * Get all header lines as an Enumeration of Strings. A Header
2105: * line is a raw RFC 822 header-line, containing both the "name"
2106: * and "value" field.
2107: *
2108: * @throws MessagingException for failures
2109: */
2110: @Override
2111: public Enumeration<String> getAllHeaderLines() throws MessagingException {
2112: return headers.getAllHeaderLines();
2113: }
2114:
2115: /**
2116: * Get matching header lines as an Enumeration of Strings.
2117: * A Header line is a raw RFC 822 header-line, containing both
2118: * the "name" and "value" field.
2119: *
2120: * @throws MessagingException for failures
2121: */
2122: @Override
2123: public Enumeration<String> getMatchingHeaderLines(String[] names)
2124: throws MessagingException {
2125: return headers.getMatchingHeaderLines(names);
2126: }
2127:
2128: /**
2129: * Get non-matching header lines as an Enumeration of Strings.
2130: * A Header line is a raw RFC 822 header-line, containing both
2131: * the "name" and "value" field.
2132: *
2133: * @throws MessagingException for failures
2134: */
2135: @Override
2136: public Enumeration<String> getNonMatchingHeaderLines(String[] names)
2137: throws MessagingException {
2138: return headers.getNonMatchingHeaderLines(names);
2139: }
2140:
2141: /**
2142: * Return a <code>Flags</code> object containing the flags for
2143: * this message. <p>
2144: *
2145: * Note that a clone of the internal Flags object is returned, so
2146: * modifying the returned Flags object will not affect the flags
2147: * of this message.
2148: *
2149: * @return Flags object containing the flags for this message
2150: * @throws MessagingException for failures
2151: * @see jakarta.mail.Flags
2152: */
2153: @Override
2154: public synchronized Flags getFlags() throws MessagingException {
2155: return (Flags) flags.clone();
2156: }
2157:
2158: /**
2159: * Check whether the flag specified in the <code>flag</code>
2160: * argument is set in this message. <p>
2161: *
2162: * This implementation checks this message's internal
2163: * <code>flags</code> object.
2164: *
2165: * @param flag the flag
2166: * @return value of the specified flag for this message
2167: * @throws MessagingException for failures
2168: * @see jakarta.mail.Flags.Flag#ANSWERED
2169: * @see jakarta.mail.Flags.Flag#DELETED
2170: * @see jakarta.mail.Flags.Flag#DRAFT
2171: * @see jakarta.mail.Flags.Flag#FLAGGED
2172: * @see jakarta.mail.Flags.Flag#RECENT
2173: * @see jakarta.mail.Flags.Flag#SEEN
2174: * @see jakarta.mail.Flags.Flag
2175: */
2176: @Override
2177: public synchronized boolean isSet(Flags.Flag flag)
2178: throws MessagingException {
2179: return (flags.contains(flag));
2180: }
2181:
2182: /**
2183: * Set the flags for this message. <p>
2184: *
2185: * This implementation modifies the <code>flags</code> field.
2186: *
2187: * @throws IllegalWriteException if the underlying
2188: * implementation does not support modification
2189: * @throws IllegalStateException if this message is
2190: * obtained from a READ_ONLY folder.
2191: * @throws MessagingException for other failures
2192: */
2193: @Override
2194: public synchronized void setFlags(Flags flag, boolean set)
2195: throws MessagingException {
2196: if (set)
2197: flags.add(flag);
2198: else
2199: flags.remove(flag);
2200: }
2201:
2202: /**
2203: * Updates the appropriate header fields of this message to be
2204: * consistent with the message's contents. If this message is
2205: * contained in a Folder, any changes made to this message are
2206: * committed to the containing folder. <p>
2207: *
2208: * If any part of a message's headers or contents are changed,
2209: * <code>saveChanges</code> must be called to ensure that those
2210: * changes are permanent. Otherwise, any such modifications may or
2211: * may not be saved, depending on the folder implementation. <p>
2212: *
2213: * Messages obtained from folders opened READ_ONLY should not be
2214: * modified and saveChanges should not be called on such messages. <p>
2215: *
2216: * This method sets the <code>modified</code> flag to true, the
2217: * <code>save</code> flag to true, and then calls the
2218: * <code>updateHeaders</code> method.
2219: *
2220: * @throws IllegalWriteException if the underlying
2221: * implementation does not support modification
2222: * @throws IllegalStateException if this message is
2223: * obtained from a READ_ONLY folder.
2224: * @throws MessagingException for other failures
2225: */
2226: @Override
2227: public void saveChanges() throws MessagingException {
2228: modified = true;
2229: saved = true;
2230: updateHeaders();
2231: }
2232:
2233: /**
2234: * Update the Message-ID header. This method is called
2235: * by the <code>updateHeaders</code> and allows a subclass
2236: * to override only the algorithm for choosing a Message-ID.
2237: *
2238: * @throws MessagingException for failures
2239: * @since JavaMail 1.4
2240: */
2241: protected void updateMessageID() throws MessagingException {
2242: setHeader("Message-ID",
2243: "<" + UniqueValue.getUniqueMessageIDValue(session) + ">");
2244:
2245: }
2246:
2247: /**
2248: * Called by the <code>saveChanges</code> method to actually
2249: * update the MIME headers. The implementation here sets the
2250: * <code>Content-Transfer-Encoding</code> header (if needed
2251: * and not already set), the <code>Date</code> header (if
2252: * not already set), the <code>MIME-Version</code> header
2253: * and the <code>Message-ID</code> header. Also, if the content
2254: * of this message is a <code>MimeMultipart</code>, its
2255: * <code>updateHeaders</code> method is called. <p>
2256: *
2257: * If the {@link #cachedContent} field is not null (that is,
2258: * it references a Multipart or Message object), then
2259: * that object is used to set a new DataHandler, any
2260: * stream data used to create this object is discarded,
2261: * and the {@link #cachedContent} field is cleared.
2262: *
2263: * @throws IllegalWriteException if the underlying
2264: * implementation does not support modification
2265: * @throws IllegalStateException if this message is
2266: * obtained from a READ_ONLY folder.
2267: * @throws MessagingException for other failures
2268: */
2269: protected synchronized void updateHeaders() throws MessagingException {
2270: MimeBodyPart.updateHeaders(this);
2271: setHeader("MIME-Version", "1.0");
2272: if (getHeader("Date") == null)
2273: setSentDate(new Date());
2274: updateMessageID();
2275:
2276: if (cachedContent != null) {
2277: dh = new DataHandler(cachedContent, getContentType());
2278: cachedContent = null;
2279: content = null;
2280: if (contentStream != null) {
2281: try {
2282: contentStream.close();
2283: } catch (IOException ioex) {
2284: } // nothing to do
2285: }
2286: contentStream = null;
2287: }
2288: }
2289:
2290: /**
2291: * Create and return an InternetHeaders object that loads the
2292: * headers from the given InputStream. Subclasses can override
2293: * this method to return a subclass of InternetHeaders, if
2294: * necessary. This implementation simply constructs and returns
2295: * an InternetHeaders object.
2296: *
2297: * @param is the InputStream to read the headers from
2298: * @return an InternetHeaders object
2299: * @throws MessagingException for failures
2300: * @since JavaMail 1.2
2301: */
2302: protected InternetHeaders createInternetHeaders(InputStream is)
2303: throws MessagingException {
2304: return new InternetHeaders(is, allowutf8);
2305: }
2306:
2307: /**
2308: * Create and return a MimeMessage object. The reply method
2309: * uses this method to create the MimeMessage object that it
2310: * will return. Subclasses can override this method to return
2311: * a subclass of MimeMessage. This implementation simply constructs
2312: * and returns a MimeMessage object using the supplied Session.
2313: *
2314: * @param session the Session to use for the new message
2315: * @return the new MimeMessage object
2316: * @throws MessagingException for failures
2317: * @since JavaMail 1.4
2318: */
2319: protected MimeMessage createMimeMessage(Session session)
2320: throws MessagingException {
2321: return new MimeMessage(session);
2322: }
2323: }