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