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