All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.mail2.jakarta.Email Maven / Gradle / Ivy

Go to download

Apache Commons Email provides an API for sending email, simplifying the JavaMail Javax API.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.mail2.jakarta;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.mail2.core.EmailConstants;
import org.apache.commons.mail2.core.EmailException;
import org.apache.commons.mail2.core.EmailUtils;
import org.apache.commons.mail2.jakarta.util.IDNEmailAddressConverter;

import jakarta.mail.Authenticator;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.Store;
import jakarta.mail.Transport;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimeUtility;

/**
 * The abstract class for all email messages. This class sets the sender's email, name, receiver's email, name, subject, and send date.
 * 

* Subclasses are responsible for setting the message body. *

* * @since 1.0 */ public abstract class Email { /** * Empty array. */ private static final InternetAddress[] EMPTY_INTERNET_ADDRESS_ARRAY = {}; /** * The email message to send. */ private MimeMessage message; /** * The charset to use for this message. */ private String charset; /** * The Address of the sending party, mandatory. */ private InternetAddress fromAddress; /** * The Subject. */ private String subject; /** * An attachment. */ private MimeMultipart emailBody; /** * The content. */ private Object content; /** * The content type. */ private String contentType; /** * Set session debugging on or off. */ private boolean debug; /** * Sent date. */ private Date sentDate; /** * Instance of an {@code Authenticator} object that will be used when authentication is requested from the mail server. */ private Authenticator authenticator; /** * The hostname of the mail server with which to connect. If null will try to get property from system.properties. If still null, quit. */ private String hostName; /** * The port number of the mail server to connect to. Defaults to the standard port ( 25 ). */ private String smtpPort = "25"; /** * The port number of the SSL enabled SMTP server; defaults to the standard port, 465. */ private String sslSmtpPort = "465"; /** * List of "to" email addresses. */ private List toList = new ArrayList<>(); /** * List of "cc" email addresses. */ private List ccList = new ArrayList<>(); /** * List of "bcc" email addresses. */ private List bccList = new ArrayList<>(); /** * List of "replyTo" email addresses. */ private List replyList = new ArrayList<>(); /** * 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 * type {@code String} rather than {@code InternetAddress}. */ private String bounceAddress; /** * Used to specify the mail headers. Example: * * X-Mailer: Sendmail, X-Priority: 1( highest ) or 2( high ) 3( normal ) 4( low ) and 5( lowest ) Disposition-Notification-To: [email protected] */ private final Map headers = new HashMap<>(); /** * Whether to use POP3 before SMTP, and if so the settings. */ private boolean popBeforeSmtp; /** * The host name of the POP3 server. */ private String popHost; /** * The user name to log into the POP3 server. */ private String popUsername; /** * The password to log into the POP3 server. */ private String popPassword; /** * Does server require TLS encryption for authentication? */ private boolean tls; /** * Does the current transport use SSL/TLS encryption upon connection? */ private boolean ssl; /** * Socket I/O timeout value in milliseconds. */ private int socketTimeout = Math.toIntExact(EmailConstants.SOCKET_TIMEOUT.toMillis()); /** * Socket connection timeout value in milliseconds. */ private int socketConnectionTimeout = Math.toIntExact(EmailConstants.SOCKET_TIMEOUT.toMillis()); /** * 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 * login commands. Note that an appropriate trust store must configured so that the client will trust the server's certificate. Defaults to false. */ private boolean startTlsEnabled; /** * 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 * fail. Defaults to false. */ private boolean startTlsRequired; /** * Does the current transport use SSL/TLS encryption upon connection? */ private boolean sslOnConnect; /** * 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 * to prevent man-in-the-middle attacks. Defaults to false. */ private boolean sslCheckServerIdentity; /** * If set to true, and a message has some valid and some invalid addresses, send the message anyway, reporting the partial failure with a * 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 * to false. */ private boolean sendPartial; /** * The Session to mail with. */ private Session session; /** * Constructs a new instance. */ public Email() { // empty } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.0 */ public Email addBcc(final String email) throws EmailException { return addBcc(email, null); } /** * 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 * 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 * characters; otherwise, it is used as is. * * @param emails A String array. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.3 */ public Email addBcc(final String... emails) throws EmailException { EmailException.checkNonEmpty(emails, () -> "BCC list invalid."); for (final String email : emails) { addBcc(email, null); } return this; } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @param name A String. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.0 */ public Email addBcc(final String email, final String name) throws EmailException { return addBcc(email, name, charset); } /** * Adds a blind BCC recipient to the email using the specified address, personal name, and charset encoding for the name. * * @param email A String. * @param name A String. * @param charset The charset to encode the name with. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.1 */ public Email addBcc(final String email, final String name, final String charset) throws EmailException { bccList.add(createInternetAddress(email, name, charset)); return this; } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email addCc(final String email) throws EmailException { return addCc(email, null); } /** * 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 * {@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; * otherwise, it is used as is. * * @param emails A String array. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.3 */ public Email addCc(final String... emails) throws EmailException { EmailException.checkNonEmpty(emails, () -> "CC list invalid."); for (final String email : emails) { addCc(email, null); } return this; } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @param name A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email addCc(final String email, final String name) throws EmailException { return addCc(email, name, charset); } /** * Adds a recipient CC to the email using the specified address, personal name, and charset encoding for the name. * * @param email A String. * @param name A String. * @param charset The charset to encode the name with. * @return An Email. * @throws EmailException Indicates an invalid email address or charset. * @since 1.1 */ public Email addCc(final String email, final String name, final String charset) throws EmailException { ccList.add(createInternetAddress(email, name, charset)); return this; } /** * Adds a header ( name, value ) to the headers Map. * * @param name A String with the name. * @param value A String with the value. * @since 1.0 * @throws IllegalArgumentException if either {@code name} or {@code value} is null or empty */ public void addHeader(final String name, final String value) { if (EmailUtils.isEmpty(name)) { throw new IllegalArgumentException("name can not be null or empty"); } if (EmailUtils.isEmpty(value)) { throw new IllegalArgumentException("value can not be null or empty"); } headers.put(name, value); } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.0 */ public Email addReplyTo(final String email) throws EmailException { return addReplyTo(email, null); } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @param name A String. * @return An Email. * @throws EmailException Indicates an invalid email address * @since 1.0 */ public Email addReplyTo(final String email, final String name) throws EmailException { return addReplyTo(email, name, charset); } /** * Adds a reply to address to the email using the specified address, personal name, and charset encoding for the name. * * @param email A String. * @param name A String. * @param charset The charset to encode the name with. * @return An Email. * @throws EmailException Indicates an invalid email address or charset. * @since 1.1 */ public Email addReplyTo(final String email, final String name, final String charset) throws EmailException { replyList.add(createInternetAddress(email, name, charset)); return this; } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email addTo(final String email) throws EmailException { return addTo(email, null); } /** * 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 * {@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; * otherwise, it is used as is. * * @param emails A String array. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.3 */ public Email addTo(final String... emails) throws EmailException { EmailException.checkNonEmpty(emails, () -> "To list invalid."); for (final String email : emails) { addTo(email, null); } return this; } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @param name A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email addTo(final String email, final String name) throws EmailException { return addTo(email, name, charset); } /** * Adds a recipient TO to the email using the specified address, personal name, and charset encoding for the name. * * @param email A String. * @param name A String. * @param charset The charset to encode the name with. * @return An Email. * @throws EmailException Indicates an invalid email address or charset. * @since 1.1 */ public Email addTo(final String email, final String name, final String charset) throws EmailException { toList.add(createInternetAddress(email, name, charset)); return this; } /** * 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 * MimeMessage without commons-email. * * @throws IllegalStateException if the MimeMessage was already built * @throws EmailException if there was an error. * @since 1.0 */ public void buildMimeMessage() throws EmailException { if (message != null) { // [EMAIL-95] we assume that an email is not reused therefore invoking // buildMimeMessage() more than once is illegal. throw new IllegalStateException("The MimeMessage is already built."); } try { message = createMimeMessage(getMailSession()); if (EmailUtils.isNotEmpty(subject)) { if (EmailUtils.isNotEmpty(charset)) { message.setSubject(subject, charset); } else { message.setSubject(subject); } } // update content type (and encoding) updateContentType(contentType); if (content != null) { if (EmailConstants.TEXT_PLAIN.equalsIgnoreCase(contentType) && content instanceof String) { // EMAIL-104: call explicitly setText to use default mime charset // (property "mail.mime.charset") in case none has been set message.setText(content.toString(), charset); } else { message.setContent(content, contentType); } } else if (emailBody != null) { if (contentType == null) { message.setContent(emailBody); } else { message.setContent(emailBody, contentType); } } else { message.setText(""); } if (fromAddress != null) { message.setFrom(fromAddress); } else if (session.getProperty(EmailConstants.MAIL_SMTP_FROM) == null && session.getProperty(EmailConstants.MAIL_FROM) == null) { throw new EmailException("From address required"); } if (toList.size() + ccList.size() + bccList.size() == 0) { throw new EmailException("At least one receiver address required"); } if (!EmailUtils.isEmpty(toList)) { message.setRecipients(Message.RecipientType.TO, toInternetAddressArray(toList)); } if (!EmailUtils.isEmpty(ccList)) { message.setRecipients(Message.RecipientType.CC, toInternetAddressArray(ccList)); } if (!EmailUtils.isEmpty(bccList)) { message.setRecipients(Message.RecipientType.BCC, toInternetAddressArray(bccList)); } if (!EmailUtils.isEmpty(replyList)) { message.setReplyTo(toInternetAddressArray(replyList)); } if (!EmailUtils.isEmpty(headers)) { for (final Map.Entry entry : headers.entrySet()) { final String foldedValue = createFoldedHeaderValue(entry.getKey(), entry.getValue()); message.addHeader(entry.getKey(), foldedValue); } } if (message.getSentDate() == null) { message.setSentDate(getSentDate()); } if (popBeforeSmtp) { // TODO Why is this not a Store leak? When to close? final Store store = session.getStore("pop3"); store.connect(popHost, popUsername, popPassword); } } catch (final MessagingException e) { throw new EmailException(e); } } /** * When a mail session is already initialized setting the session properties has no effect. In order to flag the problem throw an IllegalStateException. * * @throws IllegalStateException when the mail session is already initialized */ private void checkSessionAlreadyInitialized() { if (session != null) { throw new IllegalStateException("The mail session is already initialized"); } } /** * Creates a folded header value containing 76 character chunks. * * @param name the name of the header * @param value the value of the header * @return the folded header value * @throws IllegalArgumentException if either the name or value is null or empty */ private String createFoldedHeaderValue(final String name, final String value) { if (EmailUtils.isEmpty(name)) { throw new IllegalArgumentException("name can not be null or empty"); } if (EmailUtils.isEmpty(value)) { throw new IllegalArgumentException("value can not be null or empty"); } try { return MimeUtility.fold(name.length() + 2, MimeUtility.encodeText(value, charset, null)); } catch (final UnsupportedEncodingException e) { return value; } } /** * Creates an InternetAddress. * * @param email An email address. * @param name A name. * @param charsetName The name of the charset to encode the name with. * @return An internet address. * @throws EmailException Thrown when the supplied address, name or charset were invalid. */ private InternetAddress createInternetAddress(final String email, final String name, final String charsetName) throws EmailException { try { final InternetAddress address = new InternetAddress(new IDNEmailAddressConverter().toASCII(email)); // check name input if (EmailUtils.isNotEmpty(name)) { // check charset input. if (EmailUtils.isEmpty(charsetName)) { address.setPersonal(name); } else { // canonicalize the charset name and make sure // the current platform supports it. final Charset set = Charset.forName(charsetName); address.setPersonal(name, set.name()); } } // run sanity check on new InternetAddress object; if this fails // it will throw AddressException. address.validate(); return address; } catch (final AddressException | UnsupportedEncodingException e) { throw new EmailException(e); } } /** * Creates a customized MimeMessage which can be implemented by a derived class, e.g. to set the message id. * * @param aSession mail session to be used * @return the newly created message */ protected MimeMessage createMimeMessage(final Session aSession) { return new MimeMessage(aSession); } /** * Gets the authenticator. * * @return the authenticator. * @since 1.6.0 */ public Authenticator getAuthenticator() { return authenticator; } /** * Gets the list of "Bcc" addresses. * * @return List addresses */ public List getBccAddresses() { return bccList; } /** * Gets the "bounce address" of this email. * * @return the bounce address as string * @since 1.4 */ public String getBounceAddress() { return bounceAddress; } /** * Gets the list of "CC" addresses. * * @return List addresses */ public List getCcAddresses() { return ccList; } /** * Gets the Charset. * * @return the Charset. * @since 1.6.0 */ public String getCharsetName() { return charset; } /** * Gets the content. * * @return the content. * @since 1.6.0 */ public Object getContent() { return content; } /** * Gets the content type. * * @return the content type. * @since 1.6.0 */ public String getContentType() { return contentType; } /** * Gets the email body. * * @return the email body. * @since 1.6.0 */ public MimeMultipart getEmailBody() { return emailBody; } /** * Gets the sender of the email. * * @return from address */ public InternetAddress getFromAddress() { return fromAddress; } /** * Gets the specified header. * * @param header A string with the header. * @return The value of the header, or null if no such header. * @since 1.5 */ public String getHeader(final String header) { return headers.get(header); } /** * Gets all headers on an Email. * * @return a Map of all headers. * @since 1.5 */ public Map getHeaders() { return headers; } /** * Gets the host name of the SMTP server, * * @return host name */ public String getHostName() { if (session != null) { return session.getProperty(EmailConstants.MAIL_HOST); } if (EmailUtils.isNotEmpty(hostName)) { return hostName; } return null; } /** * Gets the mail session used when sending this Email, creating the Session if necessary. When a mail session is already initialized setting the session * related properties will cause an IllegalStateException. * * @return A Session. * @throws EmailException if the host name was not set * @since 1.0 */ public Session getMailSession() throws EmailException { if (session == null) { final Properties properties = new Properties(System.getProperties()); properties.setProperty(EmailConstants.MAIL_TRANSPORT_PROTOCOL, EmailConstants.SMTP); if (EmailUtils.isEmpty(hostName)) { hostName = properties.getProperty(EmailConstants.MAIL_HOST); } EmailException.checkNonEmpty(hostName, () -> "Cannot find valid hostname for mail session"); properties.setProperty(EmailConstants.MAIL_PORT, smtpPort); properties.setProperty(EmailConstants.MAIL_HOST, hostName); properties.setProperty(EmailConstants.MAIL_DEBUG, String.valueOf(debug)); properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE, Boolean.toString(isStartTLSEnabled())); properties.setProperty(EmailConstants.MAIL_TRANSPORT_STARTTLS_REQUIRED, Boolean.toString(isStartTLSRequired())); properties.setProperty(EmailConstants.MAIL_SMTP_SEND_PARTIAL, Boolean.toString(isSendPartial())); properties.setProperty(EmailConstants.MAIL_SMTPS_SEND_PARTIAL, Boolean.toString(isSendPartial())); if (authenticator != null) { properties.setProperty(EmailConstants.MAIL_SMTP_AUTH, "true"); } if (isSSLOnConnect()) { properties.setProperty(EmailConstants.MAIL_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT, sslSmtpPort); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory"); properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_FALLBACK, "false"); } if ((isSSLOnConnect() || isStartTLSEnabled()) && isSSLCheckServerIdentity()) { properties.setProperty(EmailConstants.MAIL_SMTP_SSL_CHECKSERVERIDENTITY, "true"); } if (bounceAddress != null) { properties.setProperty(EmailConstants.MAIL_SMTP_FROM, bounceAddress); } if (socketTimeout > 0) { properties.setProperty(EmailConstants.MAIL_SMTP_TIMEOUT, Integer.toString(socketTimeout)); } if (socketConnectionTimeout > 0) { properties.setProperty(EmailConstants.MAIL_SMTP_CONNECTIONTIMEOUT, Integer.toString(socketConnectionTimeout)); } // changed this (back) to getInstance due to security exceptions // caused when testing using Maven session = Session.getInstance(properties, authenticator); } return session; } /** * Gets the message. * * @return the message. * @since 1.6.0 */ public MimeMessage getMessage() { return message; } /** * Gets the internal MimeMessage. Please note that the MimeMessage is built by the buildMimeMessage() method. * * @return the MimeMessage */ public MimeMessage getMimeMessage() { return message; } /** * Gets the POP3 host. * * @return the POP3 host. * @since 1.6.0 */ public String getPopHost() { return popHost; } /** * Gets the POP3 password. * * @return the POP3 password. * @since 1.6.0 */ public String getPopPassword() { return popPassword; } /** * Gets the POP3 user name. * * @return the POP3 user name. * @since 1.6.0 */ public String getPopUserName() { return popUsername; } /** * Gets the list of "Reply-To" addresses. * * @return List addresses */ public List getReplyToAddresses() { return replyList; } /** * Gets the sent date for the email. * * @return date to be used as the sent date for the email * @since 1.0 */ public Date getSentDate() { if (sentDate == null) { return new Date(); } return new Date(sentDate.getTime()); } /** * Gets the listening port of the SMTP server. * * @return SMTP port */ public String getSmtpPort() { if (session != null) { return session.getProperty(EmailConstants.MAIL_PORT); } if (EmailUtils.isNotEmpty(smtpPort)) { return smtpPort; } return null; } /** * Gets the socket connection timeout value in milliseconds. * * @return the timeout in milliseconds. * @since 1.2 */ public int getSocketConnectionTimeout() { return socketConnectionTimeout; } /** * Gets the socket I/O timeout value in milliseconds. * * @return the socket I/O timeout * @since 1.2 */ public int getSocketTimeout() { return socketTimeout; } /** * Gets the current SSL port used by the SMTP transport. * * @return the current SSL port used by the SMTP transport */ public String getSslSmtpPort() { if (session != null) { return session.getProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_PORT); } if (EmailUtils.isNotEmpty(sslSmtpPort)) { return sslSmtpPort; } return null; } /** * Gets the subject of the email. * * @return email subject */ public String getSubject() { return subject; } /** * Gets the list of "To" addresses. * * @return List addresses */ public List getToAddresses() { return toList; } /** * Tests whether debug is on. * * @return whether debug is on. * @since 1.6.0 */ public boolean isDebug() { return debug; } /** * Tests whether to use POP3 before SMTP, and if so the settings. * * @return whether to use POP3 before SMTP, and if so the settings. * @since 1.6.0 */ public boolean isPopBeforeSmtp() { return popBeforeSmtp; } /** * Tests whether partial sending of email is enabled. * * @return true if sending partial email is enabled. * @since 1.3.2 */ public boolean isSendPartial() { return sendPartial; } /** * Tests whether the server identity checked as specified by RFC 2595 * * @return true if the server identity is checked. * @since 1.3 */ public boolean isSSLCheckServerIdentity() { return sslCheckServerIdentity; } /** * Tests whether SSL/TLS encryption for the transport is currently enabled (SMTPS/POPS). * * @return true if SSL enabled for the transport. * @since 1.3 */ public boolean isSSLOnConnect() { return sslOnConnect || ssl; } /** * Tests whether the client is configured to try to enable STARTTLS. * * @return true if using STARTTLS for authentication, false otherwise. * @since 1.3 */ public boolean isStartTLSEnabled() { return startTlsEnabled || tls; } /** * Tests whether the client is configured to require STARTTLS. * * @return true if using STARTTLS for authentication, false otherwise. * @since 1.3 */ public boolean isStartTLSRequired() { return startTlsRequired; } /** * Sends the email. Internally we build a MimeMessage which is afterwards sent to the SMTP server. * * @return the message id of the underlying MimeMessage * @throws IllegalStateException if the MimeMessage was already built, that is, {@link #buildMimeMessage()} was already called * @throws EmailException the sending failed */ public String send() throws EmailException { buildMimeMessage(); return sendMimeMessage(); } /** * Sends the previously created MimeMessage to the SMTP server. * * @return the message id of the underlying MimeMessage * @throws IllegalArgumentException if the MimeMessage has not been created * @throws EmailException the sending failed */ public String sendMimeMessage() throws EmailException { Objects.requireNonNull(message, "MimeMessage has not been created yet"); try { Transport.send(message); return message.getMessageID(); } catch (final Throwable t) { throw new EmailException("Sending the email to the following server failed : " + this.getHostName() + ":" + getSmtpPort(), t); } } /** * Sets the userName and password if authentication is needed. If this method is not used, no authentication will be performed. *

* This method will create a new instance of {@code DefaultAuthenticator} using the supplied parameters. *

* * @param userName User name for the SMTP server * @param password password for the SMTP server * @see DefaultAuthenticator * @see #setAuthenticator * @since 1.0 */ public void setAuthentication(final String userName, final String password) { this.setAuthenticator(new DefaultAuthenticator(userName, password)); } /** * Sets the {@code Authenticator} to be used when authentication is requested from the mail server. *

* This method should be used when your outgoing mail server requires authentication. Your mail server must also support RFC2554. *

* * @param authenticator the {@code Authenticator} object. * @see Authenticator * @since 1.0 */ public void setAuthenticator(final Authenticator authenticator) { this.authenticator = authenticator; } /** * Sets a list of "BCC" addresses. All elements in the specified {@code Collection} are expected to be of type {@code java.mail.internet.InternetAddress}. * * @param collection collection of {@code InternetAddress} objects * @return An Email. * @throws EmailException Indicates an invalid email address * @see jakarta.mail.internet.InternetAddress * @since 1.0 */ public Email setBcc(final Collection collection) throws EmailException { EmailException.checkNonEmpty(collection, () -> "BCC list invalid"); bccList = new ArrayList<>(collection); return this; } /** * 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 * the address specified with the System property "mail.smtp.from", or if that value is not set, then to the "from" address. * * @param email A String. * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.0 */ public Email setBounceAddress(final String email) { checkSessionAlreadyInitialized(); if (!EmailUtils.isEmpty(email)) { try { bounceAddress = createInternetAddress(email, null, charset).getAddress(); } catch (final EmailException e) { // Can't throw 'EmailException' to keep backward-compatibility throw new IllegalArgumentException("Failed to set the bounce address : " + email, e); } } else { bounceAddress = email; } return this; } /** * Sets a list of "CC" addresses. All elements in the specified {@code Collection} are expected to be of type {@code java.mail.internet.InternetAddress}. * * @param collection collection of {@code InternetAddress} objects. * @return An Email. * @throws EmailException Indicates an invalid email address. * @see jakarta.mail.internet.InternetAddress * @since 1.0 */ public Email setCc(final Collection collection) throws EmailException { EmailException.checkNonEmpty(collection, () -> "CC list invalid"); ccList = new ArrayList<>(collection); return this; } /** * Sets the charset of the message. Please note that you should set the charset before adding the message content. * * @param charset A String. * @throws java.nio.charset.IllegalCharsetNameException if the charset name is invalid * @throws java.nio.charset.UnsupportedCharsetException if no support for the named charset exists in the current JVM * @since 1.0 */ public void setCharset(final String charset) { final Charset set = Charset.forName(charset); this.charset = set.name(); } /** * Sets the emailBody to a MimeMultiPart * * @param mimeMultipart aMimeMultipart * @since 1.0 */ public void setContent(final MimeMultipart mimeMultipart) { this.emailBody = mimeMultipart; } /** * Sets the content. * * @param content the content. * @return {@code this} instance. * @since 1.6.0 */ public Email setContent(final Object content) { this.content = content; return this; } /** * Sets the content and contentType. * * @param content content. * @param contentType content type. * @since 1.0 */ public void setContent(final Object content, final String contentType) { this.content = content; updateContentType(contentType); } /** * Sets the content type. * * @param contentType the content type. * @return {@code this} instance. * @since 1.6.0 */ public Email setContentType(final String contentType) { this.contentType = contentType; return this; } /** * Sets the display of debug information. * * @param debug A boolean. * @since 1.0 */ public void setDebug(final boolean debug) { this.debug = debug; } /** * 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 * 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 * characters; otherwise, it is used as is. * * @param email A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email setFrom(final String email) throws EmailException { return setFrom(email, null); } /** * 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 * {@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; * otherwise, it is used as is. * * @param email A String. * @param name A String. * @return An Email. * @throws EmailException Indicates an invalid email address. * @since 1.0 */ public Email setFrom(final String email, final String name) throws EmailException { return setFrom(email, name, charset); } /** * Sets the FROM field of the email to use the specified address, personal name, and charset encoding for the name. * * @param email A String. * @param name A String. * @param charset The charset to encode the name with. * @return An Email. * @throws EmailException Indicates an invalid email address or charset. * @since 1.1 */ public Email setFrom(final String email, final String name, final String charset) throws EmailException { fromAddress = createInternetAddress(email, name, charset); return this; } /** * Sets the From address. * * @param fromAddress the From address. * @return {@code this} instance. * @since 1.6.0 */ public Email setFromAddress(final InternetAddress fromAddress) { this.fromAddress = fromAddress; return this; } /** * Sets the mail headers. Example: * * X-Mailer: Sendmail, X-Priority: 1( highest ) or 2( high ) 3( normal ) 4( low ) and 5( lowest ) Disposition-Notification-To: [email protected] * * @param map A Map. * @throws IllegalArgumentException if either of the provided header / value is null or empty * @since 1.0 */ public void setHeaders(final Map map) { headers.clear(); for (final Map.Entry entry : map.entrySet()) { addHeader(entry.getKey(), entry.getValue()); } } /** * Sets the hostname of the outgoing mail server. * * @param hostName aHostName * @throws IllegalStateException if the mail session is already initialized * @since 1.0 */ public void setHostName(final String hostName) { checkSessionAlreadyInitialized(); this.hostName = hostName; } /** * 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 * with a DefaultAuthenticator. This is a convenience but might come unexpected. * * 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 * existing mail session (as expected). * * @param session mail session to be used * @throws NullPointerException if {@code aSession} is {@code null} * @since 1.0 */ public void setMailSession(final Session session) { Objects.requireNonNull(session, "no mail session supplied"); final Properties sessionProperties = session.getProperties(); final String auth = sessionProperties.getProperty(EmailConstants.MAIL_SMTP_AUTH); if (Boolean.parseBoolean(auth)) { final String userName = sessionProperties.getProperty(EmailConstants.MAIL_SMTP_USER); final String password = sessionProperties.getProperty(EmailConstants.MAIL_SMTP_PASSWORD); if (EmailUtils.isNotEmpty(userName) && EmailUtils.isNotEmpty(password)) { // only create a new mail session with an authenticator if // authentication is required and no user name is given authenticator = new DefaultAuthenticator(userName, password); this.session = Session.getInstance(sessionProperties, authenticator); } else { // assume that the given mail session contains a working authenticator this.session = session; } } else { this.session = session; } } /** * Sets a mail Session object from a JNDI directory. * * @param jndiName name of JNDI resource (jakarta.mail.Session type), resource if searched in java:comp/env if name does not start with "java:" * @throws IllegalArgumentException if the JNDI name is null or empty * @throws NamingException if the resource cannot be retrieved from JNDI directory * @since 1.1 */ public void setMailSessionFromJNDI(final String jndiName) throws NamingException { if (EmailUtils.isEmpty(jndiName)) { throw new IllegalArgumentException("JNDI name missing"); } Context ctx = null; if (jndiName.startsWith("java:")) { ctx = new InitialContext(); } else { ctx = (Context) new InitialContext().lookup("java:comp/env"); } setMailSession((Session) ctx.lookup(jndiName)); } /** * Sets the MIME message. * * @param message the MIME message. */ public void setMessage(final MimeMessage message) { this.message = message; } /** * Sets the content of the mail. It should be overridden by the subclasses. * * @param msg A String. * @return An Email. * @throws EmailException generic exception. * @since 1.0 */ public abstract Email setMsg(String msg) throws EmailException; /** * Sets whether to use POP3 before SMTP, and if so the settings. * * @param popBeforeSmtp whether to use POP3 before SMTP, and if so the settings. * @return {@code this} instance. * @since 1.6.0 */ public Email setPopBeforeSmtp(final boolean popBeforeSmtp) { this.popBeforeSmtp = popBeforeSmtp; return this; } /** * Sets details regarding "POP3 before SMTP" authentication. * * @param popBeforeSmtp Whether or not to log into POP3 server before sending mail. * @param popHost The POP3 host to use. * @param popUserName The POP3 user name. * @param popPassword The POP3 password. * @since 1.0 */ public void setPopBeforeSmtp(final boolean popBeforeSmtp, final String popHost, final String popUserName, final String popPassword) { this.popBeforeSmtp = popBeforeSmtp; this.popHost = popHost; this.popUsername = popUserName; this.popPassword = popPassword; } /** * Sets the POP3 host. * * @param popHost The POP3 host. * @return {@code this} instance. * @since 1.6.0 */ public Email setPopHost(final String popHost) { this.popHost = popHost; return this; } /** * Sets the POP3 password. * * @param popPassword the POP3 password. * @return {@code this} instance. * @since 1.6.0 */ public Email setPopPassword(final String popPassword) { this.popPassword = popPassword; return this; } /** * Sets the POP3 user name. * * @param popUserName the POP3 user name. * @return {@code this} instance. * @since 1.6.0 */ public Email setPopUsername(final String popUserName) { this.popUsername = popUserName; return this; } /** * Sets a list of reply to addresses. All elements in the specified {@code Collection} are expected to be of type * {@code java.mail.internet.InternetAddress}. * * @param collection collection of {@code InternetAddress} objects * @return An Email. * @throws EmailException Indicates an invalid email address * @see jakarta.mail.internet.InternetAddress * @since 1.1 */ public Email setReplyTo(final Collection collection) throws EmailException { EmailException.checkNonEmpty(collection, () -> "Reply to list invalid"); replyList = new ArrayList<>(collection); return this; } /** * Sets whether the email is partially send in case of invalid addresses. *

* 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 * send mode is enabled (emails to valid addresses will be transmitted). In case the email server does not reject invalid addresses immediately, but return * a bounce message, no exception will be thrown by the {@link #send()} method. *

* * @param sendPartial whether to enable partial send mode * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.3.2 */ public Email setSendPartial(final boolean sendPartial) { checkSessionAlreadyInitialized(); this.sendPartial = sendPartial; return this; } /** * Sets the sent date for the email. The sent date will default to the current date if not explicitly set. * * @param date Date to use as the sent date on the email * @since 1.0 */ public void setSentDate(final Date date) { if (date != null) { // create a separate instance to keep findbugs happy sentDate = new Date(date.getTime()); } } /** * Sets the non-SSL port number of the outgoing mail server. * * @param portNumber aPortNumber * @throws IllegalArgumentException if the port number is < 1 * @throws IllegalStateException if the mail session is already initialized * @since 1.0 * @see #setSslSmtpPort(String) */ public void setSmtpPort(final int portNumber) { checkSessionAlreadyInitialized(); if (portNumber < 1) { throw new IllegalArgumentException("Cannot connect to a port number that is less than 1 ( " + portNumber + " )"); } this.smtpPort = Integer.toString(portNumber); } /** * Sets the socket connection timeout value in milliseconds. Default is a 60 second timeout. * * @param socketConnectionTimeout the connection timeout * @throws IllegalStateException if the mail session is already initialized * @since 1.6.0 */ public void setSocketConnectionTimeout(final Duration socketConnectionTimeout) { checkSessionAlreadyInitialized(); this.socketConnectionTimeout = Math.toIntExact(socketConnectionTimeout.toMillis()); } /** * Sets the socket I/O timeout value in milliseconds. Default is 60 second timeout. * * @param socketTimeout the socket I/O timeout * @throws IllegalStateException if the mail session is already initialized * @since 1.6.0 */ public void setSocketTimeout(final Duration socketTimeout) { checkSessionAlreadyInitialized(); this.socketTimeout = Math.toIntExact(socketTimeout.toMillis()); } /** * Sets whether the server identity is checked as specified by RFC 2595 * * @param sslCheckServerIdentity whether to enable server identity check * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.3 */ public Email setSSLCheckServerIdentity(final boolean sslCheckServerIdentity) { checkSessionAlreadyInitialized(); this.sslCheckServerIdentity = sslCheckServerIdentity; return this; } /** * Sets whether SSL/TLS encryption should be enabled for the SMTP transport upon connection (SMTPS/POPS). Takes precedence over * {@link #setStartTLSRequired(boolean)} *

* Defaults to {@link #sslSmtpPort}; can be overridden by using {@link #setSslSmtpPort(String)} *

* * @param ssl whether to enable the SSL transport * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.3 */ public Email setSSLOnConnect(final boolean ssl) { checkSessionAlreadyInitialized(); this.sslOnConnect = ssl; this.ssl = ssl; return this; } /** * Sets the SSL port to use for the SMTP transport. Defaults to the standard port, 465. * * @param sslSmtpPort the SSL port to use for the SMTP transport * @throws IllegalStateException if the mail session is already initialized * @see #setSmtpPort(int) */ public void setSslSmtpPort(final String sslSmtpPort) { checkSessionAlreadyInitialized(); this.sslSmtpPort = sslSmtpPort; } /** * Sets or disable the STARTTLS encryption. * * @param startTlsEnabled true if STARTTLS requested, false otherwise * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.3 */ public Email setStartTLSEnabled(final boolean startTlsEnabled) { checkSessionAlreadyInitialized(); this.startTlsEnabled = startTlsEnabled; this.tls = startTlsEnabled; return this; } /** * Sets or disable the required STARTTLS encryption. *

* Defaults to {@link #smtpPort}; can be overridden by using {@link #setSmtpPort(int)} *

* * @param startTlsRequired true if STARTTLS requested, false otherwise * @return An Email. * @throws IllegalStateException if the mail session is already initialized * @since 1.3 */ public Email setStartTLSRequired(final boolean startTlsRequired) { checkSessionAlreadyInitialized(); this.startTlsRequired = startTlsRequired; return this; } /** * Sets the email subject. Replaces end-of-line characters with spaces. * * @param aSubject A String. * @return An Email. * @since 1.0 */ public Email setSubject(final String aSubject) { this.subject = EmailUtils.replaceEndOfLineCharactersWithSpaces(aSubject); return this; } /** * Sets a list of "TO" addresses. All elements in the specified {@code Collection} are expected to be of type {@code java.mail.internet.InternetAddress}. * * @param collection collection of {@code InternetAddress} objects. * @return An Email. * @throws EmailException Indicates an invalid email address. * @see jakarta.mail.internet.InternetAddress * @since 1.0 */ public Email setTo(final Collection collection) throws EmailException { EmailException.checkNonEmpty(collection, () -> "To list invalid"); this.toList = new ArrayList<>(collection); return this; } /** * Converts to copy List of known InternetAddress objects into an array. * * @param list A List. * @return An InternetAddress[]. * @since 1.0 */ protected InternetAddress[] toInternetAddressArray(final List list) { return list.toArray(EMPTY_INTERNET_ADDRESS_ARRAY); } /** * Updates the contentType. * * @param contentType aContentType * @since 1.2 */ public void updateContentType(final String contentType) { if (EmailUtils.isEmpty(contentType)) { this.contentType = null; } else { // set the content type this.contentType = contentType; // set the charset if the input was properly formed final String strMarker = "; charset="; int charsetPos = EmailUtils.toLower(contentType).indexOf(strMarker); if (charsetPos != -1) { // find the next space (after the marker) charsetPos += strMarker.length(); final int intCharsetEnd = EmailUtils.toLower(contentType).indexOf(" ", charsetPos); if (intCharsetEnd != -1) { this.charset = contentType.substring(charsetPos, intCharsetEnd); } else { this.charset = contentType.substring(charsetPos); } } else if (this.contentType.startsWith("text/") && EmailUtils.isNotEmpty(this.charset)) { // use the default charset, if one exists, for messages // whose content-type is some form of text. final StringBuilder contentTypeBuf = new StringBuilder(this.contentType); contentTypeBuf.append(strMarker); contentTypeBuf.append(this.charset); this.contentType = contentTypeBuf.toString(); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy