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