1 16 17 package org.springframework.mail.javamail; 18 19 import java.io.File ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.io.OutputStream ; 23 import java.io.UnsupportedEncodingException ; 24 import java.util.Date ; 25 26 import javax.activation.DataHandler ; 27 import javax.activation.DataSource ; 28 import javax.activation.FileDataSource ; 29 import javax.activation.FileTypeMap ; 30 import javax.mail.BodyPart ; 31 import javax.mail.Message ; 32 import javax.mail.MessagingException ; 33 import javax.mail.internet.AddressException ; 34 import javax.mail.internet.InternetAddress ; 35 import javax.mail.internet.MimeBodyPart ; 36 import javax.mail.internet.MimeMessage ; 37 import javax.mail.internet.MimeMultipart ; 38 import javax.mail.internet.MimePart ; 39 40 import org.springframework.core.io.InputStreamSource; 41 import org.springframework.core.io.Resource; 42 import org.springframework.util.Assert; 43 44 102 public class MimeMessageHelper { 103 104 107 public static final int MULTIPART_MODE_NO = 0; 108 109 117 public static final int MULTIPART_MODE_MIXED = 1; 118 119 130 public static final int MULTIPART_MODE_RELATED = 2; 131 132 143 public static final int MULTIPART_MODE_MIXED_RELATED = 3; 144 145 146 private static final String MULTIPART_SUBTYPE_MIXED = "mixed"; 147 148 private static final String MULTIPART_SUBTYPE_RELATED = "related"; 149 150 private static final String MULTIPART_SUBTYPE_ALTERNATIVE = "alternative"; 151 152 private static final String CONTENT_TYPE_ALTERNATIVE = "text/alternative"; 153 154 private static final String CONTENT_TYPE_HTML = "text/html"; 155 156 private static final String CONTENT_TYPE_CHARSET_SUFFIX = ";charset="; 157 158 private static final String HEADER_PRIORITY = "X-Priority"; 159 160 private static final String HEADER_CONTENT_ID = "Content-ID"; 161 162 163 private final MimeMessage mimeMessage; 164 165 private MimeMultipart rootMimeMultipart; 166 167 private MimeMultipart mimeMultipart; 168 169 private final String encoding; 170 171 private FileTypeMap fileTypeMap; 172 173 private boolean validateAddresses = false; 174 175 176 188 public MimeMessageHelper(MimeMessage mimeMessage) { 189 this(mimeMessage, null); 190 } 191 192 200 public MimeMessageHelper(MimeMessage mimeMessage, String encoding) { 201 this.mimeMessage = mimeMessage; 202 this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage)); 203 this.fileTypeMap = getDefaultFileTypeMap(mimeMessage); 204 } 205 206 225 public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException { 226 this(mimeMessage, multipart, null); 227 } 228 229 244 public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding) 245 throws MessagingException { 246 247 this(mimeMessage, (multipart ? MULTIPART_MODE_MIXED_RELATED : MULTIPART_MODE_NO), encoding); 248 } 249 250 268 public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode) throws MessagingException { 269 this(mimeMessage, multipartMode, null); 270 } 271 272 286 public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, String encoding) 287 throws MessagingException { 288 289 this.mimeMessage = mimeMessage; 290 createMimeMultiparts(mimeMessage, multipartMode); 291 this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage)); 292 this.fileTypeMap = getDefaultFileTypeMap(mimeMessage); 293 } 294 295 296 299 public final MimeMessage getMimeMessage() { 300 return this.mimeMessage; 301 } 302 303 304 327 protected void createMimeMultiparts(MimeMessage mimeMessage, int multipartMode) throws MessagingException { 328 switch (multipartMode) { 329 case MULTIPART_MODE_NO: 330 setMimeMultiparts(null, null); 331 break; 332 case MULTIPART_MODE_MIXED: 333 MimeMultipart mixedMultipart = new MimeMultipart (MULTIPART_SUBTYPE_MIXED); 334 mimeMessage.setContent(mixedMultipart); 335 setMimeMultiparts(mixedMultipart, mixedMultipart); 336 break; 337 case MULTIPART_MODE_RELATED: 338 MimeMultipart relatedMultipart = new MimeMultipart (MULTIPART_SUBTYPE_RELATED); 339 mimeMessage.setContent(relatedMultipart); 340 setMimeMultiparts(relatedMultipart, relatedMultipart); 341 break; 342 case MULTIPART_MODE_MIXED_RELATED: 343 MimeMultipart rootMixedMultipart = new MimeMultipart (MULTIPART_SUBTYPE_MIXED); 344 mimeMessage.setContent(rootMixedMultipart); 345 MimeMultipart nestedRelatedMultipart = new MimeMultipart (MULTIPART_SUBTYPE_RELATED); 346 MimeBodyPart relatedBodyPart = new MimeBodyPart (); 347 relatedBodyPart.setContent(nestedRelatedMultipart); 348 rootMixedMultipart.addBodyPart(relatedBodyPart); 349 setMimeMultiparts(rootMixedMultipart, nestedRelatedMultipart); 350 break; 351 default: 352 throw new IllegalArgumentException ("Only multipart modes MIXED_RELATED, RELATED and NO supported"); 353 } 354 } 355 356 364 protected final void setMimeMultiparts(MimeMultipart root, MimeMultipart main) { 365 this.rootMimeMultipart = root; 366 this.mimeMultipart = main; 367 } 368 369 374 public final boolean isMultipart() { 375 return (this.rootMimeMultipart != null); 376 } 377 378 381 private void checkMultipart() throws IllegalStateException { 382 if (!isMultipart()) { 383 throw new IllegalStateException ("Not in multipart mode - " + 384 "create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " + 385 "if you need to set alternative texts or add inline elements or attachments."); 386 } 387 } 388 389 399 public final MimeMultipart getRootMimeMultipart() throws IllegalStateException { 400 checkMultipart(); 401 return this.rootMimeMultipart; 402 } 403 404 414 public final MimeMultipart getMimeMultipart() throws IllegalStateException { 415 checkMultipart(); 416 return this.mimeMultipart; 417 } 418 419 420 426 protected String getDefaultEncoding(MimeMessage mimeMessage) { 427 if (mimeMessage instanceof SmartMimeMessage) { 428 return ((SmartMimeMessage) mimeMessage).getDefaultEncoding(); 429 } 430 return null; 431 } 432 433 436 public String getEncoding() { 437 return this.encoding; 438 } 439 440 447 protected FileTypeMap getDefaultFileTypeMap(MimeMessage mimeMessage) { 448 if (mimeMessage instanceof SmartMimeMessage) { 449 FileTypeMap fileTypeMap = ((SmartMimeMessage) mimeMessage).getDefaultFileTypeMap(); 450 if (fileTypeMap != null) { 451 return fileTypeMap; 452 } 453 } 454 ConfigurableMimeFileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap(); 455 fileTypeMap.afterPropertiesSet(); 456 return fileTypeMap; 457 } 458 459 473 public void setFileTypeMap(FileTypeMap fileTypeMap) { 474 this.fileTypeMap = (fileTypeMap != null ? fileTypeMap : getDefaultFileTypeMap(getMimeMessage())); 475 } 476 477 480 public FileTypeMap getFileTypeMap() { 481 return this.fileTypeMap; 482 } 483 484 485 493 public void setValidateAddresses(boolean validateAddresses) { 494 this.validateAddresses = validateAddresses; 495 } 496 497 500 public boolean isValidateAddresses() { 501 return this.validateAddresses; 502 } 503 504 516 protected void validateAddress(InternetAddress address) throws AddressException { 517 if (isValidateAddresses()) { 518 address.validate(); 519 } 520 } 521 522 529 protected void validateAddresses(InternetAddress [] addresses) throws AddressException { 530 for (int i = 0; i < addresses.length; i++) { 531 validateAddress(addresses[i]); 532 } 533 } 534 535 536 public void setFrom(InternetAddress from) throws MessagingException { 537 Assert.notNull(from, "From address must not be null"); 538 validateAddress(from); 539 this.mimeMessage.setFrom(from); 540 } 541 542 public void setFrom(String from) throws MessagingException { 543 Assert.notNull(from, "From address must not be null"); 544 setFrom(new InternetAddress (from)); 545 } 546 547 public void setFrom(String from, String personal) throws MessagingException , UnsupportedEncodingException { 548 Assert.notNull(from, "From address must not be null"); 549 setFrom(getEncoding() != null ? 550 new InternetAddress (from, personal, getEncoding()) : new InternetAddress (from, personal)); 551 } 552 553 public void setReplyTo(InternetAddress replyTo) throws MessagingException { 554 Assert.notNull(replyTo, "Reply-to address must not be null"); 555 validateAddress(replyTo); 556 this.mimeMessage.setReplyTo(new InternetAddress [] {replyTo}); 557 } 558 559 public void setReplyTo(String replyTo) throws MessagingException { 560 Assert.notNull(replyTo, "Reply-to address must not be null"); 561 setReplyTo(new InternetAddress (replyTo)); 562 } 563 564 public void setReplyTo(String replyTo, String personal) throws MessagingException , UnsupportedEncodingException { 565 Assert.notNull(replyTo, "Reply-to address must not be null"); 566 InternetAddress replyToAddress = (getEncoding() != null) ? 567 new InternetAddress (replyTo, personal, getEncoding()) : new InternetAddress (replyTo, personal); 568 setReplyTo(replyToAddress); 569 } 570 571 572 public void setTo(InternetAddress to) throws MessagingException { 573 Assert.notNull(to, "To address must not be null"); 574 validateAddress(to); 575 this.mimeMessage.setRecipient(Message.RecipientType.TO, to); 576 } 577 578 public void setTo(InternetAddress [] to) throws MessagingException { 579 Assert.notNull(to, "To address array must not be null"); 580 validateAddresses(to); 581 this.mimeMessage.setRecipients(Message.RecipientType.TO, to); 582 } 583 584 public void setTo(String to) throws MessagingException { 585 Assert.notNull(to, "To address must not be null"); 586 setTo(new InternetAddress (to)); 587 } 588 589 public void setTo(String [] to) throws MessagingException { 590 Assert.notNull(to, "To address array must not be null"); 591 InternetAddress [] addresses = new InternetAddress [to.length]; 592 for (int i = 0; i < to.length; i++) { 593 addresses[i] = new InternetAddress (to[i]); 594 } 595 setTo(addresses); 596 } 597 598 public void addTo(InternetAddress to) throws MessagingException { 599 Assert.notNull(to, "To address must not be null"); 600 validateAddress(to); 601 this.mimeMessage.addRecipient(Message.RecipientType.TO, to); 602 } 603 604 public void addTo(String to) throws MessagingException { 605 Assert.notNull(to, "To address must not be null"); 606 addTo(new InternetAddress (to)); 607 } 608 609 public void addTo(String to, String personal) throws MessagingException , UnsupportedEncodingException { 610 Assert.notNull(to, "To address must not be null"); 611 addTo(getEncoding() != null ? 612 new InternetAddress (to, personal, getEncoding()) : 613 new InternetAddress (to, personal)); 614 } 615 616 617 public void setCc(InternetAddress cc) throws MessagingException { 618 Assert.notNull(cc, "Cc address must not be null"); 619 validateAddress(cc); 620 this.mimeMessage.setRecipient(Message.RecipientType.CC, cc); 621 } 622 623 public void setCc(InternetAddress [] cc) throws MessagingException { 624 Assert.notNull(cc, "Cc address array must not be null"); 625 validateAddresses(cc); 626 this.mimeMessage.setRecipients(Message.RecipientType.CC, cc); 627 } 628 629 public void setCc(String cc) throws MessagingException { 630 Assert.notNull(cc, "Cc address must not be null"); 631 setCc(new InternetAddress (cc)); 632 } 633 634 public void setCc(String [] cc) throws MessagingException { 635 Assert.notNull(cc, "Cc address array must not be null"); 636 InternetAddress [] addresses = new InternetAddress [cc.length]; 637 for (int i = 0; i < cc.length; i++) { 638 addresses[i] = new InternetAddress (cc[i]); 639 } 640 setCc(addresses); 641 } 642 643 public void addCc(InternetAddress cc) throws MessagingException { 644 Assert.notNull(cc, "Cc address must not be null"); 645 validateAddress(cc); 646 this.mimeMessage.addRecipient(Message.RecipientType.CC, cc); 647 } 648 649 public void addCc(String cc) throws MessagingException { 650 Assert.notNull(cc, "Cc address must not be null"); 651 addCc(new InternetAddress (cc)); 652 } 653 654 public void addCc(String cc, String personal) throws MessagingException , UnsupportedEncodingException { 655 Assert.notNull(cc, "Cc address must not be null"); 656 addCc(getEncoding() != null ? 657 new InternetAddress (cc, personal, getEncoding()) : 658 new InternetAddress (cc, personal)); 659 } 660 661 662 public void setBcc(InternetAddress bcc) throws MessagingException { 663 Assert.notNull(bcc, "Bcc address must not be null"); 664 validateAddress(bcc); 665 this.mimeMessage.setRecipient(Message.RecipientType.BCC, bcc); 666 } 667 668 public void setBcc(InternetAddress [] bcc) throws MessagingException { 669 Assert.notNull(bcc, "Bcc address array must not be null"); 670 validateAddresses(bcc); 671 this.mimeMessage.setRecipients(Message.RecipientType.BCC, bcc); 672 } 673 674 public void setBcc(String bcc) throws MessagingException { 675 Assert.notNull(bcc, "Bcc address must not be null"); 676 setBcc(new InternetAddress (bcc)); 677 } 678 679 public void setBcc(String [] bcc) throws MessagingException { 680 Assert.notNull(bcc, "Bcc address array must not be null"); 681 InternetAddress [] addresses = new InternetAddress [bcc.length]; 682 for (int i = 0; i < bcc.length; i++) { 683 addresses[i] = new InternetAddress (bcc[i]); 684 } 685 setBcc(addresses); 686 } 687 688 public void addBcc(InternetAddress bcc) throws MessagingException { 689 Assert.notNull(bcc, "Bcc address must not be null"); 690 validateAddress(bcc); 691 this.mimeMessage.addRecipient(Message.RecipientType.BCC, bcc); 692 } 693 694 public void addBcc(String bcc) throws MessagingException { 695 Assert.notNull(bcc, "Bcc address must not be null"); 696 addBcc(new InternetAddress (bcc)); 697 } 698 699 public void addBcc(String bcc, String personal) throws MessagingException , UnsupportedEncodingException { 700 Assert.notNull(bcc, "Bcc address must not be null"); 701 addBcc(getEncoding() != null ? 702 new InternetAddress (bcc, personal, getEncoding()) : 703 new InternetAddress (bcc, personal)); 704 } 705 706 707 713 public void setPriority(int priority) throws MessagingException { 714 this.mimeMessage.setHeader(HEADER_PRIORITY, Integer.toString(priority)); 715 } 716 717 722 public void setSentDate(Date sentDate) throws MessagingException { 723 Assert.notNull(sentDate, "Sent date must not be null"); 724 this.mimeMessage.setSentDate(sentDate); 725 } 726 727 732 public void setSubject(String subject) throws MessagingException { 733 Assert.notNull(subject, "Subject must not be null"); 734 if (getEncoding() != null) { 735 this.mimeMessage.setSubject(subject, getEncoding()); 736 } 737 else { 738 this.mimeMessage.setSubject(subject); 739 } 740 } 741 742 743 752 public void setText(String text) throws MessagingException { 753 setText(text, false); 754 } 755 756 767 public void setText(String text, boolean html) throws MessagingException { 768 Assert.notNull(text, "Text must not be null"); 769 MimePart partToUse = null; 770 if (isMultipart()) { 771 partToUse = getMainPart(); 772 } 773 else { 774 partToUse = this.mimeMessage; 775 } 776 if (html) { 777 setHtmlTextToMimePart(partToUse, text); 778 } 779 else { 780 setPlainTextToMimePart(partToUse, text); 781 } 782 } 783 784 793 public void setText(String plainText, String htmlText) throws MessagingException { 794 Assert.notNull(plainText, "Plain text must not be null"); 795 Assert.notNull(htmlText, "HTML text must not be null"); 796 797 MimeMultipart messageBody = new MimeMultipart (MULTIPART_SUBTYPE_ALTERNATIVE); 798 getMainPart().setContent(messageBody, CONTENT_TYPE_ALTERNATIVE); 799 800 MimeBodyPart plainTextPart = new MimeBodyPart (); 802 setPlainTextToMimePart(plainTextPart, plainText); 803 messageBody.addBodyPart(plainTextPart); 804 805 MimeBodyPart htmlTextPart = new MimeBodyPart (); 807 setHtmlTextToMimePart(htmlTextPart, htmlText); 808 messageBody.addBodyPart(htmlTextPart); 809 } 810 811 private MimeBodyPart getMainPart() throws MessagingException { 812 MimeMultipart mimeMultipart = getMimeMultipart(); 813 MimeBodyPart bodyPart = null; 814 for (int i = 0; i < mimeMultipart.getCount(); i++) { 815 BodyPart bp = mimeMultipart.getBodyPart(i); 816 if (bp.getFileName() == null) { 817 bodyPart = (MimeBodyPart ) bp; 818 } 819 } 820 if (bodyPart == null) { 821 MimeBodyPart mimeBodyPart = new MimeBodyPart (); 822 mimeMultipart.addBodyPart(mimeBodyPart); 823 bodyPart = mimeBodyPart; 824 } 825 return bodyPart; 826 } 827 828 private void setPlainTextToMimePart(MimePart mimePart, String text) throws MessagingException { 829 if (getEncoding() != null) { 830 mimePart.setText(text, getEncoding()); 831 } 832 else { 833 mimePart.setText(text); 834 } 835 } 836 837 private void setHtmlTextToMimePart(MimePart mimePart, String text) throws MessagingException { 838 if (getEncoding() != null) { 839 mimePart.setContent(text, CONTENT_TYPE_HTML + CONTENT_TYPE_CHARSET_SUFFIX + getEncoding()); 840 } 841 else { 842 mimePart.setContent(text, CONTENT_TYPE_HTML); 843 } 844 } 845 846 847 864 public void addInline(String contentId, DataSource dataSource) throws MessagingException { 865 Assert.notNull(contentId, "Content ID must not be null"); 866 Assert.notNull(dataSource, "DataSource must not be null"); 867 MimeBodyPart mimeBodyPart = new MimeBodyPart (); 868 mimeBodyPart.setDisposition(MimeBodyPart.INLINE); 869 mimeBodyPart.setHeader(HEADER_CONTENT_ID, "<" + contentId + ">"); 872 mimeBodyPart.setDataHandler(new DataHandler (dataSource)); 873 getMimeMultipart().addBodyPart(mimeBodyPart); 874 } 875 876 893 public void addInline(String contentId, File file) throws MessagingException { 894 Assert.notNull(file, "File must not be null"); 895 FileDataSource dataSource = new FileDataSource (file); 896 dataSource.setFileTypeMap(getFileTypeMap()); 897 addInline(contentId, dataSource); 898 } 899 900 920 public void addInline(String contentId, Resource resource) throws MessagingException { 921 Assert.notNull(resource, "Resource must not be null"); 922 String contentType = getFileTypeMap().getContentType(resource.getFilename()); 923 addInline(contentId, resource, contentType); 924 } 925 926 948 public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType) 949 throws MessagingException { 950 951 Assert.notNull(inputStreamSource, "InputStreamSource must not be null"); 952 if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) { 953 throw new IllegalArgumentException ( 954 "Passed-in Resource contains an open stream: invalid argument. " + 955 "JavaMail requires an InputStreamSource that creates a fresh stream for every call."); 956 } 957 DataSource dataSource = createDataSource(inputStreamSource, contentType, "inline"); 958 addInline(contentId, dataSource); 959 } 960 961 975 public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException { 976 Assert.notNull(attachmentFilename, "Attachment filename must not be null"); 977 Assert.notNull(dataSource, "DataSource must not be null"); 978 MimeBodyPart mimeBodyPart = new MimeBodyPart (); 979 mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT); 980 mimeBodyPart.setFileName(attachmentFilename); 981 mimeBodyPart.setDataHandler(new DataHandler (dataSource)); 982 getRootMimeMultipart().addBodyPart(mimeBodyPart); 983 } 984 985 998 public void addAttachment(String attachmentFilename, File file) throws MessagingException { 999 Assert.notNull(file, "File must not be null"); 1000 FileDataSource dataSource = new FileDataSource (file); 1001 dataSource.setFileTypeMap(getFileTypeMap()); 1002 addAttachment(attachmentFilename, dataSource); 1003 } 1004 1005 1023 public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource) 1024 throws MessagingException { 1025 1026 String contentType = getFileTypeMap().getContentType(attachmentFilename); 1027 addAttachment(attachmentFilename, inputStreamSource, contentType); 1028 } 1029 1030 1046 public void addAttachment( 1047 String attachmentFilename, InputStreamSource inputStreamSource, String contentType) 1048 throws MessagingException { 1049 1050 Assert.notNull(inputStreamSource, "InputStreamSource must not be null"); 1051 if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) { 1052 throw new IllegalArgumentException ( 1053 "Passed-in Resource contains an open stream: invalid argument. " + 1054 "JavaMail requires an InputStreamSource that creates a fresh stream for every call."); 1055 } 1056 DataSource dataSource = createDataSource(inputStreamSource, contentType, attachmentFilename); 1057 addAttachment(attachmentFilename, dataSource); 1058 } 1059 1060 1067 protected DataSource createDataSource( 1068 final InputStreamSource inputStreamSource, final String contentType, final String name) { 1069 1070 return new DataSource () { 1071 public InputStream getInputStream() throws IOException { 1072 return inputStreamSource.getInputStream(); 1073 } 1074 public OutputStream getOutputStream() { 1075 throw new UnsupportedOperationException ("Read-only javax.activation.DataSource"); 1076 } 1077 public String getContentType() { 1078 return contentType; 1079 } 1080 public String getName() { 1081 return name; 1082 } 1083 }; 1084 } 1085 1086} 1087 | Popular Tags |