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 1563555 2014-02-02 02:51:10Z ggregory $
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(boolean d)
334    {
335        this.debug = d;
336    }
337
338    /**
339     * Sets the userName and password if authentication is needed.  If this
340     * method is not used, no authentication will be performed.
341     * <p>
342     * This method will create a new instance of
343     * <code>DefaultAuthenticator</code> using the supplied parameters.
344     *
345     * @param userName User name for the SMTP server
346     * @param password password for the SMTP server
347     * @see DefaultAuthenticator
348     * @see #setAuthenticator
349     * @since 1.0
350     */
351    public void setAuthentication(String userName, String password)
352    {
353        this.setAuthenticator(new DefaultAuthenticator(userName, password));
354    }
355
356    /**
357     * Sets the <code>Authenticator</code> to be used when authentication
358     * is requested from the mail server.
359     * <p>
360     * This method should be used when your outgoing mail server requires
361     * authentication.  Your mail server must also support RFC2554.
362     *
363     * @param newAuthenticator the <code>Authenticator</code> object.
364     * @see Authenticator
365     * @since 1.0
366     */
367    public void setAuthenticator(Authenticator newAuthenticator)
368    {
369        this.authenticator = newAuthenticator;
370    }
371
372    /**
373     * Set the charset of the message. Please note that you should set the charset before
374     * adding the message content.
375     *
376     * @param newCharset A String.
377     * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid
378     * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset
379     * exists in the current JVM
380     * @since 1.0
381     */
382    public void setCharset(String newCharset)
383    {
384        Charset set = Charset.forName(newCharset);
385        this.charset = set.name();
386    }
387
388    /**
389     * Set the emailBody to a MimeMultiPart
390     *
391     * @param aMimeMultipart aMimeMultipart
392     * @since 1.0
393     */
394    public void setContent(MimeMultipart aMimeMultipart)
395    {
396        this.emailBody = aMimeMultipart;
397    }
398
399    /**
400     * Set the content and contentType.
401     *
402     * @param   aObject aObject
403     * @param   aContentType aContentType
404     * @since 1.0
405     */
406    public void setContent(Object aObject, String aContentType)
407    {
408        this.content = aObject;
409        this.updateContentType(aContentType);
410    }
411
412    /**
413     * Update the contentType.
414     *
415     * @param   aContentType aContentType
416     * @since 1.2
417     */
418    public void updateContentType(final String aContentType)
419    {
420        if (EmailUtils.isEmpty(aContentType))
421        {
422            this.contentType = null;
423        }
424        else
425        {
426            // set the content type
427            this.contentType = aContentType;
428
429            // set the charset if the input was properly formed
430            String strMarker = "; charset=";
431            int charsetPos = aContentType.toLowerCase().indexOf(strMarker);
432
433            if (charsetPos != -1)
434            {
435                // find the next space (after the marker)
436                charsetPos += strMarker.length();
437                int intCharsetEnd =
438                    aContentType.toLowerCase().indexOf(" ", charsetPos);
439
440                if (intCharsetEnd != -1)
441                {
442                    this.charset =
443                        aContentType.substring(charsetPos, intCharsetEnd);
444                }
445                else
446                {
447                    this.charset = aContentType.substring(charsetPos);
448                }
449            }
450            else
451            {
452                // use the default charset, if one exists, for messages
453                // whose content-type is some form of text.
454                if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset))
455                {
456                    StringBuffer contentTypeBuf = new StringBuffer(this.contentType);
457                    contentTypeBuf.append(strMarker);
458                    contentTypeBuf.append(this.charset);
459                    this.contentType = contentTypeBuf.toString();
460                }
461            }
462        }
463    }
464
465    /**
466     * Set the hostname of the outgoing mail server.
467     *
468     * @param   aHostName aHostName
469     * @since 1.0
470     */
471    public void setHostName(String aHostName)
472    {
473        checkSessionAlreadyInitialized();
474        this.hostName = aHostName;
475    }
476
477    /**
478     * Set or disable the STARTTLS encryption. Please see EMAIL-105
479     * for the reasons of deprecation.
480     *
481     * @deprecated since 1.3, use setStartTLSEnabled() instead
482     * @param withTLS true if STARTTLS requested, false otherwise
483     * @since 1.1
484     */
485    @Deprecated
486    public void setTLS(boolean withTLS)
487    {
488        setStartTLSEnabled(withTLS);
489    }
490
491    /**
492     * Set or disable the STARTTLS encryption.
493     *
494     * @param startTlsEnabled true if STARTTLS requested, false otherwise
495     * @return An Email.
496     * @since 1.3
497     */
498    public Email setStartTLSEnabled(boolean startTlsEnabled)
499    {
500        checkSessionAlreadyInitialized();
501        this.startTlsEnabled = startTlsEnabled;
502        this.tls = startTlsEnabled;
503        return this;
504    }
505
506    /**
507     * Set or disable the required STARTTLS encryption.
508     *
509     * @param startTlsRequired true if STARTTLS requested, false otherwise
510     * @return An Email.
511     * @since 1.3
512     */
513    public Email setStartTLSRequired(boolean startTlsRequired)
514    {
515        checkSessionAlreadyInitialized();
516        this.startTlsRequired = startTlsRequired;
517        return this;
518    }
519
520    /**
521     * Set the port number of the outgoing mail server.
522     *
523     * @param  aPortNumber aPortNumber
524     * @throws IllegalArgumentException if the port number is &lt; 1
525     * @since 1.0
526     */
527    public void setSmtpPort(int aPortNumber)
528    {
529        checkSessionAlreadyInitialized();
530
531        if (aPortNumber < 1)
532        {
533            throw new IllegalArgumentException(
534                "Cannot connect to a port number that is less than 1 ( "
535                    + aPortNumber
536                    + " )");
537        }
538
539        this.smtpPort = Integer.toString(aPortNumber);
540    }
541
542    /**
543     * Supply a mail Session object to use. Please note that passing
544     * a user name and password (in the case of mail authentication) will
545     * create a new mail session with a DefaultAuthenticator. This is a
546     * convenience but might come unexpected.
547     *
548     * If mail authentication is used but NO username and password
549     * is supplied the implementation assumes that you have set a
550     * authenticator and will use the existing mail session (as expected).
551     *
552     * @param aSession mail session to be used
553     * @throws IllegalArgumentException if the session is {@code null}
554     * @since 1.0
555     */
556    public void setMailSession(Session aSession)
557    {
558        EmailUtils.notNull(aSession, "no mail session supplied");
559
560        Properties sessionProperties = aSession.getProperties();
561        String auth = sessionProperties.getProperty(MAIL_SMTP_AUTH);
562
563        if ("true".equalsIgnoreCase(auth))
564        {
565            String userName = sessionProperties.getProperty(MAIL_SMTP_USER);
566            String password = sessionProperties.getProperty(MAIL_SMTP_PASSWORD);
567
568            if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password))
569            {
570                // only create a new mail session with an authenticator if
571                // authentication is required and no user name is given
572                this.authenticator = new DefaultAuthenticator(userName, password);
573                this.session = Session.getInstance(sessionProperties, this.authenticator);
574            }
575            else
576            {
577                // assume that the given mail session contains a working authenticator
578                this.session = aSession;
579            }
580        }
581        else
582        {
583            this.session = aSession;
584        }
585    }
586
587    /**
588     * Supply a mail Session object from a JNDI directory.
589     *
590     * @param jndiName name of JNDI resource (javax.mail.Session type), resource
591     * if searched in java:comp/env if name does not start with "java:"
592     * @throws IllegalArgumentException if the JNDI name is null or empty
593     * @throws NamingException if the resource cannot be retrieved from JNDI directory
594     * @since 1.1
595     */
596    public void setMailSessionFromJNDI(String jndiName) throws NamingException
597    {
598        if (EmailUtils.isEmpty(jndiName))
599        {
600            throw new IllegalArgumentException("JNDI name missing");
601        }
602        Context ctx = null;
603        if (jndiName.startsWith("java:"))
604        {
605            ctx = new InitialContext();
606        }
607        else
608        {
609            ctx = (Context) new InitialContext().lookup("java:comp/env");
610
611        }
612        this.setMailSession((Session) ctx.lookup(jndiName));
613    }
614
615    /**
616     * Determines the mail session used when sending this Email, creating
617     * the Session if necessary. When a mail session is already
618     * initialized setting the session related properties will cause
619     * an IllegalStateException.
620     *
621     * @return A Session.
622     * @throws EmailException if the host name was not set
623     * @since 1.0
624     */
625    public Session getMailSession() throws EmailException
626    {
627        if (this.session == null)
628        {
629            Properties properties = new Properties(System.getProperties());
630            properties.setProperty(MAIL_TRANSPORT_PROTOCOL, SMTP);
631
632            if (EmailUtils.isEmpty(this.hostName))
633            {
634                this.hostName = properties.getProperty(MAIL_HOST);
635            }
636
637            if (EmailUtils.isEmpty(this.hostName))
638            {
639                throw new EmailException("Cannot find valid hostname for mail session");
640            }
641
642            properties.setProperty(MAIL_PORT, this.smtpPort);
643            properties.setProperty(MAIL_HOST, this.hostName);
644            properties.setProperty(MAIL_DEBUG, String.valueOf(this.debug));
645
646            properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE,
647                    isStartTLSEnabled() ? "true" : "false");
648            properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_REQUIRED,
649                    isStartTLSRequired() ? "true" : "false");
650
651            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(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(String email, 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(String email, String name, 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(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(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 (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(String email, 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(String email, String name, 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(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(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(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 (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(String email, 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(String email, String name, 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(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(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(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 (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(String email, 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(String email, String name, 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(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(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(String email, 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(String email, String name, 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(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(Map<String, String> map)
1173    {
1174        this.headers.clear();
1175
1176        for (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(String name, 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(String aSubject)
1212    {
1213        this.subject = aSubject;
1214        return this;
1215    }
1216
1217    /**
1218     * Set the "bounce address" - the address to which undeliverable messages
1219     * will be returned.  If this value is never set, then the message will be
1220     * sent to the address specified with the System property "mail.smtp.from",
1221     * or if that value is not set, then to the "from" address.
1222     *
1223     * @param email A String.
1224     * @return An Email.
1225     * @throws IllegalStateException when the mail session is already initialized
1226     * @since 1.0
1227     */
1228    public Email setBounceAddress(String email)
1229    {
1230        checkSessionAlreadyInitialized();
1231        this.bounceAddress = email;
1232        return this;
1233    }
1234
1235
1236    /**
1237     * Define the content of the mail. It should be overridden by the
1238     * subclasses.
1239     *
1240     * @param msg A String.
1241     * @return An Email.
1242     * @throws EmailException generic exception.
1243     * @since 1.0
1244     */
1245    public abstract Email setMsg(String msg) throws EmailException;
1246
1247    /**
1248     * Does the work of actually building the MimeMessage. Please note that
1249     * a user rarely calls this method directly and only if he/she is
1250     * interested in the sending the underlying MimeMessage without
1251     * commons-email.
1252     *
1253     * @throws IllegalStateException if the MimeMessage was already built
1254     * @throws EmailException if there was an error.
1255     * @since 1.0
1256     */
1257    public void buildMimeMessage() throws EmailException
1258    {
1259        if (this.message != null)
1260        {
1261            // [EMAIL-95] we assume that an email is not reused therefore invoking
1262            // buildMimeMessage() more than once is illegal.
1263            throw new IllegalStateException("The MimeMessage is already built.");
1264        }
1265
1266        try
1267        {
1268            this.message = this.createMimeMessage(this.getMailSession());
1269
1270            if (EmailUtils.isNotEmpty(this.subject))
1271            {
1272                if (EmailUtils.isNotEmpty(this.charset))
1273                {
1274                    this.message.setSubject(this.subject, this.charset);
1275                }
1276                else
1277                {
1278                    this.message.setSubject(this.subject);
1279                }
1280            }
1281
1282            // update content type (and encoding)
1283            this.updateContentType(this.contentType);
1284
1285            if (this.content != null)
1286            {
1287                if (EmailConstants.TEXT_PLAIN.equalsIgnoreCase(this.contentType)
1288                        && this.content instanceof String)
1289                {
1290                    // EMAIL-104: call explicitly setText to use default mime charset
1291                    //            (property "mail.mime.charset") in case none has been set
1292                    this.message.setText(this.content.toString(), this.charset);
1293                }
1294                else
1295                {
1296                    this.message.setContent(this.content, this.contentType);
1297                }
1298            }
1299            else if (this.emailBody != null)
1300            {
1301                if (this.contentType == null)
1302                {
1303                    this.message.setContent(this.emailBody);
1304                }
1305                else
1306                {
1307                    this.message.setContent(this.emailBody, this.contentType);
1308                }
1309            }
1310            else
1311            {
1312                this.message.setText("");
1313            }
1314
1315            if (this.fromAddress != null)
1316            {
1317                this.message.setFrom(this.fromAddress);
1318            }
1319            else
1320            {
1321                if (session.getProperty(MAIL_SMTP_FROM) == null)
1322                {
1323                    throw new EmailException("From address required");
1324                }
1325            }
1326
1327            if (this.toList.size() + this.ccList.size() + this.bccList.size() == 0)
1328            {
1329                throw new EmailException("At least one receiver address required");
1330            }
1331
1332            if (this.toList.size() > 0)
1333            {
1334                this.message.setRecipients(
1335                    Message.RecipientType.TO,
1336                    this.toInternetAddressArray(this.toList));
1337            }
1338
1339            if (this.ccList.size() > 0)
1340            {
1341                this.message.setRecipients(
1342                    Message.RecipientType.CC,
1343                    this.toInternetAddressArray(this.ccList));
1344            }
1345
1346            if (this.bccList.size() > 0)
1347            {
1348                this.message.setRecipients(
1349                    Message.RecipientType.BCC,
1350                    this.toInternetAddressArray(this.bccList));
1351            }
1352
1353            if (this.replyList.size() > 0)
1354            {
1355                this.message.setReplyTo(
1356                    this.toInternetAddressArray(this.replyList));
1357            }
1358
1359
1360            if (this.headers.size() > 0)
1361            {
1362                for (Map.Entry<String, String> entry : this.headers.entrySet())
1363                {
1364                    String foldedValue = createFoldedHeaderValue(entry.getKey(), entry.getValue());
1365                    this.message.addHeader(entry.getKey(), foldedValue);
1366                }
1367            }
1368
1369            if (this.message.getSentDate() == null)
1370            {
1371                this.message.setSentDate(getSentDate());
1372            }
1373
1374            if (this.popBeforeSmtp)
1375            {
1376                Store store = session.getStore("pop3");
1377                store.connect(this.popHost, this.popUsername, this.popPassword);
1378            }
1379        }
1380        catch (MessagingException me)
1381        {
1382            throw new EmailException(me);
1383        }
1384    }
1385
1386    /**
1387     * Sends the previously created MimeMessage to the SMTP server.
1388     *
1389     * @return the message id of the underlying MimeMessage
1390     * @throws IllegalArgumentException if the MimeMessage has not been created
1391     * @throws EmailException the sending failed
1392     */
1393    public String sendMimeMessage()
1394       throws EmailException
1395    {
1396        EmailUtils.notNull(this.message, "MimeMessage has not been created yet");
1397
1398        try
1399        {
1400            Transport.send(this.message);
1401            return this.message.getMessageID();
1402        }
1403        catch (Throwable t)
1404        {
1405            String msg = "Sending the email to the following server failed : "
1406                + this.getHostName()
1407                + ":"
1408                + this.getSmtpPort();
1409
1410            throw new EmailException(msg, t);
1411        }
1412    }
1413
1414    /**
1415     * Returns the internal MimeMessage. Please not that the
1416     * MimeMessage is build by the buildMimeMessage() method.
1417     *
1418     * @return the MimeMessage
1419     */
1420    public MimeMessage getMimeMessage()
1421    {
1422        return this.message;
1423    }
1424
1425    /**
1426     * Sends the email. Internally we build a MimeMessage
1427     * which is afterwards sent to the SMTP server.
1428     *
1429     * @return the message id of the underlying MimeMessage
1430     * @throws IllegalStateException if the MimeMessage was already built, ie {@link #buildMimeMessage()}
1431     *   was already called
1432     * @throws EmailException the sending failed
1433     */
1434    public String send() throws EmailException
1435    {
1436        this.buildMimeMessage();
1437        return this.sendMimeMessage();
1438    }
1439
1440    /**
1441     * Sets the sent date for the email.  The sent date will default to the
1442     * current date if not explicitly set.
1443     *
1444     * @param date Date to use as the sent date on the email
1445     * @since 1.0
1446     */
1447    public void setSentDate(Date date)
1448    {
1449        if (date != null)
1450        {
1451            // create a separate instance to keep findbugs happy
1452            this.sentDate = new Date(date.getTime());
1453        }
1454    }
1455
1456    /**
1457     * Gets the sent date for the email.
1458     *
1459     * @return date to be used as the sent date for the email
1460     * @since 1.0
1461     */
1462    public Date getSentDate()
1463    {
1464        if (this.sentDate == null)
1465        {
1466            return new Date();
1467        }
1468        return new Date(this.sentDate.getTime());
1469    }
1470
1471    /**
1472     * Gets the subject of the email.
1473     *
1474     * @return email subject
1475     */
1476    public String getSubject()
1477    {
1478        return this.subject;
1479    }
1480
1481    /**
1482     * Gets the sender of the email.
1483     *
1484     * @return from address
1485     */
1486    public InternetAddress getFromAddress()
1487    {
1488        return this.fromAddress;
1489    }
1490
1491    /**
1492     * Gets the host name of the SMTP server,
1493     *
1494     * @return host name
1495     */
1496    public String getHostName()
1497    {
1498        if (this.session != null)
1499        {
1500            return this.session.getProperty(MAIL_HOST);
1501        }
1502        else if (EmailUtils.isNotEmpty(this.hostName))
1503        {
1504            return this.hostName;
1505        }
1506        return null;
1507    }
1508
1509    /**
1510     * Gets the listening port of the SMTP server.
1511     *
1512     * @return smtp port
1513     */
1514    public String getSmtpPort()
1515    {
1516        if (this.session != null)
1517        {
1518            return this.session.getProperty(MAIL_PORT);
1519        }
1520        else if (EmailUtils.isNotEmpty(this.smtpPort))
1521        {
1522            return this.smtpPort;
1523        }
1524        return null;
1525    }
1526
1527    /**
1528     * Gets whether the client is configured to require STARTTLS.
1529     *
1530     * @return true if using STARTTLS for authentication, false otherwise
1531     * @since 1.3
1532     */
1533    public boolean isStartTLSRequired()
1534    {
1535        return this.startTlsRequired;
1536    }
1537
1538    /**
1539     * Gets whether the client is configured to try to enable STARTTLS.
1540     *
1541     * @return true if using STARTTLS for authentication, false otherwise
1542     * @since 1.3
1543     */
1544    public boolean isStartTLSEnabled()
1545    {
1546        return this.startTlsEnabled || tls;
1547    }
1548
1549    /**
1550     * Gets whether the client is configured to try to enable STARTTLS.
1551     * See EMAIL-105 for reason of deprecation.
1552     *
1553     * @deprecated since 1.3, use isStartTLSEnabled() instead
1554     * @return true if using STARTTLS for authentication, false otherwise
1555     * @since 1.1
1556     */
1557    @Deprecated
1558    public boolean isTLS()
1559    {
1560        return isStartTLSEnabled();
1561    }
1562
1563    /**
1564     * Utility to copy List of known InternetAddress objects into an
1565     * array.
1566     *
1567     * @param list A List.
1568     * @return An InternetAddress[].
1569     * @since 1.0
1570     */
1571    protected InternetAddress[] toInternetAddressArray(List<InternetAddress> list)
1572    {
1573        return list.toArray(new InternetAddress[list.size()]);
1574    }
1575
1576    /**
1577     * Set details regarding "pop3 before smtp" authentication.
1578     *
1579     * @param newPopBeforeSmtp Whether or not to log into pop3 server before sending mail.
1580     * @param newPopHost The pop3 host to use.
1581     * @param newPopUsername The pop3 username.
1582     * @param newPopPassword The pop3 password.
1583     * @since 1.0
1584     */
1585    public void setPopBeforeSmtp(
1586        boolean newPopBeforeSmtp,
1587        String newPopHost,
1588        String newPopUsername,
1589        String newPopPassword)
1590    {
1591        this.popBeforeSmtp = newPopBeforeSmtp;
1592        this.popHost = newPopHost;
1593        this.popUsername = newPopUsername;
1594        this.popPassword = newPopPassword;
1595    }
1596
1597    /**
1598     * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1599     * See EMAIL-105 for reason of deprecation.
1600     *
1601     * @deprecated since 1.3, use isSSLOnConnect() instead
1602     * @return true if SSL enabled for the transport
1603     */
1604    @Deprecated
1605    public boolean isSSL()
1606    {
1607        return isSSLOnConnect();
1608    }
1609
1610    /**
1611     * Returns whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS).
1612     *
1613     * @return true if SSL enabled for the transport
1614     * @since 1.3
1615     */
1616    public boolean isSSLOnConnect()
1617    {
1618        return sslOnConnect || ssl;
1619    }
1620
1621    /**
1622     * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1623     * See EMAIL-105 for reason of deprecation.
1624     *
1625     * @deprecated since 1.3, use setSSLOnConnect() instead
1626     * @param ssl whether to enable the SSL transport
1627     */
1628    @Deprecated
1629    public void setSSL(boolean ssl)
1630    {
1631        setSSLOnConnect(ssl);
1632    }
1633
1634    /**
1635     * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS).
1636     *
1637     * @param ssl whether to enable the SSL transport
1638     * @return An Email.
1639     * @since 1.3
1640     */
1641    public Email setSSLOnConnect(boolean ssl)
1642    {
1643        checkSessionAlreadyInitialized();
1644        this.sslOnConnect = ssl;
1645        this.ssl = ssl;
1646        return this;
1647    }
1648
1649    /**
1650    * Is the server identity checked as specified by RFC 2595
1651    *
1652    * @return true if the server identity is checked
1653    * @since 1.3
1654    */
1655    public boolean isSSLCheckServerIdentity()
1656    {
1657        return sslCheckServerIdentity;
1658    }
1659
1660    /**
1661     * Sets whether the server identity is checked as specified by RFC 2595
1662     *
1663     * @param sslCheckServerIdentity whether to enable server identity check
1664     * @return An Email.
1665     * @since 1.3
1666     */
1667    public Email setSSLCheckServerIdentity(boolean sslCheckServerIdentity)
1668    {
1669        checkSessionAlreadyInitialized();
1670        this.sslCheckServerIdentity = sslCheckServerIdentity;
1671        return this;
1672    }
1673
1674    /**
1675     * Returns the current SSL port used by the SMTP transport.
1676     *
1677     * @return the current SSL port used by the SMTP transport
1678     */
1679    public String getSslSmtpPort()
1680    {
1681        if (this.session != null)
1682        {
1683            return this.session.getProperty(MAIL_SMTP_SOCKET_FACTORY_PORT);
1684        }
1685        else if (EmailUtils.isNotEmpty(this.sslSmtpPort))
1686        {
1687            return this.sslSmtpPort;
1688        }
1689        return null;
1690    }
1691
1692    /**
1693     * Sets the SSL port to use for the SMTP transport. Defaults to the standard
1694     * port, 465.
1695     *
1696     * @param sslSmtpPort the SSL port to use for the SMTP transport
1697     */
1698    public void setSslSmtpPort(String sslSmtpPort)
1699    {
1700        checkSessionAlreadyInitialized();
1701        this.sslSmtpPort = sslSmtpPort;
1702    }
1703
1704    /**
1705    * If partial sending of email enabled.
1706    *
1707    * @return true if sending partial email is enabled
1708    * @since 1.3.2
1709    */
1710    public boolean isSendPartial()
1711    {
1712        return sendPartial;
1713    }
1714
1715    /**
1716     * Sets whether the email is partially send in case of invalid addresses.
1717     * <p>
1718     * In case the mail server rejects an address as invalid, the call to {@link #send()}
1719     * may throw a {@link javax.mail.SendFailedException}, even if partial send mode is enabled (emails
1720     * to valid addresses will be transmitted). In case the email server does not reject
1721     * invalid addresses immediately, but return a bounce message, no exception will be thrown
1722     * by the {@link #send()} method.
1723     *
1724     * @param sendPartial whether to enable partial send mode
1725     * @return An Email.
1726     * @since 1.3.2
1727     */
1728    public Email setSendPartial(boolean sendPartial)
1729    {
1730        checkSessionAlreadyInitialized();
1731        this.sendPartial = sendPartial;
1732        return this;
1733    }
1734
1735    /**
1736     * Get the list of "To" addresses.
1737     *
1738     * @return List addresses
1739     */
1740    public List<InternetAddress> getToAddresses()
1741    {
1742        return this.toList;
1743    }
1744
1745    /**
1746     * Get the list of "CC" addresses.
1747     *
1748     * @return List addresses
1749     */
1750    public List<InternetAddress> getCcAddresses()
1751    {
1752        return this.ccList;
1753    }
1754
1755    /**
1756     * Get the list of "Bcc" addresses.
1757     *
1758     * @return List addresses
1759     */
1760    public List<InternetAddress> getBccAddresses()
1761    {
1762        return this.bccList;
1763    }
1764
1765    /**
1766     * Get the list of "Reply-To" addresses.
1767     *
1768     * @return List addresses
1769     */
1770    public List<InternetAddress> getReplyToAddresses()
1771    {
1772        return this.replyList;
1773    }
1774
1775    /**
1776     * Get the socket connection timeout value in milliseconds.
1777     *
1778     * @return the timeout in milliseconds.
1779     * @since 1.2
1780     */
1781    public int getSocketConnectionTimeout()
1782    {
1783        return this.socketConnectionTimeout;
1784    }
1785
1786    /**
1787     * Set the socket connection timeout value in milliseconds.
1788     * Default is a 60 second timeout.
1789     *
1790     * @param socketConnectionTimeout the connection timeout
1791     * @since 1.2
1792     */
1793    public void setSocketConnectionTimeout(int socketConnectionTimeout)
1794    {
1795        checkSessionAlreadyInitialized();
1796        this.socketConnectionTimeout = socketConnectionTimeout;
1797    }
1798
1799    /**
1800     * Get the socket I/O timeout value in milliseconds.
1801     *
1802     * @return the socket I/O timeout
1803     * @since 1.2
1804     */
1805    public int getSocketTimeout()
1806    {
1807        return this.socketTimeout;
1808    }
1809
1810    /**
1811     * Set the socket I/O timeout value in milliseconds.
1812     * Default is 60 second timeout.
1813     *
1814     * @param socketTimeout the socket I/O timeout
1815     * @since 1.2
1816     */
1817    public void setSocketTimeout(int socketTimeout)
1818    {
1819        checkSessionAlreadyInitialized();
1820        this.socketTimeout = socketTimeout;
1821    }
1822
1823    /**
1824     * Factory method to create a customized MimeMessage which can be
1825     * implemented by a derived class, e.g. to set the message id.
1826     *
1827     * @param aSession mail session to be used
1828     * @return the newly created message
1829     */
1830    protected MimeMessage createMimeMessage(Session aSession)
1831    {
1832        return new MimeMessage(aSession);
1833    }
1834
1835    /**
1836     * Create a folded header value containing 76 character chunks.
1837     *
1838     * @param name the name of the header
1839     * @param value the value of the header
1840     * @return the folded header value
1841     * @throws IllegalArgumentException if either the name or value is null or empty
1842     */
1843    private String createFoldedHeaderValue(String name, Object value)
1844    {
1845        String result;
1846
1847        if (EmailUtils.isEmpty(name))
1848        {
1849            throw new IllegalArgumentException("name can not be null or empty");
1850        }
1851        if (value == null || EmailUtils.isEmpty(value.toString()))
1852        {
1853            throw new IllegalArgumentException("value can not be null or empty");
1854        }
1855
1856        try
1857        {
1858            result = MimeUtility.fold(name.length() + 2, MimeUtility.encodeText(value.toString(), this.charset, null));
1859        }
1860        catch (UnsupportedEncodingException e)
1861        {
1862            result = value.toString();
1863        }
1864
1865        return result;
1866    }
1867
1868    /**
1869     * Creates a InternetAddress.
1870     *
1871     * @param email An email address.
1872     * @param name A name.
1873     * @param charsetName The name of the charset to encode the name with.
1874     * @return An internet address.
1875     * @throws EmailException Thrown when the supplied address, name or charset were invalid.
1876     */
1877    private InternetAddress createInternetAddress(String email, String name, String charsetName)
1878        throws EmailException
1879    {
1880        InternetAddress address = null;
1881
1882        try
1883        {
1884            address = new InternetAddress(email);
1885
1886            // check name input
1887            if (EmailUtils.isNotEmpty(name))
1888            {
1889                // check charset input.
1890                if (EmailUtils.isEmpty(charsetName))
1891                {
1892                    address.setPersonal(name);
1893                }
1894                else
1895                {
1896                    // canonicalize the charset name and make sure
1897                    // the current platform supports it.
1898                    Charset set = Charset.forName(charsetName);
1899                    address.setPersonal(name, set.name());
1900                }
1901            }
1902
1903            // run sanity check on new InternetAddress object; if this fails
1904            // it will throw AddressException.
1905            address.validate();
1906        }
1907        catch (AddressException e)
1908        {
1909            throw new EmailException(e);
1910        }
1911        catch (UnsupportedEncodingException e)
1912        {
1913            throw new EmailException(e);
1914        }
1915        return address;
1916    }
1917
1918    /**
1919     * When a mail session is already initialized setting the
1920     * session properties has no effect. In order to flag the
1921     * problem throw an IllegalStateException.
1922     *
1923     * @throws IllegalStateException when the mail session is already initialized
1924     */
1925    private void checkSessionAlreadyInitialized()
1926    {
1927        if (this.session != null)
1928        {
1929            throw new IllegalStateException("The mail session is already initialized");
1930        }
1931    }
1932}