Skip to content

Package: MimeBodyPart

MimeBodyPart

nameinstructionbranchcomplexitylinemethod
MimeBodyPart()
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
MimeBodyPart(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%
MimeBodyPart(InternetHeaders, byte[])
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%
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%
attachFile(File)
M: 19 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
attachFile(File, String, String)
M: 21 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
attachFile(String)
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
attachFile(String, String, String)
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 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%
getContent()
M: 65 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%
getContentLanguage(MimePart)
M: 53 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 16 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%
getDescription(MimePart)
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%
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%
getDisposition(MimePart)
M: 17 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 5 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%
getEncoding(MimePart)
M: 68 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 19 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%
getFileName(MimePart)
M: 59 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 19 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%
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%
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%
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%
invalidateContentHeaders(MimePart)
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
isMimeType(MimePart, String)
M: 33 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 9 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%
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%
restrictEncoding(MimePart, String)
M: 55 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 18 C: 0
0%
M: 1 C: 0
0%
saveFile(File)
M: 43 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 16 C: 0
0%
M: 1 C: 0
0%
saveFile(String)
M: 9 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 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(MimePart, String[])
M: 58 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 12 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(MimePart, String, String)
M: 24 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 9 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(MimePart, String)
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 9 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%
setEncoding(MimePart, 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%
setFileName(MimePart, String)
M: 110 C: 0
0%
M: 18 C: 0
0%
M: 10 C: 0
0%
M: 33 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%
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%
setText(MimePart, String, String, String)
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%
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: 29 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
updateHeaders()
M: 32 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
updateHeaders(MimePart)
M: 236 C: 0
0%
M: 54 C: 0
0%
M: 28 C: 0
0%
M: 71 C: 0
0%
M: 1 C: 0
0%
writeTo(MimePart, OutputStream, String[])
M: 99 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 32 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%

Coverage

1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package jakarta.mail.internet;
18:
19: import jakarta.activation.DataHandler;
20: import jakarta.activation.DataSource;
21: import jakarta.activation.FileDataSource;
22: import jakarta.mail.BodyPart;
23: import jakarta.mail.EncodingAware;
24: import jakarta.mail.FolderClosedException;
25: import jakarta.mail.Header;
26: import jakarta.mail.IllegalWriteException;
27: import jakarta.mail.Message;
28: import jakarta.mail.MessageRemovedException;
29: import jakarta.mail.MessagingException;
30: import jakarta.mail.Multipart;
31: import jakarta.mail.Part;
32: import jakarta.mail.util.LineOutputStream;
33: import jakarta.mail.util.StreamProvider;
34: import jakarta.mail.util.StreamProvider.EncoderTypes;
35:
36: import java.io.BufferedInputStream;
37: import java.io.BufferedOutputStream;
38: import java.io.ByteArrayInputStream;
39: import java.io.File;
40: import java.io.FileOutputStream;
41: import java.io.IOException;
42: import java.io.InputStream;
43: import java.io.OutputStream;
44: import java.io.UnsupportedEncodingException;
45: import java.util.ArrayList;
46: import java.util.Enumeration;
47: import java.util.HashMap;
48: import java.util.List;
49: import java.util.Map;
50:
51:
52: /**
53: * This class represents a MIME body part. It implements the
54: * <code>BodyPart</code> abstract class and the <code>MimePart</code>
55: * interface. MimeBodyParts are contained in <code>MimeMultipart</code>
56: * objects. <p>
57: *
58: * MimeBodyPart uses the <code>InternetHeaders</code> class to parse
59: * and store the headers of that body part.
60: *
61: * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
62: *
63: * RFC 822 header fields <strong>must</strong> contain only
64: * US-ASCII characters. MIME allows non ASCII characters to be present
65: * in certain portions of certain headers, by encoding those characters.
66: * RFC 2047 specifies the rules for doing this. The MimeUtility
67: * class provided in this package can be used to to achieve this.
68: * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
69: * <code>addHeaderLine</code> methods are responsible for enforcing
70: * the MIME requirements for the specified headers. In addition, these
71: * header fields must be folded (wrapped) before being sent if they
72: * exceed the line length limitation for the transport (1000 bytes for
73: * SMTP). Received headers may have been folded. The application is
74: * responsible for folding and unfolding headers as appropriate.
75: *
76: * @author John Mani
77: * @author Bill Shannon
78: * @author Kanwar Oberoi
79: * @see jakarta.mail.Part
80: * @see jakarta.mail.internet.MimePart
81: * @see jakarta.mail.internet.MimeUtility
82: */
83:
84: public class MimeBodyPart extends BodyPart implements MimePart {
85:
86: // Paranoia:
87: // allow this last minute change to be disabled if it causes problems
88: private static final boolean setDefaultTextCharset =
89: MimeUtility.getBooleanSystemProperty(
90: "mail.mime.setdefaulttextcharset", true);
91:
92: private static final boolean setContentTypeFileName =
93: MimeUtility.getBooleanSystemProperty(
94: "mail.mime.setcontenttypefilename", true);
95:
96: private static final boolean encodeFileName =
97: MimeUtility.getBooleanSystemProperty("mail.mime.encodefilename", false);
98: private static final boolean decodeFileName =
99: MimeUtility.getBooleanSystemProperty("mail.mime.decodefilename", false);
100: private static final boolean ignoreMultipartEncoding =
101: MimeUtility.getBooleanSystemProperty(
102: "mail.mime.ignoremultipartencoding", true);
103: private static final boolean allowutf8 =
104: MimeUtility.getBooleanSystemProperty("mail.mime.allowutf8", true);
105:
106: // Paranoia:
107: // allow this last minute change to be disabled if it causes problems
108: static final boolean cacheMultipart = // accessed by MimeMessage
109: MimeUtility.getBooleanSystemProperty("mail.mime.cachemultipart", true);
110:
111:
112: /**
113: * The DataHandler object representing this Part's content.
114: */
115: protected DataHandler dh;
116:
117: /**
118: * Byte array that holds the bytes of the content of this Part.
119: */
120: protected byte[] content;
121:
122: /**
123: * If the data for this body part was supplied by an
124: * InputStream that implements the SharedInputStream interface,
125: * <code>contentStream</code> is another such stream representing
126: * the content of this body part. In this case, <code>content</code>
127: * will be null.
128: *
129: * @since JavaMail 1.2
130: */
131: protected InputStream contentStream;
132:
133: /**
134: * The InternetHeaders object that stores all the headers
135: * of this body part.
136: */
137: protected InternetHeaders headers;
138:
139: /**
140: * If our content is a Multipart of Message object, we save it
141: * the first time it's created by parsing a stream so that changes
142: * to the contained objects will not be lost.
143: *
144: * If this field is not null, it's return by the {@link #getContent}
145: * method. The {@link #getContent} method sets this field if it
146: * would return a Multipart or MimeMessage object. This field is
147: * is cleared by the {@link #setDataHandler} method.
148: *
149: * @since JavaMail 1.5
150: */
151: protected Object cachedContent;
152:
153: /**
154: * An empty MimeBodyPart object is created.
155: * This body part maybe filled in by a client constructing a multipart
156: * message.
157: */
158: public MimeBodyPart() {
159: super();
160: headers = new InternetHeaders();
161: }
162:
163: /**
164: * Constructs a MimeBodyPart by reading and parsing the data from
165: * the specified input stream. The parser consumes data till the end
166: * of the given input stream. The input stream must start at the
167: * beginning of a valid MIME body part and must terminate at the end
168: * of that body part. <p>
169: *
170: * Note that the "boundary" string that delimits body parts must
171: * <strong>not</strong> be included in the input stream. The intention
172: * is that the MimeMultipart parser will extract each body part's bytes
173: * from a multipart stream and feed them into this constructor, without
174: * the delimiter strings.
175: *
176: * @param is the body part Input Stream
177: * @throws MessagingException for failures
178: */
179: public MimeBodyPart(InputStream is) throws MessagingException {
180:• if (!(is instanceof ByteArrayInputStream) &&
181: !(is instanceof BufferedInputStream) &&
182: !(is instanceof SharedInputStream))
183: is = new BufferedInputStream(is);
184:
185: headers = new InternetHeaders(is);
186:
187:• if (is instanceof SharedInputStream) {
188: SharedInputStream sis = (SharedInputStream) is;
189: contentStream = sis.newStream(sis.getPosition(), -1);
190: } else {
191: try {
192: content = MimeUtility.getBytes(is);
193: } catch (IOException ioex) {
194: throw new MessagingException("Error reading input stream", ioex);
195: }
196: }
197:
198: }
199:
200: /**
201: * Constructs a MimeBodyPart using the given header and
202: * content bytes. <p>
203: *
204: * Used by providers.
205: *
206: * @param headers The header of this part
207: * @param content bytes representing the body of this part.
208: * @throws MessagingException for failures
209: */
210: public MimeBodyPart(InternetHeaders headers, byte[] content)
211: throws MessagingException {
212: super();
213: this.headers = headers;
214: this.content = content;
215: }
216:
217: /**
218: * Return the size of the content of this body part in bytes.
219: * Return -1 if the size cannot be determined. <p>
220: *
221: * Note that this number may not be an exact measure of the
222: * content size and may or may not account for any transfer
223: * encoding of the content. <p>
224: *
225: * This implementation returns the size of the <code>content</code>
226: * array (if not null), or, if <code>contentStream</code> is not
227: * null, and the <code>available</code> method returns a positive
228: * number, it returns that number as the size. Otherwise, it returns
229: * -1.
230: *
231: * @return size in bytes, or -1 if not known
232: */
233: @Override
234: public int getSize() throws MessagingException {
235:• if (content != null)
236: return content.length;
237:• if (contentStream != null) {
238: try {
239: int size = contentStream.available();
240: // only believe the size if it's greate than zero, since zero
241: // is the default returned by the InputStream class itself
242:• if (size > 0)
243: return size;
244: } catch (IOException ex) {
245: // ignore it
246: }
247: }
248: return -1;
249: }
250:
251: /**
252: * Return the number of lines for the content of this Part.
253: * Return -1 if this number cannot be determined. <p>
254: *
255: * Note that this number may not be an exact measure of the
256: * content length and may or may not account for any transfer
257: * encoding of the content. <p>
258: *
259: * This implementation returns -1.
260: *
261: * @return number of lines, or -1 if not known
262: */
263: @Override
264: public int getLineCount() throws MessagingException {
265: return -1;
266: }
267:
268: /**
269: * Returns the value of the RFC 822 "Content-Type" header field.
270: * This represents the content type of the content of this
271: * body part. This value must not be null. If this field is
272: * unavailable, "text/plain" should be returned. <p>
273: *
274: * This implementation uses <code>getHeader(name)</code>
275: * to obtain the requisite header field.
276: *
277: * @return Content-Type of this body part
278: */
279: @Override
280: public String getContentType() throws MessagingException {
281: String s = getHeader("Content-Type", null);
282: s = MimeUtil.cleanContentType(this, s);
283:• if (s == null)
284: s = "text/plain";
285: return s;
286: }
287:
288: /**
289: * Is this Part of the specified MIME type? This method
290: * compares <strong>only the <code>primaryType</code> and
291: * <code>subType</code></strong>.
292: * The parameters of the content types are ignored. <p>
293: *
294: * For example, this method will return <code>true</code> when
295: * comparing a Part of content type <strong>"text/plain"</strong>
296: * with <strong>"text/plain; charset=foobar"</strong>. <p>
297: *
298: * If the <code>subType</code> of <code>mimeType</code> is the
299: * special character '*', then the subtype is ignored during the
300: * comparison.
301: *
302: * @throws MessagingException for failures
303: */
304: @Override
305: public boolean isMimeType(String mimeType) throws MessagingException {
306: return isMimeType(this, mimeType);
307: }
308:
309: /**
310: * Returns the disposition from the "Content-Disposition" header field.
311: * This represents the disposition of this part. The disposition
312: * describes how the part should be presented to the user. <p>
313: *
314: * If the Content-Disposition field is unavailable,
315: * null is returned. <p>
316: *
317: * This implementation uses <code>getHeader(name)</code>
318: * to obtain the requisite header field.
319: *
320: * @throws MessagingException for failures
321: * @see #headers
322: */
323: @Override
324: public String getDisposition() throws MessagingException {
325: return getDisposition(this);
326: }
327:
328: /**
329: * Set the disposition in the "Content-Disposition" header field
330: * of this body part. If the disposition is null, any existing
331: * "Content-Disposition" header field is removed.
332: *
333: * @throws IllegalWriteException if the underlying
334: * implementation does not support modification
335: * @throws IllegalStateException if this body part is
336: * obtained from a READ_ONLY folder.
337: * @throws MessagingException for other failures
338: */
339: @Override
340: public void setDisposition(String disposition) throws MessagingException {
341: setDisposition(this, disposition);
342: }
343:
344: /**
345: * Returns the content transfer encoding from the
346: * "Content-Transfer-Encoding" header
347: * field. Returns <code>null</code> if the header is unavailable
348: * or its value is absent. <p>
349: *
350: * This implementation uses <code>getHeader(name)</code>
351: * to obtain the requisite header field.
352: *
353: * @see #headers
354: */
355: @Override
356: public String getEncoding() throws MessagingException {
357: return getEncoding(this);
358: }
359:
360: /**
361: * Returns the value of the "Content-ID" header field. Returns
362: * <code>null</code> if the field is unavailable or its value is
363: * absent. <p>
364: *
365: * This implementation uses <code>getHeader(name)</code>
366: * to obtain the requisite header field.
367: */
368: @Override
369: public String getContentID() throws MessagingException {
370: return getHeader("Content-Id", null);
371: }
372:
373: /**
374: * Set the "Content-ID" header field of this body part.
375: * If the <code>cid</code> parameter is null, any existing
376: * "Content-ID" is removed.
377: *
378: * @param cid the Content-ID
379: * @throws IllegalWriteException if the underlying
380: * implementation does not support modification
381: * @throws IllegalStateException if this body part is
382: * obtained from a READ_ONLY folder.
383: * @throws MessagingException for other failures
384: * @since JavaMail 1.3
385: */
386: public void setContentID(String cid) throws MessagingException {
387:• if (cid == null)
388: removeHeader("Content-ID");
389: else
390: setHeader("Content-ID", cid);
391: }
392:
393: /**
394: * Return the value of the "Content-MD5" header field. Returns
395: * <code>null</code> if this field is unavailable or its value
396: * is absent. <p>
397: *
398: * This implementation uses <code>getHeader(name)</code>
399: * to obtain the requisite header field.
400: */
401: @Override
402: public String getContentMD5() throws MessagingException {
403: return getHeader("Content-MD5", null);
404: }
405:
406: /**
407: * Set the "Content-MD5" header field of this body part.
408: *
409: * @throws IllegalWriteException if the underlying
410: * implementation does not support modification
411: * @throws IllegalStateException if this body part is
412: * obtained from a READ_ONLY folder.
413: */
414: @Override
415: public void setContentMD5(String md5) throws MessagingException {
416: setHeader("Content-MD5", md5);
417: }
418:
419: /**
420: * Get the languages specified in the Content-Language header
421: * of this MimePart. The Content-Language header is defined by
422: * RFC 1766. Returns <code>null</code> if this header is not
423: * available or its value is absent. <p>
424: *
425: * This implementation uses <code>getHeader(name)</code>
426: * to obtain the requisite header field.
427: */
428: @Override
429: public String[] getContentLanguage() throws MessagingException {
430: return getContentLanguage(this);
431: }
432:
433: /**
434: * Set the Content-Language header of this MimePart. The
435: * Content-Language header is defined by RFC 1766.
436: *
437: * @param languages array of language tags
438: */
439: @Override
440: public void setContentLanguage(String[] languages)
441: throws MessagingException {
442: setContentLanguage(this, languages);
443: }
444:
445: /**
446: * Returns the "Content-Description" header field of this body part.
447: * This typically associates some descriptive information with
448: * this part. Returns null if this field is unavailable or its
449: * value is absent. <p>
450: *
451: * If the Content-Description field is encoded as per RFC 2047,
452: * it is decoded and converted into Unicode. If the decoding or
453: * conversion fails, the raw data is returned as is. <p>
454: *
455: * This implementation uses <code>getHeader(name)</code>
456: * to obtain the requisite header field.
457: *
458: * @return content description
459: */
460: @Override
461: public String getDescription() throws MessagingException {
462: return getDescription(this);
463: }
464:
465: /**
466: * Set the "Content-Description" header field for this body part.
467: * If the description parameter is <code>null</code>, then any
468: * existing "Content-Description" fields are removed. <p>
469: *
470: * If the description contains non US-ASCII characters, it will
471: * be encoded using the platform's default charset. If the
472: * description contains only US-ASCII characters, no encoding
473: * is done and it is used as is. <p>
474: *
475: * Note that if the charset encoding process fails, a
476: * MessagingException is thrown, and an UnsupportedEncodingException
477: * is included in the chain of nested exceptions within the
478: * MessagingException.
479: *
480: * @param description content description
481: * @throws MessagingException otherwise; an
482: * UnsupportedEncodingException may be included
483: * in the exception chain if the charset
484: * conversion fails.
485: * @throws IllegalWriteException if the underlying
486: * implementation does not support modification
487: * @throws IllegalStateException if this body part is
488: * obtained from a READ_ONLY folder.
489: */
490: @Override
491: public void setDescription(String description) throws MessagingException {
492: setDescription(description, null);
493: }
494:
495: /**
496: * Set the "Content-Description" header field for this body part.
497: * If the description parameter is <code>null</code>, then any
498: * existing "Content-Description" fields are removed. <p>
499: *
500: * If the description contains non US-ASCII characters, it will
501: * be encoded using the specified charset. If the description
502: * contains only US-ASCII characters, no encoding is done and
503: * it is used as is. <p>
504: *
505: * Note that if the charset encoding process fails, a
506: * MessagingException is thrown, and an UnsupportedEncodingException
507: * is included in the chain of nested exceptions within the
508: * MessagingException.
509: *
510: * @param description Description
511: * @param charset Charset for encoding
512: * @throws MessagingException otherwise; an
513: * UnsupportedEncodingException may be included
514: * in the exception chain if the charset
515: * conversion fails.
516: * @throws IllegalWriteException if the underlying
517: * implementation does not support modification
518: * @throws IllegalStateException if this body part is
519: * obtained from a READ_ONLY folder.
520: */
521: public void setDescription(String description, String charset)
522: throws MessagingException {
523: setDescription(this, description, charset);
524: }
525:
526: /**
527: * Get the filename associated with this body part. <p>
528: *
529: * Returns the value of the "filename" parameter from the
530: * "Content-Disposition" header field of this body part. If its
531: * not available, returns the value of the "name" parameter from
532: * the "Content-Type" header field of this body part.
533: * Returns <code>null</code> if both are absent. <p>
534: *
535: * If the <code>mail.mime.decodefilename</code> System property
536: * is set to true, the {@link MimeUtility#decodeText
537: * MimeUtility.decodeText} method will be used to decode the
538: * filename. While such encoding is not supported by the MIME
539: * spec, many mailers use this technique to support non-ASCII
540: * characters in filenames. The default value of this property
541: * is false.
542: *
543: * @return filename
544: */
545: @Override
546: public String getFileName() throws MessagingException {
547: return getFileName(this);
548: }
549:
550: /**
551: * Set the filename associated with this body part, if possible. <p>
552: *
553: * Sets the "filename" parameter of the "Content-Disposition"
554: * header field of this body part. For compatibility with older
555: * mailers, the "name" parameter of the "Content-Type" header is
556: * also set. <p>
557: *
558: * If the <code>mail.mime.encodefilename</code> System property
559: * is set to true, the {@link MimeUtility#encodeText
560: * MimeUtility.encodeText} method will be used to encode the
561: * filename. While such encoding is not supported by the MIME
562: * spec, many mailers use this technique to support non-ASCII
563: * characters in filenames. The default value of this property
564: * is false.
565: *
566: * @param filename the file name
567: * @throws IllegalWriteException if the underlying
568: * implementation does not support modification
569: * @throws IllegalStateException if this body part is
570: * obtained from a READ_ONLY folder.
571: * @throws MessagingException for other failures
572: */
573: @Override
574: public void setFileName(String filename) throws MessagingException {
575: setFileName(this, filename);
576: }
577:
578: /**
579: * Return a decoded input stream for this body part's "content". <p>
580: *
581: * This implementation obtains the input stream from the DataHandler.
582: * That is, it invokes getDataHandler().getInputStream();
583: *
584: * @return an InputStream
585: * @throws IOException this is typically thrown by the
586: * DataHandler. Refer to the documentation for
587: * jakarta.activation.DataHandler for more details.
588: * @throws MessagingException for other failures
589: * @see #getContentStream
590: * @see jakarta.activation.DataHandler#getInputStream
591: */
592: @Override
593: public InputStream getInputStream()
594: throws IOException, MessagingException {
595: return getDataHandler().getInputStream();
596: }
597:
598: /**
599: * Produce the raw bytes of the content. This method is used
600: * when creating a DataHandler object for the content. Subclasses
601: * that can provide a separate input stream for just the Part
602: * content might want to override this method.
603: *
604: * @return an InputStream containing the raw bytes
605: * @throws MessagingException for failures
606: * @see #content
607: * @see MimeMessage#getContentStream
608: */
609: protected InputStream getContentStream() throws MessagingException {
610:• if (contentStream != null)
611: return ((SharedInputStream) contentStream).newStream(0, -1);
612:• if (content != null)
613: return new ByteArrayInputStream(content);
614:
615: throw new MessagingException("No MimeBodyPart content");
616: }
617:
618: /**
619: * Return an InputStream to the raw data with any Content-Transfer-Encoding
620: * intact. This method is useful if the "Content-Transfer-Encoding"
621: * header is incorrect or corrupt, which would prevent the
622: * <code>getInputStream</code> method or <code>getContent</code> method
623: * from returning the correct data. In such a case the application may
624: * use this method and attempt to decode the raw data itself. <p>
625: *
626: * This implementation simply calls the <code>getContentStream</code>
627: * method.
628: *
629: * @return an InputStream containing the raw bytes
630: * @throws MessagingException for failures
631: * @see #getInputStream
632: * @see #getContentStream
633: * @since JavaMail 1.2
634: */
635: public InputStream getRawInputStream() throws MessagingException {
636: return getContentStream();
637: }
638:
639: /**
640: * Return a DataHandler for this body part's content. <p>
641: *
642: * The implementation provided here works just like the
643: * the implementation in MimeMessage.
644: *
645: * @see MimeMessage#getDataHandler
646: */
647: @Override
648: public DataHandler getDataHandler() throws MessagingException {
649:• if (dh == null)
650: dh = new MimePartDataHandler(this);
651: return dh;
652: }
653:
654: /**
655: * Return the content as a Java object. The type of the object
656: * returned is of course dependent on the content itself. For
657: * example, the native format of a text/plain content is usually
658: * a String object. The native format for a "multipart"
659: * content is always a Multipart subclass. For content types that are
660: * unknown to the DataHandler system, an input stream is returned
661: * as the content. <p>
662: *
663: * This implementation obtains the content from the DataHandler.
664: * That is, it invokes getDataHandler().getContent();
665: * If the content is a Multipart or Message object and was created by
666: * parsing a stream, the object is cached and returned in subsequent
667: * calls so that modifications to the content will not be lost.
668: *
669: * @return Object
670: * @throws IOException this is typically thrown by the
671: * DataHandler. Refer to the documentation for
672: * jakarta.activation.DataHandler for more details.
673: * @throws MessagingException for other failures
674: */
675: @Override
676: public Object getContent() throws IOException, MessagingException {
677:• if (cachedContent != null)
678: return cachedContent;
679: Object c;
680: try {
681: c = getDataHandler().getContent();
682: } catch (IOException e) {
683:• if (e.getCause() instanceof FolderClosedException) {
684: FolderClosedException fce = (FolderClosedException) e.getCause();
685: throw new FolderClosedException(fce.getFolder(), e.getMessage());
686:• } else if (e.getCause() instanceof MessagingException) {
687: throw new MessageRemovedException(e.getMessage());
688: } else {
689: throw e;
690: }
691: }
692:• if (cacheMultipart &&
693: (c instanceof Multipart || c instanceof Message) &&
694: (content != null || contentStream != null)) {
695: cachedContent = c;
696: /*
697: * We may abandon the input stream so make sure
698: * the MimeMultipart has consumed the stream.
699: */
700:• if (c instanceof MimeMultipart)
701: ((MimeMultipart) c).parse();
702: }
703: return c;
704: }
705:
706: /**
707: * This method provides the mechanism to set this body part's content.
708: * The given DataHandler object should wrap the actual content.
709: *
710: * @param dh The DataHandler for the content
711: * @throws IllegalWriteException if the underlying
712: * implementation does not support modification
713: * @throws IllegalStateException if this body part is
714: * obtained from a READ_ONLY folder.
715: */
716: @Override
717: public void setDataHandler(DataHandler dh)
718: throws MessagingException {
719: this.dh = dh;
720: cachedContent = null;
721: MimeBodyPart.invalidateContentHeaders(this);
722: }
723:
724: /**
725: * A convenience method for setting this body part's content. <p>
726: *
727: * The content is wrapped in a DataHandler object. Note that a
728: * DataContentHandler class for the specified type should be
729: * available to the Jakarta Mail implementation for this to work right.
730: * That is, to do <code>setContent(foobar, "application/x-foobar")</code>,
731: * a DataContentHandler for "application/x-foobar" should be installed.
732: * Refer to the Java Activation Framework for more information.
733: *
734: * @param o the content object
735: * @param type Mime type of the object
736: * @throws IllegalWriteException if the underlying
737: * implementation does not support modification of
738: * existing values
739: * @throws IllegalStateException if this body part is
740: * obtained from a READ_ONLY folder.
741: */
742: @Override
743: public void setContent(Object o, String type)
744: throws MessagingException {
745:• if (o instanceof Multipart) {
746: setContent((Multipart) o);
747: } else {
748: setDataHandler(new DataHandler(o, type));
749: }
750: }
751:
752: /**
753: * Convenience method that sets the given String as this
754: * part's content, with a MIME type of "text/plain". If the
755: * string contains non US-ASCII characters, it will be encoded
756: * using the platform's default charset. The charset is also
757: * used to set the "charset" parameter. <p>
758: *
759: * Note that there may be a performance penalty if
760: * <code>text</code> is large, since this method may have
761: * to scan all the characters to determine what charset to
762: * use. <p>
763: *
764: * If the charset is already known, use the
765: * <code>setText</code> method that takes the charset parameter.
766: *
767: * @param text the text content to set
768: * @throws MessagingException if an error occurs
769: * @see #setText(String text, String charset)
770: */
771: @Override
772: public void setText(String text) throws MessagingException {
773: setText(text, null);
774: }
775:
776: /**
777: * Convenience method that sets the given String as this part's
778: * content, with a MIME type of "text/plain" and the specified
779: * charset. The given Unicode string will be charset-encoded
780: * using the specified charset. The charset is also used to set
781: * the "charset" parameter.
782: *
783: * @param text the text content to set
784: * @param charset the charset to use for the text
785: * @throws MessagingException if an error occurs
786: */
787: @Override
788: public void setText(String text, String charset)
789: throws MessagingException {
790: setText(this, text, charset, "plain");
791: }
792:
793: /**
794: * Convenience method that sets the given String as this part's
795: * content, with a primary MIME type of "text" and the specified
796: * MIME subtype. The given Unicode string will be charset-encoded
797: * using the specified charset. The charset is also used to set
798: * the "charset" parameter.
799: *
800: * @param text the text content to set
801: * @param charset the charset to use for the text
802: * @param subtype the MIME subtype to use (e.g., "html")
803: * @throws MessagingException if an error occurs
804: * @since JavaMail 1.4
805: */
806: @Override
807: public void setText(String text, String charset, String subtype)
808: throws MessagingException {
809: setText(this, text, charset, subtype);
810: }
811:
812: /**
813: * This method sets the body part's content to a Multipart object.
814: *
815: * @param mp The multipart object that is the Message's content
816: * @throws IllegalWriteException if the underlying
817: * implementation does not support modification of
818: * existing values.
819: * @throws IllegalStateException if this body part is
820: * obtained from a READ_ONLY folder.
821: */
822: @Override
823: public void setContent(Multipart mp) throws MessagingException {
824: setDataHandler(new DataHandler(mp, mp.getContentType()));
825: mp.setParent(this);
826: }
827:
828: /**
829: * Use the specified file to provide the data for this part.
830: * The simple file name is used as the file name for this
831: * part and the data in the file is used as the data for this
832: * part. The encoding will be chosen appropriately for the
833: * file data. The disposition of this part is set to
834: * {@link Part#ATTACHMENT Part.ATTACHMENT}.
835: *
836: * @param file the File object to attach
837: * @throws IOException errors related to accessing the file
838: * @throws MessagingException message related errors
839: * @since JavaMail 1.4
840: */
841: public void attachFile(File file) throws IOException, MessagingException {
842: FileDataSource fds = new FileDataSource(file);
843: this.setDataHandler(new DataHandler(fds));
844: this.setFileName(fds.getName());
845: this.setDisposition(ATTACHMENT);
846: }
847:
848: /**
849: * Use the specified file to provide the data for this part.
850: * The simple file name is used as the file name for this
851: * part and the data in the file is used as the data for this
852: * part. The encoding will be chosen appropriately for the
853: * file data.
854: *
855: * @param file the name of the file to attach
856: * @throws IOException errors related to accessing the file
857: * @throws MessagingException message related errors
858: * @since JavaMail 1.4
859: */
860: public void attachFile(String file) throws IOException, MessagingException {
861: File f = new File(file);
862: attachFile(f);
863: }
864:
865: /**
866: * Use the specified file with the specified Content-Type and
867: * Content-Transfer-Encoding to provide the data for this part.
868: * If contentType or encoding are null, appropriate values will
869: * be chosen.
870: * The simple file name is used as the file name for this
871: * part and the data in the file is used as the data for this
872: * part. The disposition of this part is set to
873: * {@link Part#ATTACHMENT Part.ATTACHMENT}.
874: *
875: * @param file the File object to attach
876: * @param contentType the Content-Type, or null
877: * @param encoding the Content-Transfer-Encoding, or null
878: * @throws IOException errors related to accessing the file
879: * @throws MessagingException message related errors
880: * @since JavaMail 1.5
881: */
882: public void attachFile(File file, String contentType, String encoding)
883: throws IOException, MessagingException {
884: DataSource fds = new EncodedFileDataSource(file, contentType, encoding);
885: this.setDataHandler(new DataHandler(fds));
886: this.setFileName(fds.getName());
887: this.setDisposition(ATTACHMENT);
888: }
889:
890: /**
891: * Use the specified file with the specified Content-Type and
892: * Content-Transfer-Encoding to provide the data for this part.
893: * If contentType or encoding are null, appropriate values will
894: * be chosen.
895: * The simple file name is used as the file name for this
896: * part and the data in the file is used as the data for this
897: * part. The disposition of this part is set to
898: * {@link Part#ATTACHMENT Part.ATTACHMENT}.
899: *
900: * @param file the name of the file
901: * @param contentType the Content-Type, or null
902: * @param encoding the Content-Transfer-Encoding, or null
903: * @throws IOException errors related to accessing the file
904: * @throws MessagingException message related errors
905: * @since JavaMail 1.5
906: */
907: public void attachFile(String file, String contentType, String encoding)
908: throws IOException, MessagingException {
909: attachFile(new File(file), contentType, encoding);
910: }
911:
912: /**
913: * A FileDataSource class that allows us to specify the
914: * Content-Type and Content-Transfer-Encoding.
915: */
916: private static class EncodedFileDataSource extends FileDataSource
917: implements EncodingAware {
918: private String contentType;
919: private String encoding;
920:
921: public EncodedFileDataSource(File file, String contentType,
922: String encoding) {
923: super(file);
924: this.contentType = contentType;
925: this.encoding = encoding;
926: }
927:
928: // overrides DataSource.getContentType()
929: @Override
930: public String getContentType() {
931: return contentType != null ? contentType : super.getContentType();
932: }
933:
934: // implements EncodingAware.getEncoding()
935: @Override
936: public String getEncoding() {
937: return encoding;
938: }
939: }
940:
941: /**
942: * Save the contents of this part in the specified file. The content
943: * is decoded and saved, without any of the MIME headers.
944: *
945: * @param file the File object to write to
946: * @throws IOException errors related to accessing the file
947: * @throws MessagingException message related errors
948: * @since JavaMail 1.4
949: */
950: public void saveFile(File file) throws IOException, MessagingException {
951: OutputStream out = null;
952: InputStream in = null;
953: try {
954: out = new BufferedOutputStream(new FileOutputStream(file));
955: in = this.getInputStream();
956: byte[] buf = new byte[8192];
957: int len;
958:• while ((len = in.read(buf)) > 0)
959: out.write(buf, 0, len);
960: } finally {
961: // close streams, but don't mask original exception, if any
962: try {
963:• if (in != null)
964: in.close();
965: } catch (IOException ex) {
966: }
967: try {
968:• if (out != null)
969: out.close();
970: } catch (IOException ex) {
971: }
972: }
973: }
974:
975: /**
976: * Save the contents of this part in the specified file. The content
977: * is decoded and saved, without any of the MIME headers.
978: *
979: * @param file the name of the file to write to
980: * @throws IOException errors related to accessing the file
981: * @throws MessagingException message related errors
982: * @since JavaMail 1.4
983: */
984: public void saveFile(String file) throws IOException, MessagingException {
985: File f = new File(file);
986: saveFile(f);
987: }
988:
989: /**
990: * Output the body part as an RFC 822 format stream.
991: *
992: * @throws IOException if an error occurs writing to the
993: * stream or if an error is generated
994: * by the jakarta.activation layer.
995: * @throws MessagingException for other failures
996: * @see jakarta.activation.DataHandler#writeTo
997: */
998: @Override
999: public void writeTo(OutputStream os)
1000: throws IOException, MessagingException {
1001: writeTo(this, os, null);
1002: }
1003:
1004: /**
1005: * Get all the headers for this header_name. Note that certain
1006: * headers may be encoded as per RFC 2047 if they contain
1007: * non US-ASCII characters and these should be decoded.
1008: *
1009: * @param name name of header
1010: * @return array of headers
1011: * @see jakarta.mail.internet.MimeUtility
1012: */
1013: @Override
1014: public String[] getHeader(String name) throws MessagingException {
1015: return headers.getHeader(name);
1016: }
1017:
1018: /**
1019: * Get all the headers for this header name, returned as a single
1020: * String, with headers separated by the delimiter. If the
1021: * delimiter is <code>null</code>, only the first header is
1022: * returned.
1023: *
1024: * @param name the name of this header
1025: * @param delimiter delimiter between fields in returned string
1026: * @return the value fields for all headers with
1027: * this name
1028: * @throws MessagingException for failures
1029: */
1030: @Override
1031: public String getHeader(String name, String delimiter)
1032: throws MessagingException {
1033: return headers.getHeader(name, delimiter);
1034: }
1035:
1036: /**
1037: * Set the value for this header_name. Replaces all existing
1038: * header values with this new value. Note that RFC 822 headers
1039: * must contain only US-ASCII characters, so a header that
1040: * contains non US-ASCII characters must be encoded as per the
1041: * rules of RFC 2047.
1042: *
1043: * @param name header name
1044: * @param value header value
1045: * @see jakarta.mail.internet.MimeUtility
1046: */
1047: @Override
1048: public void setHeader(String name, String value)
1049: throws MessagingException {
1050: headers.setHeader(name, value);
1051: }
1052:
1053: /**
1054: * Add this value to the existing values for this header_name.
1055: * Note that RFC 822 headers must contain only US-ASCII
1056: * characters, so a header that contains non US-ASCII characters
1057: * must be encoded as per the rules of RFC 2047.
1058: *
1059: * @param name header name
1060: * @param value header value
1061: * @see jakarta.mail.internet.MimeUtility
1062: */
1063: @Override
1064: public void addHeader(String name, String value)
1065: throws MessagingException {
1066: headers.addHeader(name, value);
1067: }
1068:
1069: /**
1070: * Remove all headers with this name.
1071: */
1072: @Override
1073: public void removeHeader(String name) throws MessagingException {
1074: headers.removeHeader(name);
1075: }
1076:
1077: /**
1078: * Return all the headers from this Message as an Enumeration of
1079: * Header objects.
1080: */
1081: @Override
1082: public Enumeration<Header> getAllHeaders() throws MessagingException {
1083: return headers.getAllHeaders();
1084: }
1085:
1086: /**
1087: * Return matching headers from this Message as an Enumeration of
1088: * Header objects.
1089: */
1090: @Override
1091: public Enumeration<Header> getMatchingHeaders(String[] names)
1092: throws MessagingException {
1093: return headers.getMatchingHeaders(names);
1094: }
1095:
1096: /**
1097: * Return non-matching headers from this Message as an
1098: * Enumeration of Header objects.
1099: */
1100: @Override
1101: public Enumeration<Header> getNonMatchingHeaders(String[] names)
1102: throws MessagingException {
1103: return headers.getNonMatchingHeaders(names);
1104: }
1105:
1106: /**
1107: * Add a header line to this body part
1108: */
1109: @Override
1110: public void addHeaderLine(String line) throws MessagingException {
1111: headers.addHeaderLine(line);
1112: }
1113:
1114: /**
1115: * Get all header lines as an Enumeration of Strings. A Header
1116: * line is a raw RFC 822 header line, containing both the "name"
1117: * and "value" field.
1118: */
1119: @Override
1120: public Enumeration<String> getAllHeaderLines() throws MessagingException {
1121: return headers.getAllHeaderLines();
1122: }
1123:
1124: /**
1125: * Get matching header lines as an Enumeration of Strings.
1126: * A Header line is a raw RFC 822 header line, containing both
1127: * the "name" and "value" field.
1128: */
1129: @Override
1130: public Enumeration<String> getMatchingHeaderLines(String[] names)
1131: throws MessagingException {
1132: return headers.getMatchingHeaderLines(names);
1133: }
1134:
1135: /**
1136: * Get non-matching header lines as an Enumeration of Strings.
1137: * A Header line is a raw RFC 822 header line, containing both
1138: * the "name" and "value" field.
1139: */
1140: @Override
1141: public Enumeration<String> getNonMatchingHeaderLines(String[] names)
1142: throws MessagingException {
1143: return headers.getNonMatchingHeaderLines(names);
1144: }
1145:
1146: /**
1147: * Examine the content of this body part and update the appropriate
1148: * MIME headers. Typical headers that get set here are
1149: * <code>Content-Type</code> and <code>Content-Transfer-Encoding</code>.
1150: * Headers might need to be updated in two cases:
1151: *
1152: * <br>
1153: * - A message being crafted by a mail application will certainly
1154: * need to activate this method at some point to fill up its internal
1155: * headers.
1156: *
1157: * <br>
1158: * - A message read in from a Store will have obtained
1159: * all its headers from the store, and so doesn't need this.
1160: * However, if this message is editable and if any edits have
1161: * been made to either the content or message structure, we might
1162: * need to resync our headers.
1163: *
1164: * <br>
1165: * In both cases this method is typically called by the
1166: * <code>Message.saveChanges</code> method. <p>
1167: *
1168: * If the {@link #cachedContent} field is not null (that is,
1169: * it references a Multipart or Message object), then
1170: * that object is used to set a new DataHandler, any
1171: * stream data used to create this object is discarded,
1172: * and the {@link #cachedContent} field is cleared.
1173: *
1174: * @throws MessagingException for failures
1175: */
1176: protected void updateHeaders() throws MessagingException {
1177: updateHeaders(this);
1178: /*
1179: * If we've cached a Multipart or Message object then
1180: * we're now committed to using this instance of the
1181: * object and we discard any stream data used to create
1182: * this object.
1183: */
1184:• if (cachedContent != null) {
1185: dh = new DataHandler(cachedContent, getContentType());
1186: cachedContent = null;
1187: content = null;
1188:• if (contentStream != null) {
1189: try {
1190: contentStream.close();
1191: } catch (IOException ioex) {
1192: } // nothing to do
1193: }
1194: contentStream = null;
1195: }
1196: }
1197:
1198: /////////////////////////////////////////////////////////////
1199: // Package private convenience methods to share code among //
1200: // MimeMessage and MimeBodyPart //
1201: /////////////////////////////////////////////////////////////
1202:
1203: static boolean isMimeType(MimePart part, String mimeType)
1204: throws MessagingException {
1205: // XXX - lots of room for optimization here!
1206: String type = part.getContentType();
1207: try {
1208: return new ContentType(type).match(mimeType);
1209: } catch (ParseException ex) {
1210: // we only need the type and subtype so throw away the rest
1211: try {
1212: int i = type.indexOf(';');
1213:• if (i > 0)
1214: return new ContentType(type.substring(0, i)).match(mimeType);
1215: } catch (ParseException pex2) {
1216: }
1217: return type.equalsIgnoreCase(mimeType);
1218: }
1219: }
1220:
1221: static void setText(MimePart part, String text, String charset,
1222: String subtype) throws MessagingException {
1223:• if (charset == null) {
1224:• if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII)
1225: charset = MimeUtility.getDefaultMIMECharset();
1226: else
1227: charset = "us-ascii";
1228: }
1229: // XXX - should at least ensure that subtype is an atom
1230: part.setContent(text, "text/" + subtype + "; charset=" +
1231: MimeUtility.quote(charset, HeaderTokenizer.MIME));
1232: }
1233:
1234: static String getDisposition(MimePart part) throws MessagingException {
1235: String s = part.getHeader("Content-Disposition", null);
1236:
1237:• if (s == null)
1238: return null;
1239:
1240: ContentDisposition cd = new ContentDisposition(s);
1241: return cd.getDisposition();
1242: }
1243:
1244: static void setDisposition(MimePart part, String disposition)
1245: throws MessagingException {
1246:• if (disposition == null)
1247: part.removeHeader("Content-Disposition");
1248: else {
1249: String s = part.getHeader("Content-Disposition", null);
1250:• if (s != null) {
1251: /* A Content-Disposition header already exists ..
1252: *
1253: * Override disposition, but attempt to retain
1254: * existing disposition parameters
1255: */
1256: ContentDisposition cd = new ContentDisposition(s);
1257: cd.setDisposition(disposition);
1258: disposition = cd.toString();
1259: }
1260: part.setHeader("Content-Disposition", disposition);
1261: }
1262: }
1263:
1264: static String getDescription(MimePart part)
1265: throws MessagingException {
1266: String rawvalue = part.getHeader("Content-Description", null);
1267:
1268:• if (rawvalue == null)
1269: return null;
1270:
1271: try {
1272: return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
1273: } catch (UnsupportedEncodingException ex) {
1274: return rawvalue;
1275: }
1276: }
1277:
1278: static void
1279: setDescription(MimePart part, String description, String charset)
1280: throws MessagingException {
1281:• if (description == null) {
1282: part.removeHeader("Content-Description");
1283: return;
1284: }
1285:
1286: try {
1287: part.setHeader("Content-Description", MimeUtility.fold(21,
1288: MimeUtility.encodeText(description, charset, null)));
1289: } catch (UnsupportedEncodingException uex) {
1290: throw new MessagingException("Encoding error", uex);
1291: }
1292: }
1293:
1294: static String getFileName(MimePart part) throws MessagingException {
1295: String filename = null;
1296: String s = part.getHeader("Content-Disposition", null);
1297:
1298:• if (s != null) {
1299: // Parse the header ..
1300: ContentDisposition cd = new ContentDisposition(s);
1301: filename = cd.getParameter("filename");
1302: }
1303:• if (filename == null) {
1304: // Still no filename ? Try the "name" ContentType parameter
1305: s = part.getHeader("Content-Type", null);
1306: s = MimeUtil.cleanContentType(part, s);
1307:• if (s != null) {
1308: try {
1309: ContentType ct = new ContentType(s);
1310: filename = ct.getParameter("name");
1311: } catch (ParseException pex) {
1312: } // ignore it
1313: }
1314: }
1315:• if (decodeFileName && filename != null) {
1316: try {
1317: filename = MimeUtility.decodeText(filename);
1318: } catch (UnsupportedEncodingException ex) {
1319: throw new MessagingException("Can't decode filename", ex);
1320: }
1321: }
1322: return filename;
1323: }
1324:
1325: static void setFileName(MimePart part, String name)
1326: throws MessagingException {
1327:• if (encodeFileName && name != null) {
1328: try {
1329: name = MimeUtility.encodeText(name);
1330: } catch (UnsupportedEncodingException ex) {
1331: throw new MessagingException("Can't encode filename", ex);
1332: }
1333: }
1334:
1335: // Set the Content-Disposition "filename" parameter
1336: String s = part.getHeader("Content-Disposition", null);
1337: ContentDisposition cd =
1338:• new ContentDisposition(s == null ? Part.ATTACHMENT : s);
1339: // ensure that the filename is encoded if necessary
1340: String charset = MimeUtility.getDefaultMIMECharset();
1341: ParameterList p = cd.getParameterList();
1342:• if (p == null) {
1343: p = new ParameterList();
1344: cd.setParameterList(p);
1345: }
1346:• if (encodeFileName)
1347: p.setLiteral("filename", name);
1348: else
1349: p.set("filename", name, charset);
1350: part.setHeader("Content-Disposition", cd.toString());
1351:
1352: /*
1353: * Also attempt to set the Content-Type "name" parameter,
1354: * to satisfy ancient MUAs. XXX - This is not RFC compliant.
1355: */
1356:• if (setContentTypeFileName) {
1357: s = part.getHeader("Content-Type", null);
1358: s = MimeUtil.cleanContentType(part, s);
1359:• if (s != null) {
1360: try {
1361: ContentType cType = new ContentType(s);
1362: // ensure that the filename is encoded if necessary
1363: p = cType.getParameterList();
1364:• if (p == null) {
1365: p = new ParameterList();
1366: cType.setParameterList(p);
1367: }
1368:• if (encodeFileName)
1369: p.setLiteral("name", name);
1370: else
1371: p.set("name", name, charset);
1372: part.setHeader("Content-Type", cType.toString());
1373: } catch (ParseException pex) {
1374: } // ignore it
1375: }
1376: }
1377: }
1378:
1379: static String[] getContentLanguage(MimePart part)
1380: throws MessagingException {
1381: String s = part.getHeader("Content-Language", null);
1382:
1383:• if (s == null)
1384: return null;
1385:
1386: // Tokenize the header to obtain the Language-tags (skip comments)
1387: HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
1388: List<String> v = new ArrayList<>();
1389:
1390: HeaderTokenizer.Token tk;
1391: int tkType;
1392:
1393: while (true) {
1394: tk = h.next(); // get a language-tag
1395: tkType = tk.getType();
1396:• if (tkType == HeaderTokenizer.Token.EOF)
1397: break; // done
1398:• else if (tkType == HeaderTokenizer.Token.ATOM)
1399: v.add(tk.getValue());
1400: else // invalid token, skip it.
1401: continue;
1402: }
1403:
1404:• if (v.isEmpty())
1405: return null;
1406:
1407: String[] language = new String[v.size()];
1408: v.toArray(language);
1409: return language;
1410: }
1411:
1412: static void setContentLanguage(MimePart part, String[] languages)
1413: throws MessagingException {
1414: StringBuilder sb = new StringBuilder(languages[0]);
1415: int len = "Content-Language".length() + 2 + languages[0].length();
1416:• for (int i = 1; i < languages.length; i++) {
1417: sb.append(',');
1418: len++;
1419:• if (len > 76) {
1420: sb.append("\r\n\t");
1421: len = 8;
1422: }
1423: sb.append(languages[i]);
1424: len += languages[i].length();
1425: }
1426: part.setHeader("Content-Language", sb.toString());
1427: }
1428:
1429: static String getEncoding(MimePart part) throws MessagingException {
1430: String s = part.getHeader("Content-Transfer-Encoding", null);
1431:
1432:• if (s == null)
1433: return null;
1434:
1435: s = s.trim(); // get rid of trailing spaces
1436:• if (s.length() == 0)
1437: return null;
1438: // quick check for known values to avoid unnecessary use
1439: // of tokenizer.
1440:• if (s.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()) || s.equalsIgnoreCase(EncoderTypes.BIT8_ENCODER.getEncoder()) ||
1441:• s.equalsIgnoreCase(EncoderTypes.QUOTED_PRINTABLE_ENCODER.getEncoder()) ||
1442:• s.equalsIgnoreCase(EncoderTypes.BINARY_ENCODER.getEncoder()) ||
1443:• s.equalsIgnoreCase(EncoderTypes.BASE_64.getEncoder()))
1444: return s;
1445:
1446: // Tokenize the header to obtain the encoding (skip comments)
1447: HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
1448:
1449: HeaderTokenizer.Token tk;
1450: int tkType;
1451:
1452: for (; ; ) {
1453: tk = h.next(); // get a token
1454: tkType = tk.getType();
1455:• if (tkType == HeaderTokenizer.Token.EOF)
1456: break; // done
1457:• else if (tkType == HeaderTokenizer.Token.ATOM)
1458: return tk.getValue();
1459: else // invalid token, skip it.
1460: continue;
1461: }
1462: return s;
1463: }
1464:
1465: static void setEncoding(MimePart part, String encoding)
1466: throws MessagingException {
1467: part.setHeader("Content-Transfer-Encoding", encoding);
1468: }
1469:
1470: /**
1471: * Restrict the encoding to values allowed for the
1472: * Content-Type of the specified MimePart. Returns
1473: * either the original encoding or null.
1474: */
1475: static String restrictEncoding(MimePart part, String encoding)
1476: throws MessagingException {
1477:• if (!ignoreMultipartEncoding || encoding == null)
1478: return encoding;
1479:
1480:• if (encoding.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()) ||
1481:• encoding.equalsIgnoreCase(EncoderTypes.BIT8_ENCODER.getEncoder()) ||
1482:• encoding.equalsIgnoreCase(EncoderTypes.BINARY_ENCODER.getEncoder()))
1483: return encoding; // these encodings are always valid
1484:
1485: String type = part.getContentType();
1486:• if (type == null)
1487: return encoding;
1488:
1489: try {
1490: /*
1491: * multipart and message types aren't allowed to have
1492: * encodings except for the three mentioned above.
1493: * If it's one of these types, ignore the encoding.
1494: */
1495: ContentType cType = new ContentType(type);
1496:• if (cType.match("multipart/*"))
1497: return null;
1498:• if (cType.match("message/*") &&
1499:• !MimeUtility.getBooleanSystemProperty(
1500: "mail.mime.allowencodedmessages", false))
1501: return null;
1502: } catch (ParseException pex) {
1503: // ignore it
1504: }
1505: return encoding;
1506: }
1507:
1508: static void updateHeaders(MimePart part) throws MessagingException {
1509: DataHandler dh = part.getDataHandler();
1510:• if (dh == null) // Huh ?
1511: return;
1512:
1513: try {
1514: String type = dh.getContentType();
1515: boolean composite = false;
1516:• boolean needCTHeader = part.getHeader("Content-Type") == null;
1517:
1518: ContentType cType = new ContentType(type);
1519:
1520: /*
1521: * If this is a multipart, give sub-parts a chance to
1522: * update their headers. Even though the data for this
1523: * multipart may have come from a stream, one of the
1524: * sub-parts may have been updated.
1525: */
1526:• if (cType.match("multipart/*")) {
1527: // If multipart, recurse
1528: composite = true;
1529: Object o;
1530:• if (part instanceof MimeBodyPart) {
1531: MimeBodyPart mbp = (MimeBodyPart) part;
1532:• o = mbp.cachedContent != null ?
1533: mbp.cachedContent : dh.getContent();
1534:• } else if (part instanceof MimeMessage) {
1535: MimeMessage msg = (MimeMessage) part;
1536:• o = msg.cachedContent != null ?
1537: msg.cachedContent : dh.getContent();
1538: } else
1539: o = dh.getContent();
1540:• if (o instanceof MimeMultipart)
1541: ((MimeMultipart) o).updateHeaders();
1542: else
1543: throw new MessagingException("MIME part of type \"" +
1544: type + "\" contains object of type " +
1545: o.getClass().getName() + " instead of MimeMultipart");
1546:• } else if (cType.match("message/rfc822")) {
1547: composite = true;
1548: // XXX - call MimeMessage.updateHeaders()?
1549: }
1550:
1551: /*
1552: * If this is our own MimePartDataHandler, we can't update any
1553: * of the headers.
1554: *
1555: * If this is a MimePartDataHandler coming from another part,
1556: * we need to copy over the content headers from the other part.
1557: * Note that the MimePartDataHandler still refers to the original
1558: * data and the original MimePart.
1559: */
1560:• if (dh instanceof MimePartDataHandler) {
1561: MimePartDataHandler mdh = (MimePartDataHandler) dh;
1562: MimePart mpart = mdh.getPart();
1563:• if (mpart != part) {
1564:• if (needCTHeader)
1565: part.setHeader("Content-Type", mpart.getContentType());
1566: // XXX - can't change the encoding of the data from the
1567: // other part without decoding and reencoding it, so
1568: // we just force it to match the original, but if the
1569: // original has no encoding we'll consider reencoding it
1570: String enc = mpart.getEncoding();
1571:• if (enc != null) {
1572: setEncoding(part, enc);
1573: return;
1574: }
1575: } else
1576: return;
1577: }
1578:
1579: // Content-Transfer-Encoding, but only if we don't
1580: // already have one
1581:• if (!composite) { // not allowed on composite parts
1582:• if (part.getHeader("Content-Transfer-Encoding") == null)
1583: setEncoding(part, MimeUtility.getEncoding(dh));
1584:
1585:• if (needCTHeader && setDefaultTextCharset &&
1586:• cType.match("text/*") &&
1587:• cType.getParameter("charset") == null) {
1588: /*
1589: * Set a default charset for text parts.
1590: * We really should examine the data to determine
1591: * whether or not it's all ASCII, but that's too
1592: * expensive so we make an assumption: If we
1593: * chose 7bit encoding for this data, it's probably
1594: * ASCII. (MimeUtility.getEncoding will choose
1595: * 7bit only in this case, but someone might've
1596: * set the Content-Transfer-Encoding header manually.)
1597: */
1598: String charset;
1599: String enc = part.getEncoding();
1600:• if (enc != null && enc.equalsIgnoreCase(EncoderTypes.BIT7_ENCODER.getEncoder()))
1601: charset = "us-ascii";
1602: else
1603: charset = MimeUtility.getDefaultMIMECharset();
1604: cType.setParameter("charset", charset);
1605: type = cType.toString();
1606: }
1607: }
1608:
1609: // Now, let's update our own headers ...
1610:
1611: // Content-type, but only if we don't already have one
1612:• if (needCTHeader) {
1613: /*
1614: * Pull out "filename" from Content-Disposition, and
1615: * use that to set the "name" parameter. This is to
1616: * satisfy older MUAs (DtMail, Roam and probably
1617: * a bunch of others).
1618: */
1619:• if (setContentTypeFileName) {
1620: String s = part.getHeader("Content-Disposition", null);
1621:• if (s != null) {
1622: // Parse the header ..
1623: ContentDisposition cd = new ContentDisposition(s);
1624: String filename = cd.getParameter("filename");
1625:• if (filename != null) {
1626: ParameterList p = cType.getParameterList();
1627:• if (p == null) {
1628: p = new ParameterList();
1629: cType.setParameterList(p);
1630: }
1631:• if (encodeFileName)
1632: p.setLiteral("name",
1633: MimeUtility.encodeText(filename));
1634: else
1635: p.set("name", filename,
1636: MimeUtility.getDefaultMIMECharset());
1637: type = cType.toString();
1638: }
1639: }
1640: }
1641:
1642: part.setHeader("Content-Type", type);
1643: }
1644: } catch (IOException ex) {
1645: throw new MessagingException("IOException updating headers", ex);
1646: }
1647: }
1648:
1649: static void invalidateContentHeaders(MimePart part)
1650: throws MessagingException {
1651: part.removeHeader("Content-Type");
1652: part.removeHeader("Content-Transfer-Encoding");
1653: }
1654:
1655: static void writeTo(MimePart part, OutputStream os, String[] ignoreList)
1656: throws IOException, MessagingException {
1657:
1658: // see if we already have a LOS
1659: LineOutputStream los = null;
1660:• if (os instanceof LineOutputStream) {
1661: los = (LineOutputStream) os;
1662: } else {
1663: Map<String, Object> params = new HashMap<>();
1664: params.put("allowutf8", allowutf8);
1665: los = StreamProvider.provider().outputLineStream(os, allowutf8);
1666: }
1667:
1668: // First, write out the header
1669: Enumeration<String> hdrLines
1670: = part.getNonMatchingHeaderLines(ignoreList);
1671:• while (hdrLines.hasMoreElements())
1672: los.writeln(hdrLines.nextElement());
1673:
1674: // The CRLF separator between header and content
1675: los.writeln();
1676:
1677: // Finally, the content. Encode if required.
1678: // XXX: May need to account for ESMTP ?
1679: InputStream is = null;
1680: byte[] buf = null;
1681: try {
1682: /*
1683: * If the data for this part comes from a stream,
1684: * and is already encoded,
1685: * just copy it to the output stream without decoding
1686: * and reencoding it.
1687: */
1688: DataHandler dh = part.getDataHandler();
1689:• if (dh instanceof MimePartDataHandler) {
1690: MimePartDataHandler mpdh = (MimePartDataHandler) dh;
1691: MimePart mpart = mpdh.getPart();
1692:• if (mpart.getEncoding() != null)
1693: is = mpdh.getContentStream();
1694: }
1695:• if (is != null) {
1696: // now copy the data to the output stream
1697: buf = new byte[8192];
1698: int len;
1699:• while ((len = is.read(buf)) > 0)
1700: os.write(buf, 0, len);
1701: } else {
1702: os = MimeUtility.encode(os,
1703: restrictEncoding(part, part.getEncoding()));
1704: part.getDataHandler().writeTo(os);
1705: }
1706: } finally {
1707:• if (is != null)
1708: is.close();
1709: buf = null;
1710: }
1711: os.flush(); // Needed to complete encoding
1712: }
1713:
1714: /**
1715: * A special DataHandler used only as a marker to indicate that
1716: * the source of the data is a MimePart (that is, a byte array
1717: * or a stream). This prevents updateHeaders from trying to
1718: * change the headers for such data. In particular, the original
1719: * Content-Transfer-Encoding for the data must be preserved.
1720: * Otherwise the data would need to be decoded and reencoded.
1721: */
1722: static class MimePartDataHandler extends DataHandler {
1723: MimePart part;
1724:
1725: public MimePartDataHandler(MimePart part) {
1726: super(new MimePartDataSource(part));
1727: this.part = part;
1728: }
1729:
1730: InputStream getContentStream() throws MessagingException {
1731: InputStream is = null;
1732:
1733: if (part instanceof MimeBodyPart) {
1734: MimeBodyPart mbp = (MimeBodyPart) part;
1735: is = mbp.getContentStream();
1736: } else if (part instanceof MimeMessage) {
1737: MimeMessage msg = (MimeMessage) part;
1738: is = msg.getContentStream();
1739: }
1740: return is;
1741: }
1742:
1743: MimePart getPart() {
1744: return part;
1745: }
1746: }
1747: }