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