src/share/jaxws_classes/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/MimeBodyPart.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 /*
aoqi@0 27 * @(#)MimeBodyPart.java 1.52 03/02/12
aoqi@0 28 */
aoqi@0 29
aoqi@0 30
aoqi@0 31
aoqi@0 32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
aoqi@0 33
aoqi@0 34
aoqi@0 35 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
aoqi@0 36 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.OutputUtil;
aoqi@0 37 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
aoqi@0 38 import com.sun.xml.internal.messaging.saaj.util.FinalArrayList;
aoqi@0 39
aoqi@0 40 import java.util.logging.Level;
aoqi@0 41 import java.util.logging.Logger;
aoqi@0 42 import javax.activation.DataHandler;
aoqi@0 43 import java.io.BufferedInputStream;
aoqi@0 44 import java.io.ByteArrayInputStream;
aoqi@0 45 import java.io.IOException;
aoqi@0 46 import java.io.InputStream;
aoqi@0 47 import java.io.OutputStream;
aoqi@0 48 import java.io.UnsupportedEncodingException;
aoqi@0 49 import java.util.List;
aoqi@0 50 import javax.activation.DataSource;
aoqi@0 51 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart;
aoqi@0 52
aoqi@0 53 /**
aoqi@0 54 * This class represents a MIME body part.
aoqi@0 55 * MimeBodyParts are contained in <code>MimeMultipart</code>
aoqi@0 56 * objects. <p>
aoqi@0 57 *
aoqi@0 58 * MimeBodyPart uses the <code>InternetHeaders</code> class to parse
aoqi@0 59 * and store the headers of that body part. <p>
aoqi@0 60 *
aoqi@0 61 * <hr><strong>A note on RFC 822 and MIME headers</strong><p>
aoqi@0 62 *
aoqi@0 63 * RFC 822 header fields <strong>must</strong> contain only
aoqi@0 64 * US-ASCII characters. MIME allows non ASCII characters to be present
aoqi@0 65 * in certain portions of certain headers, by encoding those characters.
aoqi@0 66 * RFC 2047 specifies the rules for doing this. The MimeUtility
aoqi@0 67 * class provided in this package can be used to to achieve this.
aoqi@0 68 * Callers of the <code>setHeader</code>, <code>addHeader</code>, and
aoqi@0 69 * <code>addHeaderLine</code> methods are responsible for enforcing
aoqi@0 70 * the MIME requirements for the specified headers. In addition, these
aoqi@0 71 * header fields must be folded (wrapped) before being sent if they
aoqi@0 72 * exceed the line length limitation for the transport (1000 bytes for
aoqi@0 73 * SMTP). Received headers may have been folded. The application is
aoqi@0 74 * responsible for folding and unfolding headers as appropriate. <p>
aoqi@0 75 *
aoqi@0 76 * @author John Mani
aoqi@0 77 * @author Bill Shannon
aoqi@0 78 * @see MimeUtility
aoqi@0 79 */
aoqi@0 80
aoqi@0 81 public final class MimeBodyPart {
aoqi@0 82
aoqi@0 83 /**
aoqi@0 84 * This part should be presented as an attachment.
aoqi@0 85 * @see #getDisposition
aoqi@0 86 * @see #setDisposition
aoqi@0 87 */
aoqi@0 88 public static final String ATTACHMENT = "attachment";
aoqi@0 89
aoqi@0 90 /**
aoqi@0 91 * This part should be presented inline.
aoqi@0 92 * @see #getDisposition
aoqi@0 93 * @see #setDisposition
aoqi@0 94 */
aoqi@0 95 public static final String INLINE = "inline";
aoqi@0 96
aoqi@0 97
aoqi@0 98 // Paranoia:
aoqi@0 99 // allow this last minute change to be disabled if it causes problems
aoqi@0 100 private static boolean setDefaultTextCharset = true;
aoqi@0 101
aoqi@0 102 static {
aoqi@0 103 try {
aoqi@0 104 String s = System.getProperty("mail.mime.setdefaulttextcharset");
aoqi@0 105 // default to true
aoqi@0 106 setDefaultTextCharset = s == null || !s.equalsIgnoreCase("false");
aoqi@0 107 } catch (SecurityException sex) {
aoqi@0 108 // ignore it
aoqi@0 109 }
aoqi@0 110 }
aoqi@0 111
aoqi@0 112 /*
aoqi@0 113 Data is represented in one of three forms.
aoqi@0 114 Either we have a DataHandler, or byte[] as the raw content image, or the contentStream.
aoqi@0 115 It's OK to have more than one of them, provided that they are identical.
aoqi@0 116 */
aoqi@0 117
aoqi@0 118 /**
aoqi@0 119 * The DataHandler object representing this MimeBodyPart's content.
aoqi@0 120 */
aoqi@0 121 private DataHandler dh;
aoqi@0 122
aoqi@0 123 /**
aoqi@0 124 * Byte array that holds the bytes of the content of this MimeBodyPart.
aoqi@0 125 * Used in a pair with {@link #contentLength} to denote a regision of a buffer
aoqi@0 126 * as a valid data.
aoqi@0 127 */
aoqi@0 128 private byte[] content;
aoqi@0 129 private int contentLength;
aoqi@0 130 private int start = 0;
aoqi@0 131
aoqi@0 132 /**
aoqi@0 133 * If the data for this body part was supplied by an
aoqi@0 134 * InputStream that implements the SharedInputStream interface,
aoqi@0 135 * <code>contentStream</code> is another such stream representing
aoqi@0 136 * the content of this body part. In this case, <code>content</code>
aoqi@0 137 * will be null.
aoqi@0 138 *
aoqi@0 139 * @since JavaMail 1.2
aoqi@0 140 */
aoqi@0 141 private InputStream contentStream;
aoqi@0 142
aoqi@0 143
aoqi@0 144
aoqi@0 145 /**
aoqi@0 146 * The InternetHeaders object that stores all the headers
aoqi@0 147 * of this body part.
aoqi@0 148 */
aoqi@0 149 private final InternetHeaders headers;
aoqi@0 150
aoqi@0 151 /**
aoqi@0 152 * The <code>MimeMultipart</code> object containing this <code>MimeBodyPart</code>,
aoqi@0 153 * if known.
aoqi@0 154 * @since JavaMail 1.1
aoqi@0 155 */
aoqi@0 156 private MimeMultipart parent;
aoqi@0 157
aoqi@0 158 private MIMEPart mimePart;
aoqi@0 159
aoqi@0 160 /**
aoqi@0 161 * An empty MimeBodyPart object is created.
aoqi@0 162 * This body part maybe filled in by a client constructing a multipart
aoqi@0 163 * message.
aoqi@0 164 */
aoqi@0 165 public MimeBodyPart() {
aoqi@0 166 headers = new InternetHeaders();
aoqi@0 167 }
aoqi@0 168
aoqi@0 169 /**
aoqi@0 170 * Constructs a MimeBodyPart by reading and parsing the data from
aoqi@0 171 * the specified input stream. The parser consumes data till the end
aoqi@0 172 * of the given input stream. The input stream must start at the
aoqi@0 173 * beginning of a valid MIME body part and must terminate at the end
aoqi@0 174 * of that body part. <p>
aoqi@0 175 *
aoqi@0 176 * Note that the "boundary" string that delimits body parts must
aoqi@0 177 * <strong>not</strong> be included in the input stream. The intention
aoqi@0 178 * is that the MimeMultipart parser will extract each body part's bytes
aoqi@0 179 * from a multipart stream and feed them into this constructor, without
aoqi@0 180 * the delimiter strings.
aoqi@0 181 *
aoqi@0 182 * @param is the body part Input Stream
aoqi@0 183 */
aoqi@0 184 public MimeBodyPart(InputStream is) throws MessagingException {
aoqi@0 185 if (!(is instanceof ByteArrayInputStream) &&
aoqi@0 186 !(is instanceof BufferedInputStream) &&
aoqi@0 187 !(is instanceof SharedInputStream))
aoqi@0 188 is = new BufferedInputStream(is);
aoqi@0 189
aoqi@0 190 headers = new InternetHeaders(is);
aoqi@0 191
aoqi@0 192 if (is instanceof SharedInputStream) {
aoqi@0 193 SharedInputStream sis = (SharedInputStream) is;
aoqi@0 194 contentStream = sis.newStream(sis.getPosition(), -1);
aoqi@0 195 } else {
aoqi@0 196 try {
aoqi@0 197 ByteOutputStream bos = new ByteOutputStream();
aoqi@0 198 bos.write(is);
aoqi@0 199 content = bos.getBytes();
aoqi@0 200 contentLength = bos.getCount();
aoqi@0 201 } catch (IOException ioex) {
aoqi@0 202 throw new MessagingException("Error reading input stream", ioex);
aoqi@0 203 }
aoqi@0 204 }
aoqi@0 205
aoqi@0 206 }
aoqi@0 207
aoqi@0 208 /**
aoqi@0 209 * Constructs a MimeBodyPart using the given header and
aoqi@0 210 * content bytes. <p>
aoqi@0 211 *
aoqi@0 212 * Used by providers.
aoqi@0 213 *
aoqi@0 214 * @param headers The header of this part
aoqi@0 215 * @param content bytes representing the body of this part.
aoqi@0 216 */
aoqi@0 217 public MimeBodyPart(InternetHeaders headers, byte[] content, int len) {
aoqi@0 218 this.headers = headers;
aoqi@0 219 this.content = content;
aoqi@0 220 this.contentLength = len;
aoqi@0 221 }
aoqi@0 222
aoqi@0 223 public MimeBodyPart(
aoqi@0 224 InternetHeaders headers, byte[] content, int start, int len) {
aoqi@0 225 this.headers = headers;
aoqi@0 226 this.content = content;
aoqi@0 227 this.start = start;
aoqi@0 228 this.contentLength = len;
aoqi@0 229 }
aoqi@0 230
aoqi@0 231 public MimeBodyPart(MIMEPart part) {
aoqi@0 232 mimePart = part;
aoqi@0 233 headers = new InternetHeaders();
aoqi@0 234 List<? extends com.sun.xml.internal.org.jvnet.mimepull.Header> hdrs = mimePart.getAllHeaders();
aoqi@0 235 for (com.sun.xml.internal.org.jvnet.mimepull.Header hd : hdrs) {
aoqi@0 236 headers.addHeader(hd.getName(), hd.getValue());
aoqi@0 237 }
aoqi@0 238 }
aoqi@0 239 /**
aoqi@0 240 * Return the containing <code>MimeMultipart</code> object,
aoqi@0 241 * or <code>null</code> if not known.
aoqi@0 242 */
aoqi@0 243 public MimeMultipart getParent() {
aoqi@0 244 return parent;
aoqi@0 245 }
aoqi@0 246
aoqi@0 247 /**
aoqi@0 248 * Set the parent of this <code>MimeBodyPart</code> to be the specified
aoqi@0 249 * <code>MimeMultipart</code>. Normally called by <code>MimeMultipart</code>'s
aoqi@0 250 * <code>addBodyPart</code> method. <code>parent</code> may be
aoqi@0 251 * <code>null</code> if the <code>MimeBodyPart</code> is being removed
aoqi@0 252 * from its containing <code>MimeMultipart</code>.
aoqi@0 253 * @since JavaMail 1.1
aoqi@0 254 */
aoqi@0 255 public void setParent(MimeMultipart parent) {
aoqi@0 256 this.parent = parent;
aoqi@0 257 }
aoqi@0 258
aoqi@0 259 /**
aoqi@0 260 * Return the size of the content of this body part in bytes.
aoqi@0 261 * Return -1 if the size cannot be determined. <p>
aoqi@0 262 *
aoqi@0 263 * Note that this number may not be an exact measure of the
aoqi@0 264 * content size and may or may not account for any transfer
aoqi@0 265 * encoding of the content. <p>
aoqi@0 266 *
aoqi@0 267 * This implementation returns the size of the <code>content</code>
aoqi@0 268 * array (if not null), or, if <code>contentStream</code> is not
aoqi@0 269 * null, and the <code>available</code> method returns a positive
aoqi@0 270 * number, it returns that number as the size. Otherwise, it returns
aoqi@0 271 * -1.
aoqi@0 272 *
aoqi@0 273 * @return size in bytes, or -1 if not known
aoqi@0 274 */
aoqi@0 275 public int getSize() {
aoqi@0 276
aoqi@0 277 if (mimePart != null) {
aoqi@0 278 try {
aoqi@0 279 return mimePart.read().available();
aoqi@0 280 } catch (IOException ex) {
aoqi@0 281 return -1;
aoqi@0 282 }
aoqi@0 283 }
aoqi@0 284 if (content != null)
aoqi@0 285 return contentLength;
aoqi@0 286 if (contentStream != null) {
aoqi@0 287 try {
aoqi@0 288 int size = contentStream.available();
aoqi@0 289 // only believe the size if it's greate than zero, since zero
aoqi@0 290 // is the default returned by the InputStream class itself
aoqi@0 291 if (size > 0)
aoqi@0 292 return size;
aoqi@0 293 } catch (IOException ex) {
aoqi@0 294 // ignore it
aoqi@0 295 }
aoqi@0 296 }
aoqi@0 297 return -1;
aoqi@0 298 }
aoqi@0 299
aoqi@0 300 /**
aoqi@0 301 * Return the number of lines for the content of this MimeBodyPart.
aoqi@0 302 * Return -1 if this number cannot be determined. <p>
aoqi@0 303 *
aoqi@0 304 * Note that this number may not be an exact measure of the
aoqi@0 305 * content length and may or may not account for any transfer
aoqi@0 306 * encoding of the content. <p>
aoqi@0 307 *
aoqi@0 308 * This implementation returns -1.
aoqi@0 309 *
aoqi@0 310 * @return number of lines, or -1 if not known
aoqi@0 311 */
aoqi@0 312 public int getLineCount() {
aoqi@0 313 return -1;
aoqi@0 314 }
aoqi@0 315
aoqi@0 316 /**
aoqi@0 317 * Returns the value of the RFC 822 "Content-Type" header field.
aoqi@0 318 * This represents the content type of the content of this
aoqi@0 319 * body part. This value must not be null. If this field is
aoqi@0 320 * unavailable, "text/plain" should be returned. <p>
aoqi@0 321 *
aoqi@0 322 * This implementation uses <code>getHeader(name)</code>
aoqi@0 323 * to obtain the requisite header field.
aoqi@0 324 *
aoqi@0 325 * @return Content-Type of this body part
aoqi@0 326 */
aoqi@0 327 public String getContentType() {
aoqi@0 328 if (mimePart != null) {
aoqi@0 329 return mimePart.getContentType();
aoqi@0 330 }
aoqi@0 331 String s = getHeader("Content-Type", null);
aoqi@0 332 if (s == null)
aoqi@0 333 s = "text/plain";
aoqi@0 334
aoqi@0 335 return s;
aoqi@0 336 }
aoqi@0 337
aoqi@0 338 /**
aoqi@0 339 * Is this MimeBodyPart of the specified MIME type? This method
aoqi@0 340 * compares <strong>only the <code>primaryType</code> and
aoqi@0 341 * <code>subType</code></strong>.
aoqi@0 342 * The parameters of the content types are ignored. <p>
aoqi@0 343 *
aoqi@0 344 * For example, this method will return <code>true</code> when
aoqi@0 345 * comparing a MimeBodyPart of content type <strong>"text/plain"</strong>
aoqi@0 346 * with <strong>"text/plain; charset=foobar"</strong>. <p>
aoqi@0 347 *
aoqi@0 348 * If the <code>subType</code> of <code>mimeType</code> is the
aoqi@0 349 * special character '*', then the subtype is ignored during the
aoqi@0 350 * comparison.
aoqi@0 351 */
aoqi@0 352 public boolean isMimeType(String mimeType) {
aoqi@0 353 boolean result;
aoqi@0 354 // XXX - lots of room for optimization here!
aoqi@0 355 try {
aoqi@0 356 ContentType ct = new ContentType(getContentType());
aoqi@0 357 result = ct.match(mimeType);
aoqi@0 358 } catch (ParseException ex) {
aoqi@0 359 result = getContentType().equalsIgnoreCase(mimeType);
aoqi@0 360 }
aoqi@0 361 return result;
aoqi@0 362 }
aoqi@0 363
aoqi@0 364 /**
aoqi@0 365 * Returns the value of the "Content-Disposition" header field.
aoqi@0 366 * This represents the disposition of this part. The disposition
aoqi@0 367 * describes how the part should be presented to the user. <p>
aoqi@0 368 *
aoqi@0 369 * If the Content-Disposition field is unavailable,
aoqi@0 370 * null is returned. <p>
aoqi@0 371 *
aoqi@0 372 * This implementation uses <code>getHeader(name)</code>
aoqi@0 373 * to obtain the requisite header field.
aoqi@0 374 *
aoqi@0 375 * @see #headers
aoqi@0 376 */
aoqi@0 377 public String getDisposition() throws MessagingException {
aoqi@0 378 String s = getHeader("Content-Disposition", null);
aoqi@0 379
aoqi@0 380 if (s == null)
aoqi@0 381 return null;
aoqi@0 382
aoqi@0 383 ContentDisposition cd = new ContentDisposition(s);
aoqi@0 384 return cd.getDisposition();
aoqi@0 385 }
aoqi@0 386
aoqi@0 387 /**
aoqi@0 388 * Set the "Content-Disposition" header field of this body part.
aoqi@0 389 * If the disposition is null, any existing "Content-Disposition"
aoqi@0 390 * header field is removed.
aoqi@0 391 *
aoqi@0 392 * @exception IllegalStateException if this body part is
aoqi@0 393 * obtained from a READ_ONLY folder.
aoqi@0 394 */
aoqi@0 395 public void setDisposition(String disposition) throws MessagingException {
aoqi@0 396 if (disposition == null)
aoqi@0 397 removeHeader("Content-Disposition");
aoqi@0 398 else {
aoqi@0 399 String s = getHeader("Content-Disposition", null);
aoqi@0 400 if (s != null) {
aoqi@0 401 /* A Content-Disposition header already exists ..
aoqi@0 402 *
aoqi@0 403 * Override disposition, but attempt to retain
aoqi@0 404 * existing disposition parameters
aoqi@0 405 */
aoqi@0 406 ContentDisposition cd = new ContentDisposition(s);
aoqi@0 407 cd.setDisposition(disposition);
aoqi@0 408 disposition = cd.toString();
aoqi@0 409 }
aoqi@0 410 setHeader("Content-Disposition", disposition);
aoqi@0 411 }
aoqi@0 412 }
aoqi@0 413
aoqi@0 414 /**
aoqi@0 415 * Returns the content transfer encoding from the
aoqi@0 416 * "Content-Transfer-Encoding" header
aoqi@0 417 * field. Returns <code>null</code> if the header is unavailable
aoqi@0 418 * or its value is absent. <p>
aoqi@0 419 *
aoqi@0 420 * This implementation uses <code>getHeader(name)</code>
aoqi@0 421 * to obtain the requisite header field.
aoqi@0 422 *
aoqi@0 423 * @see #headers
aoqi@0 424 */
aoqi@0 425 public String getEncoding() throws MessagingException {
aoqi@0 426 String s = getHeader("Content-Transfer-Encoding", null);
aoqi@0 427
aoqi@0 428 if (s == null)
aoqi@0 429 return null;
aoqi@0 430
aoqi@0 431 s = s.trim(); // get rid of trailing spaces
aoqi@0 432 // quick check for known values to avoid unnecessary use
aoqi@0 433 // of tokenizer.
aoqi@0 434 if (s.equalsIgnoreCase("7bit") || s.equalsIgnoreCase("8bit") ||
aoqi@0 435 s.equalsIgnoreCase("quoted-printable") ||
aoqi@0 436 s.equalsIgnoreCase("base64"))
aoqi@0 437 return s;
aoqi@0 438
aoqi@0 439 // Tokenize the header to obtain the encoding (skip comments)
aoqi@0 440 HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
aoqi@0 441
aoqi@0 442 HeaderTokenizer.Token tk;
aoqi@0 443 int tkType;
aoqi@0 444
aoqi@0 445 for (;;) {
aoqi@0 446 tk = h.next(); // get a token
aoqi@0 447 tkType = tk.getType();
aoqi@0 448 if (tkType == HeaderTokenizer.Token.EOF)
aoqi@0 449 break; // done
aoqi@0 450 else if (tkType == HeaderTokenizer.Token.ATOM)
aoqi@0 451 return tk.getValue();
aoqi@0 452 else // invalid token, skip it.
aoqi@0 453 continue;
aoqi@0 454 }
aoqi@0 455 return s;
aoqi@0 456 }
aoqi@0 457
aoqi@0 458 /**
aoqi@0 459 * Returns the value of the "Content-ID" header field. Returns
aoqi@0 460 * <code>null</code> if the field is unavailable or its value is
aoqi@0 461 * absent. <p>
aoqi@0 462 *
aoqi@0 463 * This implementation uses <code>getHeader(name)</code>
aoqi@0 464 * to obtain the requisite header field.
aoqi@0 465 */
aoqi@0 466 public String getContentID() {
aoqi@0 467 return getHeader("Content-ID", null);
aoqi@0 468 }
aoqi@0 469
aoqi@0 470 /**
aoqi@0 471 * Set the "Content-ID" header field of this body part.
aoqi@0 472 * If the <code>cid</code> parameter is null, any existing
aoqi@0 473 * "Content-ID" is removed.
aoqi@0 474 *
aoqi@0 475 * @exception IllegalStateException if this body part is
aoqi@0 476 * obtained from a READ_ONLY folder.
aoqi@0 477 * @since JavaMail 1.3
aoqi@0 478 */
aoqi@0 479 public void setContentID(String cid) {
aoqi@0 480 if (cid == null)
aoqi@0 481 removeHeader("Content-ID");
aoqi@0 482 else
aoqi@0 483 setHeader("Content-ID", cid);
aoqi@0 484 }
aoqi@0 485
aoqi@0 486 /**
aoqi@0 487 * Return the value of the "Content-MD5" header field. Returns
aoqi@0 488 * <code>null</code> if this field is unavailable or its value
aoqi@0 489 * is absent. <p>
aoqi@0 490 *
aoqi@0 491 * This implementation uses <code>getHeader(name)</code>
aoqi@0 492 * to obtain the requisite header field.
aoqi@0 493 */
aoqi@0 494 public String getContentMD5() {
aoqi@0 495 return getHeader("Content-MD5", null);
aoqi@0 496 }
aoqi@0 497
aoqi@0 498 /**
aoqi@0 499 * Set the "Content-MD5" header field of this body part.
aoqi@0 500 *
aoqi@0 501 * @exception IllegalStateException if this body part is
aoqi@0 502 * obtained from a READ_ONLY folder.
aoqi@0 503 */
aoqi@0 504 public void setContentMD5(String md5) {
aoqi@0 505 setHeader("Content-MD5", md5);
aoqi@0 506 }
aoqi@0 507
aoqi@0 508 /**
aoqi@0 509 * Get the languages specified in the Content-Language header
aoqi@0 510 * of this MimeBodyPart. The Content-Language header is defined by
aoqi@0 511 * RFC 1766. Returns <code>null</code> if this header is not
aoqi@0 512 * available or its value is absent. <p>
aoqi@0 513 *
aoqi@0 514 * This implementation uses <code>getHeader(name)</code>
aoqi@0 515 * to obtain the requisite header field.
aoqi@0 516 */
aoqi@0 517 public String[] getContentLanguage() throws MessagingException {
aoqi@0 518 String s = getHeader("Content-Language", null);
aoqi@0 519
aoqi@0 520 if (s == null)
aoqi@0 521 return null;
aoqi@0 522
aoqi@0 523 // Tokenize the header to obtain the Language-tags (skip comments)
aoqi@0 524 HeaderTokenizer h = new HeaderTokenizer(s, HeaderTokenizer.MIME);
aoqi@0 525 FinalArrayList v = new FinalArrayList();
aoqi@0 526
aoqi@0 527 HeaderTokenizer.Token tk;
aoqi@0 528 int tkType;
aoqi@0 529
aoqi@0 530 while (true) {
aoqi@0 531 tk = h.next(); // get a language-tag
aoqi@0 532 tkType = tk.getType();
aoqi@0 533 if (tkType == HeaderTokenizer.Token.EOF)
aoqi@0 534 break; // done
aoqi@0 535 else if (tkType == HeaderTokenizer.Token.ATOM) v.add(tk.getValue());
aoqi@0 536 else // invalid token, skip it.
aoqi@0 537 continue;
aoqi@0 538 }
aoqi@0 539
aoqi@0 540 if (v.size() == 0)
aoqi@0 541 return null;
aoqi@0 542
aoqi@0 543 return (String[])v.toArray(new String[v.size()]);
aoqi@0 544 }
aoqi@0 545
aoqi@0 546 /**
aoqi@0 547 * Set the Content-Language header of this MimeBodyPart. The
aoqi@0 548 * Content-Language header is defined by RFC 1766.
aoqi@0 549 *
aoqi@0 550 * @param languages array of language tags
aoqi@0 551 */
aoqi@0 552 public void setContentLanguage(String[] languages) {
aoqi@0 553 StringBuffer sb = new StringBuffer(languages[0]);
aoqi@0 554 for (int i = 1; i < languages.length; i++)
aoqi@0 555 sb.append(',').append(languages[i]);
aoqi@0 556 setHeader("Content-Language", sb.toString());
aoqi@0 557 }
aoqi@0 558
aoqi@0 559 /**
aoqi@0 560 * Returns the "Content-Description" header field of this body part.
aoqi@0 561 * This typically associates some descriptive information with
aoqi@0 562 * this part. Returns null if this field is unavailable or its
aoqi@0 563 * value is absent. <p>
aoqi@0 564 *
aoqi@0 565 * If the Content-Description field is encoded as per RFC 2047,
aoqi@0 566 * it is decoded and converted into Unicode. If the decoding or
aoqi@0 567 * conversion fails, the raw data is returned as is. <p>
aoqi@0 568 *
aoqi@0 569 * This implementation uses <code>getHeader(name)</code>
aoqi@0 570 * to obtain the requisite header field.
aoqi@0 571 *
aoqi@0 572 * @return content description
aoqi@0 573 */
aoqi@0 574 public String getDescription() {
aoqi@0 575 String rawvalue = getHeader("Content-Description", null);
aoqi@0 576
aoqi@0 577 if (rawvalue == null)
aoqi@0 578 return null;
aoqi@0 579
aoqi@0 580 try {
aoqi@0 581 return MimeUtility.decodeText(MimeUtility.unfold(rawvalue));
aoqi@0 582 } catch (UnsupportedEncodingException ex) {
aoqi@0 583 return rawvalue;
aoqi@0 584 }
aoqi@0 585 }
aoqi@0 586
aoqi@0 587 /**
aoqi@0 588 * Set the "Content-Description" header field for this body part.
aoqi@0 589 * If the description parameter is <code>null</code>, then any
aoqi@0 590 * existing "Content-Description" fields are removed. <p>
aoqi@0 591 *
aoqi@0 592 * If the description contains non US-ASCII characters, it will
aoqi@0 593 * be encoded using the platform's default charset. If the
aoqi@0 594 * description contains only US-ASCII characters, no encoding
aoqi@0 595 * is done and it is used as is. <p>
aoqi@0 596 *
aoqi@0 597 * Note that if the charset encoding process fails, a
aoqi@0 598 * MessagingException is thrown, and an UnsupportedEncodingException
aoqi@0 599 * is included in the chain of nested exceptions within the
aoqi@0 600 * MessagingException.
aoqi@0 601 *
aoqi@0 602 * @param description content description
aoqi@0 603 * @exception IllegalStateException if this body part is
aoqi@0 604 * obtained from a READ_ONLY folder.
aoqi@0 605 * @exception MessagingException An
aoqi@0 606 * UnsupportedEncodingException may be included
aoqi@0 607 * in the exception chain if the charset
aoqi@0 608 * conversion fails.
aoqi@0 609 */
aoqi@0 610 public void setDescription(String description) throws MessagingException {
aoqi@0 611 setDescription(description, null);
aoqi@0 612 }
aoqi@0 613
aoqi@0 614 /**
aoqi@0 615 * Set the "Content-Description" header field for this body part.
aoqi@0 616 * If the description parameter is <code>null</code>, then any
aoqi@0 617 * existing "Content-Description" fields are removed. <p>
aoqi@0 618 *
aoqi@0 619 * If the description contains non US-ASCII characters, it will
aoqi@0 620 * be encoded using the specified charset. If the description
aoqi@0 621 * contains only US-ASCII characters, no encoding is done and
aoqi@0 622 * it is used as is. <p>
aoqi@0 623 *
aoqi@0 624 * Note that if the charset encoding process fails, a
aoqi@0 625 * MessagingException is thrown, and an UnsupportedEncodingException
aoqi@0 626 * is included in the chain of nested exceptions within the
aoqi@0 627 * MessagingException.
aoqi@0 628 *
aoqi@0 629 * @param description Description
aoqi@0 630 * @param charset Charset for encoding
aoqi@0 631 * @exception IllegalStateException if this body part is
aoqi@0 632 * obtained from a READ_ONLY folder.
aoqi@0 633 * @exception MessagingException An
aoqi@0 634 * UnsupportedEncodingException may be included
aoqi@0 635 * in the exception chain if the charset
aoqi@0 636 * conversion fails.
aoqi@0 637 */
aoqi@0 638 public void setDescription(String description, String charset)
aoqi@0 639 throws MessagingException {
aoqi@0 640 if (description == null) {
aoqi@0 641 removeHeader("Content-Description");
aoqi@0 642 return;
aoqi@0 643 }
aoqi@0 644
aoqi@0 645 try {
aoqi@0 646 setHeader("Content-Description", MimeUtility.fold(21,
aoqi@0 647 MimeUtility.encodeText(description, charset, null)));
aoqi@0 648 } catch (UnsupportedEncodingException uex) {
aoqi@0 649 throw new MessagingException("Encoding error", uex);
aoqi@0 650 }
aoqi@0 651 }
aoqi@0 652
aoqi@0 653 /**
aoqi@0 654 * Get the filename associated with this body part. <p>
aoqi@0 655 *
aoqi@0 656 * Returns the value of the "filename" parameter from the
aoqi@0 657 * "Content-Disposition" header field of this body part. If its
aoqi@0 658 * not available, returns the value of the "name" parameter from
aoqi@0 659 * the "Content-Type" header field of this body part.
aoqi@0 660 * Returns <code>null</code> if both are absent.
aoqi@0 661 *
aoqi@0 662 * @return filename
aoqi@0 663 */
aoqi@0 664 public String getFileName() throws MessagingException {
aoqi@0 665 String filename = null;
aoqi@0 666 String s = getHeader("Content-Disposition", null);
aoqi@0 667
aoqi@0 668 if (s != null) {
aoqi@0 669 // Parse the header ..
aoqi@0 670 ContentDisposition cd = new ContentDisposition(s);
aoqi@0 671 filename = cd.getParameter("filename");
aoqi@0 672 }
aoqi@0 673 if (filename == null) {
aoqi@0 674 // Still no filename ? Try the "name" ContentType parameter
aoqi@0 675 s = getHeader("Content-Type", null);
aoqi@0 676 if (s != null) {
aoqi@0 677 try {
aoqi@0 678 ContentType ct = new ContentType(s);
aoqi@0 679 filename = ct.getParameter("name");
aoqi@0 680 } catch (ParseException pex) { } // ignore it
aoqi@0 681 }
aoqi@0 682 }
aoqi@0 683 return filename;
aoqi@0 684 }
aoqi@0 685
aoqi@0 686 /**
aoqi@0 687 * Set the filename associated with this body part, if possible. <p>
aoqi@0 688 *
aoqi@0 689 * Sets the "filename" parameter of the "Content-Disposition"
aoqi@0 690 * header field of this body part.
aoqi@0 691 *
aoqi@0 692 * @exception IllegalStateException if this body part is
aoqi@0 693 * obtained from a READ_ONLY folder.
aoqi@0 694 */
aoqi@0 695 public void setFileName(String filename) throws MessagingException {
aoqi@0 696 // Set the Content-Disposition "filename" parameter
aoqi@0 697 String s = getHeader("Content-Disposition", null);
aoqi@0 698 ContentDisposition cd =
aoqi@0 699 new ContentDisposition(s == null ? ATTACHMENT : s);
aoqi@0 700 cd.setParameter("filename", filename);
aoqi@0 701 setHeader("Content-Disposition", cd.toString());
aoqi@0 702
aoqi@0 703 /* Also attempt to set the Content-Type "name" parameter,
aoqi@0 704 * to satisfy ancient MUAs.
aoqi@0 705 * XXX: This is not RFC compliant, and hence should really
aoqi@0 706 * be conditional based on some property. Fix this once we
aoqi@0 707 * figure out how to get at Properties from here !
aoqi@0 708 */
aoqi@0 709 s = getHeader("Content-Type", null);
aoqi@0 710 if (s != null) {
aoqi@0 711 try {
aoqi@0 712 ContentType cType = new ContentType(s);
aoqi@0 713 cType.setParameter("name", filename);
aoqi@0 714 setHeader("Content-Type", cType.toString());
aoqi@0 715 } catch (ParseException pex) { } // ignore it
aoqi@0 716 }
aoqi@0 717 }
aoqi@0 718
aoqi@0 719 /**
aoqi@0 720 * Return a decoded input stream for this body part's "content". <p>
aoqi@0 721 *
aoqi@0 722 * This implementation obtains the input stream from the DataHandler.
aoqi@0 723 * That is, it invokes getDataHandler().getInputStream();
aoqi@0 724 *
aoqi@0 725 * @return an InputStream
aoqi@0 726 * @exception IOException this is typically thrown by the
aoqi@0 727 * DataHandler. Refer to the documentation for
aoqi@0 728 * javax.activation.DataHandler for more details.
aoqi@0 729 *
aoqi@0 730 * @see #getContentStream
aoqi@0 731 * @see DataHandler#getInputStream
aoqi@0 732 */
aoqi@0 733 public InputStream getInputStream()
aoqi@0 734 throws IOException {
aoqi@0 735 return getDataHandler().getInputStream();
aoqi@0 736 }
aoqi@0 737
aoqi@0 738 /**
aoqi@0 739 * Produce the raw bytes of the content. This method is used
aoqi@0 740 * when creating a DataHandler object for the content. Subclasses
aoqi@0 741 * that can provide a separate input stream for just the MimeBodyPart
aoqi@0 742 * content might want to override this method. <p>
aoqi@0 743 *
aoqi@0 744 * @see #content
aoqi@0 745 */
aoqi@0 746 /*package*/ InputStream getContentStream() throws MessagingException {
aoqi@0 747 if (mimePart != null) {
aoqi@0 748 return mimePart.read();
aoqi@0 749 }
aoqi@0 750 if (contentStream != null)
aoqi@0 751 return ((SharedInputStream)contentStream).newStream(0, -1);
aoqi@0 752 if (content != null)
aoqi@0 753 return new ByteArrayInputStream(content,start,contentLength);
aoqi@0 754
aoqi@0 755 throw new MessagingException("No content");
aoqi@0 756 }
aoqi@0 757
aoqi@0 758 /**
aoqi@0 759 * Return an InputStream to the raw data with any Content-Transfer-Encoding
aoqi@0 760 * intact. This method is useful if the "Content-Transfer-Encoding"
aoqi@0 761 * header is incorrect or corrupt, which would prevent the
aoqi@0 762 * <code>getInputStream</code> method or <code>getContent</code> method
aoqi@0 763 * from returning the correct data. In such a case the application may
aoqi@0 764 * use this method and attempt to decode the raw data itself. <p>
aoqi@0 765 *
aoqi@0 766 * This implementation simply calls the <code>getContentStream</code>
aoqi@0 767 * method.
aoqi@0 768 *
aoqi@0 769 * @see #getInputStream
aoqi@0 770 * @see #getContentStream
aoqi@0 771 * @since JavaMail 1.2
aoqi@0 772 */
aoqi@0 773 public InputStream getRawInputStream() throws MessagingException {
aoqi@0 774 return getContentStream();
aoqi@0 775 }
aoqi@0 776
aoqi@0 777 /**
aoqi@0 778 * Return a DataHandler for this body part's content. <p>
aoqi@0 779 *
aoqi@0 780 * The implementation provided here works just like the
aoqi@0 781 * the implementation in MimeMessage.
aoqi@0 782 */
aoqi@0 783 public DataHandler getDataHandler() {
aoqi@0 784 if (mimePart != null) {
aoqi@0 785 //return an inputstream
aoqi@0 786 return new DataHandler(new DataSource() {
aoqi@0 787
aoqi@0 788 public InputStream getInputStream() throws IOException {
aoqi@0 789 return mimePart.read();
aoqi@0 790 }
aoqi@0 791
aoqi@0 792 public OutputStream getOutputStream() throws IOException {
aoqi@0 793 throw new UnsupportedOperationException("getOutputStream cannot be supported : You have enabled LazyAttachments Option");
aoqi@0 794 }
aoqi@0 795
aoqi@0 796 public String getContentType() {
aoqi@0 797 return mimePart.getContentType();
aoqi@0 798 }
aoqi@0 799
aoqi@0 800 public String getName() {
aoqi@0 801 return "MIMEPart Wrapped DataSource";
aoqi@0 802 }
aoqi@0 803 });
aoqi@0 804 }
aoqi@0 805 if (dh == null)
aoqi@0 806 dh = new DataHandler(new MimePartDataSource(this));
aoqi@0 807 return dh;
aoqi@0 808 }
aoqi@0 809
aoqi@0 810 /**
aoqi@0 811 * Return the content as a java object. The type of the object
aoqi@0 812 * returned is of course dependent on the content itself. For
aoqi@0 813 * example, the native format of a text/plain content is usually
aoqi@0 814 * a String object. The native format for a "multipart"
aoqi@0 815 * content is always a MimeMultipart subclass. For content types that are
aoqi@0 816 * unknown to the DataHandler system, an input stream is returned
aoqi@0 817 * as the content. <p>
aoqi@0 818 *
aoqi@0 819 * This implementation obtains the content from the DataHandler.
aoqi@0 820 * That is, it invokes getDataHandler().getContent();
aoqi@0 821 *
aoqi@0 822 * @return Object
aoqi@0 823 * @exception IOException this is typically thrown by the
aoqi@0 824 * DataHandler. Refer to the documentation for
aoqi@0 825 * javax.activation.DataHandler for more details.
aoqi@0 826 */
aoqi@0 827 public Object getContent() throws IOException {
aoqi@0 828 return getDataHandler().getContent();
aoqi@0 829 }
aoqi@0 830
aoqi@0 831 /**
aoqi@0 832 * This method provides the mechanism to set this body part's content.
aoqi@0 833 * The given DataHandler object should wrap the actual content.
aoqi@0 834 *
aoqi@0 835 * @param dh The DataHandler for the content
aoqi@0 836 * @exception IllegalStateException if this body part is
aoqi@0 837 * obtained from a READ_ONLY folder.
aoqi@0 838 */
aoqi@0 839 public void setDataHandler(DataHandler dh) {
aoqi@0 840 if (mimePart != null) {
aoqi@0 841 mimePart = null;
aoqi@0 842 }
aoqi@0 843 this.dh = dh;
aoqi@0 844 this.content = null;
aoqi@0 845 this.contentStream = null;
aoqi@0 846 removeHeader("Content-Type");
aoqi@0 847 removeHeader("Content-Transfer-Encoding");
aoqi@0 848 }
aoqi@0 849
aoqi@0 850 /**
aoqi@0 851 * A convenience method for setting this body part's content. <p>
aoqi@0 852 *
aoqi@0 853 * The content is wrapped in a DataHandler object. Note that a
aoqi@0 854 * DataContentHandler class for the specified type should be
aoqi@0 855 * available to the JavaMail implementation for this to work right.
aoqi@0 856 * That is, to do <code>setContent(foobar, "application/x-foobar")</code>,
aoqi@0 857 * a DataContentHandler for "application/x-foobar" should be installed.
aoqi@0 858 * Refer to the Java Activation Framework for more information.
aoqi@0 859 *
aoqi@0 860 * @param o the content object
aoqi@0 861 * @param type Mime type of the object
aoqi@0 862 * @exception IllegalStateException if this body part is
aoqi@0 863 * obtained from a READ_ONLY folder.
aoqi@0 864 */
aoqi@0 865 public void setContent(Object o, String type) {
aoqi@0 866 if (mimePart != null) {
aoqi@0 867 mimePart = null;
aoqi@0 868 }
aoqi@0 869 if (o instanceof MimeMultipart) {
aoqi@0 870 setContent((MimeMultipart)o);
aoqi@0 871 } else {
aoqi@0 872 setDataHandler(new DataHandler(o, type));
aoqi@0 873 }
aoqi@0 874 }
aoqi@0 875
aoqi@0 876 /**
aoqi@0 877 * Convenience method that sets the given String as this
aoqi@0 878 * part's content, with a MIME type of "text/plain". If the
aoqi@0 879 * string contains non US-ASCII characters, it will be encoded
aoqi@0 880 * using the platform's default charset. The charset is also
aoqi@0 881 * used to set the "charset" parameter. <p>
aoqi@0 882 *
aoqi@0 883 * Note that there may be a performance penalty if
aoqi@0 884 * <code>text</code> is large, since this method may have
aoqi@0 885 * to scan all the characters to determine what charset to
aoqi@0 886 * use. <p>
aoqi@0 887 * If the charset is already known, use the
aoqi@0 888 * setText() version that takes the charset parameter.
aoqi@0 889 *
aoqi@0 890 * @see #setText(String text, String charset)
aoqi@0 891 */
aoqi@0 892 public void setText(String text) {
aoqi@0 893 setText(text, null);
aoqi@0 894 }
aoqi@0 895
aoqi@0 896 /**
aoqi@0 897 * Convenience method that sets the given String as this part's
aoqi@0 898 * content, with a MIME type of "text/plain" and the specified
aoqi@0 899 * charset. The given Unicode string will be charset-encoded
aoqi@0 900 * using the specified charset. The charset is also used to set
aoqi@0 901 * the "charset" parameter.
aoqi@0 902 */
aoqi@0 903 public void setText(String text, String charset) {
aoqi@0 904 if (charset == null) {
aoqi@0 905 if (MimeUtility.checkAscii(text) != MimeUtility.ALL_ASCII)
aoqi@0 906 charset = MimeUtility.getDefaultMIMECharset();
aoqi@0 907 else
aoqi@0 908 charset = "us-ascii";
aoqi@0 909 }
aoqi@0 910 setContent(text, "text/plain; charset=" +
aoqi@0 911 MimeUtility.quote(charset, HeaderTokenizer.MIME));
aoqi@0 912 }
aoqi@0 913
aoqi@0 914 /**
aoqi@0 915 * This method sets the body part's content to a MimeMultipart object.
aoqi@0 916 *
aoqi@0 917 * @param mp The multipart object that is the Message's content
aoqi@0 918 * @exception IllegalStateException if this body part is
aoqi@0 919 * obtained from a READ_ONLY folder.
aoqi@0 920 */
aoqi@0 921 public void setContent(MimeMultipart mp) {
aoqi@0 922 if (mimePart != null) {
aoqi@0 923 mimePart = null;
aoqi@0 924 }
aoqi@0 925 setDataHandler(new DataHandler(mp, mp.getContentType().toString()));
aoqi@0 926 mp.setParent(this);
aoqi@0 927 }
aoqi@0 928
aoqi@0 929 /**
aoqi@0 930 * Output the body part as an RFC 822 format stream.
aoqi@0 931 *
aoqi@0 932 * @exception MessagingException
aoqi@0 933 * @exception IOException if an error occurs writing to the
aoqi@0 934 * stream or if an error is generated
aoqi@0 935 * by the javax.activation layer.
aoqi@0 936 * @see DataHandler#writeTo
aoqi@0 937 */
aoqi@0 938 public void writeTo(OutputStream os)
aoqi@0 939 throws IOException, MessagingException {
aoqi@0 940
aoqi@0 941 // First, write out the header
aoqi@0 942 List hdrLines = headers.getAllHeaderLines();
aoqi@0 943 int sz = hdrLines.size();
aoqi@0 944 for( int i=0; i<sz; i++ )
aoqi@0 945 OutputUtil.writeln((String)hdrLines.get(i),os);
aoqi@0 946
aoqi@0 947 // The CRLF separator between header and content
aoqi@0 948 OutputUtil.writeln(os);
aoqi@0 949
aoqi@0 950 // Finally, the content.
aoqi@0 951 // XXX: May need to account for ESMTP ?
aoqi@0 952 if (contentStream != null) {
aoqi@0 953 ((SharedInputStream)contentStream).writeTo(0,-1,os);
aoqi@0 954 } else
aoqi@0 955 if (content != null) {
aoqi@0 956 os.write(content,start,contentLength);
aoqi@0 957 } else
aoqi@0 958 if (dh!=null) {
aoqi@0 959 // this is the slowest route, so try it as the last resort
aoqi@0 960 OutputStream wos = MimeUtility.encode(os, getEncoding());
aoqi@0 961 getDataHandler().writeTo(wos);
aoqi@0 962 if(os!=wos)
aoqi@0 963 wos.flush(); // Needed to complete encoding
aoqi@0 964 } else if (mimePart != null) {
aoqi@0 965 OutputStream wos = MimeUtility.encode(os, getEncoding());
aoqi@0 966 getDataHandler().writeTo(wos);
aoqi@0 967 if(os!=wos)
aoqi@0 968 wos.flush(); // Needed to complete encoding
aoqi@0 969 }else {
aoqi@0 970 throw new MessagingException("no content");
aoqi@0 971 }
aoqi@0 972 }
aoqi@0 973
aoqi@0 974 /**
aoqi@0 975 * Get all the headers for this header_name. Note that certain
aoqi@0 976 * headers may be encoded as per RFC 2047 if they contain
aoqi@0 977 * non US-ASCII characters and these should be decoded.
aoqi@0 978 *
aoqi@0 979 * @param name name of header
aoqi@0 980 * @return array of headers
aoqi@0 981 * @see MimeUtility
aoqi@0 982 */
aoqi@0 983 public String[] getHeader(String name) {
aoqi@0 984 return headers.getHeader(name);
aoqi@0 985 }
aoqi@0 986
aoqi@0 987 /**
aoqi@0 988 * Get all the headers for this header name, returned as a single
aoqi@0 989 * String, with headers separated by the delimiter. If the
aoqi@0 990 * delimiter is <code>null</code>, only the first header is
aoqi@0 991 * returned.
aoqi@0 992 *
aoqi@0 993 * @param name the name of this header
aoqi@0 994 * @param delimiter delimiter between fields in returned string
aoqi@0 995 * @return the value fields for all headers with
aoqi@0 996 * this name
aoqi@0 997 */
aoqi@0 998 public String getHeader(String name, String delimiter) {
aoqi@0 999 return headers.getHeader(name, delimiter);
aoqi@0 1000 }
aoqi@0 1001
aoqi@0 1002 /**
aoqi@0 1003 * Set the value for this header_name. Replaces all existing
aoqi@0 1004 * header values with this new value. Note that RFC 822 headers
aoqi@0 1005 * must contain only US-ASCII characters, so a header that
aoqi@0 1006 * contains non US-ASCII characters must be encoded as per the
aoqi@0 1007 * rules of RFC 2047.
aoqi@0 1008 *
aoqi@0 1009 * @param name header name
aoqi@0 1010 * @param value header value
aoqi@0 1011 * @see MimeUtility
aoqi@0 1012 */
aoqi@0 1013 public void setHeader(String name, String value) {
aoqi@0 1014 headers.setHeader(name, value);
aoqi@0 1015 }
aoqi@0 1016
aoqi@0 1017 /**
aoqi@0 1018 * Add this value to the existing values for this header_name.
aoqi@0 1019 * Note that RFC 822 headers must contain only US-ASCII
aoqi@0 1020 * characters, so a header that contains non US-ASCII characters
aoqi@0 1021 * must be encoded as per the rules of RFC 2047.
aoqi@0 1022 *
aoqi@0 1023 * @param name header name
aoqi@0 1024 * @param value header value
aoqi@0 1025 * @see MimeUtility
aoqi@0 1026 */
aoqi@0 1027 public void addHeader(String name, String value) {
aoqi@0 1028 headers.addHeader(name, value);
aoqi@0 1029 }
aoqi@0 1030
aoqi@0 1031 /**
aoqi@0 1032 * Remove all headers with this name.
aoqi@0 1033 */
aoqi@0 1034 public void removeHeader(String name) {
aoqi@0 1035 headers.removeHeader(name);
aoqi@0 1036 }
aoqi@0 1037
aoqi@0 1038 /**
aoqi@0 1039 * Return all the headers from this Message as an Enumeration of
aoqi@0 1040 * Header objects.
aoqi@0 1041 */
aoqi@0 1042 public FinalArrayList getAllHeaders() {
aoqi@0 1043 return headers.getAllHeaders();
aoqi@0 1044 }
aoqi@0 1045
aoqi@0 1046
aoqi@0 1047 /**
aoqi@0 1048 * Add a header line to this body part
aoqi@0 1049 */
aoqi@0 1050 public void addHeaderLine(String line) {
aoqi@0 1051 headers.addHeaderLine(line);
aoqi@0 1052 }
aoqi@0 1053
aoqi@0 1054 /**
aoqi@0 1055 * Examine the content of this body part and update the appropriate
aoqi@0 1056 * MIME headers. Typical headers that get set here are
aoqi@0 1057 * <code>Content-Type</code> and <code>Content-Transfer-Encoding</code>.
aoqi@0 1058 * Headers might need to be updated in two cases:
aoqi@0 1059 *
aoqi@0 1060 * <br>
aoqi@0 1061 * - A message being crafted by a mail application will certainly
aoqi@0 1062 * need to activate this method at some point to fill up its internal
aoqi@0 1063 * headers.
aoqi@0 1064 *
aoqi@0 1065 * <br>
aoqi@0 1066 * - A message read in from a Store will have obtained
aoqi@0 1067 * all its headers from the store, and so doesn't need this.
aoqi@0 1068 * However, if this message is editable and if any edits have
aoqi@0 1069 * been made to either the content or message structure, we might
aoqi@0 1070 * need to resync our headers.
aoqi@0 1071 *
aoqi@0 1072 * <br>
aoqi@0 1073 * In both cases this method is typically called by the
aoqi@0 1074 * <code>Message.saveChanges</code> method.
aoqi@0 1075 */
aoqi@0 1076 protected void updateHeaders() throws MessagingException {
aoqi@0 1077 DataHandler dh = getDataHandler();
aoqi@0 1078 if (dh == null) // Huh ?
aoqi@0 1079 return;
aoqi@0 1080
aoqi@0 1081 try {
aoqi@0 1082 String type = dh.getContentType();
aoqi@0 1083 boolean composite = false;
aoqi@0 1084 boolean needCTHeader = getHeader("Content-Type") == null;
aoqi@0 1085
aoqi@0 1086 ContentType cType = new ContentType(type);
aoqi@0 1087 if (cType.match("multipart/*")) {
aoqi@0 1088 // If multipart, recurse
aoqi@0 1089 composite = true;
aoqi@0 1090 Object o = dh.getContent();
aoqi@0 1091 ((MimeMultipart) o).updateHeaders();
aoqi@0 1092 } else if (cType.match("message/rfc822")) {
aoqi@0 1093 composite = true;
aoqi@0 1094 }
aoqi@0 1095
aoqi@0 1096 // Content-Transfer-Encoding, but only if we don't
aoqi@0 1097 // already have one
aoqi@0 1098 if (!composite) { // not allowed on composite parts
aoqi@0 1099 if (getHeader("Content-Transfer-Encoding") == null)
aoqi@0 1100 setEncoding(MimeUtility.getEncoding(dh));
aoqi@0 1101
aoqi@0 1102 if (needCTHeader && setDefaultTextCharset &&
aoqi@0 1103 cType.match("text/*") &&
aoqi@0 1104 cType.getParameter("charset") == null) {
aoqi@0 1105 /*
aoqi@0 1106 * Set a default charset for text parts.
aoqi@0 1107 * We really should examine the data to determine
aoqi@0 1108 * whether or not it's all ASCII, but that's too
aoqi@0 1109 * expensive so we make an assumption: If we
aoqi@0 1110 * chose 7bit encoding for this data, it's probably
aoqi@0 1111 * ASCII. (MimeUtility.getEncoding will choose
aoqi@0 1112 * 7bit only in this case, but someone might've
aoqi@0 1113 * set the Content-Transfer-Encoding header manually.)
aoqi@0 1114 */
aoqi@0 1115 String charset;
aoqi@0 1116 String enc = getEncoding();
aoqi@0 1117 if (enc != null && enc.equalsIgnoreCase("7bit"))
aoqi@0 1118 charset = "us-ascii";
aoqi@0 1119 else
aoqi@0 1120 charset = MimeUtility.getDefaultMIMECharset();
aoqi@0 1121 cType.setParameter("charset", charset);
aoqi@0 1122 type = cType.toString();
aoqi@0 1123 }
aoqi@0 1124 }
aoqi@0 1125
aoqi@0 1126 // Now, let's update our own headers ...
aoqi@0 1127
aoqi@0 1128 // Content-type, but only if we don't already have one
aoqi@0 1129 if (needCTHeader) {
aoqi@0 1130 /*
aoqi@0 1131 * Pull out "filename" from Content-Disposition, and
aoqi@0 1132 * use that to set the "name" parameter. This is to
aoqi@0 1133 * satisfy older MUAs (DtMail, Roam and probably
aoqi@0 1134 * a bunch of others).
aoqi@0 1135 */
aoqi@0 1136 String s = getHeader("Content-Disposition", null);
aoqi@0 1137 if (s != null) {
aoqi@0 1138 // Parse the header ..
aoqi@0 1139 ContentDisposition cd = new ContentDisposition(s);
aoqi@0 1140 String filename = cd.getParameter("filename");
aoqi@0 1141 if (filename != null) {
aoqi@0 1142 cType.setParameter("name", filename);
aoqi@0 1143 type = cType.toString();
aoqi@0 1144 }
aoqi@0 1145 }
aoqi@0 1146
aoqi@0 1147 setHeader("Content-Type", type);
aoqi@0 1148 }
aoqi@0 1149 } catch (IOException ex) {
aoqi@0 1150 throw new MessagingException("IOException updating headers", ex);
aoqi@0 1151 }
aoqi@0 1152 }
aoqi@0 1153
aoqi@0 1154 private void setEncoding(String encoding) {
aoqi@0 1155 setHeader("Content-Transfer-Encoding", encoding);
aoqi@0 1156 }
aoqi@0 1157 }

mercurial