1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.mail2.jakarta;
18
19 import java.io.UnsupportedEncodingException;
20 import java.nio.charset.Charset;
21 import java.time.Duration;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Properties;
30
31 import javax.naming.Context;
32 import javax.naming.InitialContext;
33 import javax.naming.NamingException;
34
35 import org.apache.commons.mail2.core.EmailConstants;
36 import org.apache.commons.mail2.core.EmailException;
37 import org.apache.commons.mail2.core.EmailUtils;
38 import org.apache.commons.mail2.jakarta.util.IDNEmailAddressConverter;
39
40 import jakarta.mail.Authenticator;
41 import jakarta.mail.Message;
42 import jakarta.mail.MessagingException;
43 import jakarta.mail.Session;
44 import jakarta.mail.Store;
45 import jakarta.mail.Transport;
46 import jakarta.mail.internet.AddressException;
47 import jakarta.mail.internet.InternetAddress;
48 import jakarta.mail.internet.MimeMessage;
49 import jakarta.mail.internet.MimeMultipart;
50 import jakarta.mail.internet.MimeUtility;
51
52 /**
53 * The abstract class for all email messages. This class sets the sender's email, name, receiver's email, name, subject, and send date.
54 * <p>
55 * Subclasses are responsible for setting the message body.
56 * </p>
57 *
58 * @since 1.0
59 */
60 public abstract class Email {
61
62 /**
63 * Empty array.
64 */
65 private static final InternetAddress[] EMPTY_INTERNET_ADDRESS_ARRAY = {};
66
67 /**
68 * The email message to send.
69 */
70 private MimeMessage message;
71
72 /**
73 * The charset to use for this message.
74 */
75 private String charset;
76
77 /**
78 * The Address of the sending party, mandatory.
79 */
80 private InternetAddress fromAddress;
81
82 /**
83 * The Subject.
84 */
85 private String subject;
86
87 /**
88 * An attachment.
89 */
90 private MimeMultipart emailBody;
91
92 /**
93 * The content.
94 */
95 private Object content;
96
97 /**
98 * The content type.
99 */
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 < 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 }