KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > mail > internet > MimeBodyPart


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21
22 /*
23  * @(#)MimeBodyPart.java 1.63 06/03/02
24  *
25  * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
26  */

27
28 package javax.mail.internet;
29
30 import javax.mail.*;
31 import javax.activation.*;
32 import java.io.*;
33 import java.util.*;
34 import com.sun.mail.util.ASCIIUtility;
35 import com.sun.mail.util.LineOutputStream;
36
37 /**
38  * This class represents a MIME body part. It implements the
39  * <code>BodyPart</code> abstract class and the <code>MimePart</code>
40  * interface. MimeBodyParts are contained in <code>MimeMultipart</code>
41  * objects. <p>
42  *
43  * MimeBodyPart uses the <code>InternetHeaders</code> class to parse
44  * and store the headers of that body part. <p>
45  *
46  * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
47  *
48  * RFC 822 header fields <strong>must</strong> contain only
49  * US-ASCII characters. MIME allows non ASCII characters to be present
50  * in certain portions of certain headers, by encoding those characters.
51  * RFC 2047 specifies the rules for doing this. The MimeUtility
52  * class provided in this package can be used to to achieve this.
53  * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
54  * <code>addHeaderLine</code> methods are responsible for enforcing
55  * the MIME requirements for the specified headers. In addition, these
56  * header fields must be folded (wrapped) before being sent if they
57  * exceed the line length limitation for the transport (1000 bytes for
58  * SMTP). Received headers may have been folded. The application is
59  * responsible for folding and unfolding headers as appropriate. <p>
60  *
61  * @author John Mani
62  * @author Bill Shannon
63  * @author Kanwar Oberoi
64  * @see javax.mail.Part
65  * @see javax.mail.internet.MimePart
66  * @see javax.mail.internet.MimeUtility
67  */

68
69 public class MimeBodyPart extends BodyPart implements MimePart JavaDoc {
70
71     // Paranoia:
72
// allow this last minute change to be disabled if it causes problems
73
private static boolean setDefaultTextCharset = true;
74
75     private static boolean setContentTypeFileName = true;
76
77     private static boolean encodeFileName = false;
78     private static boolean decodeFileName = false;
79
80     // Paranoia:
81
// allow this last minute change to be disabled if it causes problems
82
static boolean cacheMultipart = true; // accessed by MimeMessage
83

84     static {
85     try {
86         String JavaDoc s = System.getProperty("mail.mime.setdefaulttextcharset");
87         // default to true
88
setDefaultTextCharset = s == null || !s.equalsIgnoreCase("false");
89
90         s = System.getProperty("mail.mime.setcontenttypefilename");
91         // default to true
92
setContentTypeFileName = s == null || !s.equalsIgnoreCase("false");
93
94         s = System.getProperty("mail.mime.encodefilename");
95         // default to false
96
encodeFileName = s != null && !s.equalsIgnoreCase("false");
97
98         s = System.getProperty("mail.mime.decodefilename");
99         // default to false
100
decodeFileName = s != null && !s.equalsIgnoreCase("false");
101
102         s = System.getProperty("mail.mime.cachemultipart");
103         // default to true
104
cacheMultipart = s == null || !s.equalsIgnoreCase("false");
105     } catch (SecurityException JavaDoc sex) {
106         // ignore it
107
}
108     }
109    
110     /**
111      * The DataHandler object representing this Part's content.
112      */

113     protected DataHandler dh;
114
115     /**
116      * Byte array that holds the bytes of the content of this Part.
117      */

118     protected byte[] content;
119
120     /**
121      * If the data for this body part was supplied by an
122      * InputStream that implements the SharedInputStream interface,
123      * <code>contentStream</code> is another such stream representing
124      * the content of this body part. In this case, <code>content</code>
125      * will be null.
126      *
127      * @since JavaMail 1.2
128      */

129     protected InputStream contentStream;
130
131     /**
132      * The InternetHeaders object that stores all the headers
133      * of this body part.
134      */

135     protected InternetHeaders JavaDoc headers;
136
137     /**
138      * An empty MimeBodyPart object is created.
139      * This body part maybe filled in by a client constructing a multipart
140      * message.
141      */

142     public MimeBodyPart() {
143     super();
144     headers = new InternetHeaders JavaDoc();
145     }
146
147     /**
148      * Constructs a MimeBodyPart by reading and parsing the data from
149      * the specified input stream. The parser consumes data till the end
150      * of the given input stream. The input stream must start at the
151      * beginning of a valid MIME body part and must terminate at the end
152      * of that body part. <p>
153      *
154      * Note that the "boundary" string that delimits body parts must
155      * <strong>not</strong> be included in the input stream. The intention
156      * is that the MimeMultipart parser will extract each body part's bytes
157      * from a multipart stream and feed them into this constructor, without
158      * the delimiter strings.
159      *
160      * @param is the body part Input Stream
161      */

162     public MimeBodyPart(InputStream is) throws MessagingException {
163     if (!(is instanceof ByteArrayInputStream) &&
164         !(is instanceof BufferedInputStream) &&
165         !(is instanceof SharedInputStream JavaDoc))
166         is = new BufferedInputStream(is);
167     
168     headers = new InternetHeaders JavaDoc(is);
169
170     if (is instanceof SharedInputStream JavaDoc) {
171         SharedInputStream JavaDoc sis = (SharedInputStream JavaDoc)is;
172         contentStream = sis.newStream(sis.getPosition(), -1);
173     } else {
174         try {
175         content = ASCIIUtility.getBytes(is);
176         } catch (IOException ioex) {
177         throw new MessagingException("Error reading input stream", ioex);
178         }
179     }
180
181     }
182
183     /**
184      * Constructs a MimeBodyPart using the given header and
185      * content bytes. <p>
186      *
187      * Used by providers.
188      *
189      * @param headers The header of this part
190      * @param content bytes representing the body of this part.
191      */

192     public MimeBodyPart(InternetHeaders JavaDoc headers, byte[] content)
193             throws MessagingException {
194     super();
195     this.headers = headers;
196     this.content = content;
197     }
198
199     /**
200      * Return the size of the content of this body part in bytes.
201      * Return -1 if the size cannot be determined. <p>
202      *
203      * Note that this number may not be an exact measure of the
204      * content size and may or may not account for any transfer
205      * encoding of the content. <p>
206      *
207      * This implementation returns the size of the <code>content</code>
208      * array (if not null), or, if <code>contentStream</code> is not
209      * null, and the <code>available</code> method returns a positive
210      * number, it returns that number as the size. Otherwise, it returns
211      * -1.
212      *
213      * @return size in bytes, or -1 if not known
214      */

215     public int getSize() throws MessagingException {
216     if (content != null)
217         return content.length;
218     if (contentStream != null) {
219         try {
220         int size = contentStream.available();
221         // only believe the size if it's greate than zero, since zero
222
// is the default returned by the InputStream class itself
223
if (size > 0)
224             return size;
225         } catch (IOException ex) {
226         // ignore it
227
}
228     }
229     return -1;
230     }
231
232     /**
233      * Return the number of lines for the content of this Part.
234      * Return -1 if this number cannot be determined. <p>
235      *
236      * Note that this number may not be an exact measure of the
237      * content length and may or may not account for any transfer
238      * encoding of the content. <p>
239      *
240      * This implementation returns -1.
241      *
242      * @return number of lines, or -1 if not known
243      */

244      public int getLineCount() throws MessagingException {
245     return -1;
246      }
247
248     /**
249      * Returns the value of the RFC 822 "Content-Type" header field.
250      * This represents the content type of the content of this
251      * body part. This value must not be null. If this field is
252      * unavailable, "text/plain" should be returned. <p>
253      *
254      * This implementation uses <code>getHeader(name)</code>
255      * to obtain the requisite header field.
256      *
257      * @return Content-Type of this body part
258      */

259     public String JavaDoc getContentType() throws MessagingException {
260     String JavaDoc s = getHeader("Content-Type", null);
261     if (s == null)
262         s = "text/plain";
263     
264     return s;
265     }
266
267     /**
268      * Is this Part of the specified MIME type? This method
269      * compares <strong>only the <code>primaryType</code> and
270      * <code>subType</code></strong>.
271      * The parameters of the content types are ignored. <p>
272      *
273      * For example, this method will return <code>true</code> when
274      * comparing a Part of content type <strong>"text/plain"</strong>
275      * with <strong>"text/plain; charset=foobar"</strong>. <p>
276      *
277      * If the <code>subType</code> of <code>mimeType</code> is the
278      * special character '*', then the subtype is ignored during the
279      * comparison.
280      */

281     public boolean isMimeType(String JavaDoc mimeType) throws MessagingException {
282     return isMimeType(this, mimeType);
283     }
284
285     /**
286      * Returns the value of the "Content-Disposition" header field.
287      * This represents the disposition of this part. The disposition
288      * describes how the part should be presented to the user. <p>
289      *
290      * If the Content-Disposition field is unavailable,
291      * null is returned. <p>
292      *
293      * This implementation uses <code>getHeader(name)</code>
294      * to obtain the requisite header field.
295      *
296      * @see #headers
297      */

298     public String JavaDoc getDisposition() throws MessagingException {
299     return getDisposition(this);
300     }
301
302     /**
303      * Set the "Content-Disposition" header field of this body part.
304      * If the disposition is null, any existing "Content-Disposition"
305      * header field is removed.
306      *
307      * @exception IllegalWriteException if the underlying
308      * implementation does not support modification
309      * @exception IllegalStateException if this body part is
310      * obtained from a READ_ONLY folder.
311      */

312     public void setDisposition(String JavaDoc disposition) throws MessagingException {
313     setDisposition(this, disposition);
314     }
315
316     /**
317      * Returns the content transfer encoding from the
318      * "Content-Transfer-Encoding" header
319      * field. Returns <code>null</code> if the header is unavailable
320      * or its value is absent. <p>
321      *
322      * This implementation uses <code>getHeader(name)</code>
323      * to obtain the requisite header field.
324      *
325      * @see #headers
326      */

327     public String JavaDoc getEncoding() throws MessagingException {
328     return getEncoding(this);
329     }
330
331     /**
332      * Returns the value of the "Content-ID" header field. Returns
333      * <code>null</code> if the field is unavailable or its value is
334      * absent. <p>
335      *
336      * This implementation uses <code>getHeader(name)</code>
337      * to obtain the requisite header field.
338      */

339     public String JavaDoc getContentID() throws MessagingException {
340     return getHeader("Content-Id", null);
341     }
342
343     /**
344      * Set the "Content-ID" header field of this body part.
345      * If the <code>cid</code> parameter is null, any existing
346      * "Content-ID" is removed.
347      *
348      * @exception IllegalWriteException if the underlying
349      * implementation does not support modification
350      * @exception IllegalStateException if this body part is
351      * obtained from a READ_ONLY folder.
352      * @exception MessagingException
353      * @since JavaMail 1.3
354      */

355     public void setContentID(String JavaDoc cid) throws MessagingException {
356     if (cid == null)
357         removeHeader("Content-ID");
358     else
359         setHeader("Content-ID", cid);
360     }
361
362     /**
363      * Return the value of the "Content-MD5" header field. Returns
364      * <code>null</code> if this field is unavailable or its value
365      * is absent. <p>
366      *
367      * This implementation uses <code>getHeader(name)</code>
368      * to obtain the requisite header field.
369      */

370     public String JavaDoc getContentMD5() throws MessagingException {
371     return getHeader("Content-MD5", null);
372     }
373
374     /**
375      * Set the "Content-MD5" header field of this body part.
376      *
377      * @exception IllegalWriteException if the underlying
378      * implementation does not support modification
379      * @exception IllegalStateException if this body part is
380      * obtained from a READ_ONLY folder.
381      */

382     public void setContentMD5(String JavaDoc md5) throws MessagingException {
383     setHeader("Content-MD5", md5);
384     }
385
386     /**
387      * Get the languages specified in the Content-Language header
388      * of this MimePart. The Content-Language header is defined by
389      * RFC 1766. Returns <code>null</code> if this header is not
390      * available or its value is absent. <p>
391      *
392      * This implementation uses <code>getHeader(name)</code>
393      * to obtain the requisite header field.
394      */

395     public String JavaDoc[] getContentLanguage() throws MessagingException {
396     return getContentLanguage(this);
397     }
398
399     /**
400      * Set the Content-Language header of this MimePart. The
401      * Content-Language header is defined by RFC 1766.
402      *
403      * @param languages array of language tags
404      */

405     public void setContentLanguage(String JavaDoc[] languages)
406             throws MessagingException {
407     setContentLanguage(this, languages);
408     }
409
410     /**
411      * Returns the "Content-Description" header field of this body part.
412      * This typically associates some descriptive information with
413      * this part. Returns null if this field is unavailable or its
414      * value is absent. <p>
415      *
416      * If the Content-Description field is encoded as per RFC 2047,
417      * it is decoded and converted into Unicode. If the decoding or
418      * conversion fails, the raw data is returned as is. <p>
419      *
420      * This implementation uses <code>getHeader(name)</code>
421      * to obtain the requisite header field.
422      *
423      * @return content description
424      */

425     public String JavaDoc getDescription() throws MessagingException {
426     return getDescription(this);
427     }
428
429     /**
430      * Set the "Content-Description" header field for this body part.
431      * If the description parameter is <code>null</code>, then any
432      * existing "Content-Description" fields are removed. <p>
433      *
434      * If the description contains non US-ASCII characters, it will
435      * be encoded using the platform's default charset. If the
436      * description contains only US-ASCII characters, no encoding
437      * is done and it is used as is. <p>
438      *
439      * Note that if the charset encoding process fails, a
440      * MessagingException is thrown, and an UnsupportedEncodingException
441      * is included in the chain of nested exceptions within the
442      * MessagingException.
443      *
444      * @param description content description
445      * @exception IllegalWriteException if the underlying
446      * implementation does not support modification
447      * @exception IllegalStateException if this body part is
448      * obtained from a READ_ONLY folder.
449      * @exception MessagingException otherwise; an
450      * UnsupportedEncodingException may be included
451      * in the exception chain if the charset
452      * conversion fails.
453      */

454     public void setDescription(String JavaDoc description) throws MessagingException {
455     setDescription(description, null);
456     }
457
458     /**
459      * Set the "Content-Description" header field for this body part.
460      * If the description parameter is <code>null</code>, then any
461      * existing "Content-Description" fields are removed. <p>
462      *
463      * If the description contains non US-ASCII characters, it will
464      * be encoded using the specified charset. If the description
465      * contains only US-ASCII characters, no encoding is done and
466      * it is used as is. <p>
467      *
468      * Note that if the charset encoding process fails, a
469      * MessagingException is thrown, and an UnsupportedEncodingException
470      * is included in the chain of nested exceptions within the
471      * MessagingException.
472      *
473      * @param description Description
474      * @param charset Charset for encoding
475      * @exception IllegalWriteException if the underlying
476      * implementation does not support modification
477      * @exception IllegalStateException if this body part is
478      * obtained from a READ_ONLY folder.
479      * @exception MessagingException otherwise; an
480      * UnsupportedEncodingException may be included
481      * in the exception chain if the charset
482      * conversion fails.
483      */

484     public void setDescription(String JavaDoc description, String JavaDoc charset)
485         throws MessagingException {
486     setDescription(this, description, charset);
487     }
488
489     /**
490      * Get the filename associated with this body part. <p>
491      *
492      * Returns the value of the "filename" parameter from the
493      * "Content-Disposition" header field of this body part. If its
494      * not available, returns the value of the "name" parameter from
495      * the "Content-Type" header field of this body part.
496      * Returns <code>null</code> if both are absent. <p>
497      *
498      * If the <code>mail.mime.encodefilename</code> System property
499      * is set to true, the {@link MimeUtility#decodeText
500      * MimeUtility.decodeText} method will be used to decode the
501      * filename. While such encoding is not supported by the MIME
502      * spec, many mailers use this technique to support non-ASCII
503      * characters in filenames. The default value of this property
504      * is false.
505      *
506      * @return filename
507      */

508     public String JavaDoc getFileName() throws MessagingException {
509     return getFileName(this);
510     }
511
512     /**
513      * Set the filename associated with this body part, if possible. <p>
514      *
515      * Sets the "filename" parameter of the "Content-Disposition"
516      * header field of this body part. For compatibility with older
517      * mailers, the "name" parameter of the "Content-Type" header is
518      * also set. <p>
519      *
520      * If the <code>mail.mime.encodefilename</code> System property
521      * is set to true, the {@link MimeUtility#encodeText
522      * MimeUtility.encodeText} method will be used to encode the
523      * filename. While such encoding is not supported by the MIME
524      * spec, many mailers use this technique to support non-ASCII
525      * characters in filenames. The default value of this property
526      * is false.
527      *
528      * @exception IllegalWriteException if the underlying
529      * implementation does not support modification
530      * @exception IllegalStateException if this body part is
531      * obtained from a READ_ONLY folder.
532      */

533     public void setFileName(String JavaDoc filename) throws MessagingException {
534     setFileName(this, filename);
535     }
536
537     /**
538      * Return a decoded input stream for this body part's "content". <p>
539      *
540      * This implementation obtains the input stream from the DataHandler.
541      * That is, it invokes getDataHandler().getInputStream();
542      *
543      * @return an InputStream
544      * @exception MessagingException
545      * @exception IOException this is typically thrown by the
546      * DataHandler. Refer to the documentation for
547      * javax.activation.DataHandler for more details.
548      *
549      * @see #getContentStream
550      * @see javax.activation.DataHandler#getInputStream
551      */

552     public InputStream getInputStream()
553         throws IOException, MessagingException {
554     return getDataHandler().getInputStream();
555     }
556
557    /**
558      * Produce the raw bytes of the content. This method is used
559      * when creating a DataHandler object for the content. Subclasses
560      * that can provide a separate input stream for just the Part
561      * content might want to override this method. <p>
562      *
563      * @see #content
564      * @see MimeMessage#getContentStream
565      */

566     protected InputStream getContentStream() throws MessagingException {
567     if (contentStream != null)
568         return ((SharedInputStream JavaDoc)contentStream).newStream(0, -1);
569     if (content != null)
570         return new ByteArrayInputStream(content);
571     
572     throw new MessagingException("No content");
573     }
574
575     /**
576      * Return an InputStream to the raw data with any Content-Transfer-Encoding
577      * intact. This method is useful if the "Content-Transfer-Encoding"
578      * header is incorrect or corrupt, which would prevent the
579      * <code>getInputStream</code> method or <code>getContent</code> method
580      * from returning the correct data. In such a case the application may
581      * use this method and attempt to decode the raw data itself. <p>
582      *
583      * This implementation simply calls the <code>getContentStream</code>
584      * method.
585      *
586      * @see #getInputStream
587      * @see #getContentStream
588      * @since JavaMail 1.2
589      */

590     public InputStream getRawInputStream() throws MessagingException {
591     return getContentStream();
592     }
593
594     /**
595      * Return a DataHandler for this body part's content. <p>
596      *
597      * The implementation provided here works just like the
598      * the implementation in MimeMessage.
599      * @see MimeMessage#getDataHandler
600      */

601     public DataHandler getDataHandler() throws MessagingException {
602     if (dh == null)
603         dh = new DataHandler(new MimePartDataSource JavaDoc(this));
604     return dh;
605     }
606
607     /**
608      * Return the content as a java object. The type of the object
609      * returned is of course dependent on the content itself. For
610      * example, the native format of a text/plain content is usually
611      * a String object. The native format for a "multipart"
612      * content is always a Multipart subclass. For content types that are
613      * unknown to the DataHandler system, an input stream is returned
614      * as the content. <p>
615      *
616      * This implementation obtains the content from the DataHandler.
617      * That is, it invokes getDataHandler().getContent();
618      * If the content is a Multipart or Message object and was created by
619      * parsing a stream, the object is cached and returned in subsequent
620      * calls so that modifications to the content will not be lost.
621      *
622      * @return Object
623      * @exception MessagingException
624      * @exception IOException this is typically thrown by the
625      * DataHandler. Refer to the documentation for
626      * javax.activation.DataHandler for more details.
627      */

628     public Object JavaDoc getContent() throws IOException, MessagingException {
629     Object JavaDoc c = getDataHandler().getContent();
630     if (cacheMultipart &&
631         (c instanceof Multipart || c instanceof Message) &&
632         !(dh instanceof CachedDataHandler) &&
633         (content != null || contentStream != null)) {
634         dh = createCachedDataHandler(c, getContentType());
635     }
636     return c;
637     }
638
639     /**
640      * This method provides the mechanism to set this body part's content.
641      * The given DataHandler object should wrap the actual content.
642      *
643      * @param dh The DataHandler for the content
644      * @exception IllegalWriteException if the underlying
645      * implementation does not support modification
646      * @exception IllegalStateException if this body part is
647      * obtained from a READ_ONLY folder.
648      */

649     public void setDataHandler(DataHandler dh)
650         throws MessagingException {
651     this.dh = dh;
652     MimeBodyPart.invalidateContentHeaders(this);
653     }
654
655     /**
656      * A convenience method for setting this body part's content. <p>
657      *
658      * The content is wrapped in a DataHandler object. Note that a
659      * DataContentHandler class for the specified type should be
660      * available to the JavaMail implementation for this to work right.
661      * That is, to do <code>setContent(foobar, "application/x-foobar")</code>,
662      * a DataContentHandler for "application/x-foobar" should be installed.
663      * Refer to the Java Activation Framework for more information.
664      *
665      * @param o the content object
666      * @param type Mime type of the object
667      * @exception IllegalWriteException if the underlying
668      * implementation does not support modification of
669      * existing values
670      * @exception IllegalStateException if this body part is
671      * obtained from a READ_ONLY folder.
672      */

673     public void setContent(Object JavaDoc o, String JavaDoc type)
674         throws MessagingException {
675     if (o instanceof Multipart) {
676         setContent((Multipart)o);
677     } else {
678         setDataHandler(new DataHandler(o, type));
679     }
680     }
681
682     /**
683      * Convenience method that sets the given String as this
684      * part's content, with a MIME type of "text/plain". If the
685      * string contains non US-ASCII characters, it will be encoded
686      * using the platform's default charset. The charset is also
687      * used to set the "charset" parameter. <p>
688      *
689      * Note that there may be a performance penalty if
690      * <code>text</code> is large, since this method may have
691      * to scan all the characters to determine what charset to
692      * use. <p>
693      *
694      * If the charset is already known, use the
695      * <code>setText</code> method that takes the charset parameter.
696      *
697      * @param text the text content to set
698      * @exception MessagingException if an error occurs
699      * @see #setText(String text, String charset)
700      */

701     public void setText(String JavaDoc text) throws MessagingException {
702     setText(text, null);
703     }
704
705     /**
706      * Convenience method that sets the given String as this part's
707      * content, with a MIME type of "text/plain" and the specified
708      * charset. The given Unicode string will be charset-encoded
709      * using the specified charset. The charset is also used to set
710      * the "charset" parameter.
711      *
712      * @param text the text content to set
713      * @param charset the charset to use for the text
714      * @exception MessagingException if an error occurs
715      */

716     public void setText(String JavaDoc text, String JavaDoc charset)
717             throws MessagingException {
718     setText(this, text, charset, "plain");
719     }
720
721     /**
722      * Convenience method that sets the given String as this part's
723      * content, with a primary MIME type of "text" and the specified
724      * MIME subtype. The given Unicode string will be charset-encoded
725      * using the specified charset. The charset is also used to set
726      * the "charset" parameter.
727      *
728      * @param text the text content to set
729      * @param charset the charset to use for the text
730      * @param subtype the MIME subtype to use (e.g., "html")
731      * @exception MessagingException if an error occurs
732      * @since JavaMail 1.4
733      */

734     public void setText(String JavaDoc text, String JavaDoc charset, String JavaDoc subtype)
735                         throws MessagingException {
736     setText(this, text, charset, subtype);
737     }
738  
739     /**
740      * This method sets the body part's content to a Multipart object.
741      *
742      * @param mp The multipart object that is the Message's content
743      * @exception IllegalWriteException if the underlying
744      * implementation does not support modification of
745      * existing values.
746      * @exception IllegalStateException if this body part is
747      * obtained from a READ_ONLY folder.
748      */

749     public void setContent(Multipart mp) throws MessagingException {
750     setDataHandler(new DataHandler(mp, mp.getContentType()));
751     mp.setParent(this);
752     }
753
754     /**
755      * Use the specified file to provide the data for this part.
756      * The simple file name is used as the file name for this
757      * part and the data in the file is used as the data for this
758      * part. The encoding will be chosen appropriately for the
759      * file data.
760      *
761      * @param file the File object to attach
762      * @exception IOException errors related to accessing the file
763      * @exception MessagingException message related errors
764      * @since JavaMail 1.4
765      */

766     public void attachFile(File file) throws IOException, MessagingException {
767         FileDataSource fds = new FileDataSource(file);
768         this.setDataHandler(new DataHandler(fds));
769         this.setFileName(fds.getName());
770     }
771
772     /**
773      * Use the specified file to provide the data for this part.
774      * The simple file name is used as the file name for this
775      * part and the data in the file is used as the data for this
776      * part. The encoding will be chosen appropriately for the
777      * file data.
778      *
779      * @param file the name of the file to attach
780      * @exception IOException errors related to accessing the file
781      * @exception MessagingException message related errors
782      * @since JavaMail 1.4
783      */

784     public void attachFile(String JavaDoc file) throws IOException, MessagingException {
785         File f = new File(file);
786         attachFile(f);
787     }
788
789     /**
790      * Save the contents of this part in the specified file. The content
791      * is decoded and saved, without any of the MIME headers.
792      *
793      * @param file the File object to write to
794      * @exception IOException errors related to accessing the file
795      * @exception MessagingException message related errors
796      * @since JavaMail 1.4
797      */

798     public void saveFile(File file) throws IOException, MessagingException {
799         OutputStream out =
800             new BufferedOutputStream(new FileOutputStream(file));
801         InputStream in = this.getInputStream();
802         try {
803         byte[] buf = new byte[8192];
804         int len;
805         while ((len = in.read(buf)) > 0)
806         out.write(buf, 0, len);
807         } finally {
808         // close streams, but don't mask original exception, if any
809
try {
810         if (in != null)
811             in.close();
812         } catch (IOException ex) { }
813         try {
814         if (out != null)
815             out.close();
816         } catch (IOException ex) { }
817         }
818     }
819
820     /**
821      * Save the contents of this part in the specified file. The content
822      * is decoded and saved, without any of the MIME headers.
823      *
824      * @param file the name of the file to write to
825      * @exception IOException errors related to accessing the file
826      * @exception MessagingException message related errors
827      * @since JavaMail 1.4
828      */

829     public void saveFile(String JavaDoc file) throws IOException, MessagingException {
830         File f = new File(file);
831         saveFile(f);
832     }
833
834     /**
835      * Output the body part as an RFC 822 format stream.
836      *
837      * @exception MessagingException
838      * @exception IOException if an error occurs writing to the
839      * stream or if an error is generated
840      * by the javax.activation layer.
841      * @see javax.activation.DataHandler#writeTo
842      */

843     public void writeTo(OutputStream os)
844                 throws IOException, MessagingException {
845     writeTo(this, os, null);
846     }
847
848     /**
849      * Get all the headers for this header_name. Note that certain
850      * headers may be encoded as per RFC 2047 if they contain
851      * non US-ASCII characters and these should be decoded.
852      *
853      * @param name name of header
854      * @return array of headers
855      * @see javax.mail.internet.MimeUtility
856      */

857     public String JavaDoc[] getHeader(String JavaDoc name) throws MessagingException {
858     return headers.getHeader(name);
859     }
860
861     /**
862      * Get all the headers for this header name, returned as a single
863      * String, with headers separated by the delimiter. If the
864      * delimiter is <code>null</code>, only the first header is
865      * returned.
866      *
867      * @param name the name of this header
868      * @param delimiter delimiter between fields in returned string
869      * @return the value fields for all headers with
870      * this name
871      * @exception MessagingException
872      */

873     public String JavaDoc getHeader(String JavaDoc name, String JavaDoc delimiter)
874                 throws MessagingException {
875     return headers.getHeader(name, delimiter);
876     }
877
878     /**
879      * Set the value for this header_name. Replaces all existing
880      * header values with this new value. Note that RFC 822 headers
881      * must contain only US-ASCII characters, so a header that
882      * contains non US-ASCII characters must be encoded as per the
883      * rules of RFC 2047.
884      *
885      * @param name header name
886      * @param value header value
887      * @see javax.mail.internet.MimeUtility
888      */

889     public void setHeader(String JavaDoc name, String JavaDoc value)
890                                 throws MessagingException {
891     headers.setHeader(name, value);
892     }
893  
894     /**
895      * Add this value to the existing values for this header_name.
896      * Note that RFC 822 headers must contain only US-ASCII
897      * characters, so a header that contains non US-ASCII characters
898      * must be encoded as per the rules of RFC 2047.
899      *
900      * @param name header name
901      * @param value header value
902      * @see javax.mail.internet.MimeUtility
903      */

904     public void addHeader(String JavaDoc name, String JavaDoc value)
905                                 throws MessagingException {
906     headers.addHeader(name, value);
907     }
908
909     /**
910      * Remove all headers with this name.
911      */

912     public void removeHeader(String JavaDoc name) throws MessagingException {
913     headers.removeHeader(name);
914     }
915  
916     /**
917      * Return all the headers from this Message as an Enumeration of
918      * Header objects.
919      */

920     public Enumeration getAllHeaders() throws MessagingException {
921     return headers.getAllHeaders();
922     }
923    
924     /**
925      * Return matching headers from this Message as an Enumeration of
926      * Header objects. <p>
927      */

928     public Enumeration getMatchingHeaders(String JavaDoc[] names)
929                         throws MessagingException {
930     return headers.getMatchingHeaders(names);
931     }
932  
933     /**
934      * Return non-matching headers from this Message as an
935      * Enumeration of Header objects.
936      */

937     public Enumeration getNonMatchingHeaders(String JavaDoc[] names)
938                         throws MessagingException {
939     return headers.getNonMatchingHeaders(names);
940     }
941       
942     /**
943      * Add a header line to this body part
944      */

945     public void addHeaderLine(String JavaDoc line) throws MessagingException {
946     headers.addHeaderLine(line);
947     }
948      
949     /**
950      * Get all header lines as an Enumeration of Strings. A Header
951      * line is a raw RFC 822 header line, containing both the "name"
952      * and "value" field.
953      */

954     public Enumeration getAllHeaderLines() throws MessagingException {
955     return headers.getAllHeaderLines();
956     }
957  
958     /**
959      * Get matching header lines as an Enumeration of Strings.
960      * A Header line is a raw RFC 822 header line, containing both
961      * the "name" and "value" field.
962      */

963     public Enumeration getMatchingHeaderLines(String JavaDoc[] names)
964                                     throws MessagingException {
965     return headers.getMatchingHeaderLines(names);
966     }
967  
968     /**
969      * Get non-matching header lines as an Enumeration of Strings.
970      * A Header line is a raw RFC 822 header line, containing both
971      * the "name" and "value" field.
972      */

973     public Enumeration getNonMatchingHeaderLines(String JavaDoc[] names)
974                                         throws MessagingException {
975     return headers.getNonMatchingHeaderLines(names);
976     }
977
978     /**
979      * Examine the content of this body part and update the appropriate
980      * MIME headers. Typical headers that get set here are
981      * <code>Content-Type</code> and <code>Content-Transfer-Encoding</code>.
982      * Headers might need to be updated in two cases:
983      *
984      * <br>
985      * - A message being crafted by a mail application will certainly
986      * need to activate this method at some point to fill up its internal
987      * headers.
988      *
989      * <br>
990      * - A message read in from a Store will have obtained
991      * all its headers from the store, and so doesn't need this.
992      * However, if this message is editable and if any edits have
993      * been made to either the content or message structure, we might
994      * need to resync our headers.
995      *
996      * <br>
997      * In both cases this method is typically called by the
998      * <code>Message.saveChanges</code> method.
999      */

1000    protected void updateHeaders() throws MessagingException {
1001    updateHeaders(this);
1002    }
1003
1004    /////////////////////////////////////////////////////////////
1005
// Package private convenience methods to share code among //
1006
// MimeMessage and MimeBodyPart //
1007
/////////////////////////////////////////////////////////////
1008

1009    static boolean isMimeType(MimePart JavaDoc part, String JavaDoc mimeType)
1010                throws MessagingException {
1011    // XXX - lots of room for optimization here!
1012
try {
1013        ContentType JavaDoc ct = new ContentType JavaDoc(part.getContentType());
1014        return ct.match(mimeType);
1015    } catch (ParseException JavaDoc ex) {
1016        return part.getContentType().equalsIgnoreCase(mimeType);
1017    }
1018    }
1019
1020    static void setText(MimePart JavaDoc part, String JavaDoc text, String JavaDoc charset,
1021            String JavaDoc subtype) throws MessagingException {
1022    if (charset == null) {
1023        if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII)
1024        charset = MimeUtility.getDefaultMIMECharset();
1025        else
1026        charset = "us-ascii";
1027    }
1028    // XXX - should at least ensure that subtype is an atom
1029
part.setContent(text, "text/" + subtype + "; charset=" +
1030            MimeUtility.quote(charset, HeaderTokenizer.MIME));
1031    }
1032
1033    static String JavaDoc getDisposition(MimePart JavaDoc part) throws MessagingException {
1034    String JavaDoc s = part.getHeader("Content-Disposition", null);
1035
1036    if (s == null)
1037        return null;
1038
1039    ContentDisposition JavaDoc cd = new ContentDisposition JavaDoc(s);
1040    return cd.getDisposition();
1041    }
1042
1043    static void setDisposition(MimePart JavaDoc part, String JavaDoc disposition)
1044            throws MessagingException {
1045    if (disposition == null)
1046        part.removeHeader("Content-Disposition");
1047    else {
1048        String JavaDoc s = part.getHeader("Content-Disposition", null);
1049        if (s != null) {
1050        /* A Content-Disposition header already exists ..
1051         *
1052         * Override disposition, but attempt to retain
1053         * existing disposition parameters
1054         */

1055        ContentDisposition JavaDoc cd = new ContentDisposition JavaDoc(s);
1056        cd.setDisposition(disposition);
1057        disposition = cd.toString();
1058        }
1059        part.setHeader("Content-Disposition", disposition);
1060    }
1061    }
1062
1063    static String JavaDoc getDescription(MimePart JavaDoc part)
1064            throws MessagingException {
1065    String JavaDoc rawvalue = part.getHeader("Content-Description", null);
1066
1067    if (rawvalue == null)
1068        return null;
1069
1070    try {
1071        return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
1072    } catch (UnsupportedEncodingException ex) {
1073        return rawvalue;
1074    }
1075    }
1076
1077    static void
1078    setDescription(MimePart JavaDoc part, String JavaDoc description, String JavaDoc charset)
1079            throws MessagingException {
1080    if (description == null) {
1081        part.removeHeader("Content-Description");
1082        return;
1083    }
1084    
1085    try {
1086        part.setHeader("Content-Description", MimeUtility.fold(21,
1087        MimeUtility.encodeText(description, charset, null)));
1088    } catch (UnsupportedEncodingException uex) {
1089        throw new MessagingException("Encoding error", uex);
1090    }
1091    }
1092
1093    static String JavaDoc getFileName(MimePart JavaDoc part) throws MessagingException {
1094    String JavaDoc filename = null;
1095    String JavaDoc s = part.getHeader("Content-Disposition", null);
1096
1097    if (s != null) {
1098        // Parse the header ..
1099
ContentDisposition JavaDoc cd = new ContentDisposition JavaDoc(s);
1100        filename = cd.getParameter("filename");
1101    }
1102    if (filename == null) {
1103        // Still no filename ? Try the "name" ContentType parameter
1104
s = part.getHeader("Content-Type", null);
1105        if (s != null) {
1106        try {
1107            ContentType JavaDoc ct = new ContentType JavaDoc(s);
1108            filename = ct.getParameter("name");
1109        } catch (ParseException JavaDoc pex) { } // ignore it
1110
}
1111    }
1112    if (decodeFileName && filename != null) {
1113        try {
1114        filename = MimeUtility.decodeText(filename);
1115        } catch (UnsupportedEncodingException ex) {
1116        throw new MessagingException("Can't decode filename", ex);
1117        }
1118    }
1119    return filename;
1120    }
1121
1122    static void setFileName(MimePart JavaDoc part, String JavaDoc name)
1123        throws MessagingException {
1124    if (encodeFileName && name != null) {
1125        try {
1126        name = MimeUtility.encodeText(name);
1127        } catch (UnsupportedEncodingException ex) {
1128        throw new MessagingException("Can't encode filename", ex);
1129        }
1130    }
1131
1132    // Set the Content-Disposition "filename" parameter
1133
String JavaDoc s = part.getHeader("Content-Disposition", null);
1134    ContentDisposition JavaDoc cd =
1135        new ContentDisposition JavaDoc(s == null ? Part.ATTACHMENT : s);
1136    cd.setParameter("filename", name);
1137    part.setHeader("Content-Disposition", cd.toString());
1138
1139    /*
1140     * Also attempt to set the Content-Type "name" parameter,
1141     * to satisfy ancient MUAs. XXX - This is not RFC compliant.
1142     */

1143    if (setContentTypeFileName) {
1144        s = part.getHeader("Content-Type", null);
1145        if (s != null) {
1146        try {
1147            ContentType JavaDoc cType = new ContentType JavaDoc(s);
1148            cType.setParameter("name", name);
1149            part.setHeader("Content-Type", cType.toString());
1150        } catch (ParseException JavaDoc pex) { } // ignore it
1151
}
1152    }
1153    }
1154
1155    static String JavaDoc[] getContentLanguage(MimePart JavaDoc part)
1156        throws MessagingException {
1157    String JavaDoc s = part.getHeader("Content-Language", null);
1158
1159    if (s == null)
1160        return null;
1161
1162    // Tokenize the header to obtain the Language-tags (skip comments)
1163
HeaderTokenizer JavaDoc h = new HeaderTokenizer JavaDoc(s, HeaderTokenizer.MIME);
1164    Vector v = new Vector();
1165
1166    HeaderTokenizer.Token JavaDoc tk;
1167    int tkType;
1168
1169    while (true) {
1170        tk = h.next(); // get a language-tag
1171
tkType = tk.getType();
1172        if (tkType == HeaderTokenizer.Token.EOF)
1173        break; // done
1174
else if (tkType == HeaderTokenizer.Token.ATOM)
1175        v.addElement(tk.getValue());
1176        else // invalid token, skip it.
1177
continue;
1178    }
1179
1180    if (v.size() == 0)
1181        return null;
1182
1183    String JavaDoc[] language = new String JavaDoc[v.size()];
1184    v.copyInto(language);
1185    return language;
1186    }
1187
1188    static void setContentLanguage(MimePart JavaDoc part, String JavaDoc[] languages)
1189            throws MessagingException {
1190    StringBuffer JavaDoc sb = new StringBuffer JavaDoc(languages[0]);
1191    for (int i = 1; i < languages.length; i++)
1192        sb.append(',').append(languages[i]);
1193    part.setHeader("Content-Language", sb.toString());
1194    }
1195
1196    static String JavaDoc getEncoding(MimePart JavaDoc part) throws MessagingException {
1197    String JavaDoc s = part.getHeader("Content-Transfer-Encoding", null);
1198
1199    if (s == null)
1200        return null;
1201
1202    s = s.trim(); // get rid of trailing spaces
1203
// quick check for known values to avoid unnecessary use
1204
// of tokenizer.
1205
if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") ||
1206        s.equalsIgnoreCase("quoted-printable") ||
1207        s.equalsIgnoreCase("binary") ||
1208        s.equalsIgnoreCase("base64"))
1209        return s;
1210
1211    // Tokenize the header to obtain the encoding (skip comments)
1212
HeaderTokenizer JavaDoc h = new HeaderTokenizer JavaDoc(s, HeaderTokenizer.MIME);
1213
1214    HeaderTokenizer.Token JavaDoc tk;
1215    int tkType;
1216
1217    for (;;) {
1218        tk = h.next(); // get a token
1219
tkType = tk.getType();
1220        if (tkType == HeaderTokenizer.Token.EOF)
1221        break; // done
1222
else if (tkType == HeaderTokenizer.Token.ATOM)
1223        return tk.getValue();
1224        else // invalid token, skip it.
1225
continue;
1226    }
1227    return s;
1228    }
1229
1230    static void setEncoding(MimePart JavaDoc part, String JavaDoc encoding)
1231                throws MessagingException {
1232    part.setHeader("Content-Transfer-Encoding", encoding);
1233    }
1234
1235    static DataHandler createCachedDataHandler(Object JavaDoc o, String JavaDoc mimeType) {
1236    return new CachedDataHandler(o, mimeType);
1237    }
1238
1239    static void updateHeaders(MimePart JavaDoc part) throws MessagingException {
1240    DataHandler dh = part.getDataHandler();
1241    if (dh == null) // Huh ?
1242
return;
1243
1244    try {
1245        String JavaDoc type = dh.getContentType();
1246        boolean composite = false;
1247        boolean needCTHeader = part.getHeader("Content-Type") == null;
1248
1249        ContentType JavaDoc cType = new ContentType JavaDoc(type);
1250        if (cType.match("multipart/*")) {
1251        // If multipart, recurse
1252
composite = true;
1253        Object JavaDoc o = dh.getContent();
1254        if (o instanceof MimeMultipart JavaDoc)
1255            ((MimeMultipart JavaDoc)o).updateHeaders();
1256        else
1257            throw new MessagingException("MIME part of type \"" +
1258            type + "\" contains object of type " +
1259            o.getClass().getName() + " instead of MimeMultipart");
1260        } else if (cType.match("message/rfc822")) {
1261        composite = true;
1262        }
1263
1264        // Content-Transfer-Encoding, but only if we don't
1265
// already have one
1266
if (!composite) { // not allowed on composite parts
1267
if (part.getHeader("Content-Transfer-Encoding") == null)
1268            setEncoding(part, MimeUtility.getEncoding(dh));
1269
1270        if (needCTHeader && setDefaultTextCharset &&
1271            cType.match("text/*") &&
1272            cType.getParameter("charset") == null) {
1273            /*
1274             * Set a default charset for text parts.
1275             * We really should examine the data to determine
1276             * whether or not it's all ASCII, but that's too
1277             * expensive so we make an assumption: If we
1278             * chose 7bit encoding for this data, it's probably
1279             * ASCII. (MimeUtility.getEncoding will choose
1280             * 7bit only in this case, but someone might've
1281             * set the Content-Transfer-Encoding header manually.)
1282             */

1283            String JavaDoc charset;
1284            String JavaDoc enc = part.getEncoding();
1285            if (enc != null && enc.equalsIgnoreCase("7bit"))
1286            charset = "us-ascii";
1287            else
1288            charset = MimeUtility.getDefaultMIMECharset();
1289            cType.setParameter("charset", charset);
1290            type = cType.toString();
1291        }
1292        }
1293
1294        // Now, let's update our own headers ...
1295

1296        // Content-type, but only if we don't already have one
1297
if (needCTHeader) {
1298        /*
1299         * Pull out "filename" from Content-Disposition, and
1300         * use that to set the "name" parameter. This is to
1301         * satisfy older MUAs (DtMail, Roam and probably
1302         * a bunch of others).
1303         */

1304        String JavaDoc s = part.getHeader("Content-Disposition", null);
1305        if (s != null) {
1306            // Parse the header ..
1307
ContentDisposition JavaDoc cd = new ContentDisposition JavaDoc(s);
1308            String JavaDoc filename = cd.getParameter("filename");
1309            if (filename != null) {
1310            cType.setParameter("name", filename);
1311            type = cType.toString();
1312            }
1313        }
1314        
1315        part.setHeader("Content-Type", type);
1316        }
1317    } catch (IOException ex) {
1318        throw new MessagingException("IOException updating headers", ex);
1319    }
1320    }
1321
1322    static void invalidateContentHeaders(MimePart JavaDoc part)
1323                    throws MessagingException {
1324    part.removeHeader("Content-Type");
1325    part.removeHeader("Content-Transfer-Encoding");
1326    }
1327    
1328    static void writeTo(MimePart JavaDoc part, OutputStream os, String JavaDoc[] ignoreList)
1329            throws IOException, MessagingException {
1330
1331    // see if we already have a LOS
1332
LineOutputStream los = null;
1333    if (os instanceof LineOutputStream) {
1334        los = (LineOutputStream) os;
1335    } else {
1336        los = new LineOutputStream(os);
1337    }
1338
1339    // First, write out the header
1340
Enumeration hdrLines = part.getNonMatchingHeaderLines(ignoreList);
1341    while (hdrLines.hasMoreElements())
1342        los.writeln((String JavaDoc)hdrLines.nextElement());
1343
1344    // The CRLF separator between header and content
1345
los.writeln();
1346
1347    // Finally, the content. Encode if required.
1348
// XXX: May need to account for ESMTP ?
1349
os = MimeUtility.encode(os, part.getEncoding());
1350    part.getDataHandler().writeTo(os);
1351    os.flush(); // Needed to complete encoding
1352
}
1353}
1354
1355/**
1356 * A package-private class that serves just as a "flag" that
1357 * we're using a special data handler to cache getContent data.
1358 */

1359class CachedDataHandler extends DataHandler {
1360    public CachedDataHandler(Object JavaDoc o, String JavaDoc mimeType) {
1361    super(o, mimeType);
1362    }
1363}
1364
Popular Tags