View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.mail;
18  
19  import java.io.UnsupportedEncodingException;
20  import java.nio.charset.Charset;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Date;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  
30  import javax.mail.Authenticator;
31  import javax.mail.Message;
32  import javax.mail.MessagingException;
33  import javax.mail.Session;
34  import javax.mail.Store;
35  import javax.mail.Transport;
36  import javax.mail.internet.AddressException;
37  import javax.mail.internet.InternetAddress;
38  import javax.mail.internet.MimeMessage;
39  import javax.mail.internet.MimeMultipart;
40  import javax.mail.internet.MimeUtility;
41  import javax.naming.Context;
42  import javax.naming.InitialContext;
43  import javax.naming.NamingException;
44  
45  /**
46   * The base class for all email messages.  This class sets the
47   * sender's email & name, receiver's email & name, subject, and the
48   * sent date.  Subclasses are responsible for setting the message
49   * body.
50   *
51   * @since 1.0
52   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
53   * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
54   * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
55   * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
56   * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
57   * @author <a href="mailto:unknown">Regis Koenig</a>
58   * @author <a href="mailto:colin.chalmers@maxware.nl">Colin Chalmers</a>
59   * @author <a href="mailto:matthias@wessendorf.net">Matthias Wessendorf</a>
60   * @author <a href="mailto:corey.scott@gmail.com">Corey Scott</a>
61   * @version $Id: Email.java 1448626 2013-02-21 12:57:29Z tn $
62   */
63  public abstract class Email
64  {
65      /** @deprecated since 1.3, use {@link EmailConstants#SENDER_EMAIL} instead */
66      @Deprecated
67      public static final String SENDER_EMAIL = EmailConstants.SENDER_EMAIL;
68  
69      /** @deprecated since 1.3, use {@link EmailConstants#SENDER_NAME} instead */
70      @Deprecated
71      public static final String SENDER_NAME = EmailConstants.SENDER_NAME;
72  
73      /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_EMAIL} instead */
74      @Deprecated
75      public static final String RECEIVER_EMAIL = EmailConstants.RECEIVER_EMAIL;
76  
77      /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_NAME} instead */
78      @Deprecated
79      public static final String RECEIVER_NAME = EmailConstants.RECEIVER_NAME;
80  
81      /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_SUBJECT} instead */
82      @Deprecated
83      public static final String EMAIL_SUBJECT = EmailConstants.EMAIL_SUBJECT;
84  
85      /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_BODY} instead */
86      @Deprecated
87      public static final String EMAIL_BODY = EmailConstants.EMAIL_BODY;
88  
89      /** @deprecated since 1.3, use {@link EmailConstants#CONTENT_TYPE} instead */
90      @Deprecated
91      public static final String CONTENT_TYPE = EmailConstants.CONTENT_TYPE;
92  
93      /** @deprecated since 1.3, use {@link EmailConstants#ATTACHMENTS} instead */
94      @Deprecated
95      public static final String ATTACHMENTS = EmailConstants.ATTACHMENTS;
96  
97      /** @deprecated since 1.3, use {@link EmailConstants#FILE_SERVER} instead */
98      @Deprecated
99      public static final String FILE_SERVER = EmailConstants.FILE_SERVER;
100 
101     /** @deprecated since 1.3, use {@link EmailConstants#KOI8_R} instead */
102     @Deprecated
103     public static final String KOI8_R = EmailConstants.KOI8_R;
104 
105     /** @deprecated since 1.3, use {@link EmailConstants#ISO_8859_1} instead */
106     @Deprecated
107     public static final String ISO_8859_1 = EmailConstants.ISO_8859_1;
108 
109     /** @deprecated since 1.3, use {@link EmailConstants#US_ASCII} instead */
110     @Deprecated
111     public static final String US_ASCII = EmailConstants.US_ASCII;
112 
113     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_DEBUG} instead */
114     @Deprecated
115     public static final String MAIL_DEBUG = EmailConstants.MAIL_DEBUG;
116 
117     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_HOST} instead */
118     @Deprecated
119     public static final String MAIL_HOST = EmailConstants.MAIL_HOST;
120 
121     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_PORT} instead */
122     @Deprecated
123     public static final String MAIL_PORT = EmailConstants.MAIL_PORT;
124 
125     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_FROM} instead */
126     @Deprecated
127     public static final String MAIL_SMTP_FROM = EmailConstants.MAIL_SMTP_FROM;
128 
129     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_AUTH} instead */
130     @Deprecated
131     public static final String MAIL_SMTP_AUTH = EmailConstants.MAIL_SMTP_AUTH;
132 
133     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_USER} instead */
134     @Deprecated
135     public static final String MAIL_SMTP_USER = EmailConstants.MAIL_SMTP_USER;
136 
137     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_PASSWORD} instead */
138     @Deprecated
139     public static final String MAIL_SMTP_PASSWORD = EmailConstants.MAIL_SMTP_PASSWORD;
140 
141     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_PROTOCOL} instead */
142     @Deprecated
143     public static final String MAIL_TRANSPORT_PROTOCOL = EmailConstants.MAIL_TRANSPORT_PROTOCOL;
144 
145     /** @deprecated since 1.3, use {@link EmailConstants#SMTP} instead */
146     @Deprecated
147     public static final String SMTP = EmailConstants.SMTP;
148 
149     /** @deprecated since 1.3, use {@link EmailConstants#TEXT_HTML} instead */
150     @Deprecated
151     public static final String TEXT_HTML = EmailConstants.TEXT_HTML;
152 
153     /** @deprecated since 1.3, use {@link EmailConstants#TEXT_PLAIN} instead */
154     @Deprecated
155     public static final String TEXT_PLAIN = EmailConstants.TEXT_PLAIN;
156 
157     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_TLS} instead */
158     @Deprecated
159     public static final String MAIL_TRANSPORT_TLS = EmailConstants.MAIL_TRANSPORT_TLS;
160 
161     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_FALLBACK} instead */
162     @Deprecated
163     public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_FALLBACK;
164 
165     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_CLASS} instead */
166     @Deprecated
167     public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS;
168 
169     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_PORT} instead */
170     @Deprecated
171     public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT;
172 
173     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_CONNECTIONTIMEOUT} instead */
174     @Deprecated
175     public static final String MAIL_SMTP_CONNECTIONTIMEOUT = EmailConstants.MAIL_SMTP_CONNECTIONTIMEOUT;
176 
177     /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_TIMEOUT} instead */
178     @Deprecated
179     public static final String MAIL_SMTP_TIMEOUT = EmailConstants.MAIL_SMTP_TIMEOUT;
180 
181     /** The email message to send. */
182     protected MimeMessage message;
183 
184     /** The charset to use for this message. */
185     protected String charset;
186 
187     /** The Address of the sending party, mandatory. */
188     protected InternetAddress fromAddress;
189 
190     /** The Subject. */
191     protected String subject;
192 
193     /** An attachment. */
194     protected MimeMultipart emailBody;
195 
196     /** The content. */
197     protected Object content;
198 
199     /** The content type. */
200     protected String contentType;
201 
202     /** Set session debugging on or off. */
203     protected boolean debug;
204 
205     /** Sent date. */
206     protected Date sentDate;
207 
208     /**
209      * Instance of an <code>Authenticator</code> object that will be used
210      * when authentication is requested from the mail server.
211      */
212     protected Authenticator authenticator;
213 
214     /**
215      * The hostname of the mail server with which to connect. If null will try
216      * to get property from system.properties. If still null, quit.
217      */
218     protected String hostName;
219 
220     /**
221      * The port number of the mail server to connect to.
222      * Defaults to the standard port ( 25 ).
223      */
224     protected String smtpPort = "25";
225 
226     /**
227      * The port number of the SSL enabled SMTP server;
228      * defaults to the standard port, 465.
229      */
230     protected String sslSmtpPort = "465";
231 
232     /** List of "to" email addresses. */
233     protected List<InternetAddress> toList = new ArrayList<InternetAddress>();
234 
235     /** List of "cc" email addresses. */
236     protected List<InternetAddress> ccList = new ArrayList<InternetAddress>();
237 
238     /** List of "bcc" email addresses. */
239     protected List<InternetAddress> bccList = new ArrayList<InternetAddress>();
240 
241     /** List of "replyTo" email addresses. */
242     protected List<InternetAddress> replyList = new ArrayList<InternetAddress>();
243 
244     /**
245      * Address to which undeliverable mail should be sent.
246      * Because this is handled by JavaMail as a String property
247      * in the mail session, this property is of type <code>String</code>
248      * rather than <code>InternetAddress</code>.
249      */
250     protected String bounceAddress;
251 
252     /**
253      * Used to specify the mail headers.  Example:
254      *
255      * X-Mailer: Sendmail, X-Priority: 1( highest )
256      * or  2( high ) 3( normal ) 4( low ) and 5( lowest )
257      * Disposition-Notification-To: user@domain.net
258      */
259     protected Map<String, String> headers = new HashMap<String, String>();
260 
261     /**
262      * Used to determine whether to use pop3 before smtp, and if so the settings.
263      */
264     protected boolean popBeforeSmtp;
265 
266     /** the host name of the pop3 server. */
267     protected String popHost;
268 
269     /** the user name to log into the pop3 server. */
270     protected String popUsername;
271 
272     /** the password to log into the pop3 server. */
273     protected String popPassword;
274 
275     /**
276      * Does server require TLS encryption for authentication?
277      * @deprecated  since 1.3, use setStartTLSEnabled() instead
278      */
279     @Deprecated
280     protected boolean tls;
281 
282     /**
283      * Does the current transport use SSL/TLS encryption upon connection?
284      * @deprecated since 1.3, use setSSLOnConnect() instead
285      */
286     @Deprecated
287     protected boolean ssl;
288 
289     /** socket I/O timeout value in milliseconds. */
290     protected int socketTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
291 
292     /** socket connection timeout value in milliseconds. */
293     protected int socketConnectionTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
294 
295     /**
296      * If true, enables the use of the STARTTLS command (if supported by
297      * the server) to switch the connection to a TLS-protected connection
298      * before issuing any login commands. Note that an appropriate trust
299      * store must configured so that the client will trust the server's
300      * certificate.
301      * Defaults to false.
302      */
303     private boolean startTlsEnabled;
304 
305     /**
306      * If true, requires the use of the STARTTLS command. If the server doesn't
307      * support the STARTTLS command, or the command fails, the connect method
308      * will fail.
309      * Defaults to false.
310      */
311     private boolean startTlsRequired;
312 
313     /** does the current transport use SSL/TLS encryption upon connection? */
314     private boolean sslOnConnect;
315 
316     /**
317      * If set to true, check the server identity as specified by RFC 2595. These
318      * additional checks based on the content of the server's certificate are
319      * intended to prevent man-in-the-middle attacks.
320      * Defaults to false.
321      */
322     private boolean sslCheckServerIdentity;
323 
324     /** The Session to mail with. */
325     private Session session;
326 
327     /**
328      * Setting to true will enable the display of debug information.
329      *
330      * @param d A boolean.
331      * @since 1.0
332      */
333     public void setDebug(boolean d)
334     {
335         this.debug = d;
336     }
337 
338     /**
339      * Sets the userName and password if authentication is needed.  If this
340      * method is not used, no authentication will be performed.
341      * <p>
342      * This method will create a new instance of
343      * <code>DefaultAuthenticator</code> using the supplied parameters.
344      *
345      * @param userName User name for the SMTP server
346      * @param password password for the SMTP server
347      * @see DefaultAuthenticator
348      * @see #setAuthenticator
349      * @since 1.0
350      */
351     public void setAuthentication(String userName, String password)
352     {
353         this.setAuthenticator(new DefaultAuthenticator(userName, password));
354     }
355 
356     /**
357      * Sets the <code>Authenticator</code> to be used when authentication
358      * is requested from the mail server.
359      * <p>
360      * This method should be used when your outgoing mail server requires
361      * authentication.  Your mail server must also support RFC2554.
362      *
363      * @param newAuthenticator the <code>Authenticator</code> object.
364      * @see Authenticator
365      * @since 1.0
366      */
367     public void setAuthenticator(Authenticator newAuthenticator)
368     {
369         this.authenticator = newAuthenticator;
370     }
371 
372     /**
373      * Set the charset of the message. Please note that you should set the charset before
374      * adding the message content.
375      *
376      * @param newCharset A String.
377      * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid
378      * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset
379      * exists in the current JVM
380      * @since 1.0
381      */
382     public void setCharset(String newCharset)
383     {
384         Charset set = Charset.forName(newCharset);
385         this.charset = set.name();
386     }
387 
388     /**
389      * Set the emailBody to a MimeMultiPart
390      *
391      * @param aMimeMultipart aMimeMultipart
392      * @since 1.0
393      */
394     public void setContent(MimeMultipart aMimeMultipart)
395     {
396         this.emailBody = aMimeMultipart;
397     }
398 
399     /**
400      * Set the content & contentType
401      *
402      * @param   aObject aObject
403      * @param   aContentType aContentType
404      * @since 1.0
405      */
406     public void setContent(Object aObject, String aContentType)
407     {
408         this.content = aObject;
409         this.updateContentType(aContentType);
410     }
411 
412     /**
413      * Update the contentType.
414      *
415      * @param   aContentType aContentType
416      * @since 1.2
417      */
418     public void updateContentType(final String aContentType)
419     {
420         if (EmailUtils.isEmpty(aContentType))
421         {
422             this.contentType = null;
423         }
424         else
425         {
426             // set the content type
427             this.contentType = aContentType;
428 
429             // set the charset if the input was properly formed
430             String strMarker = "; charset=";
431             int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
432 
433             if (charsetPos != -1)
434             {
435                 // find the next space (after the marker)
436                 charsetPos += strMarker.length();
437                 int intCharsetEnd =
438                     aContentType.toLowerCase().indexOf(" ", charsetPos);
439 
440                 if (intCharsetEnd != -1)
441                 {
442                     this.charset =
443                         aContentType.substring(charsetPos, intCharsetEnd);
444                 }
445                 else
446                 {
447                     this.charset = aContentType.substring(charsetPos);
448                 }
449             }
450             else
451             {
452                 // use the default charset, if one exists, for messages
453                 // whose content-type is some form of text.
454                 if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset))
455                 {
456                     StringBuffer contentTypeBuf = new StringBuffer(this.contentType);
457                     contentTypeBuf.append(strMarker);
458                     contentTypeBuf.append(this.charset);
459                     this.contentType = contentTypeBuf.toString();
460                 }
461             }
462         }
463     }
464 
465     /**
466      * Set the hostname of the outgoing mail server.
467      *
468      * @param   aHostName aHostName
469      * @since 1.0
470      */
471     public void setHostName(String aHostName)
472     {
473         checkSessionAlreadyInitialized();
474         this.hostName = aHostName;
475     }
476 
477     /**
478      * Set or disable the STARTTLS encryption. Please see EMAIL-105
479      * for the reasons of deprecation.
480      *
481      * @deprecated since 1.3, use setStartTLSEnabled() instead
482      * @param withTLS true if STARTTLS requested, false otherwise
483      * @since 1.1
484      */
485     @Deprecated
486     public void setTLS(boolean withTLS)
487     {
488         setStartTLSEnabled(withTLS);
489     }
490 
491     /**
492      * Set or disable the STARTTLS encryption.
493      *
494      * @param startTlsEnabled true if STARTTLS requested, false otherwise
495      * @return An Email.
496      * @since 1.3
497      */
498     public Email setStartTLSEnabled(boolean startTlsEnabled)
499     {
500         checkSessionAlreadyInitialized();
501         this.startTlsEnabled = startTlsEnabled;
502         this.tls = startTlsEnabled;
503         return this;
504     }
505 
506     /**
507      * Set or disable the required STARTTLS encryption.
508      *
509      * @param startTlsRequired true if STARTTLS requested, false otherwise
510      * @return An Email.
511      * @since 1.3
512      */
513     public Email setStartTLSRequired(boolean startTlsRequired)
514     {
515         checkSessionAlreadyInitialized();
516         this.startTlsRequired = startTlsRequired;
517         return this;
518     }
519 
520     /**
521      * Set the port number of the outgoing mail server.
522      *
523      * @param  aPortNumber aPortNumber
524      * @throws IllegalArgumentException if the port number is &lt; 1
525      * @since 1.0
526      */
527     public void setSmtpPort(int aPortNumber)
528     {
529         checkSessionAlreadyInitialized();
530 
531         if (aPortNumber < 1)
532         {
533             throw new IllegalArgumentException(
534                 "Cannot connect to a port number that is less than 1 ( "
535                     + aPortNumber
536                     + " )");
537         }
538 
539         this.smtpPort = Integer.toString(aPortNumber);
540     }
541 
542     /**
543      * Supply a mail Session object to use. Please note that passing
544      * a user name and password (in the case of mail authentication) will
545      * create a new mail session with a DefaultAuthenticator. This is a
546      * convenience but might come unexpected.
547      *
548      * If mail authentication is used but NO username and password
549      * is supplied the implementation assumes that you have set a
550      * authenticator and will use the existing mail session (as expected).
551      *
552      * @param aSession mail session to be used
553      * @throws IllegalArgumentException if the session is {@code null}
554      * @since 1.0
555      */
556     public void setMailSession(Session aSession)
557     {
558         EmailUtils.notNull(aSession, "no mail session supplied");
559 
560         Properties sessionProperties = aSession.getProperties();
561         String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH);
562 
563         if ("true".equalsIgnoreCase(auth))
564         {
565             String userName = sessionProperties.getProperty(MAIL_SMTP_USER);
566             String password = sessionProperties.getProperty(MAIL_SMTP_PASSWORD);
567 
568             if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password))
569             {
570                 // only create a new mail session with an authenticator if
571                 // authentication is required and no user name is given
572                 this.authenticator = new DefaultAuthenticator(userName, password);
573                 this.session = Session.getInstance(sessionProperties, this.authenticator);
574             }
575             else
576             {
577                 // assume that the given mail session contains a working authenticator
578                 this.session = aSession;
579             }
580         }
581         else
582         {
583             this.session = aSession;
584         }
585     }
586 
587     /**
588      * Supply a mail Session object from a JNDI directory.
589      *
590      * @param jndiName name of JNDI resource (javax.mail.Session type), resource
591      * if searched in java:comp/env if name does not start with "java:"
592      * @throws IllegalArgumentException if the JNDI name is null or empty
593      * @throws NamingException if the resource cannot be retrieved from JNDI directory
594      * @since 1.1
595      */
596     public void setMailSessionFromJNDI(String jndiName) throws NamingException
597     {
598         if (EmailUtils.isEmpty(jndiName))
599         {
600             throw new IllegalArgumentException("JNDI name missing");
601         }
602         Context ctx = null;
603         if (jndiName.startsWith("java:"))
604         {
605             ctx = new InitialContext();
606         }
607         else
608         {
609             ctx = (Context) new InitialContext().lookup("java:comp/env");
610 
611         }
612         this.setMailSession((Session) ctx.lookup(jndiName));
613     }
614 
615     /**
616      * Determines the mail session used when sending this Email, creating
617      * the Session if necessary. When a mail session is already
618      * initialized setting the session related properties will cause
619      * an IllegalStateException.
620      *
621      * @return A Session.
622      * @throws EmailException if the host name was not set
623      * @since 1.0
624      */
625     public Session getMailSession() throws EmailException
626     {
627         if (this.session == null)
628         {
629             Properties properties = new Properties(System.getProperties());
630             properties.setProperty(MAIL_TRANSPORT_PROTOCOL, SMTP);
631 
632             if (EmailUtils.isEmpty(this.hostName))
633             {
634                 this.hostName = properties.getProperty(MAIL_HOST);
635             }
636 
637             if (EmailUtils.isEmpty(this.hostName))
638             {
639                 throw new EmailException("Cannot find valid hostname for mail session");
640             }
641 
642             properties.setProperty(MAIL_PORT, this.smtpPort);
643             properties.setProperty(MAIL_HOST, this.hostName);
644             properties.setProperty(MAIL_DEBUG, String.valueOf(this.debug));
645 
646             properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE,
647                     isStartTLSEnabled() ? "true" : "false");
648             properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_REQUIRED,
649                     isStartTLSRequired() ? "true" : "false");
650 
651             if (this.authenticator != null)
652             {
653                 properties.setProperty(MAIL_SMTP_AUTH, "true");
654             }
655 
656             if (isSSLOnConnect())
657             {
658                 properties.setProperty(MAIL_PORT, this.sslSmtpPort);
659                 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, this.sslSmtpPort);
660                 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
661                 properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false");
662             }
663 
664             if ((isSSLOnConnect() || isStartTLSEnabled()) && isSSLCheckServerIdentity())
665             {
666                 properties.setProperty(EmailConstants.MAIL_SMTP_SSL_CHECKSERVERIDENTITY, "true");
667             }
668 
669             if (this.bounceAddress != null)
670             {
671                 properties.setProperty(MAIL_SMTP_FROM, this.bounceAddress);
672             }
673 
674             if (this.socketTimeout > 0)
675             {
676                 properties.setProperty(MAIL_SMTP_TIMEOUT, Integer.toString(this.socketTimeout));
677             }
678 
679             if (this.socketConnectionTimeout > 0)
680             {
681                 properties.setProperty(MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(this.socketConnectionTimeout));
682             }
683 
684             // changed this (back) to getInstance due to security exceptions
685             // caused when testing using maven
686             this.session = Session.getInstance(properties, this.authenticator);
687         }
688         return this.session;
689     }
690 
691     /**
692      * Set the FROM field of the email to use the specified address. The email
693      * address will also be used as the personal name.
694      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
695      * If it is not set, it will be encoded using
696      * the Java platform's default charset (UTF-16) if it contains
697      * non-ASCII characters; otherwise, it is used as is.
698      *
699      * @param email A String.
700      * @return An Email.
701      * @throws EmailException Indicates an invalid email address.
702      * @since 1.0
703      */
704     public Email setFrom(String email)
705         throws EmailException
706     {
707         return setFrom(email, null);
708     }
709 
710     /**
711      * Set the FROM field of the email to use the specified address and the
712      * specified personal name.
713      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
714      * If it is not set, it will be encoded using
715      * the Java platform's default charset (UTF-16) if it contains
716      * non-ASCII characters; otherwise, it is used as is.
717      *
718      * @param email A String.
719      * @param name A String.
720      * @return An Email.
721      * @throws EmailException Indicates an invalid email address.
722      * @since 1.0
723      */
724     public Email setFrom(String email, String name)
725         throws EmailException
726     {
727         return setFrom(email, name, this.charset);
728     }
729 
730     /**
731      * Set the FROM field of the email to use the specified address, personal
732      * name, and charset encoding for the name.
733      *
734      * @param email A String.
735      * @param name A String.
736      * @param charset The charset to encode the name with.
737      * @return An Email.
738      * @throws EmailException Indicates an invalid email address or charset.
739      * @since 1.1
740      */
741     public Email setFrom(String email, String name, String charset)
742         throws EmailException
743     {
744         this.fromAddress = createInternetAddress(email, name, charset);
745         return this;
746     }
747 
748     /**
749      * Add a recipient TO to the email. The email
750      * address will also be used as the personal name.
751      * The name will be encoded by the charset of
752      * {@link #setCharset(java.lang.String) setCharset()}.
753      * If it is not set, it will be encoded using
754      * the Java platform's default charset (UTF-16) if it contains
755      * non-ASCII characters; otherwise, it is used as is.
756      *
757      * @param email A String.
758      * @return An Email.
759      * @throws EmailException Indicates an invalid email address.
760      * @since 1.0
761      */
762     public Email addTo(String email)
763         throws EmailException
764     {
765         return addTo(email, null);
766     }
767 
768     /**
769      * Add a list of TO recipients to the email. The email
770      * addresses will also be used as the personal names.
771      * The names will be encoded by the charset of
772      * {@link #setCharset(java.lang.String) setCharset()}.
773      * If it is not set, it will be encoded using
774      * the Java platform's default charset (UTF-16) if it contains
775      * non-ASCII characters; otherwise, it is used as is.
776      *
777      * @param emails A String array.
778      * @return An Email.
779      * @throws EmailException Indicates an invalid email address.
780      * @since 1.3
781      */
782     public Email addTo(String... emails)
783         throws EmailException
784     {
785         if (emails == null || emails.length == 0)
786         {
787             throw new EmailException("Address List provided was invalid");
788         }
789 
790         for (String email : emails)
791         {
792             addTo(email, null);
793         }
794 
795         return this;
796     }
797 
798     /**
799      * Add a recipient TO to the email using the specified address and the
800      * specified personal name.
801      * The name will be encoded by the charset of
802      * {@link #setCharset(java.lang.String) setCharset()}.
803      * If it is not set, it will be encoded using
804      * the Java platform's default charset (UTF-16) if it contains
805      * non-ASCII characters; otherwise, it is used as is.
806      *
807      * @param email A String.
808      * @param name A String.
809      * @return An Email.
810      * @throws EmailException Indicates an invalid email address.
811      * @since 1.0
812      */
813     public Email addTo(String email, String name)
814         throws EmailException
815     {
816         return addTo(email, name, this.charset);
817     }
818 
819     /**
820      * Add a recipient TO to the email using the specified address, personal
821      * name, and charset encoding for the name.
822      *
823      * @param email A String.
824      * @param name A String.
825      * @param charset The charset to encode the name with.
826      * @return An Email.
827      * @throws EmailException Indicates an invalid email address or charset.
828      * @since 1.1
829      */
830     public Email addTo(String email, String name, String charset)
831         throws EmailException
832     {
833         this.toList.add(createInternetAddress(email, name, charset));
834         return this;
835     }
836 
837     /**
838      * Set a list of "TO" addresses. All elements in the specified
839      * <code>Collection</code> are expected to be of type
840      * <code>java.mail.internet.InternetAddress</code>.
841      *
842      * @param  aCollection collection of <code>InternetAddress</code> objects.
843      * @return An Email.
844      * @throws EmailException Indicates an invalid email address.
845      * @see javax.mail.internet.InternetAddress
846      * @since 1.0
847      */
848     public Email setTo(Collection<InternetAddress> aCollection) throws EmailException
849     {
850         if (aCollection == null || aCollection.isEmpty())
851         {
852             throw new EmailException("Address List provided was invalid");
853         }
854 
855         this.toList = new ArrayList<InternetAddress>(aCollection);
856         return this;
857     }
858 
859     /**
860      * Add a recipient CC to the email. The email
861      * address will also be used as the personal name.
862      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
863      * If it is not set, it will be encoded using
864      * the Java platform's default charset (UTF-16) if it contains
865      * non-ASCII characters; otherwise, it is used as is.
866      *
867      * @param email A String.
868      * @return An Email.
869      * @throws EmailException Indicates an invalid email address.
870      * @since 1.0
871      */
872     public Email addCc(String email)
873         throws EmailException
874     {
875         return this.addCc(email, null);
876     }
877 
878     /**
879      * Add an array of CC recipients to the email. The email
880      * addresses will also be used as the personal name.
881      * The names will be encoded by the charset of
882      * {@link #setCharset(java.lang.String) setCharset()}.
883      * If it is not set, it will be encoded using
884      * the Java platform's default charset (UTF-16) if it contains
885      * non-ASCII characters; otherwise, it is used as is.
886      *
887      * @param emails A String array.
888      * @return An Email.
889      * @throws EmailException Indicates an invalid email address.
890      * @since 1.3
891      */
892     public Email addCc(String... emails)
893         throws EmailException
894     {
895         if (emails == null || emails.length == 0)
896         {
897             throw new EmailException("Address List provided was invalid");
898         }
899 
900         for (String email : emails)
901         {
902             addCc(email, null);
903         }
904 
905         return this;
906     }
907 
908     /**
909      * Add a recipient CC to the email using the specified address and the
910      * specified personal name.
911      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
912      * If it is not set, it will be encoded using
913      * the Java platform's default charset (UTF-16) if it contains
914      * non-ASCII characters; otherwise, it is used as is.
915      *
916      * @param email A String.
917      * @param name A String.
918      * @return An Email.
919      * @throws EmailException Indicates an invalid email address.
920      * @since 1.0
921      */
922     public Email addCc(String email, String name)
923         throws EmailException
924     {
925         return addCc(email, name, this.charset);
926     }
927 
928     /**
929      * Add a recipient CC to the email using the specified address, personal
930      * name, and charset encoding for the name.
931      *
932      * @param email A String.
933      * @param name A String.
934      * @param charset The charset to encode the name with.
935      * @return An Email.
936      * @throws EmailException Indicates an invalid email address or charset.
937      * @since 1.1
938      */
939     public Email addCc(String email, String name, String charset)
940         throws EmailException
941     {
942         this.ccList.add(createInternetAddress(email, name, charset));
943         return this;
944     }
945 
946     /**
947      * Set a list of "CC" addresses. All elements in the specified
948      * <code>Collection</code> are expected to be of type
949      * <code>java.mail.internet.InternetAddress</code>.
950      *
951      * @param aCollection collection of <code>InternetAddress</code> objects.
952      * @return An Email.
953      * @throws EmailException Indicates an invalid email address.
954      * @see javax.mail.internet.InternetAddress
955      * @since 1.0
956      */
957     public Email setCc(Collection<InternetAddress> aCollection) throws EmailException
958     {
959         if (aCollection == null || aCollection.isEmpty())
960         {
961             throw new EmailException("Address List provided was invalid");
962         }
963 
964         this.ccList = new ArrayList<InternetAddress>(aCollection);
965         return this;
966     }
967 
968     /**
969      * Add a blind BCC recipient to the email. The email
970      * address will also be used as the personal name.
971      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
972      * If it is not set, it will be encoded using
973      * the Java platform's default charset (UTF-16) if it contains
974      * non-ASCII characters; otherwise, it is used as is.
975      *
976      * @param email A String.
977      * @return An Email.
978      * @throws EmailException Indicates an invalid email address
979      * @since 1.0
980      */
981     public Email addBcc(String email)
982         throws EmailException
983     {
984         return this.addBcc(email, null);
985     }
986 
987     /**
988      * Add an array of blind BCC recipients to the email. The email
989      * addresses will also be used as the personal name.
990      * The names will be encoded by the charset of
991      * {@link #setCharset(java.lang.String) setCharset()}.
992      * If it is not set, it will be encoded using
993      * the Java platform's default charset (UTF-16) if it contains
994      * non-ASCII characters; otherwise, it is used as is.
995      *
996      * @param emails A String array.
997      * @return An Email.
998      * @throws EmailException Indicates an invalid email address
999      * @since 1.3
1000      */
1001     public Email addBcc(String... emails)
1002         throws EmailException
1003     {
1004         if (emails == null || emails.length == 0)
1005         {
1006             throw new EmailException("Address List provided was invalid");
1007         }
1008 
1009         for (String email : emails)
1010         {
1011             addBcc(email, null);
1012         }
1013 
1014         return this;
1015     }
1016 
1017     /**
1018      * Add a blind BCC recipient to the email using the specified address and
1019      * the specified personal name.
1020      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1021      * If it is not set, it will be encoded using
1022      * the Java platform's default charset (UTF-16) if it contains
1023      * non-ASCII characters; otherwise, it is used as is.
1024      *
1025      * @param email A String.
1026      * @param name A String.
1027      * @return An Email.
1028      * @throws EmailException Indicates an invalid email address
1029      * @since 1.0
1030      */
1031     public Email addBcc(String email, String name)
1032         throws EmailException
1033     {
1034         return addBcc(email, name, this.charset);
1035     }
1036 
1037     /**
1038      * Add a blind BCC recipient to the email using the specified address,
1039      * personal name, and charset encoding for the name.
1040      *
1041      * @param email A String.
1042      * @param name A String.
1043      * @param charset The charset to encode the name with.
1044      * @return An Email.
1045      * @throws EmailException Indicates an invalid email address
1046      * @since 1.1
1047      */
1048     public Email addBcc(String email, String name, String charset)
1049         throws EmailException
1050     {
1051         this.bccList.add(createInternetAddress(email, name, charset));
1052         return this;
1053     }
1054 
1055     /**
1056      * Set a list of "BCC" addresses. All elements in the specified
1057      * <code>Collection</code> are expected to be of type
1058      * <code>java.mail.internet.InternetAddress</code>.
1059      *
1060      * @param  aCollection collection of <code>InternetAddress</code> objects
1061      * @return An Email.
1062      * @throws EmailException Indicates an invalid email address
1063      * @see javax.mail.internet.InternetAddress
1064      * @since 1.0
1065      */
1066     public Email setBcc(Collection<InternetAddress> aCollection) throws EmailException
1067     {
1068         if (aCollection == null || aCollection.isEmpty())
1069         {
1070             throw new EmailException("Address List provided was invalid");
1071         }
1072 
1073         this.bccList = new ArrayList<InternetAddress>(aCollection);
1074         return this;
1075     }
1076 
1077     /**
1078      * Add a reply to address to the email. The email
1079      * address will also be used as the personal name.
1080      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1081      * If it is not set, it will be encoded using
1082      * the Java platform's default charset (UTF-16) if it contains
1083      * non-ASCII characters; otherwise, it is used as is.
1084      *
1085      * @param email A String.
1086      * @return An Email.
1087      * @throws EmailException Indicates an invalid email address
1088      * @since 1.0
1089      */
1090     public Email addReplyTo(String email)
1091         throws EmailException
1092     {
1093         return this.addReplyTo(email, null);
1094     }
1095 
1096     /**
1097      * Add a reply to address to the email using the specified address and
1098      * the specified personal name.
1099      * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1100      * If it is not set, it will be encoded using
1101      * the Java platform's default charset (UTF-16) if it contains
1102      * non-ASCII characters; otherwise, it is used as is.
1103      *
1104      * @param email A String.
1105      * @param name A String.
1106      * @return An Email.
1107      * @throws EmailException Indicates an invalid email address
1108      * @since 1.0
1109      */
1110     public Email addReplyTo(String email, String name)
1111         throws EmailException
1112     {
1113         return addReplyTo(email, name, this.charset);
1114     }
1115 
1116     /**
1117      * Add a reply to address to the email using the specified address,
1118      * personal name, and charset encoding for the name.
1119      *
1120      * @param email A String.
1121      * @param name A String.
1122      * @param charset The charset to encode the name with.
1123      * @return An Email.
1124      * @throws EmailException Indicates an invalid email address or charset.
1125      * @since 1.1
1126      */
1127     public Email addReplyTo(String email, String name, String charset)
1128         throws EmailException
1129     {
1130         this.replyList.add(createInternetAddress(email, name, charset));
1131         return this;
1132     }
1133 
1134     /**
1135      * Set a list of reply to addresses. All elements in the specified
1136      * <code>Collection</code> are expected to be of type
1137      * <code>java.mail.internet.InternetAddress</code>.
1138      *
1139      * @param   aCollection collection of <code>InternetAddress</code> objects
1140      * @return  An Email.
1141      * @throws EmailException Indicates an invalid email address
1142      * @see javax.mail.internet.InternetAddress
1143      * @since 1.1
1144      */
1145     public Email setReplyTo(Collection<InternetAddress> aCollection) throws EmailException
1146     {
1147         if (aCollection == null || aCollection.isEmpty())
1148         {
1149             throw new EmailException("Address List provided was invalid");
1150         }
1151 
1152         this.replyList = new ArrayList<InternetAddress>(aCollection);
1153         return this;
1154     }
1155 
1156     /**
1157      * Used to specify the mail headers.  Example:
1158      *
1159      * X-Mailer: Sendmail, X-Priority: 1( highest )
1160      * or  2( high ) 3( normal ) 4( low ) and 5( lowest )
1161      * Disposition-Notification-To: user@domain.net
1162      *
1163      * @param map A Map.
1164      * @throws IllegalArgumentException if either of the provided header / value is null or empty
1165      * @since 1.0
1166      */
1167     public void setHeaders(Map<String, String> map)
1168     {
1169         this.headers.clear();
1170 
1171         Iterator<Map.Entry<String, String>> iterKeyBad = map.entrySet().iterator();
1172 
1173         while (iterKeyBad.hasNext())
1174         {
1175             Map.Entry<String, String> entry = iterKeyBad.next();
1176             addHeader(entry.getKey(), entry.getValue());
1177         }
1178     }
1179 
1180     /**
1181      * Adds a header ( name, value ) to the headers Map.
1182      *
1183      * @param name A String with the name.
1184      * @param value A String with the value.
1185      * @since 1.0
1186      * @throws IllegalArgumentException if either {@code name} or {@code value} is null or empty
1187      */
1188     public void addHeader(String name, String value)
1189     {
1190         if (EmailUtils.isEmpty(name))
1191         {
1192             throw new IllegalArgumentException("name can not be null or empty");
1193         }
1194         if (EmailUtils.isEmpty(value))
1195         {
1196             throw new IllegalArgumentException("value can not be null or empty");
1197         }
1198 
1199         this.headers.put(name, value);
1200     }
1201 
1202     /**
1203      * Set the email subject.
1204      *
1205      * @param aSubject A String.
1206      * @return An Email.
1207      * @since 1.0
1208      */
1209     public Email setSubject(String aSubject)
1210     {
1211         this.subject = aSubject;
1212         return this;
1213     }
1214 
1215     /**
1216      * Set the "bounce address" - the address to which undeliverable messages
1217      * will be returned.  If this value is never set, then the message will be
1218      * sent to the address specified with the System property "mail.smtp.from",
1219      * or if that value is not set, then to the "from" address.
1220      *
1221      * @param email A String.
1222      * @return An Email.
1223      * @throws IllegalStateException when the mail session is already initialized
1224      * @since 1.0
1225      */
1226     public Email setBounceAddress(String email)
1227     {
1228         checkSessionAlreadyInitialized();
1229         this.bounceAddress = email;
1230         return this;
1231     }
1232 
1233 
1234     /**
1235      * Define the content of the mail. It should be overridden by the
1236      * subclasses.
1237      *
1238      * @param msg A String.
1239      * @return An Email.
1240      * @throws EmailException generic exception.
1241      * @since 1.0
1242      */
1243     public abstract Email setMsg(String msg) throws EmailException;
1244 
1245     /**
1246      * Does the work of actually building the MimeMessage. Please note that
1247      * a user rarely calls this method directly and only if he/she is
1248      * interested in the sending the underlying MimeMessage without
1249      * commons-email.
1250      *
1251      * @throws IllegalStateException if the MimeMessage was already built
1252      * @throws EmailException if there was an error.
1253      * @since 1.0
1254      */
1255     public void buildMimeMessage() throws EmailException
1256     {
1257         if (this.message != null)
1258         {
1259             // [EMAIL-95] we assume that an email is not reused therefore invoking
1260             // buildMimeMessage() more than once is illegal.
1261             throw new IllegalStateException("The MimeMessage is already built.");
1262         }
1263 
1264         try
1265         {
1266             this.message = this.createMimeMessage(this.getMailSession());
1267 
1268             if (EmailUtils.isNotEmpty(this.subject))
1269             {
1270                 if (EmailUtils.isNotEmpty(this.charset))
1271                 {
1272                     this.message.setSubject(this.subject, this.charset);
1273                 }
1274                 else
1275                 {
1276                     this.message.setSubject(this.subject);
1277                 }
1278             }
1279 
1280             // update content type (and encoding)
1281             this.updateContentType(this.contentType);
1282 
1283             if (this.content != null)
1284             {
1285                 this.message.setContent(this.content, this.contentType);
1286             }
1287             else if (this.emailBody != null)
1288             {
1289                 if (this.contentType == null)
1290                 {
1291                     this.message.setContent(this.emailBody);
1292                 }
1293                 else
1294                 {
1295                     this.message.setContent(this.emailBody, this.contentType);
1296                 }
1297             }
1298             else
1299             {
1300                 this.message.setContent("", Email.TEXT_PLAIN);
1301             }
1302 
1303             if (this.fromAddress != null)
1304             {
1305                 this.message.setFrom(this.fromAddress);
1306             }
1307             else
1308             {
1309                 if (session.getProperty(MAIL_SMTP_FROM) == null)
1310                 {
1311                     throw new EmailException("From address required");
1312                 }
1313             }
1314 
1315             if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
1316             {
1317                 throw new EmailException("At least one receiver address required");
1318             }
1319 
1320             if (this.toList.size() > 0)
1321             {
1322                 this.message.setRecipients(
1323                     Message.RecipientType.TO,
1324                     this.toInternetAddressArray(this.toList));
1325             }
1326 
1327             if (this.ccList.size() > 0)
1328             {
1329                 this.message.setRecipients(
1330                     Message.RecipientType.CC,
1331                     this.toInternetAddressArray(this.ccList));
1332             }
1333 
1334             if (this.bccList.size() > 0)
1335             {
1336                 this.message.setRecipients(
1337                     Message.RecipientType.BCC,
1338                     this.toInternetAddressArray(this.bccList));
1339             }
1340 
1341             if (this.replyList.size() > 0)
1342             {
1343                 this.message.setReplyTo(
1344                     this.toInternetAddressArray(this.replyList));
1345             }
1346 
1347 
1348             if (this.headers.size() > 0)
1349             {
1350                 Iterator<String> iterHeaderKeys = this.headers.keySet().iterator();
1351                 while (iterHeaderKeys.hasNext())
1352                 {
1353                     String name = iterHeaderKeys.next();
1354                     String value = headers.get(name);
1355                     String foldedValue = createFoldedHeaderValue(name, value);
1356                     this.message.addHeader(name, foldedValue);
1357                 }
1358             }
1359 
1360             if (this.message.getSentDate() == null)
1361             {
1362                 this.message.setSentDate(getSentDate());
1363             }
1364 
1365             if (this.popBeforeSmtp)
1366             {
1367                 Store store = session.getStore("pop3");
1368                 store.connect(this.popHost, this.popUsername, this.popPassword);
1369             }
1370         }
1371         catch (MessagingException me)
1372         {
1373             throw new EmailException(me);
1374         }
1375     }
1376 
1377     /**
1378      * Sends the previously created MimeMessage to the SMTP server.
1379      *
1380      * @return the message id of the underlying MimeMessage
1381      * @throws IllegalArgumentException if the MimeMessage has not been created
1382      * @throws EmailException the sending failed
1383      */
1384     public String sendMimeMessage()
1385        throws EmailException
1386     {
1387         EmailUtils.notNull(this.message, "MimeMessage has not been created yet");
1388 
1389         try
1390         {
1391             Transport.send(this.message);
1392             return this.message.getMessageID();
1393         }
1394         catch (Throwable t)
1395         {
1396             String msg = "Sending the email to the following server failed : "
1397                 + this.getHostName()
1398                 + ":"
1399                 + this.getSmtpPort();
1400 
1401             throw new EmailException(msg, t);
1402         }
1403     }
1404 
1405     /**
1406      * Returns the internal MimeMessage. Please not that the
1407      * MimeMessage is build by the buildMimeMessage() method.
1408      *
1409      * @return the MimeMessage
1410      */
1411     public MimeMessage getMimeMessage()
1412     {
1413         return this.message;
1414     }
1415 
1416     /**
1417      * Sends the email. Internally we build a MimeMessage
1418      * which is afterwards sent to the SMTP server.
1419      *
1420      * @return the message id of the underlying MimeMessage
1421      * @throws IllegalStateException if the MimeMessage was already built, ie {@link #buildMimeMessage()}
1422      *   was already called
1423      * @throws EmailException the sending failed
1424      */
1425     public String send() throws EmailException
1426     {
1427         this.buildMimeMessage();
1428         return this.sendMimeMessage();
1429     }
1430 
1431     /**
1432      * Sets the sent date for the email.  The sent date will default to the
1433      * current date if not explicitly set.
1434      *
1435      * @param date Date to use as the sent date on the email
1436      * @since 1.0
1437      */
1438     public void setSentDate(Date date)
1439     {
1440         if (date != null)
1441         {
1442             // create a separate instance to keep findbugs happy
1443             this.sentDate = new Date(date.getTime());
1444         }
1445     }
1446 
1447     /**
1448      * Gets the sent date for the email.
1449      *
1450      * @return date to be used as the sent date for the email
1451      * @since 1.0
1452      */
1453     public Date getSentDate()
1454     {
1455         if (this.sentDate == null)
1456         {
1457             return new Date();
1458         }
1459         return new Date(this.sentDate.getTime());
1460     }
1461 
1462     /**
1463      * Gets the subject of the email.
1464      *
1465      * @return email subject
1466      */
1467     public String getSubject()
1468     {
1469         return this.subject;
1470     }
1471 
1472     /**
1473      * Gets the sender of the email.
1474      *
1475      * @return from address
1476      */
1477     public InternetAddress getFromAddress()
1478     {
1479         return this.fromAddress;
1480     }
1481 
1482     /**
1483      * Gets the host name of the SMTP server,
1484      *
1485      * @return host name
1486      */
1487     public String getHostName()
1488     {
1489         if (this.session != null)
1490         {
1491             return this.session.getProperty(MAIL_HOST);
1492         }
1493         else if (EmailUtils.isNotEmpty(this.hostName))
1494         {
1495             return this.hostName;
1496         }
1497         return null;
1498     }
1499 
1500     /**
1501      * Gets the listening port of the SMTP server.
1502      *
1503      * @return smtp port
1504      */
1505     public String getSmtpPort()
1506     {
1507         if (this.session != null)
1508         {
1509             return this.session.getProperty(MAIL_PORT);
1510         }
1511         else if (EmailUtils.isNotEmpty(this.smtpPort))
1512         {
1513             return this.smtpPort;
1514         }
1515         return null;
1516     }
1517 
1518     /**
1519      * Gets whether the client is configured to require STARTTLS.
1520      *
1521      * @return true if using STARTTLS for authentication, false otherwise
1522      * @since 1.3
1523      */
1524     public boolean isStartTLSRequired()
1525     {
1526         return this.startTlsRequired;
1527     }
1528 
1529     /**
1530      * Gets whether the client is configured to try to enable STARTTLS.
1531      *
1532      * @return true if using STARTTLS for authentication, false otherwise
1533      * @since 1.3
1534      */
1535     public boolean isStartTLSEnabled()
1536     {
1537         return this.startTlsEnabled || tls;
1538     }
1539 
1540     /**
1541      * Gets whether the client is configured to try to enable STARTTLS.
1542      * See EMAIL-105 for reason of deprecation.
1543      *
1544      * @deprecated since 1.3, use isStartTLSEnabled() instead
1545      * @return true if using STARTTLS for authentication, false otherwise
1546      * @since 1.1
1547      */
1548     @Deprecated
1549     public boolean isTLS()
1550     {
1551         return isStartTLSEnabled();
1552     }
1553 
1554     /**
1555      * Utility to copy List of known InternetAddress objects into an
1556      * array.
1557      *
1558      * @param list A List.
1559      * @return An InternetAddress[].
1560      * @since 1.0
1561      */
1562     protected InternetAddress[] toInternetAddressArray(List<InternetAddress> list)
1563     {
1564         InternetAddress[] ia = list.toArray(new InternetAddress[list.size()]);
1565 
1566         return ia;
1567     }
1568 
1569     /**
1570      * Set details regarding "pop3 before smtp" authentication.
1571      *
1572      * @param newPopBeforeSmtp Whether or not to log into pop3 server before sending mail.
1573      * @param newPopHost The pop3 host to use.
1574      * @param newPopUsername The pop3 username.
1575      * @param newPopPassword The pop3 password.
1576      * @since 1.0
1577      */
1578     public void setPopBeforeSmtp(
1579         boolean newPopBeforeSmtp,
1580         String newPopHost,
1581         String newPopUsername,
1582         String newPopPassword)
1583     {
1584         this.popBeforeSmtp = newPopBeforeSmtp;
1585         this.popHost = newPopHost;
1586         this.popUsername = newPopUsername;
1587         this.popPassword = newPopPassword;
1588     }
1589 
1590     /**
1591      * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1592      * See EMAIL-105 for reason of deprecation.
1593      *
1594      * @deprecated since 1.3, use isSSLOnConnect() instead
1595      * @return true if SSL enabled for the transport
1596      */
1597     @Deprecated
1598     public boolean isSSL()
1599     {
1600         return isSSLOnConnect();
1601     }
1602 
1603     /**
1604      * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1605      *
1606      * @return true if SSL enabled for the transport
1607      * @since 1.3
1608      */
1609     public boolean isSSLOnConnect()
1610     {
1611         return sslOnConnect || ssl;
1612     }
1613 
1614     /**
1615      * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1616      * See EMAIL-105 for reason of deprecation.
1617      *
1618      * @deprecated since 1.3, use setSSLOnConnect() instead
1619      * @param ssl whether to enable the SSL transport
1620      */
1621     @Deprecated
1622     public void setSSL(boolean ssl)
1623     {
1624         setSSLOnConnect(ssl);
1625     }
1626 
1627     /**
1628      * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1629      *
1630      * @param ssl whether to enable the SSL transport
1631      * @return An Email.
1632      * @since 1.3
1633      */
1634     public Email setSSLOnConnect(boolean ssl)
1635     {
1636         checkSessionAlreadyInitialized();
1637         this.sslOnConnect = ssl;
1638         this.ssl = ssl;
1639         return this;
1640     }
1641 
1642     /**
1643     * Is the server identity checked as specified by RFC 2595
1644     *
1645     * @return true if the server identity is checked
1646     * @since 1.3
1647     */
1648     public boolean isSSLCheckServerIdentity()
1649     {
1650         return sslCheckServerIdentity;
1651     }
1652 
1653     /**
1654      * Sets whether the server identity is checked as specified by RFC 2595
1655      *
1656      * @param sslCheckServerIdentity whether to enable server identity check
1657      * @return An Email.
1658      * @since 1.3
1659      */
1660     public Email setSSLCheckServerIdentity(boolean sslCheckServerIdentity)
1661     {
1662         this.sslCheckServerIdentity = sslCheckServerIdentity;
1663         return this;
1664     }
1665 
1666     /**
1667      * Returns the current SSL port used by the SMTP transport.
1668      *
1669      * @return the current SSL port used by the SMTP transport
1670      */
1671     public String getSslSmtpPort()
1672     {
1673         if (this.session != null)
1674         {
1675             return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT);
1676         }
1677         else if (EmailUtils.isNotEmpty(this.sslSmtpPort))
1678         {
1679             return this.sslSmtpPort;
1680         }
1681         return null;
1682     }
1683 
1684     /**
1685      * Sets the SSL port to use for the SMTP transport. Defaults to the standard
1686      * port, 465.
1687      *
1688      * @param sslSmtpPort the SSL port to use for the SMTP transport
1689      */
1690     public void setSslSmtpPort(String sslSmtpPort)
1691     {
1692         checkSessionAlreadyInitialized();
1693         this.sslSmtpPort = sslSmtpPort;
1694     }
1695 
1696     /**
1697      * Get the list of "To" addresses.
1698      *
1699      * @return List addresses
1700      */
1701     public List<InternetAddress> getToAddresses()
1702     {
1703         return this.toList;
1704     }
1705 
1706     /**
1707      * Get the list of "CC" addresses.
1708      *
1709      * @return List addresses
1710      */
1711     public List<InternetAddress> getCcAddresses()
1712     {
1713         return this.ccList;
1714     }
1715 
1716     /**
1717      * Get the list of "Bcc" addresses.
1718      *
1719      * @return List addresses
1720      */
1721     public List<InternetAddress> getBccAddresses()
1722     {
1723         return this.bccList;
1724     }
1725 
1726     /**
1727      * Get the list of "Reply-To" addresses.
1728      *
1729      * @return List addresses
1730      */
1731     public List<InternetAddress> getReplyToAddresses()
1732     {
1733         return this.replyList;
1734     }
1735 
1736     /**
1737      * Get the socket connection timeout value in milliseconds.
1738      *
1739      * @return the timeout in milliseconds.
1740      * @since 1.2
1741      */
1742     public int getSocketConnectionTimeout()
1743     {
1744         return this.socketConnectionTimeout;
1745     }
1746 
1747     /**
1748      * Set the socket connection timeout value in milliseconds.
1749      * Default is a 60 second timeout.
1750      *
1751      * @param socketConnectionTimeout the connection timeout
1752      * @since 1.2
1753      */
1754     public void setSocketConnectionTimeout(int socketConnectionTimeout)
1755     {
1756         checkSessionAlreadyInitialized();
1757         this.socketConnectionTimeout = socketConnectionTimeout;
1758     }
1759 
1760     /**
1761      * Get the socket I/O timeout value in milliseconds.
1762      *
1763      * @return the socket I/O timeout
1764      * @since 1.2
1765      */
1766     public int getSocketTimeout()
1767     {
1768         return this.socketTimeout;
1769     }
1770 
1771     /**
1772      * Set the socket I/O timeout value in milliseconds.
1773      * Default is 60 second timeout.
1774      *
1775      * @param socketTimeout the socket I/O timeout
1776      * @since 1.2
1777      */
1778     public void setSocketTimeout(int socketTimeout)
1779     {
1780         checkSessionAlreadyInitialized();
1781         this.socketTimeout = socketTimeout;
1782     }
1783 
1784     /**
1785      * Factory method to create a customized MimeMessage which can be
1786      * implemented by a derived class, e.g. to set the message id.
1787      *
1788      * @param aSession mail session to be used
1789      * @return the newly created message
1790      */
1791     protected MimeMessage createMimeMessage(Session aSession)
1792     {
1793         return new MimeMessage(aSession);
1794     }
1795 
1796     /**
1797      * Create a folded header value containing 76 character chunks.
1798      *
1799      * @param name the name of the header
1800      * @param value the value of the header
1801      * @return the folded header value
1802      * @throws IllegalArgumentException if either the name or value is null or empty
1803      */
1804     private String createFoldedHeaderValue(String name, Object value)
1805     {
1806         String result;
1807 
1808         if (EmailUtils.isEmpty(name))
1809         {
1810             throw new IllegalArgumentException("name can not be null or empty");
1811         }
1812         if (value == null || EmailUtils.isEmpty(value.toString()))
1813         {
1814             throw new IllegalArgumentException("value can not be null or empty");
1815         }
1816 
1817         try
1818         {
1819             result = MimeUtility.fold(name.length() + 2, MimeUtility.encodeText(value.toString(), this.charset, null));
1820         }
1821         catch (UnsupportedEncodingException e)
1822         {
1823             result = value.toString();
1824         }
1825 
1826         return result;
1827     }
1828 
1829     /**
1830      * Creates a InternetAddress.
1831      *
1832      * @param email An email address.
1833      * @param name A name.
1834      * @param charsetName The name of the charset to encode the name with.
1835      * @return An internet address.
1836      * @throws EmailException Thrown when the supplied address, name or charset were invalid.
1837      */
1838     private InternetAddress createInternetAddress(String email, String name, String charsetName)
1839         throws EmailException
1840     {
1841         InternetAddress address = null;
1842 
1843         try
1844         {
1845             address = new InternetAddress(email);
1846 
1847             // check name input
1848             if (EmailUtils.isNotEmpty(name))
1849             {
1850                 // check charset input.
1851                 if (EmailUtils.isEmpty(charsetName))
1852                 {
1853                     address.setPersonal(name);
1854                 }
1855                 else
1856                 {
1857                     // canonicalize the charset name and make sure
1858                     // the current platform supports it.
1859                     Charset set = Charset.forName(charsetName);
1860                     address.setPersonal(name, set.name());
1861                 }
1862             }
1863 
1864             // run sanity check on new InternetAddress object; if this fails
1865             // it will throw AddressException.
1866             address.validate();
1867         }
1868         catch (AddressException e)
1869         {
1870             throw new EmailException(e);
1871         }
1872         catch (UnsupportedEncodingException e)
1873         {
1874             throw new EmailException(e);
1875         }
1876         return address;
1877     }
1878 
1879     /**
1880      * When a mail session is already initialized setting the
1881      * session properties has no effect. In order to flag the
1882      * problem throw an IllegalStateException.
1883      *
1884      * @throws IllegalStateException when the mail session is already initialized
1885      */
1886     private void checkSessionAlreadyInitialized()
1887     {
1888         if (this.session != null)
1889         {
1890             throw new IllegalStateException("The mail session is already initialized");
1891         }
1892     }
1893 }