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