Skip to content

Package: MimeMessage

MimeMessage

nameinstructionbranchcomplexitylinemethod
MimeMessage(Folder, InputStream, int)
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
MimeMessage(Folder, InternetHeaders, byte[], int)
M: 13 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
MimeMessage(Folder, int)
M: 27 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
MimeMessage(MimeMessage)
M: 73 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 22 C: 0
0%
M: 1 C: 0
0%
MimeMessage(Session)
M: 31 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
MimeMessage(Session, InputStream)
M: 29 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
addAddressHeader(String, Address[])
M: 66 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
addFrom(Address[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
addHeader(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
addHeaderLine(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
addRecipients(Message.RecipientType, Address[])
M: 20 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
addRecipients(Message.RecipientType, String)
M: 21 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
createInternetHeaders(InputStream)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
createMimeMessage(Session)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
eliminateDuplicates(List, Address[])
M: 92 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 22 C: 0
0%
M: 1 C: 0
0%
getAddressHeader(String)
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getAllHeaderLines()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getAllHeaders()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getAllRecipients()
M: 39 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
getContent()
M: 67 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 16 C: 0
0%
M: 1 C: 0
0%
getContentID()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentLanguage()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentMD5()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentStream()
M: 24 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
getContentType()
M: 15 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
getDataHandler()
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
getDescription()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getDisposition()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getEncoding()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getFileName()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getFlags()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getFrom()
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getHeader(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getHeader(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getHeaderName(Message.RecipientType)
M: 31 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
getInputStream()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getLineCount()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getMatchingHeaderLines(String[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getMatchingHeaders(String[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getMessageID()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getNonMatchingHeaderLines(String[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getNonMatchingHeaders(String[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getRawInputStream()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getReceivedDate()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getRecipients(Message.RecipientType)
M: 21 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getReplyTo()
M: 14 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getSender()
M: 15 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getSentDate()
M: 22 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
getSize()
M: 22 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
getSubject()
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
initStrict()
M: 20 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
isMimeType(String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isSet(Flags.Flag)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
parse(InputStream)
M: 49 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
provider()
M: 34 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
removeHeader(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
reply(boolean)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
reply(boolean, boolean)
M: 208 C: 0
0%
M: 40 C: 0
0%
M: 21 C: 0
0%
M: 54 C: 0
0%
M: 1 C: 0
0%
saveChanges()
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setAddressHeader(String, Address[])
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
setContent(Multipart)
M: 12 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
setContent(Object, String)
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setContentID(String)
M: 11 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setContentLanguage(String[])
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setContentMD5(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setDataHandler(DataHandler)
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setDescription(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setDescription(String, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setDisposition(String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setFileName(String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setFlags(Flags, boolean)
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setFrom()
M: 26 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
setFrom(Address)
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setFrom(String)
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setHeader(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setRecipients(Message.RecipientType, Address[])
M: 25 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
setRecipients(Message.RecipientType, String)
M: 29 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
setReplyTo(Address[])
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setSender(Address)
M: 16 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
setSentDate(Date)
M: 20 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
setSubject(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setSubject(String, String)
M: 24 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
setText(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setText(String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
setText(String, String, String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
updateHeaders()
M: 47 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
updateMessageID()
M: 16 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
writeTo(OutputStream)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
writeTo(OutputStream, String[])
M: 72 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 23 C: 0
0%
M: 1 C: 0
0%

Coverage

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