< prev index next >

jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/MimeBodyPart.java

Print this page


   1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @(#)MimeBodyPart.java      1.52 03/02/12
  28  */
  29 
  30 
  31 
  32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
  33 
  34 
  35 import com.sun.xml.internal.messaging.saaj.packaging.mime.Header;
  36 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
  37 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.OutputUtil;
  38 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
  39 import com.sun.xml.internal.messaging.saaj.util.FinalArrayList;
  40 
  41 import javax.activation.DataHandler;
  42 import java.io.BufferedInputStream;
  43 import java.io.ByteArrayInputStream;
  44 import java.io.IOException;
  45 import java.io.InputStream;
  46 import java.io.OutputStream;
  47 import java.io.UnsupportedEncodingException;
  48 import java.util.List;
  49 import javax.activation.DataSource;
  50 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart;
  51 
  52 /**
  53  * This class represents a MIME body part.
  54  * MimeBodyParts are contained in <code>MimeMultipart</code>
  55  * objects. <p>
  56  *
  57  * MimeBodyPart uses the <code>InternetHeaders</code> class to parse
  58  * and store the headers of that body part. <p>
  59  *
  60  * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
  61  *
  62  * RFC 822 header fields <strong>must</strong> contain only
  63  * US-ASCII characters. MIME allows non ASCII characters to be present
  64  * in certain portions of certain headers, by encoding those characters.
  65  * RFC 2047 specifies the rules for doing this. The MimeUtility
  66  * class provided in this package can be used to to achieve this.
  67  * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
  68  * <code>addHeaderLine</code> methods are responsible for enforcing
  69  * the MIME requirements for the specified headers.  In addition, these
  70  * header fields must be folded (wrapped) before being sent if they
  71  * exceed the line length limitation for the transport (1000 bytes for
  72  * SMTP).  Received headers may have been folded.  The application is
  73  * responsible for folding and unfolding headers as appropriate. <p>
  74  *
  75  * @author John Mani
  76  * @author Bill Shannon
  77  * @see MimeUtility
  78  */
  79 
  80 public final class MimeBodyPart {
  81 
  82     /**
  83      * This part should be presented as an attachment.
  84      * @see #getDisposition
  85      * @see #setDisposition
  86      */
  87     public static final String ATTACHMENT = "attachment";
  88 
  89     /**
  90      * This part should be presented inline.
  91      * @see #getDisposition
  92      * @see #setDisposition
  93      */


 162      * message.
 163      */
 164     public MimeBodyPart() {
 165         headers = new InternetHeaders();
 166     }
 167 
 168     /**
 169      * Constructs a MimeBodyPart by reading and parsing the data from
 170      * the specified input stream. The parser consumes data till the end
 171      * of the given input stream.  The input stream must start at the
 172      * beginning of a valid MIME body part and must terminate at the end
 173      * of that body part. <p>
 174      *
 175      * Note that the "boundary" string that delimits body parts must
 176      * <strong>not</strong> be included in the input stream. The intention
 177      * is that the MimeMultipart parser will extract each body part's bytes
 178      * from a multipart stream and feed them into this constructor, without
 179      * the delimiter strings.
 180      *
 181      * @param   is      the body part Input Stream


 182      */
 183     public MimeBodyPart(InputStream is) throws MessagingException {
 184         if (!(is instanceof ByteArrayInputStream) &&
 185                 !(is instanceof BufferedInputStream) &&
 186                 !(is instanceof SharedInputStream))
 187             is = new BufferedInputStream(is);
 188 
 189         headers = new InternetHeaders(is);
 190 
 191         if (is instanceof SharedInputStream) {
 192             SharedInputStream sis = (SharedInputStream) is;
 193             contentStream = sis.newStream(sis.getPosition(), -1);
 194         } else {
 195             ByteOutputStream bos = null;
 196             try {
 197                 bos = new ByteOutputStream();
 198                 bos.write(is);
 199                 content = bos.getBytes();
 200                 contentLength = bos.getCount();
 201             } catch (IOException ioex) {
 202                 throw new MessagingException("Error reading input stream", ioex);
 203             } finally {
 204                 if (bos != null)
 205                     bos.close();
 206             }
 207         }
 208 
 209     }
 210 
 211     /**
 212      * Constructs a MimeBodyPart using the given header and
 213      * content bytes. <p>
 214      *
 215      * Used by providers.
 216      *
 217      * @param   headers The header of this part
 218      * @param   content bytes representing the body of this part.

 219      */
 220     public MimeBodyPart(InternetHeaders headers, byte[] content, int len) {
 221         this.headers = headers;
 222         this.content = content;
 223         this.contentLength = len;
 224     }
 225 
 226     public MimeBodyPart(
 227         InternetHeaders headers, byte[] content, int start,  int len) {
 228         this.headers = headers;
 229         this.content = content;
 230         this.start = start;
 231         this.contentLength = len;
 232     }
 233 
 234     public MimeBodyPart(MIMEPart part) {
 235        mimePart = part;
 236        headers = new InternetHeaders();
 237        List<? extends com.sun.xml.internal.org.jvnet.mimepull.Header> hdrs = mimePart.getAllHeaders();
 238         for (com.sun.xml.internal.org.jvnet.mimepull.Header hd : hdrs) {
 239             headers.addHeader(hd.getName(), hd.getValue());
 240         }
 241     }
 242     /**
 243      * Return the containing <code>MimeMultipart</code> object,
 244      * or <code>null</code> if not known.

 245      */
 246     public MimeMultipart getParent() {
 247         return parent;
 248     }
 249 
 250     /**
 251      * Set the parent of this <code>MimeBodyPart</code> to be the specified
 252      * <code>MimeMultipart</code>.  Normally called by <code>MimeMultipart</code>'s
 253      * <code>addBodyPart</code> method.  <code>parent</code> may be
 254      * <code>null</code> if the <code>MimeBodyPart</code> is being removed
 255      * from its containing <code>MimeMultipart</code>.

 256      * @since   JavaMail 1.1
 257      */
 258     public void setParent(MimeMultipart parent) {
 259         this.parent = parent;
 260     }
 261 
 262     /**
 263      * Return the size of the content of this body part in bytes.
 264      * Return -1 if the size cannot be determined. <p>
 265      *
 266      * Note that this number may not be an exact measure of the
 267      * content size and may or may not account for any transfer
 268      * encoding of the content. <p>
 269      *
 270      * This implementation returns the size of the <code>content</code>
 271      * array (if not null), or, if <code>contentStream</code> is not
 272      * null, and the <code>available</code> method returns a positive
 273      * number, it returns that number as the size.  Otherwise, it returns
 274      * -1.
 275      *


 334         String s = getHeader("Content-Type", null);
 335         if (s == null)
 336             s = "text/plain";
 337 
 338         return s;
 339     }
 340 
 341     /**
 342      * Is this MimeBodyPart of the specified MIME type?  This method
 343      * compares <strong>only the <code>primaryType</code> and
 344      * <code>subType</code></strong>.
 345      * The parameters of the content types are ignored. <p>
 346      *
 347      * For example, this method will return <code>true</code> when
 348      * comparing a MimeBodyPart of content type <strong>"text/plain"</strong>
 349      * with <strong>"text/plain; charset=foobar"</strong>. <p>
 350      *
 351      * If the <code>subType</code> of <code>mimeType</code> is the
 352      * special character '*', then the subtype is ignored during the
 353      * comparison.



 354      */
 355     public boolean isMimeType(String mimeType) {
 356         boolean result;
 357         // XXX - lots of room for optimization here!
 358         try {
 359             ContentType ct = new ContentType(getContentType());
 360             result = ct.match(mimeType);
 361         } catch (ParseException ex) {
 362             result = getContentType().equalsIgnoreCase(mimeType);
 363         }
 364         return result;
 365     }
 366 
 367     /**
 368      * Returns the value of the "Content-Disposition" header field.
 369      * This represents the disposition of this part. The disposition
 370      * describes how the part should be presented to the user. <p>
 371      *
 372      * If the Content-Disposition field is unavailable,
 373      * null is returned. <p>
 374      *
 375      * This implementation uses <code>getHeader(name)</code>
 376      * to obtain the requisite header field.
 377      *



 378      * @see #headers
 379      */
 380     public String getDisposition() throws MessagingException {
 381         String s = getHeader("Content-Disposition", null);
 382 
 383         if (s == null)
 384             return null;
 385 
 386         ContentDisposition cd = new ContentDisposition(s);
 387         return cd.getDisposition();
 388     }
 389 
 390     /**
 391      * Set the "Content-Disposition" header field of this body part.
 392      * If the disposition is null, any existing "Content-Disposition"
 393      * header field is removed.
 394      *



 395      * @exception       IllegalStateException if this body part is
 396      *                  obtained from a READ_ONLY folder.
 397      */
 398     public void setDisposition(String disposition) throws MessagingException {
 399         if (disposition == null)
 400             removeHeader("Content-Disposition");
 401         else {
 402             String s = getHeader("Content-Disposition", null);
 403             if (s != null) {
 404                 /* A Content-Disposition header already exists ..
 405                  *
 406                  * Override disposition, but attempt to retain
 407                  * existing disposition parameters
 408                  */
 409                 ContentDisposition cd = new ContentDisposition(s);
 410                 cd.setDisposition(disposition);
 411                 disposition = cd.toString();
 412             }
 413             setHeader("Content-Disposition", disposition);
 414         }
 415     }
 416 
 417     /**
 418      * Returns the content transfer encoding from the
 419      * "Content-Transfer-Encoding" header
 420      * field. Returns <code>null</code> if the header is unavailable
 421      * or its value is absent. <p>
 422      *
 423      * This implementation uses <code>getHeader(name)</code>
 424      * to obtain the requisite header field.
 425      *



 426      * @see #headers
 427      */
 428     public String getEncoding() throws MessagingException {
 429         String s = getHeader("Content-Transfer-Encoding", null);
 430 
 431         if (s == null)
 432             return null;
 433 
 434         s = s.trim();   // get rid of trailing spaces
 435         // quick check for known values to avoid unnecessary use
 436         // of tokenizer.
 437         if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") ||
 438             s.equalsIgnoreCase("quoted-printable") ||
 439             s.equalsIgnoreCase("base64"))
 440             return s;
 441 
 442         // Tokenize the header to obtain the encoding (skip comments)
 443         HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
 444 
 445         HeaderTokenizer.Token tk;


 448         for (;;) {
 449             tk = h.next(); // get a token
 450             tkType = tk.getType();
 451             if (tkType == HeaderTokenizer.Token.EOF)
 452             break; // done
 453             else if (tkType == HeaderTokenizer.Token.ATOM)
 454             return tk.getValue();
 455             else // invalid token, skip it.
 456             continue;
 457         }
 458         return s;
 459     }
 460 
 461     /**
 462      * Returns the value of the "Content-ID" header field. Returns
 463      * <code>null</code> if the field is unavailable or its value is
 464      * absent. <p>
 465      *
 466      * This implementation uses <code>getHeader(name)</code>
 467      * to obtain the requisite header field.


 468      */
 469     public String getContentID() {
 470         return getHeader("Content-ID", null);
 471     }
 472 
 473     /**
 474      * Set the "Content-ID" header field of this body part.
 475      * If the <code>cid</code> parameter is null, any existing
 476      * "Content-ID" is removed.
 477      *

 478      * @exception       IllegalStateException if this body part is
 479      *                  obtained from a READ_ONLY folder.
 480      * @since           JavaMail 1.3
 481      */
 482     public void setContentID(String cid) {
 483         if (cid == null)
 484             removeHeader("Content-ID");
 485         else
 486             setHeader("Content-ID", cid);
 487     }
 488 
 489     /**
 490      * Return the value of the "Content-MD5" header field. Returns
 491      * <code>null</code> if this field is unavailable or its value
 492      * is absent. <p>
 493      *
 494      * This implementation uses <code>getHeader(name)</code>
 495      * to obtain the requisite header field.


 496      */
 497     public String getContentMD5() {
 498         return getHeader("Content-MD5", null);
 499     }
 500 
 501     /**
 502      * Set the "Content-MD5" header field of this body part.
 503      *


 504      * @exception       IllegalStateException if this body part is
 505      *                  obtained from a READ_ONLY folder.
 506      */
 507     public void setContentMD5(String md5) {
 508         setHeader("Content-MD5", md5);
 509     }
 510 
 511     /**
 512      * Get the languages specified in the Content-Language header
 513      * of this MimeBodyPart. The Content-Language header is defined by
 514      * RFC 1766. Returns <code>null</code> if this header is not
 515      * available or its value is absent. <p>
 516      *
 517      * This implementation uses <code>getHeader(name)</code>
 518      * to obtain the requisite header field.



 519      */
 520     public String[] getContentLanguage() throws MessagingException {
 521         String s = getHeader("Content-Language", null);
 522 
 523         if (s == null)
 524             return null;
 525 
 526         // Tokenize the header to obtain the Language-tags (skip comments)
 527         HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
 528         FinalArrayList<String> v = new FinalArrayList<String>();
 529 
 530         HeaderTokenizer.Token tk;
 531         int tkType;
 532 
 533         while (true) {
 534             tk = h.next(); // get a language-tag
 535             tkType = tk.getType();
 536             if (tkType == HeaderTokenizer.Token.EOF)
 537             break; // done
 538             else if (tkType == HeaderTokenizer.Token.ATOM) v.add(tk.getValue());


 646         }
 647 
 648         try {
 649             setHeader("Content-Description", MimeUtility.fold(21,
 650             MimeUtility.encodeText(description, charset, null)));
 651         } catch (UnsupportedEncodingException uex) {
 652             throw new MessagingException("Encoding error", uex);
 653         }
 654     }
 655 
 656     /**
 657      * Get the filename associated with this body part. <p>
 658      *
 659      * Returns the value of the "filename" parameter from the
 660      * "Content-Disposition" header field of this body part. If its
 661      * not available, returns the value of the "name" parameter from
 662      * the "Content-Type" header field of this body part.
 663      * Returns <code>null</code> if both are absent.
 664      *
 665      * @return  filename

 666      */
 667     public String getFileName() throws MessagingException {
 668         String filename = null;
 669         String s = getHeader("Content-Disposition", null);
 670 
 671         if (s != null) {
 672             // Parse the header ..
 673             ContentDisposition cd = new ContentDisposition(s);
 674             filename = cd.getParameter("filename");
 675         }
 676         if (filename == null) {
 677             // Still no filename ? Try the "name" ContentType parameter
 678             s = getHeader("Content-Type", null);
 679             if (s != null) {
 680             try {
 681                 ContentType ct = new ContentType(s);
 682                 filename = ct.getParameter("name");
 683             } catch (ParseException pex) { }    // ignore it
 684             }
 685         }
 686         return filename;
 687     }
 688 
 689     /**
 690      * Set the filename associated with this body part, if possible. <p>
 691      *
 692      * Sets the "filename" parameter of the "Content-Disposition"
 693      * header field of this body part.
 694      *



 695      * @exception       IllegalStateException if this body part is
 696      *                  obtained from a READ_ONLY folder.
 697      */
 698     public void setFileName(String filename) throws MessagingException {
 699         // Set the Content-Disposition "filename" parameter
 700         String s = getHeader("Content-Disposition", null);
 701         ContentDisposition cd =
 702             new ContentDisposition(s == null ? ATTACHMENT : s);
 703         cd.setParameter("filename", filename);
 704         setHeader("Content-Disposition", cd.toString());
 705 
 706         /* Also attempt to set the Content-Type "name" parameter,
 707          * to satisfy ancient MUAs.
 708          * XXX: This is not RFC compliant, and hence should really
 709          * be conditional based on some property. Fix this once we
 710          * figure out how to get at Properties from here !
 711          */
 712         s = getHeader("Content-Type", null);
 713         if (s != null) {
 714             try {


 752         }
 753         if (contentStream != null)
 754             return ((SharedInputStream)contentStream).newStream(0, -1);
 755         if (content != null)
 756             return new ByteArrayInputStream(content,start,contentLength);
 757 
 758         throw new MessagingException("No content");
 759     }
 760 
 761     /**
 762      * Return an InputStream to the raw data with any Content-Transfer-Encoding
 763      * intact.  This method is useful if the "Content-Transfer-Encoding"
 764      * header is incorrect or corrupt, which would prevent the
 765      * <code>getInputStream</code> method or <code>getContent</code> method
 766      * from returning the correct data.  In such a case the application may
 767      * use this method and attempt to decode the raw data itself. <p>
 768      *
 769      * This implementation simply calls the <code>getContentStream</code>
 770      * method.
 771      *




 772      * @see     #getInputStream
 773      * @see     #getContentStream
 774      * @since   JavaMail 1.2

 775      */
 776     public InputStream getRawInputStream() throws MessagingException {
 777         return getContentStream();
 778     }
 779 
 780     /**
 781      * Return a DataHandler for this body part's content. <p>
 782      *
 783      * The implementation provided here works just like the
 784      * the implementation in MimeMessage.


 785      */
 786     public DataHandler getDataHandler() {
 787         if (mimePart != null) {
 788             //return an inputstream
 789             return new DataHandler(new DataSource() {
 790 

 791                 public InputStream getInputStream() throws IOException {
 792                     return mimePart.read();
 793                 }
 794 

 795                 public OutputStream getOutputStream() throws IOException {
 796                     throw new UnsupportedOperationException("getOutputStream cannot be supported : You have enabled LazyAttachments Option");
 797                 }
 798 

 799                 public String getContentType() {
 800                     return mimePart.getContentType();
 801                 }
 802 

 803                 public String getName() {
 804                     return "MIMEPart Wrapped DataSource";
 805                 }
 806             });
 807         }
 808         if (dh == null)
 809             dh = new DataHandler(new MimePartDataSource(this));
 810         return dh;
 811     }
 812 
 813     /**
 814      * Return the content as a java object. The type of the object
 815      * returned is of course dependent on the content itself. For
 816      * example, the native format of a text/plain content is usually
 817      * a String object. The native format for a "multipart"
 818      * content is always a MimeMultipart subclass. For content types that are
 819      * unknown to the DataHandler system, an input stream is returned
 820      * as the content. <p>
 821      *
 822      * This implementation obtains the content from the DataHandler.


 873             setContent((MimeMultipart)o);
 874         } else {
 875             setDataHandler(new DataHandler(o, type));
 876         }
 877     }
 878 
 879     /**
 880      * Convenience method that sets the given String as this
 881      * part's content, with a MIME type of "text/plain". If the
 882      * string contains non US-ASCII characters, it will be encoded
 883      * using the platform's default charset. The charset is also
 884      * used to set the "charset" parameter. <p>
 885      *
 886      * Note that there may be a performance penalty if
 887      * <code>text</code> is large, since this method may have
 888      * to scan all the characters to determine what charset to
 889      * use. <p>
 890      * If the charset is already known, use the
 891      * setText() version that takes the charset parameter.
 892      *


 893      * @see     #setText(String text, String charset)
 894      */
 895     public void setText(String text) {
 896         setText(text, null);
 897     }
 898 
 899     /**
 900      * Convenience method that sets the given String as this part's
 901      * content, with a MIME type of "text/plain" and the specified
 902      * charset. The given Unicode string will be charset-encoded
 903      * using the specified charset. The charset is also used to set
 904      * the "charset" parameter.



 905      */
 906     public void setText(String text, String charset) {
 907         if (charset == null) {
 908             if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII)
 909                 charset = MimeUtility.getDefaultMIMECharset();
 910             else
 911                 charset = "us-ascii";
 912         }
 913         setContent(text, "text/plain; charset=" +
 914                 MimeUtility.quote(charset, HeaderTokenizer.MIME));
 915     }
 916 
 917     /**
 918      * This method sets the body part's content to a MimeMultipart object.
 919      *
 920      * @param  mp       The multipart object that is the Message's content
 921      * @exception       IllegalStateException if this body part is
 922      *                  obtained from a READ_ONLY folder.
 923      */
 924     public void setContent(MimeMultipart mp) {
 925         if (mimePart != null) {
 926             mimePart = null;
 927         }
 928         setDataHandler(new DataHandler(mp, mp.getContentType().toString()));
 929         mp.setParent(this);
 930     }
 931 
 932     /**
 933      * Output the body part as an RFC 822 format stream.
 934      *
 935      * @exception MessagingException


 936      * @exception IOException   if an error occurs writing to the
 937      *                          stream or if an error is generated
 938      *                          by the javax.activation layer.
 939      * @see DataHandler#writeTo
 940      */
 941     public void writeTo(OutputStream os)
 942                                 throws IOException, MessagingException {
 943 
 944         // First, write out the header
 945         List<String> hdrLines = headers.getAllHeaderLines();
 946         int sz = hdrLines.size();
 947         for( int i=0; i<sz; i++ )
 948             OutputUtil.writeln(hdrLines.get(i),os);
 949 
 950         // The CRLF separator between header and content
 951         OutputUtil.writeln(os);
 952 
 953         // Finally, the content.
 954         // XXX: May need to account for ESMTP ?
 955         if (contentStream != null) {


1016     public void setHeader(String name, String value) {
1017         headers.setHeader(name, value);
1018     }
1019 
1020     /**
1021      * Add this value to the existing values for this header_name.
1022      * Note that RFC 822 headers must contain only US-ASCII
1023      * characters, so a header that contains non US-ASCII characters
1024      * must be encoded as per the rules of RFC 2047.
1025      *
1026      * @param   name    header name
1027      * @param   value   header value
1028      * @see     MimeUtility
1029      */
1030     public void addHeader(String name, String value) {
1031         headers.addHeader(name, value);
1032     }
1033 
1034     /**
1035      * Remove all headers with this name.


1036      */
1037     public void removeHeader(String name) {
1038         headers.removeHeader(name);
1039     }
1040 
1041     /**
1042      * Return all the headers from this Message as an Enumeration of
1043      * Header objects.


1044      */
1045     public List<? extends Header> getAllHeaders() {
1046         return headers.getAllHeaders();
1047     }
1048 
1049 
1050     /**
1051      * Add a header line to this body part


1052      */
1053     public void addHeaderLine(String line) {
1054         headers.addHeaderLine(line);
1055     }
1056 
1057     /**
1058      * Examine the content of this body part and update the appropriate
1059      * MIME headers.  Typical headers that get set here are
1060      * <code>Content-Type</code> and <code>Content-Transfer-Encoding</code>.
1061      * Headers might need to be updated in two cases:
1062      *
1063      * <br>
1064      * - A message being crafted by a mail application will certainly
1065      * need to activate this method at some point to fill up its internal
1066      * headers.
1067      *
1068      * <br>
1069      * - A message read in from a Store will have obtained
1070      * all its headers from the store, and so doesn't need this.
1071      * However, if this message is editable and if any edits have
1072      * been made to either the content or message structure, we might
1073      * need to resync our headers.
1074      *
1075      * <br>
1076      * In both cases this method is typically called by the
1077      * <code>Message.saveChanges</code> method.


1078      */
1079     protected void updateHeaders() throws MessagingException {
1080         DataHandler dh = getDataHandler();
1081         /*
1082          * Code flow indicates null is never returned from
1083          * getdataHandler() - findbugs
1084          */
1085         //if (dh == null) // Huh ?
1086         //    return;
1087 
1088         try {
1089             String type = dh.getContentType();
1090             boolean composite = false;
1091             boolean needCTHeader = getHeader("Content-Type") == null;
1092 
1093             ContentType cType = new ContentType(type);
1094             if (cType.match("multipart/*")) {
1095                 // If multipart, recurse
1096                 composite = true;
1097                 Object o = dh.getContent();


   1 /*
   2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @(#)MimeBodyPart.java      1.52 03/02/12
  28  */
  29 
  30 
  31 
  32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
  33 
  34 

  35 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
  36 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.OutputUtil;
  37 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
  38 import com.sun.xml.internal.messaging.saaj.util.FinalArrayList;
  39 
  40 import javax.activation.DataHandler;
  41 import java.io.BufferedInputStream;
  42 import java.io.ByteArrayInputStream;
  43 import java.io.IOException;
  44 import java.io.InputStream;
  45 import java.io.OutputStream;
  46 import java.io.UnsupportedEncodingException;
  47 import java.util.List;
  48 import javax.activation.DataSource;
  49 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart;
  50 
  51 /**
  52  * This class represents a MIME body part.
  53  * MimeBodyParts are contained in <code>MimeMultipart</code>
  54  * objects.
  55  * <p>
  56  * MimeBodyPart uses the <code>InternetHeaders</code> class to parse
  57  * and store the headers of that body part.
  58  *
  59  * <hr><strong>A note on RFC 822 and MIME headers</strong>
  60  *
  61  * RFC 822 header fields <strong>must</strong> contain only
  62  * US-ASCII characters. MIME allows non ASCII characters to be present
  63  * in certain portions of certain headers, by encoding those characters.
  64  * RFC 2047 specifies the rules for doing this. The MimeUtility
  65  * class provided in this package can be used to to achieve this.
  66  * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
  67  * <code>addHeaderLine</code> methods are responsible for enforcing
  68  * the MIME requirements for the specified headers.  In addition, these
  69  * header fields must be folded (wrapped) before being sent if they
  70  * exceed the line length limitation for the transport (1000 bytes for
  71  * SMTP).  Received headers may have been folded.  The application is
  72  * responsible for folding and unfolding headers as appropriate.
  73  *
  74  * @author John Mani
  75  * @author Bill Shannon
  76  * @see MimeUtility
  77  */
  78 
  79 public final class MimeBodyPart {
  80 
  81     /**
  82      * This part should be presented as an attachment.
  83      * @see #getDisposition
  84      * @see #setDisposition
  85      */
  86     public static final String ATTACHMENT = "attachment";
  87 
  88     /**
  89      * This part should be presented inline.
  90      * @see #getDisposition
  91      * @see #setDisposition
  92      */


 161      * message.
 162      */
 163     public MimeBodyPart() {
 164         headers = new InternetHeaders();
 165     }
 166 
 167     /**
 168      * Constructs a MimeBodyPart by reading and parsing the data from
 169      * the specified input stream. The parser consumes data till the end
 170      * of the given input stream.  The input stream must start at the
 171      * beginning of a valid MIME body part and must terminate at the end
 172      * of that body part. <p>
 173      *
 174      * Note that the "boundary" string that delimits body parts must
 175      * <strong>not</strong> be included in the input stream. The intention
 176      * is that the MimeMultipart parser will extract each body part's bytes
 177      * from a multipart stream and feed them into this constructor, without
 178      * the delimiter strings.
 179      *
 180      * @param   is      the body part Input Stream
 181      *
 182      * @exception MessagingException in case of error
 183      */
 184     public MimeBodyPart(InputStream is) throws MessagingException {
 185         if (!(is instanceof ByteArrayInputStream) &&
 186                 !(is instanceof BufferedInputStream) &&
 187                 !(is instanceof SharedInputStream))
 188             is = new BufferedInputStream(is);
 189 
 190         headers = new InternetHeaders(is);
 191 
 192         if (is instanceof SharedInputStream) {
 193             SharedInputStream sis = (SharedInputStream) is;
 194             contentStream = sis.newStream(sis.getPosition(), -1);
 195         } else {
 196             ByteOutputStream bos = null;
 197             try {
 198                 bos = new ByteOutputStream();
 199                 bos.write(is);
 200                 content = bos.getBytes();
 201                 contentLength = bos.getCount();
 202             } catch (IOException ioex) {
 203                 throw new MessagingException("Error reading input stream", ioex);
 204             } finally {
 205                 if (bos != null)
 206                     bos.close();
 207             }
 208         }
 209 
 210     }
 211 
 212     /**
 213      * Constructs a MimeBodyPart using the given header and
 214      * content bytes. <p>
 215      *
 216      * Used by providers.
 217      *
 218      * @param   headers The header of this part
 219      * @param   content bytes representing the body of this part.
 220      * @param   len content length.
 221      */
 222     public MimeBodyPart(InternetHeaders headers, byte[] content, int len) {
 223         this.headers = headers;
 224         this.content = content;
 225         this.contentLength = len;
 226     }
 227 
 228     public MimeBodyPart(
 229         InternetHeaders headers, byte[] content, int start,  int len) {
 230         this.headers = headers;
 231         this.content = content;
 232         this.start = start;
 233         this.contentLength = len;
 234     }
 235 
 236     public MimeBodyPart(MIMEPart part) {
 237        mimePart = part;
 238        headers = new InternetHeaders();
 239        List<? extends com.sun.xml.internal.org.jvnet.mimepull.Header> hdrs = mimePart.getAllHeaders();
 240         for (com.sun.xml.internal.org.jvnet.mimepull.Header hd : hdrs) {
 241             headers.addHeader(hd.getName(), hd.getValue());
 242         }
 243     }
 244     /**
 245      * Return the containing <code>MimeMultipart</code> object,
 246      * or <code>null</code> if not known.
 247      * @return parent part.
 248      */
 249     public MimeMultipart getParent() {
 250         return parent;
 251     }
 252 
 253     /**
 254      * Set the parent of this <code>MimeBodyPart</code> to be the specified
 255      * <code>MimeMultipart</code>.  Normally called by <code>MimeMultipart</code>'s
 256      * <code>addBodyPart</code> method.  <code>parent</code> may be
 257      * <code>null</code> if the <code>MimeBodyPart</code> is being removed
 258      * from its containing <code>MimeMultipart</code>.
 259      * @param parent parent part
 260      * @since   JavaMail 1.1
 261      */
 262     public void setParent(MimeMultipart parent) {
 263         this.parent = parent;
 264     }
 265 
 266     /**
 267      * Return the size of the content of this body part in bytes.
 268      * Return -1 if the size cannot be determined. <p>
 269      *
 270      * Note that this number may not be an exact measure of the
 271      * content size and may or may not account for any transfer
 272      * encoding of the content. <p>
 273      *
 274      * This implementation returns the size of the <code>content</code>
 275      * array (if not null), or, if <code>contentStream</code> is not
 276      * null, and the <code>available</code> method returns a positive
 277      * number, it returns that number as the size.  Otherwise, it returns
 278      * -1.
 279      *


 338         String s = getHeader("Content-Type", null);
 339         if (s == null)
 340             s = "text/plain";
 341 
 342         return s;
 343     }
 344 
 345     /**
 346      * Is this MimeBodyPart of the specified MIME type?  This method
 347      * compares <strong>only the <code>primaryType</code> and
 348      * <code>subType</code></strong>.
 349      * The parameters of the content types are ignored. <p>
 350      *
 351      * For example, this method will return <code>true</code> when
 352      * comparing a MimeBodyPart of content type <strong>"text/plain"</strong>
 353      * with <strong>"text/plain; charset=foobar"</strong>. <p>
 354      *
 355      * If the <code>subType</code> of <code>mimeType</code> is the
 356      * special character '*', then the subtype is ignored during the
 357      * comparison.
 358      *
 359      * @param mimeType string
 360      * @return true if it is valid mime type
 361      */
 362     public boolean isMimeType(String mimeType) {
 363         boolean result;
 364         // XXX - lots of room for optimization here!
 365         try {
 366             ContentType ct = new ContentType(getContentType());
 367             result = ct.match(mimeType);
 368         } catch (ParseException ex) {
 369             result = getContentType().equalsIgnoreCase(mimeType);
 370         }
 371         return result;
 372     }
 373 
 374     /**
 375      * Returns the value of the "Content-Disposition" header field.
 376      * This represents the disposition of this part. The disposition
 377      * describes how the part should be presented to the user. <p>
 378      *
 379      * If the Content-Disposition field is unavailable,
 380      * null is returned. <p>
 381      *
 382      * This implementation uses <code>getHeader(name)</code>
 383      * to obtain the requisite header field.
 384      *
 385      * @return content disposition
 386      * @exception MessagingException in case of error
 387      *
 388      * @see #headers
 389      */
 390     public String getDisposition() throws MessagingException {
 391         String s = getHeader("Content-Disposition", null);
 392 
 393         if (s == null)
 394             return null;
 395 
 396         ContentDisposition cd = new ContentDisposition(s);
 397         return cd.getDisposition();
 398     }
 399 
 400     /**
 401      * Set the "Content-Disposition" header field of this body part.
 402      * If the disposition is null, any existing "Content-Disposition"
 403      * header field is removed.
 404      *
 405      * @param disposition value
 406      *
 407      * @exception MessagingException in case of error
 408      * @exception       IllegalStateException if this body part is
 409      *                  obtained from a READ_ONLY folder.
 410      */
 411     public void setDisposition(String disposition) throws MessagingException {
 412         if (disposition == null)
 413             removeHeader("Content-Disposition");
 414         else {
 415             String s = getHeader("Content-Disposition", null);
 416             if (s != null) {
 417                 /* A Content-Disposition header already exists ..
 418                  *
 419                  * Override disposition, but attempt to retain
 420                  * existing disposition parameters
 421                  */
 422                 ContentDisposition cd = new ContentDisposition(s);
 423                 cd.setDisposition(disposition);
 424                 disposition = cd.toString();
 425             }
 426             setHeader("Content-Disposition", disposition);
 427         }
 428     }
 429 
 430     /**
 431      * Returns the content transfer encoding from the
 432      * "Content-Transfer-Encoding" header
 433      * field. Returns <code>null</code> if the header is unavailable
 434      * or its value is absent. <p>
 435      *
 436      * This implementation uses <code>getHeader(name)</code>
 437      * to obtain the requisite header field.
 438      *
 439      * @return encoding
 440      * @exception MessagingException in case of error
 441      *
 442      * @see #headers
 443      */
 444     public String getEncoding() throws MessagingException {
 445         String s = getHeader("Content-Transfer-Encoding", null);
 446 
 447         if (s == null)
 448             return null;
 449 
 450         s = s.trim();   // get rid of trailing spaces
 451         // quick check for known values to avoid unnecessary use
 452         // of tokenizer.
 453         if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") ||
 454             s.equalsIgnoreCase("quoted-printable") ||
 455             s.equalsIgnoreCase("base64"))
 456             return s;
 457 
 458         // Tokenize the header to obtain the encoding (skip comments)
 459         HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
 460 
 461         HeaderTokenizer.Token tk;


 464         for (;;) {
 465             tk = h.next(); // get a token
 466             tkType = tk.getType();
 467             if (tkType == HeaderTokenizer.Token.EOF)
 468             break; // done
 469             else if (tkType == HeaderTokenizer.Token.ATOM)
 470             return tk.getValue();
 471             else // invalid token, skip it.
 472             continue;
 473         }
 474         return s;
 475     }
 476 
 477     /**
 478      * Returns the value of the "Content-ID" header field. Returns
 479      * <code>null</code> if the field is unavailable or its value is
 480      * absent. <p>
 481      *
 482      * This implementation uses <code>getHeader(name)</code>
 483      * to obtain the requisite header field.
 484      *
 485      * @return conent id
 486      */
 487     public String getContentID() {
 488         return getHeader("Content-ID", null);
 489     }
 490 
 491     /**
 492      * Set the "Content-ID" header field of this body part.
 493      * If the <code>cid</code> parameter is null, any existing
 494      * "Content-ID" is removed.
 495      *
 496      * @param cid content id
 497      * @exception       IllegalStateException if this body part is
 498      *                  obtained from a READ_ONLY folder.
 499      * @since           JavaMail 1.3
 500      */
 501     public void setContentID(String cid) {
 502         if (cid == null)
 503             removeHeader("Content-ID");
 504         else
 505             setHeader("Content-ID", cid);
 506     }
 507 
 508     /**
 509      * Return the value of the "Content-MD5" header field. Returns
 510      * <code>null</code> if this field is unavailable or its value
 511      * is absent. <p>
 512      *
 513      * This implementation uses <code>getHeader(name)</code>
 514      * to obtain the requisite header field.
 515      *
 516      * @return content MD5 sum
 517      */
 518     public String getContentMD5() {
 519         return getHeader("Content-MD5", null);
 520     }
 521 
 522     /**
 523      * Set the "Content-MD5" header field of this body part.
 524      *
 525      * @param md5 content md5 sum
 526      *
 527      * @exception       IllegalStateException if this body part is
 528      *                  obtained from a READ_ONLY folder.
 529      */
 530     public void setContentMD5(String md5) {
 531         setHeader("Content-MD5", md5);
 532     }
 533 
 534     /**
 535      * Get the languages specified in the Content-Language header
 536      * of this MimeBodyPart. The Content-Language header is defined by
 537      * RFC 1766. Returns <code>null</code> if this header is not
 538      * available or its value is absent. <p>
 539      *
 540      * This implementation uses <code>getHeader(name)</code>
 541      * to obtain the requisite header field.
 542      *
 543      * @return array of language tags
 544      * @exception MessagingException in case of error
 545      */
 546     public String[] getContentLanguage() throws MessagingException {
 547         String s = getHeader("Content-Language", null);
 548 
 549         if (s == null)
 550             return null;
 551 
 552         // Tokenize the header to obtain the Language-tags (skip comments)
 553         HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
 554         FinalArrayList<String> v = new FinalArrayList<String>();
 555 
 556         HeaderTokenizer.Token tk;
 557         int tkType;
 558 
 559         while (true) {
 560             tk = h.next(); // get a language-tag
 561             tkType = tk.getType();
 562             if (tkType == HeaderTokenizer.Token.EOF)
 563             break; // done
 564             else if (tkType == HeaderTokenizer.Token.ATOM) v.add(tk.getValue());


 672         }
 673 
 674         try {
 675             setHeader("Content-Description", MimeUtility.fold(21,
 676             MimeUtility.encodeText(description, charset, null)));
 677         } catch (UnsupportedEncodingException uex) {
 678             throw new MessagingException("Encoding error", uex);
 679         }
 680     }
 681 
 682     /**
 683      * Get the filename associated with this body part. <p>
 684      *
 685      * Returns the value of the "filename" parameter from the
 686      * "Content-Disposition" header field of this body part. If its
 687      * not available, returns the value of the "name" parameter from
 688      * the "Content-Type" header field of this body part.
 689      * Returns <code>null</code> if both are absent.
 690      *
 691      * @return  filename
 692      * @exception MessagingException in case of error
 693      */
 694     public String getFileName() throws MessagingException {
 695         String filename = null;
 696         String s = getHeader("Content-Disposition", null);
 697 
 698         if (s != null) {
 699             // Parse the header ..
 700             ContentDisposition cd = new ContentDisposition(s);
 701             filename = cd.getParameter("filename");
 702         }
 703         if (filename == null) {
 704             // Still no filename ? Try the "name" ContentType parameter
 705             s = getHeader("Content-Type", null);
 706             if (s != null) {
 707             try {
 708                 ContentType ct = new ContentType(s);
 709                 filename = ct.getParameter("name");
 710             } catch (ParseException pex) { }    // ignore it
 711             }
 712         }
 713         return filename;
 714     }
 715 
 716     /**
 717      * Set the filename associated with this body part, if possible. <p>
 718      *
 719      * Sets the "filename" parameter of the "Content-Disposition"
 720      * header field of this body part.
 721      *
 722      * @param filename filename
 723      *
 724      * @exception MessagingException in case of error
 725      * @exception       IllegalStateException if this body part is
 726      *                  obtained from a READ_ONLY folder.
 727      */
 728     public void setFileName(String filename) throws MessagingException {
 729         // Set the Content-Disposition "filename" parameter
 730         String s = getHeader("Content-Disposition", null);
 731         ContentDisposition cd =
 732             new ContentDisposition(s == null ? ATTACHMENT : s);
 733         cd.setParameter("filename", filename);
 734         setHeader("Content-Disposition", cd.toString());
 735 
 736         /* Also attempt to set the Content-Type "name" parameter,
 737          * to satisfy ancient MUAs.
 738          * XXX: This is not RFC compliant, and hence should really
 739          * be conditional based on some property. Fix this once we
 740          * figure out how to get at Properties from here !
 741          */
 742         s = getHeader("Content-Type", null);
 743         if (s != null) {
 744             try {


 782         }
 783         if (contentStream != null)
 784             return ((SharedInputStream)contentStream).newStream(0, -1);
 785         if (content != null)
 786             return new ByteArrayInputStream(content,start,contentLength);
 787 
 788         throw new MessagingException("No content");
 789     }
 790 
 791     /**
 792      * Return an InputStream to the raw data with any Content-Transfer-Encoding
 793      * intact.  This method is useful if the "Content-Transfer-Encoding"
 794      * header is incorrect or corrupt, which would prevent the
 795      * <code>getInputStream</code> method or <code>getContent</code> method
 796      * from returning the correct data.  In such a case the application may
 797      * use this method and attempt to decode the raw data itself. <p>
 798      *
 799      * This implementation simply calls the <code>getContentStream</code>
 800      * method.
 801      *
 802      * @return input stream
 803      *
 804      * @exception MessagingException in case of error
 805      *
 806      * @see     #getInputStream
 807      * @see     #getContentStream
 808      * @since   JavaMail 1.2
 809      *
 810      */
 811     public InputStream getRawInputStream() throws MessagingException {
 812         return getContentStream();
 813     }
 814 
 815     /**
 816      * Return a DataHandler for this body part's content. <p>
 817      *
 818      * The implementation provided here works just like the
 819      * the implementation in MimeMessage.
 820      *
 821      * @return data handler
 822      */
 823     public DataHandler getDataHandler() {
 824         if (mimePart != null) {
 825             //return an inputstream
 826             return new DataHandler(new DataSource() {
 827 
 828                 @Override
 829                 public InputStream getInputStream() throws IOException {
 830                     return mimePart.read();
 831                 }
 832 
 833                 @Override
 834                 public OutputStream getOutputStream() throws IOException {
 835                     throw new UnsupportedOperationException("getOutputStream cannot be supported : You have enabled LazyAttachments Option");
 836                 }
 837 
 838                 @Override
 839                 public String getContentType() {
 840                     return mimePart.getContentType();
 841                 }
 842 
 843                 @Override
 844                 public String getName() {
 845                     return "MIMEPart Wrapped DataSource";
 846                 }
 847             });
 848         }
 849         if (dh == null)
 850             dh = new DataHandler(new MimePartDataSource(this));
 851         return dh;
 852     }
 853 
 854     /**
 855      * Return the content as a java object. The type of the object
 856      * returned is of course dependent on the content itself. For
 857      * example, the native format of a text/plain content is usually
 858      * a String object. The native format for a "multipart"
 859      * content is always a MimeMultipart subclass. For content types that are
 860      * unknown to the DataHandler system, an input stream is returned
 861      * as the content. <p>
 862      *
 863      * This implementation obtains the content from the DataHandler.


 914             setContent((MimeMultipart)o);
 915         } else {
 916             setDataHandler(new DataHandler(o, type));
 917         }
 918     }
 919 
 920     /**
 921      * Convenience method that sets the given String as this
 922      * part's content, with a MIME type of "text/plain". If the
 923      * string contains non US-ASCII characters, it will be encoded
 924      * using the platform's default charset. The charset is also
 925      * used to set the "charset" parameter. <p>
 926      *
 927      * Note that there may be a performance penalty if
 928      * <code>text</code> is large, since this method may have
 929      * to scan all the characters to determine what charset to
 930      * use. <p>
 931      * If the charset is already known, use the
 932      * setText() version that takes the charset parameter.
 933      *
 934      * @param text string
 935      *
 936      * @see     #setText(String text, String charset)
 937      */
 938     public void setText(String text) {
 939         setText(text, null);
 940     }
 941 
 942     /**
 943      * Convenience method that sets the given String as this part's
 944      * content, with a MIME type of "text/plain" and the specified
 945      * charset. The given Unicode string will be charset-encoded
 946      * using the specified charset. The charset is also used to set
 947      * the "charset" parameter.
 948      *
 949      * @param text string
 950      * @param charset character set
 951      */
 952     public void setText(String text, String charset) {
 953         if (charset == null) {
 954             if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII)
 955                 charset = MimeUtility.getDefaultMIMECharset();
 956             else
 957                 charset = "us-ascii";
 958         }
 959         setContent(text, "text/plain; charset=" +
 960                 MimeUtility.quote(charset, HeaderTokenizer.MIME));
 961     }
 962 
 963     /**
 964      * This method sets the body part's content to a MimeMultipart object.
 965      *
 966      * @param  mp       The multipart object that is the Message's content
 967      * @exception       IllegalStateException if this body part is
 968      *                  obtained from a READ_ONLY folder.
 969      */
 970     public void setContent(MimeMultipart mp) {
 971         if (mimePart != null) {
 972             mimePart = null;
 973         }
 974         setDataHandler(new DataHandler(mp, mp.getContentType().toString()));
 975         mp.setParent(this);
 976     }
 977 
 978     /**
 979      * Output the body part as an RFC 822 format stream.
 980      *
 981      * @param os output stream
 982      *
 983      * @exception MessagingException in case of error
 984      * @exception IOException   if an error occurs writing to the
 985      *                          stream or if an error is generated
 986      *                          by the javax.activation layer.
 987      * @see DataHandler#writeTo
 988      */
 989     public void writeTo(OutputStream os)
 990                                 throws IOException, MessagingException {
 991 
 992         // First, write out the header
 993         List<String> hdrLines = headers.getAllHeaderLines();
 994         int sz = hdrLines.size();
 995         for( int i=0; i<sz; i++ )
 996             OutputUtil.writeln(hdrLines.get(i),os);
 997 
 998         // The CRLF separator between header and content
 999         OutputUtil.writeln(os);
1000 
1001         // Finally, the content.
1002         // XXX: May need to account for ESMTP ?
1003         if (contentStream != null) {


1064     public void setHeader(String name, String value) {
1065         headers.setHeader(name, value);
1066     }
1067 
1068     /**
1069      * Add this value to the existing values for this header_name.
1070      * Note that RFC 822 headers must contain only US-ASCII
1071      * characters, so a header that contains non US-ASCII characters
1072      * must be encoded as per the rules of RFC 2047.
1073      *
1074      * @param   name    header name
1075      * @param   value   header value
1076      * @see     MimeUtility
1077      */
1078     public void addHeader(String name, String value) {
1079         headers.addHeader(name, value);
1080     }
1081 
1082     /**
1083      * Remove all headers with this name.
1084      *
1085      * @param name header name
1086      */
1087     public void removeHeader(String name) {
1088         headers.removeHeader(name);
1089     }
1090 
1091     /**
1092      * Return all the headers from this Message as an Enumeration of
1093      * Header objects.
1094      *
1095      * @return all headers
1096      */
1097     public FinalArrayList<hdr> getAllHeaders() {
1098         return headers.getAllHeaders();
1099     }
1100 
1101 
1102     /**
1103      * Add a header line to this body part
1104      *
1105      * @param line header line to add
1106      */
1107     public void addHeaderLine(String line) {
1108         headers.addHeaderLine(line);
1109     }
1110 
1111     /**
1112      * Examine the content of this body part and update the appropriate
1113      * MIME headers.  Typical headers that get set here are
1114      * <code>Content-Type</code> and <code>Content-Transfer-Encoding</code>.
1115      * Headers might need to be updated in two cases:
1116      *
1117      * <br>
1118      * - A message being crafted by a mail application will certainly
1119      * need to activate this method at some point to fill up its internal
1120      * headers.
1121      *
1122      * <br>
1123      * - A message read in from a Store will have obtained
1124      * all its headers from the store, and so doesn't need this.
1125      * However, if this message is editable and if any edits have
1126      * been made to either the content or message structure, we might
1127      * need to resync our headers.
1128      *
1129      * <br>
1130      * In both cases this method is typically called by the
1131      * <code>Message.saveChanges</code> method.
1132      *
1133      * @exception MessagingException in case of error.
1134      */
1135     protected void updateHeaders() throws MessagingException {
1136         DataHandler dh = getDataHandler();
1137         /*
1138          * Code flow indicates null is never returned from
1139          * getdataHandler() - findbugs
1140          */
1141         //if (dh == null) // Huh ?
1142         //    return;
1143 
1144         try {
1145             String type = dh.getContentType();
1146             boolean composite = false;
1147             boolean needCTHeader = getHeader("Content-Type") == null;
1148 
1149             ContentType cType = new ContentType(type);
1150             if (cType.match("multipart/*")) {
1151                 // If multipart, recurse
1152                 composite = true;
1153                 Object o = dh.getContent();


< prev index next >