001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.mail;
018
019import java.io.UnsupportedEncodingException;
020import java.nio.charset.Charset;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Properties;
028
029import javax.mail.Authenticator;
030import javax.mail.Message;
031import javax.mail.MessagingException;
032import javax.mail.Session;
033import javax.mail.Store;
034import javax.mail.Transport;
035import javax.mail.internet.AddressException;
036import javax.mail.internet.InternetAddress;
037import javax.mail.internet.MimeMessage;
038import javax.mail.internet.MimeMultipart;
039import javax.mail.internet.MimeUtility;
040import javax.naming.Context;
041import javax.naming.InitialContext;
042import javax.naming.NamingException;
043
044/**
045 * The base class for all email messages.  This class sets the
046 * sender's email & name, receiver's email & name, subject, and the
047 * sent date.
048 * <p>
049 * Subclasses are responsible for setting the message body.
050 *
051 * @since 1.0
052 * @version $Id: Email.java 1665699 2015-03-10 21:12:09Z tn $
053 */
054public abstract class Email
055{
056    /** @deprecated since 1.3, use {@link EmailConstants#SENDER_EMAIL} instead */
057    @Deprecated
058    public static final String SENDER_EMAIL = EmailConstants.SENDER_EMAIL;
059
060    /** @deprecated since 1.3, use {@link EmailConstants#SENDER_NAME} instead */
061    @Deprecated
062    public static final String SENDER_NAME = EmailConstants.SENDER_NAME;
063
064    /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_EMAIL} instead */
065    @Deprecated
066    public static final String RECEIVER_EMAIL = EmailConstants.RECEIVER_EMAIL;
067
068    /** @deprecated since 1.3, use {@link EmailConstants#RECEIVER_NAME} instead */
069    @Deprecated
070    public static final String RECEIVER_NAME = EmailConstants.RECEIVER_NAME;
071
072    /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_SUBJECT} instead */
073    @Deprecated
074    public static final String EMAIL_SUBJECT = EmailConstants.EMAIL_SUBJECT;
075
076    /** @deprecated since 1.3, use {@link EmailConstants#EMAIL_BODY} instead */
077    @Deprecated
078    public static final String EMAIL_BODY = EmailConstants.EMAIL_BODY;
079
080    /** @deprecated since 1.3, use {@link EmailConstants#CONTENT_TYPE} instead */
081    @Deprecated
082    public static final String CONTENT_TYPE = EmailConstants.CONTENT_TYPE;
083
084    /** @deprecated since 1.3, use {@link EmailConstants#ATTACHMENTS} instead */
085    @Deprecated
086    public static final String ATTACHMENTS = EmailConstants.ATTACHMENTS;
087
088    /** @deprecated since 1.3, use {@link EmailConstants#FILE_SERVER} instead */
089    @Deprecated
090    public static final String FILE_SERVER = EmailConstants.FILE_SERVER;
091
092    /** @deprecated since 1.3, use {@link EmailConstants#KOI8_R} instead */
093    @Deprecated
094    public static final String KOI8_R = EmailConstants.KOI8_R;
095
096    /** @deprecated since 1.3, use {@link EmailConstants#ISO_8859_1} instead */
097    @Deprecated
098    public static final String ISO_8859_1 = EmailConstants.ISO_8859_1;
099
100    /** @deprecated since 1.3, use {@link EmailConstants#US_ASCII} instead */
101    @Deprecated
102    public static final String US_ASCII = EmailConstants.US_ASCII;
103
104    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_DEBUG} instead */
105    @Deprecated
106    public static final String MAIL_DEBUG = EmailConstants.MAIL_DEBUG;
107
108    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_HOST} instead */
109    @Deprecated
110    public static final String MAIL_HOST = EmailConstants.MAIL_HOST;
111
112    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_PORT} instead */
113    @Deprecated
114    public static final String MAIL_PORT = EmailConstants.MAIL_PORT;
115
116    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_FROM} instead */
117    @Deprecated
118    public static final String MAIL_SMTP_FROM = EmailConstants.MAIL_SMTP_FROM;
119
120    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_AUTH} instead */
121    @Deprecated
122    public static final String MAIL_SMTP_AUTH = EmailConstants.MAIL_SMTP_AUTH;
123
124    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_USER} instead */
125    @Deprecated
126    public static final String MAIL_SMTP_USER = EmailConstants.MAIL_SMTP_USER;
127
128    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_PASSWORD} instead */
129    @Deprecated
130    public static final String MAIL_SMTP_PASSWORD = EmailConstants.MAIL_SMTP_PASSWORD;
131
132    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_PROTOCOL} instead */
133    @Deprecated
134    public static final String MAIL_TRANSPORT_PROTOCOL = EmailConstants.MAIL_TRANSPORT_PROTOCOL;
135
136    /** @deprecated since 1.3, use {@link EmailConstants#SMTP} instead */
137    @Deprecated
138    public static final String SMTP = EmailConstants.SMTP;
139
140    /** @deprecated since 1.3, use {@link EmailConstants#TEXT_HTML} instead */
141    @Deprecated
142    public static final String TEXT_HTML = EmailConstants.TEXT_HTML;
143
144    /** @deprecated since 1.3, use {@link EmailConstants#TEXT_PLAIN} instead */
145    @Deprecated
146    public static final String TEXT_PLAIN = EmailConstants.TEXT_PLAIN;
147
148    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_TRANSPORT_TLS} instead */
149    @Deprecated
150    public static final String MAIL_TRANSPORT_TLS = EmailConstants.MAIL_TRANSPORT_TLS;
151
152    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_FALLBACK} instead */
153    @Deprecated
154    public static final String MAIL_SMTP_SOCKET_FACTORY_FALLBACK = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_FALLBACK;
155
156    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_CLASS} instead */
157    @Deprecated
158    public static final String MAIL_SMTP_SOCKET_FACTORY_CLASS = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS;
159
160    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_SOCKET_FACTORY_PORT} instead */
161    @Deprecated
162    public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT;
163
164    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_CONNECTIONTIMEOUT} instead */
165    @Deprecated
166    public static final String MAIL_SMTP_CONNECTIONTIMEOUT = EmailConstants.MAIL_SMTP_CONNECTIONTIMEOUT;
167
168    /** @deprecated since 1.3, use {@link EmailConstants#MAIL_SMTP_TIMEOUT} instead */
169    @Deprecated
170    public static final String MAIL_SMTP_TIMEOUT = EmailConstants.MAIL_SMTP_TIMEOUT;
171
172    /** The email message to send. */
173    protected MimeMessage message;
174
175    /** The charset to use for this message. */
176    protected String charset;
177
178    /** The Address of the sending party, mandatory. */
179    protected InternetAddress fromAddress;
180
181    /** The Subject. */
182    protected String subject;
183
184    /** An attachment. */
185    protected MimeMultipart emailBody;
186
187    /** The content. */
188    protected Object content;
189
190    /** The content type. */
191    protected String contentType;
192
193    /** Set session debugging on or off. */
194    protected boolean debug;
195
196    /** Sent date. */
197    protected Date sentDate;
198
199    /**
200     * Instance of an <code>Authenticator</code> object that will be used
201     * when authentication is requested from the mail server.
202     */
203    protected Authenticator authenticator;
204
205    /**
206     * The hostname of the mail server with which to connect. If null will try
207     * to get property from system.properties. If still null, quit.
208     */
209    protected String hostName;
210
211    /**
212     * The port number of the mail server to connect to.
213     * Defaults to the standard port ( 25 ).
214     */
215    protected String smtpPort = "25";
216
217    /**
218     * The port number of the SSL enabled SMTP server;
219     * defaults to the standard port, 465.
220     */
221    protected String sslSmtpPort = "465";
222
223    /** List of "to" email addresses. */
224    protected List<InternetAddress> toList = new ArrayList<InternetAddress>();
225
226    /** List of "cc" email addresses. */
227    protected List<InternetAddress> ccList = new ArrayList<InternetAddress>();
228
229    /** List of "bcc" email addresses. */
230    protected List<InternetAddress> bccList = new ArrayList<InternetAddress>();
231
232    /** List of "replyTo" email addresses. */
233    protected List<InternetAddress> replyList = new ArrayList<InternetAddress>();
234
235    /**
236     * Address to which undeliverable mail should be sent.
237     * Because this is handled by JavaMail as a String property
238     * in the mail session, this property is of type <code>String</code>
239     * rather than <code>InternetAddress</code>.
240     */
241    protected String bounceAddress;
242
243    /**
244     * Used to specify the mail headers.  Example:
245     *
246     * X-Mailer: Sendmail, X-Priority: 1( highest )
247     * or  2( high ) 3( normal ) 4( low ) and 5( lowest )
248     * Disposition-Notification-To: user@domain.net
249     */
250    protected Map<String, String> headers = new HashMap<String, String>();
251
252    /**
253     * Used to determine whether to use pop3 before smtp, and if so the settings.
254     */
255    protected boolean popBeforeSmtp;
256
257    /** the host name of the pop3 server. */
258    protected String popHost;
259
260    /** the user name to log into the pop3 server. */
261    protected String popUsername;
262
263    /** the password to log into the pop3 server. */
264    protected String popPassword;
265
266    /**
267     * Does server require TLS encryption for authentication?
268     * @deprecated  since 1.3, use setStartTLSEnabled() instead
269     */
270    @Deprecated
271    protected boolean tls;
272
273    /**
274     * Does the current transport use SSL/TLS encryption upon connection?
275     * @deprecated since 1.3, use setSSLOnConnect() instead
276     */
277    @Deprecated
278    protected boolean ssl;
279
280    /** socket I/O timeout value in milliseconds. */
281    protected int socketTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
282
283    /** socket connection timeout value in milliseconds. */
284    protected int socketConnectionTimeout = EmailConstants.SOCKET_TIMEOUT_MS;
285
286    /**
287     * If true, enables the use of the STARTTLS command (if supported by
288     * the server) to switch the connection to a TLS-protected connection
289     * before issuing any login commands. Note that an appropriate trust
290     * store must configured so that the client will trust the server's
291     * certificate.
292     * Defaults to false.
293     */
294    private boolean startTlsEnabled;
295
296    /**
297     * If true, requires the use of the STARTTLS command. If the server doesn't
298     * support the STARTTLS command, or the command fails, the connect method
299     * will fail.
300     * Defaults to false.
301     */
302    private boolean startTlsRequired;
303
304    /** does the current transport use SSL/TLS encryption upon connection? */
305    private boolean sslOnConnect;
306
307    /**
308     * If set to true, check the server identity as specified by RFC 2595. These
309     * additional checks based on the content of the server's certificate are
310     * intended to prevent man-in-the-middle attacks.
311     * Defaults to false.
312     */
313    private boolean sslCheckServerIdentity;
314
315    /**
316     * If set to true, and a message has some valid and some invalid addresses, send the message anyway,
317     * reporting the partial failure with a SendFailedException.
318     * If set to false (the default), the message is not sent to any of the recipients
319     * if there is an invalid recipient address.
320     * Defaults to false.
321     */
322    private boolean sendPartial;
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(final 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(final String userName, final 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(final 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(final String newCharset)
383    {
384        final 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(final MimeMultipart aMimeMultipart)
395    {
396        this.emailBody = aMimeMultipart;
397    }
398
399    /**
400     * Set the content and contentType.
401     *
402     * @param   aObject aObject
403     * @param   aContentType aContentType
404     * @since 1.0
405     */
406    public void setContent(final Object aObject, final 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            final 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                final 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                    final 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(final 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(final 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(final 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(final 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(final 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(final Session aSession)
557    {
558        EmailUtils.notNull(aSession, "no mail session supplied");
559
560        final Properties sessionProperties = aSession.getProperties();
561        final String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH);
562
563        if ("true".equalsIgnoreCase(auth))
564        {
565            final String userName = sessionProperties.getProperty(MAIL_SMTP_USER);
566            final 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(final 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            final 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            properties.setProperty(EmailConstants.MAIL_SMTP_SEND_PARTIAL,
652                    isSendPartial() ? "true" : "false");
653            properties.setProperty(EmailConstants.MAIL_SMTPS_SEND_PARTIAL,
654                    isSendPartial() ? "true" : "false");
655
656            if (this.authenticator != null)
657            {
658                properties.setProperty(MAIL_SMTP_AUTH, "true");
659            }
660
661            if (isSSLOnConnect())
662            {
663                properties.setProperty(MAIL_PORT, this.sslSmtpPort);
664                properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_PORT, this.sslSmtpPort);
665                properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory");
666                properties.setProperty(MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false");
667            }
668
669            if ((isSSLOnConnect() || isStartTLSEnabled()) && isSSLCheckServerIdentity())
670            {
671                properties.setProperty(EmailConstants.MAIL_SMTP_SSL_CHECKSERVERIDENTITY, "true");
672            }
673
674            if (this.bounceAddress != null)
675            {
676                properties.setProperty(MAIL_SMTP_FROM, this.bounceAddress);
677            }
678
679            if (this.socketTimeout > 0)
680            {
681                properties.setProperty(MAIL_SMTP_TIMEOUT, Integer.toString(this.socketTimeout));
682            }
683
684            if (this.socketConnectionTimeout > 0)
685            {
686                properties.setProperty(MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(this.socketConnectionTimeout));
687            }
688
689            // changed this (back) to getInstance due to security exceptions
690            // caused when testing using maven
691            this.session = Session.getInstance(properties, this.authenticator);
692        }
693        return this.session;
694    }
695
696    /**
697     * Set the FROM field of the email to use the specified address. The email
698     * address will also be used as the personal name.
699     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
700     * If it is not set, it will be encoded using
701     * the Java platform's default charset (UTF-16) if it contains
702     * non-ASCII characters; otherwise, it is used as is.
703     *
704     * @param email A String.
705     * @return An Email.
706     * @throws EmailException Indicates an invalid email address.
707     * @since 1.0
708     */
709    public Email setFrom(final String email)
710        throws EmailException
711    {
712        return setFrom(email, null);
713    }
714
715    /**
716     * Set the FROM field of the email to use the specified address and the
717     * specified personal name.
718     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
719     * If it is not set, it will be encoded using
720     * the Java platform's default charset (UTF-16) if it contains
721     * non-ASCII characters; otherwise, it is used as is.
722     *
723     * @param email A String.
724     * @param name A String.
725     * @return An Email.
726     * @throws EmailException Indicates an invalid email address.
727     * @since 1.0
728     */
729    public Email setFrom(final String email, final String name)
730        throws EmailException
731    {
732        return setFrom(email, name, this.charset);
733    }
734
735    /**
736     * Set the FROM field of the email to use the specified address, personal
737     * name, and charset encoding for the name.
738     *
739     * @param email A String.
740     * @param name A String.
741     * @param charset The charset to encode the name with.
742     * @return An Email.
743     * @throws EmailException Indicates an invalid email address or charset.
744     * @since 1.1
745     */
746    public Email setFrom(final String email, final String name, final String charset)
747        throws EmailException
748    {
749        this.fromAddress = createInternetAddress(email, name, charset);
750        return this;
751    }
752
753    /**
754     * Add a recipient TO to the email. The email
755     * address will also be used as the personal name.
756     * The name will be encoded by the charset of
757     * {@link #setCharset(java.lang.String) setCharset()}.
758     * If it is not set, it will be encoded using
759     * the Java platform's default charset (UTF-16) if it contains
760     * non-ASCII characters; otherwise, it is used as is.
761     *
762     * @param email A String.
763     * @return An Email.
764     * @throws EmailException Indicates an invalid email address.
765     * @since 1.0
766     */
767    public Email addTo(final String email)
768        throws EmailException
769    {
770        return addTo(email, null);
771    }
772
773    /**
774     * Add a list of TO recipients to the email. The email
775     * addresses will also be used as the personal names.
776     * The names will be encoded by the charset of
777     * {@link #setCharset(java.lang.String) setCharset()}.
778     * If it is not set, it will be encoded using
779     * the Java platform's default charset (UTF-16) if it contains
780     * non-ASCII characters; otherwise, it is used as is.
781     *
782     * @param emails A String array.
783     * @return An Email.
784     * @throws EmailException Indicates an invalid email address.
785     * @since 1.3
786     */
787    public Email addTo(final String... emails)
788        throws EmailException
789    {
790        if (emails == null || emails.length == 0)
791        {
792            throw new EmailException("Address List provided was invalid");
793        }
794
795        for (final String email : emails)
796        {
797            addTo(email, null);
798        }
799
800        return this;
801    }
802
803    /**
804     * Add a recipient TO to the email using the specified address and the
805     * specified personal name.
806     * The name will be encoded by the charset of
807     * {@link #setCharset(java.lang.String) setCharset()}.
808     * If it is not set, it will be encoded using
809     * the Java platform's default charset (UTF-16) if it contains
810     * non-ASCII characters; otherwise, it is used as is.
811     *
812     * @param email A String.
813     * @param name A String.
814     * @return An Email.
815     * @throws EmailException Indicates an invalid email address.
816     * @since 1.0
817     */
818    public Email addTo(final String email, final String name)
819        throws EmailException
820    {
821        return addTo(email, name, this.charset);
822    }
823
824    /**
825     * Add a recipient TO to the email using the specified address, personal
826     * name, and charset encoding for the name.
827     *
828     * @param email A String.
829     * @param name A String.
830     * @param charset The charset to encode the name with.
831     * @return An Email.
832     * @throws EmailException Indicates an invalid email address or charset.
833     * @since 1.1
834     */
835    public Email addTo(final String email, final String name, final String charset)
836        throws EmailException
837    {
838        this.toList.add(createInternetAddress(email, name, charset));
839        return this;
840    }
841
842    /**
843     * Set a list of "TO" addresses. All elements in the specified
844     * <code>Collection</code> are expected to be of type
845     * <code>java.mail.internet.InternetAddress</code>.
846     *
847     * @param  aCollection collection of <code>InternetAddress</code> objects.
848     * @return An Email.
849     * @throws EmailException Indicates an invalid email address.
850     * @see javax.mail.internet.InternetAddress
851     * @since 1.0
852     */
853    public Email setTo(final Collection<InternetAddress> aCollection) throws EmailException
854    {
855        if (aCollection == null || aCollection.isEmpty())
856        {
857            throw new EmailException("Address List provided was invalid");
858        }
859
860        this.toList = new ArrayList<InternetAddress>(aCollection);
861        return this;
862    }
863
864    /**
865     * Add a recipient CC to the email. The email
866     * address will also be used as the personal name.
867     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
868     * If it is not set, it will be encoded using
869     * the Java platform's default charset (UTF-16) if it contains
870     * non-ASCII characters; otherwise, it is used as is.
871     *
872     * @param email A String.
873     * @return An Email.
874     * @throws EmailException Indicates an invalid email address.
875     * @since 1.0
876     */
877    public Email addCc(final String email)
878        throws EmailException
879    {
880        return this.addCc(email, null);
881    }
882
883    /**
884     * Add an array of CC recipients to the email. The email
885     * addresses will also be used as the personal name.
886     * The names will be encoded by the charset of
887     * {@link #setCharset(java.lang.String) setCharset()}.
888     * If it is not set, it will be encoded using
889     * the Java platform's default charset (UTF-16) if it contains
890     * non-ASCII characters; otherwise, it is used as is.
891     *
892     * @param emails A String array.
893     * @return An Email.
894     * @throws EmailException Indicates an invalid email address.
895     * @since 1.3
896     */
897    public Email addCc(final String... emails)
898        throws EmailException
899    {
900        if (emails == null || emails.length == 0)
901        {
902            throw new EmailException("Address List provided was invalid");
903        }
904
905        for (final String email : emails)
906        {
907            addCc(email, null);
908        }
909
910        return this;
911    }
912
913    /**
914     * Add a recipient CC to the email using the specified address and the
915     * specified personal name.
916     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
917     * If it is not set, it will be encoded using
918     * the Java platform's default charset (UTF-16) if it contains
919     * non-ASCII characters; otherwise, it is used as is.
920     *
921     * @param email A String.
922     * @param name A String.
923     * @return An Email.
924     * @throws EmailException Indicates an invalid email address.
925     * @since 1.0
926     */
927    public Email addCc(final String email, final String name)
928        throws EmailException
929    {
930        return addCc(email, name, this.charset);
931    }
932
933    /**
934     * Add a recipient CC to the email using the specified address, personal
935     * name, and charset encoding for the name.
936     *
937     * @param email A String.
938     * @param name A String.
939     * @param charset The charset to encode the name with.
940     * @return An Email.
941     * @throws EmailException Indicates an invalid email address or charset.
942     * @since 1.1
943     */
944    public Email addCc(final String email, final String name, final String charset)
945        throws EmailException
946    {
947        this.ccList.add(createInternetAddress(email, name, charset));
948        return this;
949    }
950
951    /**
952     * Set a list of "CC" addresses. All elements in the specified
953     * <code>Collection</code> are expected to be of type
954     * <code>java.mail.internet.InternetAddress</code>.
955     *
956     * @param aCollection collection of <code>InternetAddress</code> objects.
957     * @return An Email.
958     * @throws EmailException Indicates an invalid email address.
959     * @see javax.mail.internet.InternetAddress
960     * @since 1.0
961     */
962    public Email setCc(final Collection<InternetAddress> aCollection) throws EmailException
963    {
964        if (aCollection == null || aCollection.isEmpty())
965        {
966            throw new EmailException("Address List provided was invalid");
967        }
968
969        this.ccList = new ArrayList<InternetAddress>(aCollection);
970        return this;
971    }
972
973    /**
974     * Add a blind BCC recipient to the email. The email
975     * address will also be used as the personal name.
976     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
977     * If it is not set, it will be encoded using
978     * the Java platform's default charset (UTF-16) if it contains
979     * non-ASCII characters; otherwise, it is used as is.
980     *
981     * @param email A String.
982     * @return An Email.
983     * @throws EmailException Indicates an invalid email address
984     * @since 1.0
985     */
986    public Email addBcc(final String email)
987        throws EmailException
988    {
989        return this.addBcc(email, null);
990    }
991
992    /**
993     * Add an array of blind BCC recipients to the email. The email
994     * addresses will also be used as the personal name.
995     * The names will be encoded by the charset of
996     * {@link #setCharset(java.lang.String) setCharset()}.
997     * If it is not set, it will be encoded using
998     * the Java platform's default charset (UTF-16) if it contains
999     * non-ASCII characters; otherwise, it is used as is.
1000     *
1001     * @param emails A String array.
1002     * @return An Email.
1003     * @throws EmailException Indicates an invalid email address
1004     * @since 1.3
1005     */
1006    public Email addBcc(final String... emails)
1007        throws EmailException
1008    {
1009        if (emails == null || emails.length == 0)
1010        {
1011            throw new EmailException("Address List provided was invalid");
1012        }
1013
1014        for (final String email : emails)
1015        {
1016            addBcc(email, null);
1017        }
1018
1019        return this;
1020    }
1021
1022    /**
1023     * Add a blind BCC recipient to the email using the specified address and
1024     * the specified personal name.
1025     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1026     * If it is not set, it will be encoded using
1027     * the Java platform's default charset (UTF-16) if it contains
1028     * non-ASCII characters; otherwise, it is used as is.
1029     *
1030     * @param email A String.
1031     * @param name A String.
1032     * @return An Email.
1033     * @throws EmailException Indicates an invalid email address
1034     * @since 1.0
1035     */
1036    public Email addBcc(final String email, final String name)
1037        throws EmailException
1038    {
1039        return addBcc(email, name, this.charset);
1040    }
1041
1042    /**
1043     * Add a blind BCC recipient to the email using the specified address,
1044     * personal name, and charset encoding for the name.
1045     *
1046     * @param email A String.
1047     * @param name A String.
1048     * @param charset The charset to encode the name with.
1049     * @return An Email.
1050     * @throws EmailException Indicates an invalid email address
1051     * @since 1.1
1052     */
1053    public Email addBcc(final String email, final String name, final String charset)
1054        throws EmailException
1055    {
1056        this.bccList.add(createInternetAddress(email, name, charset));
1057        return this;
1058    }
1059
1060    /**
1061     * Set a list of "BCC" addresses. All elements in the specified
1062     * <code>Collection</code> are expected to be of type
1063     * <code>java.mail.internet.InternetAddress</code>.
1064     *
1065     * @param  aCollection collection of <code>InternetAddress</code> objects
1066     * @return An Email.
1067     * @throws EmailException Indicates an invalid email address
1068     * @see javax.mail.internet.InternetAddress
1069     * @since 1.0
1070     */
1071    public Email setBcc(final Collection<InternetAddress> aCollection) throws EmailException
1072    {
1073        if (aCollection == null || aCollection.isEmpty())
1074        {
1075            throw new EmailException("Address List provided was invalid");
1076        }
1077
1078        this.bccList = new ArrayList<InternetAddress>(aCollection);
1079        return this;
1080    }
1081
1082    /**
1083     * Add a reply to address to the email. The email
1084     * address will also be used as the personal name.
1085     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1086     * If it is not set, it will be encoded using
1087     * the Java platform's default charset (UTF-16) if it contains
1088     * non-ASCII characters; otherwise, it is used as is.
1089     *
1090     * @param email A String.
1091     * @return An Email.
1092     * @throws EmailException Indicates an invalid email address
1093     * @since 1.0
1094     */
1095    public Email addReplyTo(final String email)
1096        throws EmailException
1097    {
1098        return this.addReplyTo(email, null);
1099    }
1100
1101    /**
1102     * Add a reply to address to the email using the specified address and
1103     * the specified personal name.
1104     * The name will be encoded by the charset of {@link #setCharset(java.lang.String) setCharset()}.
1105     * If it is not set, it will be encoded using
1106     * the Java platform's default charset (UTF-16) if it contains
1107     * non-ASCII characters; otherwise, it is used as is.
1108     *
1109     * @param email A String.
1110     * @param name A String.
1111     * @return An Email.
1112     * @throws EmailException Indicates an invalid email address
1113     * @since 1.0
1114     */
1115    public Email addReplyTo(final String email, final String name)
1116        throws EmailException
1117    {
1118        return addReplyTo(email, name, this.charset);
1119    }
1120
1121    /**
1122     * Add a reply to address to the email using the specified address,
1123     * personal name, and charset encoding for the name.
1124     *
1125     * @param email A String.
1126     * @param name A String.
1127     * @param charset The charset to encode the name with.
1128     * @return An Email.
1129     * @throws EmailException Indicates an invalid email address or charset.
1130     * @since 1.1
1131     */
1132    public Email addReplyTo(final String email, final String name, final String charset)
1133        throws EmailException
1134    {
1135        this.replyList.add(createInternetAddress(email, name, charset));
1136        return this;
1137    }
1138
1139    /**
1140     * Set a list of reply to addresses. All elements in the specified
1141     * <code>Collection</code> are expected to be of type
1142     * <code>java.mail.internet.InternetAddress</code>.
1143     *
1144     * @param   aCollection collection of <code>InternetAddress</code> objects
1145     * @return  An Email.
1146     * @throws EmailException Indicates an invalid email address
1147     * @see javax.mail.internet.InternetAddress
1148     * @since 1.1
1149     */
1150    public Email setReplyTo(final Collection<InternetAddress> aCollection) throws EmailException
1151    {
1152        if (aCollection == null || aCollection.isEmpty())
1153        {
1154            throw new EmailException("Address List provided was invalid");
1155        }
1156
1157        this.replyList = new ArrayList<InternetAddress>(aCollection);
1158        return this;
1159    }
1160
1161    /**
1162     * Used to specify the mail headers.  Example:
1163     *
1164     * X-Mailer: Sendmail, X-Priority: 1( highest )
1165     * or  2( high ) 3( normal ) 4( low ) and 5( lowest )
1166     * Disposition-Notification-To: user@domain.net
1167     *
1168     * @param map A Map.
1169     * @throws IllegalArgumentException if either of the provided header / value is null or empty
1170     * @since 1.0
1171     */
1172    public void setHeaders(final Map<String, String> map)
1173    {
1174        this.headers.clear();
1175
1176        for (final Map.Entry<String, String> entry : map.entrySet())
1177        {
1178            addHeader(entry.getKey(), entry.getValue());
1179        }
1180    }
1181
1182    /**
1183     * Adds a header ( name, value ) to the headers Map.
1184     *
1185     * @param name A String with the name.
1186     * @param value A String with the value.
1187     * @since 1.0
1188     * @throws IllegalArgumentException if either {@code name} or {@code value} is null or empty
1189     */
1190    public void addHeader(final String name, final String value)
1191    {
1192        if (EmailUtils.isEmpty(name))
1193        {
1194            throw new IllegalArgumentException("name can not be null or empty");
1195        }
1196        if (EmailUtils.isEmpty(value))
1197        {
1198            throw new IllegalArgumentException("value can not be null or empty");
1199        }
1200
1201        this.headers.put(name, value);
1202    }
1203
1204    /**
1205     * Set the email subject.
1206     *
1207     * @param aSubject A String.
1208     * @return An Email.
1209     * @since 1.0
1210     */
1211    public Email setSubject(final String aSubject)
1212    {
1213        this.subject = aSubject;
1214        return this;
1215    }
1216
1217    /**
1218     * Gets the "bounce address" of this email.
1219     *
1220     * @return the bounce address as string
1221     * @since 1.4
1222     */
1223    public String getBounceAddress()
1224    {
1225        return this.bounceAddress;
1226    }
1227
1228    /**
1229     * Set the "bounce address" - the address to which undeliverable messages
1230     * will be returned.  If this value is never set, then the message will be
1231     * sent to the address specified with the System property "mail.smtp.from",
1232     * or if that value is not set, then to the "from" address.
1233     *
1234     * @param email A String.
1235     * @return An Email.
1236     * @throws IllegalStateException when the mail session is already initialized
1237     * @since 1.0
1238     */
1239    public Email setBounceAddress(final String email)
1240    {
1241        checkSessionAlreadyInitialized();
1242        this.bounceAddress = email;
1243        return this;
1244    }
1245
1246    /**
1247     * Define the content of the mail. It should be overridden by the
1248     * subclasses.
1249     *
1250     * @param msg A String.
1251     * @return An Email.
1252     * @throws EmailException generic exception.
1253     * @since 1.0
1254     */
1255    public abstract Email setMsg(String msg) throws EmailException;
1256
1257    /**
1258     * Does the work of actually building the MimeMessage. Please note that
1259     * a user rarely calls this method directly and only if he/she is
1260     * interested in the sending the underlying MimeMessage without
1261     * commons-email.
1262     *
1263     * @throws IllegalStateException if the MimeMessage was already built
1264     * @throws EmailException if there was an error.
1265     * @since 1.0
1266     */
1267    public void buildMimeMessage() throws EmailException
1268    {
1269        if (this.message != null)
1270        {
1271            // [EMAIL-95] we assume that an email is not reused therefore invoking
1272            // buildMimeMessage() more than once is illegal.
1273            throw new IllegalStateException("The MimeMessage is already built.");
1274        }
1275
1276        try
1277        {
1278            this.message = this.createMimeMessage(this.getMailSession());
1279
1280            if (EmailUtils.isNotEmpty(this.subject))
1281            {
1282                if (EmailUtils.isNotEmpty(this.charset))
1283                {
1284                    this.message.setSubject(this.subject, this.charset);
1285                }
1286                else
1287                {
1288                    this.message.setSubject(this.subject);
1289                }
1290            }
1291
1292            // update content type (and encoding)
1293            this.updateContentType(this.contentType);
1294
1295            if (this.content != null)
1296            {
1297                if (EmailConstants.TEXT_PLAIN.equalsIgnoreCase(this.contentType)
1298                        && this.content instanceof String)
1299                {
1300                    // EMAIL-104: call explicitly setText to use default mime charset
1301                    //            (property "mail.mime.charset") in case none has been set
1302                    this.message.setText(this.content.toString(), this.charset);
1303                }
1304                else
1305                {
1306                    this.message.setContent(this.content, this.contentType);
1307                }
1308            }
1309            else if (this.emailBody != null)
1310            {
1311                if (this.contentType == null)
1312                {
1313                    this.message.setContent(this.emailBody);
1314                }
1315                else
1316                {
1317                    this.message.setContent(this.emailBody, this.contentType);
1318                }
1319            }
1320            else
1321            {
1322                this.message.setText("");
1323            }
1324
1325            if (this.fromAddress != null)
1326            {
1327                this.message.setFrom(this.fromAddress);
1328            }
1329            else
1330            {
1331                if (session.getProperty(EmailConstants.MAIL_SMTP_FROM) == null
1332                        && session.getProperty(EmailConstants.MAIL_FROM) == null)
1333                {
1334                    throw new EmailException("From address required");
1335                }
1336            }
1337
1338            if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
1339            {
1340                throw new EmailException("At least one receiver address required");
1341            }
1342
1343            if (this.toList.size() > 0)
1344            {
1345                this.message.setRecipients(
1346                    Message.RecipientType.TO,
1347                    this.toInternetAddressArray(this.toList));
1348            }
1349
1350            if (this.ccList.size() > 0)
1351            {
1352                this.message.setRecipients(
1353                    Message.RecipientType.CC,
1354                    this.toInternetAddressArray(this.ccList));
1355            }
1356
1357            if (this.bccList.size() > 0)
1358            {
1359                this.message.setRecipients(
1360                    Message.RecipientType.BCC,
1361                    this.toInternetAddressArray(this.bccList));
1362            }
1363
1364            if (this.replyList.size() > 0)
1365            {
1366                this.message.setReplyTo(
1367                    this.toInternetAddressArray(this.replyList));
1368            }
1369
1370
1371            if (this.headers.size() > 0)
1372            {
1373                for (final Map.Entry<String, String> entry : this.headers.entrySet())
1374                {
1375                    final String foldedValue = createFoldedHeaderValue(entry.getKey(), entry.getValue());
1376                    this.message.addHeader(entry.getKey(), foldedValue);
1377                }
1378            }
1379
1380            if (this.message.getSentDate() == null)
1381            {
1382                this.message.setSentDate(getSentDate());
1383            }
1384
1385            if (this.popBeforeSmtp)
1386            {
1387                final Store store = session.getStore("pop3");
1388                store.connect(this.popHost, this.popUsername, this.popPassword);
1389            }
1390        }
1391        catch (final MessagingException me)
1392        {
1393            throw new EmailException(me);
1394        }
1395    }
1396
1397    /**
1398     * Sends the previously created MimeMessage to the SMTP server.
1399     *
1400     * @return the message id of the underlying MimeMessage
1401     * @throws IllegalArgumentException if the MimeMessage has not been created
1402     * @throws EmailException the sending failed
1403     */
1404    public String sendMimeMessage()
1405       throws EmailException
1406    {
1407        EmailUtils.notNull(this.message, "MimeMessage has not been created yet");
1408
1409        try
1410        {
1411            Transport.send(this.message);
1412            return this.message.getMessageID();
1413        }
1414        catch (final Throwable t)
1415        {
1416            final String msg = "Sending the email to the following server failed : "
1417                + this.getHostName()
1418                + ":"
1419                + this.getSmtpPort();
1420
1421            throw new EmailException(msg, t);
1422        }
1423    }
1424
1425    /**
1426     * Returns the internal MimeMessage. Please not that the
1427     * MimeMessage is build by the buildMimeMessage() method.
1428     *
1429     * @return the MimeMessage
1430     */
1431    public MimeMessage getMimeMessage()
1432    {
1433        return this.message;
1434    }
1435
1436    /**
1437     * Sends the email. Internally we build a MimeMessage
1438     * which is afterwards sent to the SMTP server.
1439     *
1440     * @return the message id of the underlying MimeMessage
1441     * @throws IllegalStateException if the MimeMessage was already built, ie {@link #buildMimeMessage()}
1442     *   was already called
1443     * @throws EmailException the sending failed
1444     */
1445    public String send() throws EmailException
1446    {
1447        this.buildMimeMessage();
1448        return this.sendMimeMessage();
1449    }
1450
1451    /**
1452     * Sets the sent date for the email.  The sent date will default to the
1453     * current date if not explicitly set.
1454     *
1455     * @param date Date to use as the sent date on the email
1456     * @since 1.0
1457     */
1458    public void setSentDate(final Date date)
1459    {
1460        if (date != null)
1461        {
1462            // create a separate instance to keep findbugs happy
1463            this.sentDate = new Date(date.getTime());
1464        }
1465    }
1466
1467    /**
1468     * Gets the sent date for the email.
1469     *
1470     * @return date to be used as the sent date for the email
1471     * @since 1.0
1472     */
1473    public Date getSentDate()
1474    {
1475        if (this.sentDate == null)
1476        {
1477            return new Date();
1478        }
1479        return new Date(this.sentDate.getTime());
1480    }
1481
1482    /**
1483     * Gets the subject of the email.
1484     *
1485     * @return email subject
1486     */
1487    public String getSubject()
1488    {
1489        return this.subject;
1490    }
1491
1492    /**
1493     * Gets the sender of the email.
1494     *
1495     * @return from address
1496     */
1497    public InternetAddress getFromAddress()
1498    {
1499        return this.fromAddress;
1500    }
1501
1502    /**
1503     * Gets the host name of the SMTP server,
1504     *
1505     * @return host name
1506     */
1507    public String getHostName()
1508    {
1509        if (this.session != null)
1510        {
1511            return this.session.getProperty(MAIL_HOST);
1512        }
1513        else if (EmailUtils.isNotEmpty(this.hostName))
1514        {
1515            return this.hostName;
1516        }
1517        return null;
1518    }
1519
1520    /**
1521     * Gets the listening port of the SMTP server.
1522     *
1523     * @return smtp port
1524     */
1525    public String getSmtpPort()
1526    {
1527        if (this.session != null)
1528        {
1529            return this.session.getProperty(MAIL_PORT);
1530        }
1531        else if (EmailUtils.isNotEmpty(this.smtpPort))
1532        {
1533            return this.smtpPort;
1534        }
1535        return null;
1536    }
1537
1538    /**
1539     * Gets whether the client is configured to require STARTTLS.
1540     *
1541     * @return true if using STARTTLS for authentication, false otherwise
1542     * @since 1.3
1543     */
1544    public boolean isStartTLSRequired()
1545    {
1546        return this.startTlsRequired;
1547    }
1548
1549    /**
1550     * Gets whether the client is configured to try to enable STARTTLS.
1551     *
1552     * @return true if using STARTTLS for authentication, false otherwise
1553     * @since 1.3
1554     */
1555    public boolean isStartTLSEnabled()
1556    {
1557        return this.startTlsEnabled || tls;
1558    }
1559
1560    /**
1561     * Gets whether the client is configured to try to enable STARTTLS.
1562     * See EMAIL-105 for reason of deprecation.
1563     *
1564     * @deprecated since 1.3, use isStartTLSEnabled() instead
1565     * @return true if using STARTTLS for authentication, false otherwise
1566     * @since 1.1
1567     */
1568    @Deprecated
1569    public boolean isTLS()
1570    {
1571        return isStartTLSEnabled();
1572    }
1573
1574    /**
1575     * Utility to copy List of known InternetAddress objects into an
1576     * array.
1577     *
1578     * @param list A List.
1579     * @return An InternetAddress[].
1580     * @since 1.0
1581     */
1582    protected InternetAddress[] toInternetAddressArray(final List<InternetAddress> list)
1583    {
1584        return list.toArray(new InternetAddress[list.size()]);
1585    }
1586
1587    /**
1588     * Set details regarding "pop3 before smtp" authentication.
1589     *
1590     * @param newPopBeforeSmtp Whether or not to log into pop3 server before sending mail.
1591     * @param newPopHost The pop3 host to use.
1592     * @param newPopUsername The pop3 username.
1593     * @param newPopPassword The pop3 password.
1594     * @since 1.0
1595     */
1596    public void setPopBeforeSmtp(
1597        final boolean newPopBeforeSmtp,
1598        final String newPopHost,
1599        final String newPopUsername,
1600        final String newPopPassword)
1601    {
1602        this.popBeforeSmtp = newPopBeforeSmtp;
1603        this.popHost = newPopHost;
1604        this.popUsername = newPopUsername;
1605        this.popPassword = newPopPassword;
1606    }
1607
1608    /**
1609     * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1610     * See EMAIL-105 for reason of deprecation.
1611     *
1612     * @deprecated since 1.3, use isSSLOnConnect() instead
1613     * @return true if SSL enabled for the transport
1614     */
1615    @Deprecated
1616    public boolean isSSL()
1617    {
1618        return isSSLOnConnect();
1619    }
1620
1621    /**
1622     * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1623     *
1624     * @return true if SSL enabled for the transport
1625     * @since 1.3
1626     */
1627    public boolean isSSLOnConnect()
1628    {
1629        return sslOnConnect || ssl;
1630    }
1631
1632    /**
1633     * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1634     * See EMAIL-105 for reason of deprecation.
1635     *
1636     * @deprecated since 1.3, use setSSLOnConnect() instead
1637     * @param ssl whether to enable the SSL transport
1638     */
1639    @Deprecated
1640    public void setSSL(final boolean ssl)
1641    {
1642        setSSLOnConnect(ssl);
1643    }
1644
1645    /**
1646     * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1647     *
1648     * @param ssl whether to enable the SSL transport
1649     * @return An Email.
1650     * @since 1.3
1651     */
1652    public Email setSSLOnConnect(final boolean ssl)
1653    {
1654        checkSessionAlreadyInitialized();
1655        this.sslOnConnect = ssl;
1656        this.ssl = ssl;
1657        return this;
1658    }
1659
1660    /**
1661    * Is the server identity checked as specified by RFC 2595
1662    *
1663    * @return true if the server identity is checked
1664    * @since 1.3
1665    */
1666    public boolean isSSLCheckServerIdentity()
1667    {
1668        return sslCheckServerIdentity;
1669    }
1670
1671    /**
1672     * Sets whether the server identity is checked as specified by RFC 2595
1673     *
1674     * @param sslCheckServerIdentity whether to enable server identity check
1675     * @return An Email.
1676     * @since 1.3
1677     */
1678    public Email setSSLCheckServerIdentity(final boolean sslCheckServerIdentity)
1679    {
1680        checkSessionAlreadyInitialized();
1681        this.sslCheckServerIdentity = sslCheckServerIdentity;
1682        return this;
1683    }
1684
1685    /**
1686     * Returns the current SSL port used by the SMTP transport.
1687     *
1688     * @return the current SSL port used by the SMTP transport
1689     */
1690    public String getSslSmtpPort()
1691    {
1692        if (this.session != null)
1693        {
1694            return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT);
1695        }
1696        else if (EmailUtils.isNotEmpty(this.sslSmtpPort))
1697        {
1698            return this.sslSmtpPort;
1699        }
1700        return null;
1701    }
1702
1703    /**
1704     * Sets the SSL port to use for the SMTP transport. Defaults to the standard
1705     * port, 465.
1706     *
1707     * @param sslSmtpPort the SSL port to use for the SMTP transport
1708     */
1709    public void setSslSmtpPort(final String sslSmtpPort)
1710    {
1711        checkSessionAlreadyInitialized();
1712        this.sslSmtpPort = sslSmtpPort;
1713    }
1714
1715    /**
1716    * If partial sending of email enabled.
1717    *
1718    * @return true if sending partial email is enabled
1719    * @since 1.3.2
1720    */
1721    public boolean isSendPartial()
1722    {
1723        return sendPartial;
1724    }
1725
1726    /**
1727     * Sets whether the email is partially send in case of invalid addresses.
1728     * <p>
1729     * In case the mail server rejects an address as invalid, the call to {@link #send()}
1730     * may throw a {@link javax.mail.SendFailedException}, even if partial send mode is enabled (emails
1731     * to valid addresses will be transmitted). In case the email server does not reject
1732     * invalid addresses immediately, but return a bounce message, no exception will be thrown
1733     * by the {@link #send()} method.
1734     *
1735     * @param sendPartial whether to enable partial send mode
1736     * @return An Email.
1737     * @since 1.3.2
1738     */
1739    public Email setSendPartial(final boolean sendPartial)
1740    {
1741        checkSessionAlreadyInitialized();
1742        this.sendPartial = sendPartial;
1743        return this;
1744    }
1745
1746    /**
1747     * Get the list of "To" addresses.
1748     *
1749     * @return List addresses
1750     */
1751    public List<InternetAddress> getToAddresses()
1752    {
1753        return this.toList;
1754    }
1755
1756    /**
1757     * Get the list of "CC" addresses.
1758     *
1759     * @return List addresses
1760     */
1761    public List<InternetAddress> getCcAddresses()
1762    {
1763        return this.ccList;
1764    }
1765
1766    /**
1767     * Get the list of "Bcc" addresses.
1768     *
1769     * @return List addresses
1770     */
1771    public List<InternetAddress> getBccAddresses()
1772    {
1773        return this.bccList;
1774    }
1775
1776    /**
1777     * Get the list of "Reply-To" addresses.
1778     *
1779     * @return List addresses
1780     */
1781    public List<InternetAddress> getReplyToAddresses()
1782    {
1783        return this.replyList;
1784    }
1785
1786    /**
1787     * Get the socket connection timeout value in milliseconds.
1788     *
1789     * @return the timeout in milliseconds.
1790     * @since 1.2
1791     */
1792    public int getSocketConnectionTimeout()
1793    {
1794        return this.socketConnectionTimeout;
1795    }
1796
1797    /**
1798     * Set the socket connection timeout value in milliseconds.
1799     * Default is a 60 second timeout.
1800     *
1801     * @param socketConnectionTimeout the connection timeout
1802     * @since 1.2
1803     */
1804    public void setSocketConnectionTimeout(final int socketConnectionTimeout)
1805    {
1806        checkSessionAlreadyInitialized();
1807        this.socketConnectionTimeout = socketConnectionTimeout;
1808    }
1809
1810    /**
1811     * Get the socket I/O timeout value in milliseconds.
1812     *
1813     * @return the socket I/O timeout
1814     * @since 1.2
1815     */
1816    public int getSocketTimeout()
1817    {
1818        return this.socketTimeout;
1819    }
1820
1821    /**
1822     * Set the socket I/O timeout value in milliseconds.
1823     * Default is 60 second timeout.
1824     *
1825     * @param socketTimeout the socket I/O timeout
1826     * @since 1.2
1827     */
1828    public void setSocketTimeout(final int socketTimeout)
1829    {
1830        checkSessionAlreadyInitialized();
1831        this.socketTimeout = socketTimeout;
1832    }
1833
1834    /**
1835     * Factory method to create a customized MimeMessage which can be
1836     * implemented by a derived class, e.g. to set the message id.
1837     *
1838     * @param aSession mail session to be used
1839     * @return the newly created message
1840     */
1841    protected MimeMessage createMimeMessage(final Session aSession)
1842    {
1843        return new MimeMessage(aSession);
1844    }
1845
1846    /**
1847     * Create a folded header value containing 76 character chunks.
1848     *
1849     * @param name the name of the header
1850     * @param value the value of the header
1851     * @return the folded header value
1852     * @throws IllegalArgumentException if either the name or value is null or empty
1853     */
1854    private String createFoldedHeaderValue(final String name, final Object value)
1855    {
1856        String result;
1857
1858        if (EmailUtils.isEmpty(name))
1859        {
1860            throw new IllegalArgumentException("name can not be null or empty");
1861        }
1862        if (value == null || EmailUtils.isEmpty(value.toString()))
1863        {
1864            throw new IllegalArgumentException("value can not be null or empty");
1865        }
1866
1867        try
1868        {
1869            result = MimeUtility.fold(name.length() + 2, MimeUtility.encodeText(value.toString(), this.charset, null));
1870        }
1871        catch (final UnsupportedEncodingException e)
1872        {
1873            result = value.toString();
1874        }
1875
1876        return result;
1877    }
1878
1879    /**
1880     * Creates a InternetAddress.
1881     *
1882     * @param email An email address.
1883     * @param name A name.
1884     * @param charsetName The name of the charset to encode the name with.
1885     * @return An internet address.
1886     * @throws EmailException Thrown when the supplied address, name or charset were invalid.
1887     */
1888    private InternetAddress createInternetAddress(final String email, final String name, final String charsetName)
1889        throws EmailException
1890    {
1891        InternetAddress address = null;
1892
1893        try
1894        {
1895            address = new InternetAddress(email);
1896
1897            // check name input
1898            if (EmailUtils.isNotEmpty(name))
1899            {
1900                // check charset input.
1901                if (EmailUtils.isEmpty(charsetName))
1902                {
1903                    address.setPersonal(name);
1904                }
1905                else
1906                {
1907                    // canonicalize the charset name and make sure
1908                    // the current platform supports it.
1909                    final Charset set = Charset.forName(charsetName);
1910                    address.setPersonal(name, set.name());
1911                }
1912            }
1913
1914            // run sanity check on new InternetAddress object; if this fails
1915            // it will throw AddressException.
1916            address.validate();
1917        }
1918        catch (final AddressException e)
1919        {
1920            throw new EmailException(e);
1921        }
1922        catch (final UnsupportedEncodingException e)
1923        {
1924            throw new EmailException(e);
1925        }
1926        return address;
1927    }
1928
1929    /**
1930     * When a mail session is already initialized setting the
1931     * session properties has no effect. In order to flag the
1932     * problem throw an IllegalStateException.
1933     *
1934     * @throws IllegalStateException when the mail session is already initialized
1935     */
1936    private void checkSessionAlreadyInitialized()
1937    {
1938        if (this.session != null)
1939        {
1940            throw new IllegalStateException("The mail session is already initialized");
1941        }
1942    }
1943}