001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.mail; 018 019import java.io.UnsupportedEncodingException; 020import java.nio.charset.Charset; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029 030import javax.mail.Authenticator; 031import javax.mail.Message; 032import javax.mail.MessagingException; 033import javax.mail.Session; 034import javax.mail.Store; 035import javax.mail.Transport; 036import javax.mail.internet.AddressException; 037import javax.mail.internet.InternetAddress; 038import javax.mail.internet.MimeMessage; 039import javax.mail.internet.MimeMultipart; 040import javax.naming.Context; 041import javax.naming.InitialContext; 042import javax.naming.NamingException; 043 044/** 045 * The base class for all email messages. This class sets the 046 * sender's email & name, receiver's email & name, subject, and the 047 * sent date. Subclasses are responsible for setting the message 048 * body. 049 * 050 * @since 1.0 051 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a> 052 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> 053 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> 054 * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a> 055 * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a> 056 * @author <a href="mailto:unknown">Regis Koenig</a> 057 * @author <a href="mailto:colin.chalmers@maxware.nl">Colin Chalmers</a> 058 * @author <a href="mailto:matthias@wessendorf.net">Matthias Wessendorf</a> 059 * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a> 060 * @version $Revision: 783910 $ $Date: 2009-06-11 23:13:54 +0200 (Thu, 11 Jun 2009) $ 061 * @version $Id: Email.java 783910 2009-06-11 21:13:54Z sgoeschl $ 062 */ 063public abstract class Email 064{ 065 /** Constants used by Email classes. */ 066 067 /** */ 068 public static final String SENDER_EMAIL = "sender.email"; 069 /** */ 070 public static final String SENDER_NAME = "sender.name"; 071 /** */ 072 public static final String RECEIVER_EMAIL = "receiver.email"; 073 /** */ 074 public static final String RECEIVER_NAME = "receiver.name"; 075 /** */ 076 public static final String EMAIL_SUBJECT = "email.subject"; 077 /** */ 078 public static final String EMAIL_BODY = "email.body"; 079 /** */ 080 public static final String CONTENT_TYPE = "content.type"; 081 082 /** */ 083 public static final String MAIL_HOST = "mail.smtp.host"; 084 /** */ 085 public static final String MAIL_PORT = "mail.smtp.port"; 086 /** */ 087 public static final String MAIL_SMTP_FROM = "mail.smtp.from"; 088 /** */ 089 public static final String MAIL_SMTP_AUTH = "mail.smtp.auth"; 090 /** */ 091 public static final String MAIL_SMTP_USER = "mail.smtp.user"; 092 /** */ 093 public static final String MAIL_SMTP_PASSWORD = "mail.smtp.password"; 094 /** */ 095 public static final String MAIL_TRANSPORT_PROTOCOL = 096 "mail.transport.protocol"; 097 /** 098 * @since 1.1 099 */ 100 public static final String MAIL_TRANSPORT_TLS = "mail.smtp.starttls.enable"; 101 /** */ 102 public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = "mail.smtp.socketFactory.fallback"; 103 /** */ 104 public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = "mail.smtp.socketFactory.class"; 105 /** */ 106 public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = "mail.smtp.socketFactory.port"; 107 108 109 /** 110 * Socket connection timeout value in milliseconds. Default is infinite timeout. 111 * @since 1.2 112 */ 113 public static final String MAIL_SMTP_CONNECTIONTIMEOUT = "mail.smtp.connectiontimeout"; 114 115 /** 116 * Socket I/O timeout value in milliseconds. Default is infinite timeout. 117 * @since 1.2 118 */ 119 public static final String MAIL_SMTP_TIMEOUT = "mail.smtp.timeout"; 120 121 122 /** */ 123 public static final String SMTP = "smtp"; 124 /** */ 125 public static final String TEXT_HTML = "text/html"; 126 /** */ 127 public static final String TEXT_PLAIN = "text/plain"; 128 /** */ 129 public static final String ATTACHMENTS = "attachments"; 130 /** */ 131 public static final String FILE_SERVER = "file.server"; 132 /** */ 133 public static final String MAIL_DEBUG = "mail.debug"; 134 135 /** */ 136 public static final String KOI8_R = "koi8-r"; 137 /** */ 138 public static final String ISO_8859_1 = "iso-8859-1"; 139 /** */ 140 public static final String US_ASCII = "us-ascii"; 141 142 /** The email message to send. */ 143 protected MimeMessage message; 144 145 /** The charset to use for this message */ 146 protected String charset; 147 148 /** The Address of the sending party, mandatory */ 149 protected InternetAddress fromAddress; 150 151 /** The Subject */ 152 protected String subject; 153 154 /** An attachment */ 155 protected MimeMultipart emailBody; 156 157 /** The content */ 158 protected Object content; 159 160 /** The content type */ 161 protected String contentType; 162 163 /** Set session debugging on or off */ 164 protected boolean debug; 165 166 /** Sent date */ 167 protected Date sentDate; 168 169 /** 170 * Instance of an <code>Authenticator</code> object that will be used 171 * when authentication is requested from the mail server. 172 */ 173 protected Authenticator authenticator; 174 175 /** 176 * The hostname of the mail server with which to connect. If null will try 177 * to get property from system.properties. If still null, quit 178 */ 179 protected String hostName; 180 181 /** 182 * The port number of the mail server to connect to. 183 * Defaults to the standard port ( 25 ). 184 */ 185 protected String smtpPort = "25"; 186 187 /** 188 * The port number of the SSL enabled SMTP server; 189 * defaults to the standard port, 465. 190 */ 191 protected String sslSmtpPort = "465"; 192 193 /** List of "to" email adresses */ 194 protected List toList = new ArrayList(); 195 196 /** List of "cc" email adresses */ 197 protected List ccList = new ArrayList(); 198 199 /** List of "bcc" email adresses */ 200 protected List bccList = new ArrayList(); 201 202 /** List of "replyTo" email adresses */ 203 protected List replyList = new ArrayList(); 204 205 /** 206 * Address to which undeliverable mail should be sent. 207 * Because this is handled by JavaMail as a String property 208 * in the mail session, this property is of type <code>String</code> 209 * rather than <code>InternetAddress</code>. 210 */ 211 protected String bounceAddress; 212 213 /** 214 * Used to specify the mail headers. Example: 215 * 216 * X-Mailer: Sendmail, X-Priority: 1( highest ) 217 * or 2( high ) 3( normal ) 4( low ) and 5( lowest ) 218 * Disposition-Notification-To: user@domain.net 219 */ 220 protected Map headers = new HashMap(); 221 222 /** 223 * Used to determine whether to use pop3 before smtp, and if so the settings. 224 */ 225 protected boolean popBeforeSmtp; 226 /** the host name of the pop3 server */ 227 protected String popHost; 228 /** the user name to log into the pop3 server */ 229 protected String popUsername; 230 /** the password to log into the pop3 server */ 231 protected String popPassword; 232 233 /** does server require TLS encryption for authentication */ 234 protected boolean tls; 235 /** does the current transport use SSL encryption? */ 236 protected boolean ssl; 237 238 /** socket I/O timeout value in milliseconds */ 239 protected int socketTimeout; 240 /** socket connection timeout value in milliseconds */ 241 protected int socketConnectionTimeout; 242 243 /** The Session to mail with */ 244 private Session session; 245 246 /** 247 * Setting to true will enable the display of debug information. 248 * 249 * @param d A boolean. 250 * @since 1.0 251 */ 252 public void setDebug(boolean d) 253 { 254 this.debug = d; 255 } 256 257 /** 258 * Sets the userName and password if authentication is needed. If this 259 * method is not used, no authentication will be performed. 260 * <p> 261 * This method will create a new instance of 262 * <code>DefaultAuthenticator</code> using the supplied parameters. 263 * 264 * @param userName User name for the SMTP server 265 * @param password password for the SMTP server 266 * @see DefaultAuthenticator 267 * @see #setAuthenticator 268 * @since 1.0 269 */ 270 public void setAuthentication(String userName, String password) 271 { 272 this.authenticator = new DefaultAuthenticator(userName, password); 273 this.setAuthenticator(this.authenticator); 274 } 275 276 /** 277 * Sets the <code>Authenticator</code> to be used when authentication 278 * is requested from the mail server. 279 * <p> 280 * This method should be used when your outgoing mail server requires 281 * authentication. Your mail server must also support RFC2554. 282 * 283 * @param newAuthenticator the <code>Authenticator</code> object. 284 * @see Authenticator 285 * @since 1.0 286 */ 287 public void setAuthenticator(Authenticator newAuthenticator) 288 { 289 this.authenticator = newAuthenticator; 290 } 291 292 /** 293 * Set the charset of the message. 294 * 295 * @param newCharset A String. 296 * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid 297 * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset 298 * exists in the current JVM 299 * @since 1.0 300 */ 301 public void setCharset(String newCharset) 302 { 303 Charset set = Charset.forName(newCharset); 304 this.charset = set.name(); 305 } 306 307 /** 308 * Set the emailBody to a MimeMultiPart 309 * 310 * @param aMimeMultipart aMimeMultipart 311 * @since 1.0 312 */ 313 public void setContent(MimeMultipart aMimeMultipart) 314 { 315 this.emailBody = aMimeMultipart; 316 } 317 318 /** 319 * Set the content & contentType 320 * 321 * @param aObject aObject 322 * @param aContentType aContentType 323 * @since 1.0 324 */ 325 public void setContent(Object aObject, String aContentType) 326 { 327 this.content = aObject; 328 this.updateContentType(aContentType); 329 } 330 331 332 /** 333 * Update the contentType. 334 * 335 * @param aContentType aContentType 336 * @since 1.2 337 */ 338 public void updateContentType(final String aContentType) 339 { 340 if (EmailUtils.isEmpty(aContentType)) 341 { 342 this.contentType = null; 343 } 344 else 345 { 346 // set the content type 347 this.contentType = aContentType; 348 349 // set the charset if the input was properly formed 350 String strMarker = "; charset="; 351 int charsetPos = aContentType.toLowerCase().indexOf(strMarker); 352 353 if (charsetPos != -1) 354 { 355 // find the next space (after the marker) 356 charsetPos += strMarker.length(); 357 int intCharsetEnd = 358 aContentType.toLowerCase().indexOf(" ", charsetPos); 359 360 if (intCharsetEnd != -1) 361 { 362 this.charset = 363 aContentType.substring(charsetPos, intCharsetEnd); 364 } 365 else 366 { 367 this.charset = aContentType.substring(charsetPos); 368 } 369 } 370 else 371 { 372 // use the default charset, if one exists, for messages 373 // whose content-type is some form of text. 374 if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset)) 375 { 376 StringBuffer contentTypeBuf = new StringBuffer(this.contentType); 377 contentTypeBuf.append(strMarker); 378 contentTypeBuf.append(this.charset); 379 this.contentType = contentTypeBuf.toString(); 380 } 381 } 382 } 383 } 384 385 /** 386 * Set the hostname of the outgoing mail server 387 * 388 * @param aHostName aHostName 389 * @since 1.0 390 */ 391 public void setHostName(String aHostName) 392 { 393 this.hostName = aHostName; 394 } 395 396 /** 397 * Set or disable the TLS encryption 398 * 399 * @param withTLS true if TLS needed, false otherwise 400 * @since 1.1 401 */ 402 public void setTLS(boolean withTLS) 403 { 404 this.tls = withTLS; 405 } 406 407 /** 408 * Set the port number of the outgoing mail server. 409 * @param aPortNumber aPortNumber 410 * @since 1.0 411 */ 412 public void setSmtpPort(int aPortNumber) 413 { 414 if (aPortNumber < 1) 415 { 416 throw new IllegalArgumentException( 417 "Cannot connect to a port number that is less than 1 ( " 418 + aPortNumber 419 + " )"); 420 } 421 422 this.smtpPort = Integer.toString(aPortNumber); 423 } 424 425 /** 426 * Supply a mail Session object to use. Please note that passing 427 * a username and password (in the case of mail authentication) will 428 * create a new mail session with a DefaultAuthenticator. This is a 429 * convience but might come unexpected. 430 * 431 * If mail authentication is used but NO username and password 432 * is supplied the implementation assumes that you have set a 433 * authenticator and will use the existing mail session (as expected). 434 * 435 * @param aSession mail session to be used 436 * @since 1.0 437 */ 438 public void setMailSession(Session aSession) 439 { 440 EmailUtils.notNull(aSession, "no mail session supplied"); 441 442 Properties sessionProperties = aSession.getProperties(); 443 String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH); 444 445 if ("true".equalsIgnoreCase(auth)) 446 { 447 String userName = sessionProperties.getProperty(MAIL_SMTP_USER); 448 String password = sessionProperties.getProperty(MAIL_SMTP_PASSWORD); 449 450 if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password)) 451 { 452 // only create a new mail session with an authenticator if 453 // authentication is required and no user name is given 454 this.authenticator = new DefaultAuthenticator(userName, password); 455 this.session = Session.getInstance(sessionProperties, this.authenticator); 456 } 457 else 458 { 459 // assume that the given mail session contains a working authenticator 460 this.session = aSession; 461 } 462 } 463 else 464 { 465 this.session = aSession; 466 } 467 } 468 469 /** 470 * Supply a mail Session object from a JNDI directory 471 * @param jndiName name of JNDI ressource (javax.mail.Session type), ressource 472 * if searched in java:comp/env if name dont start with "java:" 473 * @throws IllegalArgumentException JNDI name null or empty 474 * @throws NamingException ressource can be retrieved from JNDI directory 475 * @since 1.1 476 */ 477 public void setMailSessionFromJNDI(String jndiName) throws NamingException 478 { 479 if (EmailUtils.isEmpty(jndiName)) 480 { 481 throw new IllegalArgumentException("JNDI name missing"); 482 } 483 Context ctx = null; 484 if (jndiName.startsWith("java:")) 485 { 486 ctx = new InitialContext(); 487 } 488 else 489 { 490 ctx = (Context) new InitialContext().lookup("java:comp/env"); 491 492 } 493 this.setMailSession((Session) ctx.lookup(jndiName)); 494 } 495 496 /** 497 * Initialise a mailsession object 498 * 499 * @return A Session. 500 * @throws EmailException thrown when host name was not set. 501 * @since 1.0 502 */ 503 public Session getMailSession() throws EmailException 504 { 505 if (this.session == null) 506 { 507 Properties properties = new Properties(System.getProperties()); 508 properties.setProperty(MAIL_TRANSPORT_PROTOCOL, SMTP); 509 510 if (EmailUtils.isEmpty(this.hostName)) 511 { 512 this.hostName = properties.getProperty(MAIL_HOST); 513 } 514 515 if (EmailUtils.isEmpty(this.hostName)) 516 { 517 throw new EmailException( 518 "Cannot find valid hostname for mail session"); 519 } 520 521 properties.setProperty(MAIL_PORT, smtpPort); 522 properties.setProperty(MAIL_HOST, hostName); 523 properties.setProperty(MAIL_DEBUG, String.valueOf(this.debug)); 524 525 if (this.authenticator != null) 526 { 527 properties.setProperty(MAIL_TRANSPORT_TLS, tls ? "true" : "false"); 528 properties.setProperty(MAIL_SMTP_AUTH, "true"); 529 } 530 531 if (this.ssl) 532 { 533 properties.setProperty(MAIL_PORT, sslSmtpPort); 534 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); 535 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory"); 536 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false"); 537 } 538 539 if (this.bounceAddress != null) 540 { 541 properties.setProperty(MAIL_SMTP_FROM, this.bounceAddress); 542 } 543 544 if (this.socketTimeout > 0) 545 { 546 properties.setProperty(MAIL_SMTP_TIMEOUT, Integer.toString(this.socketTimeout)); 547 } 548 549 if (this.socketConnectionTimeout > 0) 550 { 551 properties.setProperty(MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(this.socketConnectionTimeout)); 552 } 553 554 // changed this (back) to getInstance due to security exceptions 555 // caused when testing using maven 556 this.session = 557 Session.getInstance(properties, this.authenticator); 558 } 559 return this.session; 560 } 561 562 /** 563 * Creates a InternetAddress. 564 * 565 * @param email An email address. 566 * @param name A name. 567 * @param charsetName The name of the charset to encode the name with. 568 * @return An internet address. 569 * @throws EmailException Thrown when the supplied address, name or charset were invalid. 570 */ 571 private InternetAddress createInternetAddress(String email, String name, String charsetName) 572 throws EmailException 573 { 574 InternetAddress address = null; 575 576 try 577 { 578 address = new InternetAddress(email); 579 580 // check name input 581 if (EmailUtils.isEmpty(name)) 582 { 583 name = email; 584 } 585 586 // check charset input. 587 if (EmailUtils.isEmpty(charsetName)) 588 { 589 address.setPersonal(name); 590 } 591 else 592 { 593 // canonicalize the charset name and make sure 594 // the current platform supports it. 595 Charset set = Charset.forName(charsetName); 596 address.setPersonal(name, set.name()); 597 } 598 599 // run sanity check on new InternetAddress object; if this fails 600 // it will throw AddressException. 601 address.validate(); 602 } 603 catch (AddressException e) 604 { 605 throw new EmailException(e); 606 } 607 catch (UnsupportedEncodingException e) 608 { 609 throw new EmailException(e); 610 } 611 return address; 612 } 613 614 615 /** 616 * Set the FROM field of the email to use the specified address. The email 617 * address will also be used as the personal name. 618 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 619 * If it is not set, it will be encoded using 620 * the Java platform's default charset (UTF-16) if it contains 621 * non-ASCII characters; otherwise, it is used as is. 622 * 623 * @param email A String. 624 * @return An Email. 625 * @throws EmailException Indicates an invalid email address. 626 * @since 1.0 627 */ 628 public Email setFrom(String email) 629 throws EmailException 630 { 631 return setFrom(email, null); 632 } 633 634 /** 635 * Set the FROM field of the email to use the specified address and the 636 * specified personal name. 637 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 638 * If it is not set, it will be encoded using 639 * the Java platform's default charset (UTF-16) if it contains 640 * non-ASCII characters; otherwise, it is used as is. 641 * 642 * @param email A String. 643 * @param name A String. 644 * @throws EmailException Indicates an invalid email address. 645 * @return An Email. 646 * @since 1.0 647 */ 648 public Email setFrom(String email, String name) 649 throws EmailException 650 { 651 return setFrom(email, name, this.charset); 652 } 653 654 /** 655 * Set the FROM field of the email to use the specified address, personal 656 * name, and charset encoding for the name. 657 * 658 * @param email A String. 659 * @param name A String. 660 * @param charset The charset to encode the name with. 661 * @throws EmailException Indicates an invalid email address or charset. 662 * @return An Email. 663 * @since 1.1 664 */ 665 public Email setFrom(String email, String name, String charset) 666 throws EmailException 667 { 668 this.fromAddress = createInternetAddress(email, name, charset); 669 return this; 670 } 671 672 /** 673 * Add a recipient TO to the email. The email 674 * address will also be used as the personal name. 675 * The name will be encoded by the charset of 676 * {@link #setCharset(java.lang.String) setCharset()}. 677 * If it is not set, it will be encoded using 678 * the Java platform's default charset (UTF-16) if it contains 679 * non-ASCII characters; otherwise, it is used as is. 680 * 681 * @param email A String. 682 * @throws EmailException Indicates an invalid email address. 683 * @return An Email. 684 * @since 1.0 685 */ 686 public Email addTo(String email) 687 throws EmailException 688 { 689 return addTo(email, null); 690 } 691 692 /** 693 * Add a recipient TO to the email using the specified address and the 694 * specified personal name. 695 * The name will be encoded by the charset of 696 * {@link #setCharset(java.lang.String) setCharset()}. 697 * If it is not set, it will be encoded using 698 * the Java platform's default charset (UTF-16) if it contains 699 * non-ASCII characters; otherwise, it is used as is. 700 * 701 * @param email A String. 702 * @param name A String. 703 * @throws EmailException Indicates an invalid email address. 704 * @return An Email. 705 * @since 1.0 706 */ 707 public Email addTo(String email, String name) 708 throws EmailException 709 { 710 return addTo(email, name, this.charset); 711 } 712 713 /** 714 * Add a recipient TO to the email using the specified address, personal 715 * name, and charset encoding for the name. 716 * 717 * @param email A String. 718 * @param name A String. 719 * @param charset The charset to encode the name with. 720 * @throws EmailException Indicates an invalid email address or charset. 721 * @return An Email. 722 * @since 1.1 723 */ 724 public Email addTo(String email, String name, String charset) 725 throws EmailException 726 { 727 this.toList.add(createInternetAddress(email, name, charset)); 728 return this; 729 } 730 731 /** 732 * Set a list of "TO" addresses. All elements in the specified 733 * <code>Collection</code> are expected to be of type 734 * <code>java.mail.internet.InternetAddress</code>. 735 * 736 * @param aCollection collection of <code>InternetAddress</code> objects. 737 * @throws EmailException Indicates an invalid email address. 738 * @return An Email. 739 * @see javax.mail.internet.InternetAddress 740 * @since 1.0 741 */ 742 public Email setTo(Collection aCollection) throws EmailException 743 { 744 if (aCollection == null || aCollection.isEmpty()) 745 { 746 throw new EmailException("Address List provided was invalid"); 747 } 748 749 this.toList = new ArrayList(aCollection); 750 return this; 751 } 752 753 /** 754 * Add a recipient CC to the email. The email 755 * address will also be used as the personal name. 756 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 757 * If it is not set, it will be encoded using 758 * the Java platform's default charset (UTF-16) if it contains 759 * non-ASCII characters; otherwise, it is used as is. 760 * 761 * @param email A String. 762 * @return An Email. 763 * @throws EmailException Indicates an invalid email address. 764 * @since 1.0 765 */ 766 public Email addCc(String email) 767 throws EmailException 768 { 769 return this.addCc(email, null); 770 } 771 772 /** 773 * Add a recipient CC to the email using the specified address and the 774 * specified personal name. 775 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 776 * If it is not set, it will be encoded using 777 * the Java platform's default charset (UTF-16) if it contains 778 * non-ASCII characters; otherwise, it is used as is. 779 * 780 * @param email A String. 781 * @param name A String. 782 * @throws EmailException Indicates an invalid email address. 783 * @return An Email. 784 * @since 1.0 785 */ 786 public Email addCc(String email, String name) 787 throws EmailException 788 { 789 return addCc(email, name, this.charset); 790 } 791 792 /** 793 * Add a recipient CC to the email using the specified address, personal 794 * name, and charset encoding for the name. 795 * 796 * @param email A String. 797 * @param name A String. 798 * @param charset The charset to encode the name with. 799 * @throws EmailException Indicates an invalid email address or charset. 800 * @return An Email. 801 * @since 1.1 802 */ 803 public Email addCc(String email, String name, String charset) 804 throws EmailException 805 { 806 this.ccList.add(createInternetAddress(email, name, charset)); 807 return this; 808 } 809 810 /** 811 * Set a list of "CC" addresses. All elements in the specified 812 * <code>Collection</code> are expected to be of type 813 * <code>java.mail.internet.InternetAddress</code>. 814 * 815 * @param aCollection collection of <code>InternetAddress</code> objects. 816 * @return An Email. 817 * @throws EmailException Indicates an invalid email address. 818 * @see javax.mail.internet.InternetAddress 819 * @since 1.0 820 */ 821 public Email setCc(Collection aCollection) throws EmailException 822 { 823 if (aCollection == null || aCollection.isEmpty()) 824 { 825 throw new EmailException("Address List provided was invalid"); 826 } 827 828 this.ccList = new ArrayList(aCollection); 829 return this; 830 } 831 832 /** 833 * Add a blind BCC recipient to the email. The email 834 * address will also be used as the personal name. 835 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 836 * If it is not set, it will be encoded using 837 * the Java platform's default charset (UTF-16) if it contains 838 * non-ASCII characters; otherwise, it is used as is. 839 * 840 * @param email A String. 841 * @return An Email. 842 * @throws EmailException Indicates an invalid email address 843 * @since 1.0 844 */ 845 public Email addBcc(String email) 846 throws EmailException 847 { 848 return this.addBcc(email, null); 849 } 850 851 /** 852 * Add a blind BCC recipient to the email using the specified address and 853 * the specified personal name. 854 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 855 * If it is not set, it will be encoded using 856 * the Java platform's default charset (UTF-16) if it contains 857 * non-ASCII characters; otherwise, it is used as is. 858 * 859 * @param email A String. 860 * @param name A String. 861 * @return An Email. 862 * @throws EmailException Indicates an invalid email address 863 * @since 1.0 864 */ 865 public Email addBcc(String email, String name) 866 throws EmailException 867 { 868 return addBcc(email, name, this.charset); 869 } 870 871 /** 872 * Add a blind BCC recipient to the email using the specified address, 873 * personal name, and charset encoding for the name. 874 * 875 * @param email A String. 876 * @param name A String. 877 * @param charset The charset to encode the name with. 878 * @return An Email. 879 * @throws EmailException Indicates an invalid email address 880 * @since 1.1 881 */ 882 public Email addBcc(String email, String name, String charset) 883 throws EmailException 884 { 885 this.bccList.add(createInternetAddress(email, name, charset)); 886 return this; 887 } 888 889 /** 890 * Set a list of "BCC" addresses. All elements in the specified 891 * <code>Collection</code> are expected to be of type 892 * <code>java.mail.internet.InternetAddress</code>. 893 * 894 * @param aCollection collection of <code>InternetAddress</code> objects 895 * @return An Email. 896 * @throws EmailException Indicates an invalid email address 897 * @see javax.mail.internet.InternetAddress 898 * @since 1.0 899 */ 900 public Email setBcc(Collection aCollection) throws EmailException 901 { 902 if (aCollection == null || aCollection.isEmpty()) 903 { 904 throw new EmailException("Address List provided was invalid"); 905 } 906 907 this.bccList = new ArrayList(aCollection); 908 return this; 909 } 910 911 /** 912 * Add a reply to address to the email. The email 913 * address will also be used as the personal name. 914 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 915 * If it is not set, it will be encoded using 916 * the Java platform's default charset (UTF-16) if it contains 917 * non-ASCII characters; otherwise, it is used as is. 918 * 919 * @param email A String. 920 * @return An Email. 921 * @throws EmailException Indicates an invalid email address 922 * @since 1.0 923 */ 924 public Email addReplyTo(String email) 925 throws EmailException 926 { 927 return this.addReplyTo(email, null); 928 } 929 930 /** 931 * Add a reply to address to the email using the specified address and 932 * the specified personal name. 933 * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}. 934 * If it is not set, it will be encoded using 935 * the Java platform's default charset (UTF-16) if it contains 936 * non-ASCII characters; otherwise, it is used as is. 937 * 938 * @param email A String. 939 * @param name A String. 940 * @return An Email. 941 * @throws EmailException Indicates an invalid email address 942 * @since 1.0 943 */ 944 public Email addReplyTo(String email, String name) 945 throws EmailException 946 { 947 return addReplyTo(email, name, this.charset); 948 } 949 950 /** 951 * Add a reply to address to the email using the specified address, 952 * personal name, and charset encoding for the name. 953 * 954 * @param email A String. 955 * @param name A String. 956 * @param charset The charset to encode the name with. 957 * @return An Email. 958 * @throws EmailException Indicates an invalid email address or charset. 959 * @since 1.1 960 */ 961 public Email addReplyTo(String email, String name, String charset) 962 throws EmailException 963 { 964 this.replyList.add(createInternetAddress(email, name, charset)); 965 return this; 966 } 967 968 /** 969 * Set a list of reply to addresses. All elements in the specified 970 * <code>Collection</code> are expected to be of type 971 * <code>java.mail.internet.InternetAddress</code>. 972 * 973 * @param aCollection collection of <code>InternetAddress</code> objects 974 * @return An Email. 975 * @throws EmailException Indicates an invalid email address 976 * @see javax.mail.internet.InternetAddress 977 * @since 1.1 978 */ 979 public Email setReplyTo(Collection aCollection) throws EmailException 980 { 981 if (aCollection == null || aCollection.isEmpty()) 982 { 983 throw new EmailException("Address List provided was invalid"); 984 } 985 986 this.replyList = new ArrayList(aCollection); 987 return this; 988 } 989 990 /** 991 * Used to specify the mail headers. Example: 992 * 993 * X-Mailer: Sendmail, X-Priority: 1( highest ) 994 * or 2( high ) 3( normal ) 4( low ) and 5( lowest ) 995 * Disposition-Notification-To: user@domain.net 996 * 997 * @param map A Map. 998 * @since 1.0 999 */ 1000 public void setHeaders(Map map) 1001 { 1002 Iterator iterKeyBad = map.entrySet().iterator(); 1003 1004 while (iterKeyBad.hasNext()) 1005 { 1006 Map.Entry entry = (Map.Entry) iterKeyBad.next(); 1007 String strName = (String) entry.getKey(); 1008 String strValue = (String) entry.getValue(); 1009 1010 if (EmailUtils.isEmpty(strName)) 1011 { 1012 throw new IllegalArgumentException("name can not be null"); 1013 } 1014 if (EmailUtils.isEmpty(strValue)) 1015 { 1016 throw new IllegalArgumentException("value can not be null"); 1017 } 1018 } 1019 1020 // all is ok, update headers 1021 this.headers = map; 1022 } 1023 1024 /** 1025 * Adds a header ( name, value ) to the headers Map. 1026 * 1027 * @param name A String with the name. 1028 * @param value A String with the value. 1029 * @since 1.0 1030 */ 1031 public void addHeader(String name, String value) 1032 { 1033 if (EmailUtils.isEmpty(name)) 1034 { 1035 throw new IllegalArgumentException("name can not be null"); 1036 } 1037 if (EmailUtils.isEmpty(value)) 1038 { 1039 throw new IllegalArgumentException("value can not be null"); 1040 } 1041 1042 this.headers.put(name, value); 1043 } 1044 1045 /** 1046 * Set the email subject. 1047 * 1048 * @param aSubject A String. 1049 * @return An Email. 1050 * @since 1.0 1051 */ 1052 public Email setSubject(String aSubject) 1053 { 1054 this.subject = EmailUtils.replaceEndOfLineCharactersWithSpaces(aSubject); 1055 return this; 1056 } 1057 1058 /** 1059 * Set the "bounce address" - the address to which undeliverable messages 1060 * will be returned. If this value is never set, then the message will be 1061 * sent to the address specified with the System property "mail.smtp.from", 1062 * or if that value is not set, then to the "from" address. 1063 * 1064 * @param email A String. 1065 * @return An Email. 1066 * @since 1.0 1067 */ 1068 public Email setBounceAddress(String email) 1069 { 1070 if (email != null && !email.isEmpty()) 1071 { 1072 try 1073 { 1074 this.bounceAddress = createInternetAddress(email, null, this.charset).getAddress(); 1075 } 1076 catch (final EmailException e) 1077 { 1078 // Can't throw 'EmailException' to keep backward-compatibility 1079 throw new IllegalArgumentException("Failed to set the bounce address : " + email, e); 1080 } 1081 } 1082 else 1083 { 1084 this.bounceAddress = email; 1085 } 1086 1087 return this; 1088 } 1089 1090 1091 /** 1092 * Define the content of the mail. It should be overidden by the 1093 * subclasses. 1094 * 1095 * @param msg A String. 1096 * @return An Email. 1097 * @throws EmailException generic exception. 1098 * @since 1.0 1099 */ 1100 public abstract Email setMsg(String msg) throws EmailException; 1101 1102 /** 1103 * Build the internal MimeMessage to be sent. 1104 * 1105 * @throws EmailException if there was an error. 1106 * @since 1.0 1107 */ 1108 public void buildMimeMessage() throws EmailException 1109 { 1110 try 1111 { 1112 this.getMailSession(); 1113 this.message = this.createMimeMessage(this.session); 1114 1115 if (EmailUtils.isNotEmpty(this.subject)) 1116 { 1117 if (EmailUtils.isNotEmpty(this.charset)) 1118 { 1119 this.message.setSubject(this.subject, this.charset); 1120 } 1121 else 1122 { 1123 this.message.setSubject(this.subject); 1124 } 1125 } 1126 1127 // update content type (and encoding) 1128 this.updateContentType(this.contentType); 1129 1130 if (this.content != null) 1131 { 1132 this.message.setContent(this.content, this.contentType); 1133 } 1134 else if (this.emailBody != null) 1135 { 1136 if (this.contentType == null) 1137 { 1138 this.message.setContent(this.emailBody); 1139 } 1140 else 1141 { 1142 this.message.setContent(this.emailBody, this.contentType); 1143 } 1144 } 1145 else 1146 { 1147 this.message.setContent("", Email.TEXT_PLAIN); 1148 } 1149 1150 if (this.fromAddress != null) 1151 { 1152 this.message.setFrom(this.fromAddress); 1153 } 1154 else 1155 { 1156 if (session.getProperty(MAIL_SMTP_FROM) == null) 1157 { 1158 throw new EmailException("From address required"); 1159 } 1160 } 1161 1162 if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0) 1163 { 1164 throw new EmailException( 1165 "At least one receiver address required"); 1166 } 1167 1168 if (this.toList.size() > 0) 1169 { 1170 this.message.setRecipients( 1171 Message.RecipientType.TO, 1172 this.toInternetAddressArray(this.toList)); 1173 } 1174 1175 if (this.ccList.size() > 0) 1176 { 1177 this.message.setRecipients( 1178 Message.RecipientType.CC, 1179 this.toInternetAddressArray(this.ccList)); 1180 } 1181 1182 if (this.bccList.size() > 0) 1183 { 1184 this.message.setRecipients( 1185 Message.RecipientType.BCC, 1186 this.toInternetAddressArray(this.bccList)); 1187 } 1188 1189 if (this.replyList.size() > 0) 1190 { 1191 this.message.setReplyTo( 1192 this.toInternetAddressArray(this.replyList)); 1193 } 1194 1195 if (this.headers.size() > 0) 1196 { 1197 Iterator iterHeaderKeys = this.headers.keySet().iterator(); 1198 while (iterHeaderKeys.hasNext()) 1199 { 1200 String name = (String) iterHeaderKeys.next(); 1201 String value = (String) headers.get(name); 1202 this.message.addHeader(name, value); 1203 } 1204 } 1205 1206 if (this.message.getSentDate() == null) 1207 { 1208 this.message.setSentDate(getSentDate()); 1209 } 1210 1211 if (this.popBeforeSmtp) 1212 { 1213 Store store = session.getStore("pop3"); 1214 store.connect(this.popHost, this.popUsername, this.popPassword); 1215 } 1216 } 1217 catch (MessagingException me) 1218 { 1219 throw new EmailException(me); 1220 } 1221 } 1222 1223 /** 1224 * Factory method to create a customized MimeMessage which can be 1225 * implemented by a derived class, e.g. to set the message id. 1226 * 1227 * @param aSession mail session to be used 1228 * @return the newly created message 1229 */ 1230 protected MimeMessage createMimeMessage(Session aSession) 1231 { 1232 return new MimeMessage(aSession); 1233 } 1234 1235 /** 1236 * Sends the previously created MimeMessage to the SMTP server. 1237 * 1238 * @return the message id of the underlying MimeMessage 1239 * @throws EmailException the sending failed 1240 */ 1241 public String sendMimeMessage() 1242 throws EmailException 1243 { 1244 EmailUtils.notNull(this.message, "message"); 1245 1246 try 1247 { 1248 Transport.send(this.message); 1249 return this.message.getMessageID(); 1250 } 1251 catch (Throwable t) 1252 { 1253 String msg = "Sending the email to the following server failed : " 1254 + this.getHostName() 1255 + ":" 1256 + this.getSmtpPort(); 1257 1258 throw new EmailException(msg, t); 1259 } 1260 } 1261 1262 /** 1263 * Returns the internal MimeMessage. Please not that the 1264 * MimeMessage is build by the buildMimeMessage() method. 1265 * 1266 * @return the MimeMessage 1267 */ 1268 public MimeMessage getMimeMessage() 1269 { 1270 return this.message; 1271 } 1272 1273 /** 1274 * Sends the email. Internally we build a MimeMessage 1275 * which is afterwards sent to the SMTP server. 1276 * 1277 * @return the message id of the underlying MimeMessage 1278 * @throws EmailException the sending failed 1279 */ 1280 public String send() throws EmailException 1281 { 1282 this.buildMimeMessage(); 1283 return this.sendMimeMessage(); 1284 } 1285 1286 /** 1287 * Sets the sent date for the email. The sent date will default to the 1288 * current date if not explictly set. 1289 * 1290 * @param date Date to use as the sent date on the email 1291 * @since 1.0 1292 */ 1293 public void setSentDate(Date date) 1294 { 1295 if (date != null) 1296 { 1297 // create a seperate instance to keep findbugs happy 1298 this.sentDate = new Date(date.getTime()); 1299 } 1300 } 1301 1302 /** 1303 * Gets the sent date for the email. 1304 * 1305 * @return date to be used as the sent date for the email 1306 * @since 1.0 1307 */ 1308 public Date getSentDate() 1309 { 1310 if (this.sentDate == null) 1311 { 1312 return new Date(); 1313 } 1314 return new Date(this.sentDate.getTime()); 1315 } 1316 1317 /** 1318 * Gets the subject of the email. 1319 * 1320 * @return email subject 1321 */ 1322 public String getSubject() 1323 { 1324 return this.subject; 1325 } 1326 1327 /** 1328 * Gets the sender of the email. 1329 * 1330 * @return from address 1331 */ 1332 public InternetAddress getFromAddress() 1333 { 1334 return this.fromAddress; 1335 } 1336 1337 /** 1338 * Gets the host name of the SMTP server, 1339 * 1340 * @return host name 1341 */ 1342 public String getHostName() 1343 { 1344 if (EmailUtils.isNotEmpty(this.hostName)) 1345 { 1346 return this.hostName; 1347 } 1348 else if (this.session != null) 1349 { 1350 return this.session.getProperty(MAIL_HOST); 1351 } 1352 return null; 1353 } 1354 1355 /** 1356 * Gets the listening port of the SMTP server. 1357 * 1358 * @return smtp port 1359 */ 1360 public String getSmtpPort() 1361 { 1362 if (EmailUtils.isNotEmpty(this.smtpPort)) 1363 { 1364 return this.smtpPort; 1365 } 1366 else if (this.session != null) 1367 { 1368 return this.session.getProperty(MAIL_PORT); 1369 } 1370 return null; 1371 } 1372 1373 /** 1374 * Gets encryption mode for authentication 1375 * 1376 * @return true if using TLS for authentication, false otherwise 1377 * @since 1.1 1378 */ 1379 public boolean isTLS() 1380 { 1381 return this.tls; 1382 } 1383 1384 /** 1385 * Utility to copy List of known InternetAddress objects into an 1386 * array. 1387 * 1388 * @param list A List. 1389 * @return An InternetAddress[]. 1390 * @since 1.0 1391 */ 1392 protected InternetAddress[] toInternetAddressArray(List list) 1393 { 1394 InternetAddress[] ia = 1395 (InternetAddress[]) list.toArray(new InternetAddress[list.size()]); 1396 1397 return ia; 1398 } 1399 1400 /** 1401 * Set details regarding "pop3 before smtp" authentication. 1402 * 1403 * @param newPopBeforeSmtp Wether or not to log into pop3 1404 * server before sending mail. 1405 * @param newPopHost The pop3 host to use. 1406 * @param newPopUsername The pop3 username. 1407 * @param newPopPassword The pop3 password. 1408 * @since 1.0 1409 */ 1410 public void setPopBeforeSmtp( 1411 boolean newPopBeforeSmtp, 1412 String newPopHost, 1413 String newPopUsername, 1414 String newPopPassword) 1415 { 1416 this.popBeforeSmtp = newPopBeforeSmtp; 1417 this.popHost = newPopHost; 1418 this.popUsername = newPopUsername; 1419 this.popPassword = newPopPassword; 1420 } 1421 1422 /** 1423 * Returns whether SSL encryption for the transport is currently enabled. 1424 * @return true if SSL enabled for the transport 1425 */ 1426 public boolean isSSL() 1427 { 1428 return ssl; 1429 } 1430 1431 /** 1432 * Sets whether SSL encryption should be enabled for the SMTP transport. 1433 * @param ssl whether to enable the SSL transport 1434 */ 1435 public void setSSL(boolean ssl) 1436 { 1437 this.ssl = ssl; 1438 } 1439 1440 /** 1441 * Returns the current SSL port used by the SMTP transport. 1442 * @return the current SSL port used by the SMTP transport 1443 */ 1444 public String getSslSmtpPort() 1445 { 1446 if (EmailUtils.isNotEmpty(this.sslSmtpPort)) 1447 { 1448 return this.sslSmtpPort; 1449 } 1450 else if (this.session != null) 1451 { 1452 return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT); 1453 } 1454 return null; 1455 } 1456 1457 /** 1458 * Sets the SSL port to use for the SMTP transport. Defaults to the standard 1459 * port, 465. 1460 * @param sslSmtpPort the SSL port to use for the SMTP transport 1461 */ 1462 public void setSslSmtpPort(String sslSmtpPort) 1463 { 1464 this.sslSmtpPort = sslSmtpPort; 1465 } 1466 1467 /** 1468 * Get the list of "To" addresses. 1469 * 1470 * @return List addresses 1471 */ 1472 public List getToAddresses() 1473 { 1474 return this.toList; 1475 } 1476 1477 /** 1478 * Get the list of "CC" addresses. 1479 * 1480 * @return List addresses 1481 */ 1482 public List getCcAddresses() 1483 { 1484 return this.ccList; 1485 } 1486 1487 /** 1488 * Get the list of "Bcc" addresses. 1489 * 1490 * @return List addresses 1491 */ 1492 public List getBccAddresses() 1493 { 1494 return this.bccList; 1495 } 1496 1497 /** 1498 * Get the list of "Reply-To" addresses. 1499 * 1500 * @return List addresses 1501 */ 1502 public List getReplyToAddresses() 1503 { 1504 return this.replyList; 1505 } 1506 1507 /** 1508 * Get the socket connection timeout value in milliseconds. 1509 * 1510 * @return the timeout in milliseconds. 1511 * @since 1.2 1512 */ 1513 public int getSocketConnectionTimeout() 1514 { 1515 return this.socketConnectionTimeout; 1516 } 1517 1518 /** 1519 * Set the socket connection timeout value in milliseconds. 1520 * Default is infinite timeout. 1521 * 1522 * @param socketConnectionTimeout the connection timeout 1523 * @since 1.2 1524 */ 1525 public void setSocketConnectionTimeout(int socketConnectionTimeout) 1526 { 1527 this.socketConnectionTimeout = socketConnectionTimeout; 1528 } 1529 1530 /** 1531 * Get the socket I/O timeout value in milliseconds. 1532 * 1533 * @return the socket I/O timeout 1534 * @since 1.2 1535 */ 1536 public int getSocketTimeout() 1537 { 1538 return this.socketTimeout; 1539 } 1540 1541 /** 1542 * Set the socket I/O timeout value in milliseconds. 1543 * Default is infinite timeout. 1544 * 1545 * @param socketTimeout the socket I/O timeout 1546 * @since 1.2 1547 */ 1548 public void setSocketTimeout(int socketTimeout) 1549 { 1550 this.socketTimeout = socketTimeout; 1551 } 1552}