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