package org.simplejavamail.email;
import org.simplejavamail.internal.util.MiscUtil;
import javax.activation.DataSource;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.mail.Message;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.simplejavamail.internal.util.MiscUtil.extractEmailAddresses;
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_BCC_ADDRESS;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_BCC_NAME;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_CC_ADDRESS;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_CC_NAME;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_FROM_ADDRESS;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_FROM_NAME;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_REPLYTO_ADDRESS;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_REPLYTO_NAME;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_SUBJECT;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_TO_ADDRESS;
import static org.simplejavamail.util.ConfigLoader.Property.DEFAULT_TO_NAME;
import static org.simplejavamail.util.ConfigLoader.getProperty;
import static org.simplejavamail.util.ConfigLoader.hasProperty;
/**
* Fluent interface Builder for Emails
*
* @author Jared Stewart, Benny Bottema
*/
@SuppressWarnings("UnusedReturnValue")
public class EmailBuilder {
private Recipient fromRecipient;
/**
* The reply-to-address, optional. Can be used in conjunction with {@link #fromRecipient}.
*/
private Recipient replyToRecipient;
/**
* The email message body in plain text.
*/
private String text;
/**
* The email message body in html.
*/
private String textHTML;
/**
* The subject of the email message.
*/
private String subject;
/**
* List of {@link Recipient}.
*/
private final List<Recipient> recipients;
/**
* List of {@link AttachmentResource}.
*/
private final List<AttachmentResource> embeddedImages;
/**
* List of {@link AttachmentResource}.
*/
private final List<AttachmentResource> attachments;
/**
* Map of header name and values, such as <code>X-Priority</code> etc.
*/
private final Map<String, String> headers;
/**
* A file reference to the private key to be used for signing with DKIM.
*/
private File dkimPrivateKeyFile;
/**
* An input stream containg the private key data to be used for signing with DKIM.
*/
private InputStream dkimPrivateKeyInputStream;
/**
* The domain used for signing with DKIM.
*/
private String signingDomain;
/**
* The selector to be used in combination with the domain.
*/
private String selector;
public EmailBuilder() {
recipients = new ArrayList<>();
embeddedImages = new ArrayList<>();
attachments = new ArrayList<>();
headers = new HashMap<>();
if (hasProperty(DEFAULT_FROM_ADDRESS)) {
from((String) getProperty(DEFAULT_FROM_NAME), (String) getProperty(DEFAULT_FROM_ADDRESS));
}
if (hasProperty(DEFAULT_REPLYTO_ADDRESS)) {
replyTo((String) getProperty(DEFAULT_REPLYTO_NAME), (String) getProperty(DEFAULT_REPLYTO_ADDRESS));
}
if (hasProperty(DEFAULT_TO_ADDRESS)) {
if (hasProperty(DEFAULT_TO_NAME)) {
to((String) getProperty(DEFAULT_TO_NAME), (String) getProperty(DEFAULT_TO_ADDRESS));
} else {
to((String) getProperty(DEFAULT_TO_ADDRESS));
}
}
if (hasProperty(DEFAULT_CC_ADDRESS)) {
if (hasProperty(DEFAULT_CC_NAME)) {
cc((String) getProperty(DEFAULT_CC_NAME), (String) getProperty(DEFAULT_CC_ADDRESS));
} else {
cc((String) getProperty(DEFAULT_CC_ADDRESS));
}
}
if (hasProperty(DEFAULT_BCC_ADDRESS)) {
if (hasProperty(DEFAULT_BCC_NAME)) {
bcc((String) getProperty(DEFAULT_BCC_NAME), (String) getProperty(DEFAULT_BCC_ADDRESS));
} else {
bcc((String) getProperty(DEFAULT_BCC_ADDRESS));
}
}
if (hasProperty(DEFAULT_SUBJECT)) {
subject((String) getProperty(DEFAULT_SUBJECT));
}
}
/**
*
*/
public Email build() {
return new Email(this);
}
/**
* Sets the sender address {@link #fromRecipient}.
*
* @param name The sender's name.
* @param fromAddress The sender's email address.
*/
public EmailBuilder from(@Nullable final String name, @Nonnull final String fromAddress) {
checkNonEmptyArgument(fromAddress, "fromAddress");
this.fromRecipient = new Recipient(name, fromAddress, null);
return this;
}
/**
* Sets the sender address {@link #fromRecipient} with preconfigured {@link Recipient}.
*
* @param recipient Preconfigured recipient (name is optional).
*/
public EmailBuilder from(@Nonnull final Recipient recipient) {
checkNonEmptyArgument(recipient, "recipient");
this.fromRecipient = new Recipient(recipient.getName(), recipient.getAddress(), null);
return this;
}
/**
* Sets {@link #replyToRecipient} (optional).
*
* @param name The replied-to-receiver name.
* @param replyToAddress The replied-to-receiver email address.
*/
public EmailBuilder replyTo(@Nullable final String name, @Nonnull final String replyToAddress) {
checkNonEmptyArgument(replyToAddress, "replyToAddress");
this.replyToRecipient = new Recipient(name, replyToAddress, null);
return this;
}
/**
* Sets {@link #replyToRecipient} (optional) with preconfigured {@link Recipient}.
*
* @param recipient Preconfigured recipient (name is optional).
*/
public EmailBuilder replyTo(@Nonnull final Recipient recipient) {
checkNonEmptyArgument(recipient, "recipient");
this.replyToRecipient = new Recipient(recipient.getName(), recipient.getAddress(), null);
return this;
}
/**
* Sets the {@link #subject}.
*/
public EmailBuilder subject(@Nonnull final String subject) {
this.subject = checkNonEmptyArgument(subject, "subject");
return this;
}
/**
* Sets the {@link #text}.
*/
public EmailBuilder text(@Nullable final String text) {
this.text = text;
return this;
}
/**
* Sets the {@link #textHTML}.
*/
public EmailBuilder textHTML(@Nullable final String textHTML) {
this.textHTML = textHTML;
return this;
}
/**
* Adds a new {@link Recipient} to the list on account of name, address with recipient type {@link Message.RecipientType#TO}.
*
* @param name The name of the recipient.
* @param address The emailaddress of the recipient.
* @see #recipients
* @see Recipient
*/
public EmailBuilder to(@Nullable final String name, @Nonnull final String address) {
checkNonEmptyArgument(address, "address");
recipients.add(new Recipient(name, address, Message.RecipientType.TO));
return this;
}
/**
* Adds new {@link Recipient} instances to the list on account of name, address with recipient type {@link Message.RecipientType#TO}.
*
* @param recipientsToAdd The recipients whose name and address to use
* @see #recipients
* @see Recipient
*/
public EmailBuilder to(@Nonnull final Recipient... recipientsToAdd) {
for (final Recipient recipient : checkNonEmptyArgument(recipientsToAdd, "recipientsToAdd")) {
recipients.add(new Recipient(recipient.getName(), recipient.getAddress(), Message.RecipientType.TO));
}
return this;
}
/**
* Adds anew {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#TO}. List can be
* comma ',' or semicolon ';' separated.
*
* @param emailAddressList The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
public EmailBuilder to(@Nonnull final String emailAddressList) {
checkNonEmptyArgument(emailAddressList, "emailAddressList");
return addCommaOrSemicolonSeparatedEmailAddresses(emailAddressList, Message.RecipientType.TO);
}
@Nonnull
private EmailBuilder addCommaOrSemicolonSeparatedEmailAddresses(@Nonnull final String emailAddressList, @Nonnull final Message.RecipientType type) {
checkNonEmptyArgument(type, "type");
for (final String emailAddress : extractEmailAddresses(checkNonEmptyArgument(emailAddressList, "emailAddressList"))) {
recipients.add(new Recipient(null, emailAddress, type));
}
return this;
}
/**
* Adds new {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#TO}.
*
* @param emailAddresses The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
public EmailBuilder to(@Nonnull final String... emailAddresses) {
for (final String emailAddress : checkNonEmptyArgument(emailAddresses, "emailAddresses")) {
recipients.add(new Recipient(null, emailAddress, Message.RecipientType.TO));
}
return this;
}
/**
* Adds a new {@link Recipient} to the list on account of name, address with recipient type {@link Message.RecipientType#CC}.
*
* @param name The name of the recipient.
* @param address The emailaddress of the recipient.
* @see #recipients
* @see Recipient
*/
@SuppressWarnings({ "WeakerAccess", "QuestionableName" })
public EmailBuilder cc(@Nullable final String name, @Nonnull final String address) {
checkNonEmptyArgument(address, "address");
recipients.add(new Recipient(name, address, Message.RecipientType.CC));
return this;
}
/**
* Adds new {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#CC}.
*
* @param emailAddresses The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
@SuppressWarnings("QuestionableName")
public EmailBuilder cc(@Nonnull final String... emailAddresses) {
for (final String emailAddress : checkNonEmptyArgument(emailAddresses, "emailAddresses")) {
recipients.add(new Recipient(null, emailAddress, Message.RecipientType.CC));
}
return this;
}
/**
* Adds anew {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#CC}. List can be
* comma ',' or semicolon ';' separated.
*
* @param emailAddressList The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
@SuppressWarnings("QuestionableName")
public EmailBuilder cc(@Nonnull final String emailAddressList) {
checkNonEmptyArgument(emailAddressList, "emailAddressList");
return addCommaOrSemicolonSeparatedEmailAddresses(emailAddressList, Message.RecipientType.CC);
}
/**
* Adds new {@link Recipient} instances to the list on account of name, address with recipient type {@link Message.RecipientType#CC}.
*
* @param recipientsToAdd The recipients whose name and address to use
* @see #recipients
* @see Recipient
*/
@SuppressWarnings("QuestionableName")
public EmailBuilder cc(@Nonnull final Recipient... recipientsToAdd) {
for (final Recipient recipient : checkNonEmptyArgument(recipientsToAdd, "recipientsToAdd")) {
recipients.add(new Recipient(recipient.getName(), recipient.getAddress(), Message.RecipientType.CC));
}
return this;
}
/**
* Adds a new {@link Recipient} to the list on account of name, address with recipient type {@link Message.RecipientType#BCC}.
*
* @param name The name of the recipient.
* @param address The emailaddress of the recipient.
* @see #recipients
* @see Recipient
*/
@SuppressWarnings("WeakerAccess")
public EmailBuilder bcc(@Nullable final String name, @Nonnull final String address) {
checkNonEmptyArgument(address, "address");
recipients.add(new Recipient(name, address, Message.RecipientType.BCC));
return this;
}
/**
* Adds new {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#BCC}.
*
* @param emailAddresses The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
public EmailBuilder bcc(@Nonnull final String... emailAddresses) {
for (final String emailAddress : checkNonEmptyArgument(emailAddresses, "emailAddresses")) {
recipients.add(new Recipient(null, emailAddress, Message.RecipientType.BCC));
}
return this;
}
/**
* Adds anew {@link Recipient} instances to the list on account of empty name, address with recipient type {@link Message.RecipientType#BCC}. List can be
* comma ',' or semicolon ';' separated.
*
* @param emailAddressList The recipients whose address to use for both name and address
* @see #recipients
* @see Recipient
*/
public EmailBuilder bcc(@Nonnull final String emailAddressList) {
checkNonEmptyArgument(emailAddressList, "emailAddressList");
return addCommaOrSemicolonSeparatedEmailAddresses(emailAddressList, Message.RecipientType.BCC);
}
/**
* Adds new {@link Recipient} instances to the list on account of name, address with recipient type {@link Message.RecipientType#BCC}.
*
* @param recipientsToAdd The recipients whose name and address to use
* @see #recipients
* @see Recipient
*/
public EmailBuilder bcc(@Nonnull final Recipient... recipientsToAdd) {
for (final Recipient recipient : checkNonEmptyArgument(recipientsToAdd, "recipientsToAdd")) {
recipients.add(new Recipient(recipient.getName(), recipient.getAddress(), Message.RecipientType.BCC));
}
return this;
}
/**
* Adds an embedded image (attachment type) to the email message and generates the necessary {@link DataSource} with the given byte data. Then
* delegates to {@link Email#addEmbeddedImage(String, DataSource)}. At this point the datasource is actually a {@link ByteArrayDataSource}.
*
* @param name The name of the image as being referred to from the message content body (eg. 'signature').
* @param data The byte data of the image to be embedded.
* @param mimetype The content type of the given data (eg. "image/gif" or "image/jpeg").
* @see ByteArrayDataSource
* @see Email#addEmbeddedImage(String, DataSource)
*/
public EmailBuilder embedImage(@Nonnull final String name, @Nonnull final byte[] data, @Nonnull final String mimetype) {
checkNonEmptyArgument(name, "name");
checkNonEmptyArgument(data, "data");
checkNonEmptyArgument(mimetype, "mimetype");
final ByteArrayDataSource dataSource = new ByteArrayDataSource(data, mimetype);
dataSource.setName(name);
return embedImage(name, dataSource);
}
/**
* Overloaded method which sets an embedded image on account of name and {@link DataSource}.
*
* @param name The name of the image as being referred to from the message content body (eg. 'embeddedimage'). If not provided, the name of the given
* data source is used instead.
* @param imagedata The image data.
*/
@SuppressWarnings("WeakerAccess")
public EmailBuilder embedImage(@Nullable final String name, @Nonnull final DataSource imagedata) {
checkNonEmptyArgument(imagedata, "imagedata");
if (valueNullOrEmpty(name) && valueNullOrEmpty(imagedata.getName())) {
throw new EmailException(EmailException.NAME_MISSING_FOR_EMBEDDED_IMAGE);
}
embeddedImages.add(new AttachmentResource(name, imagedata));
return this;
}
/**
* Adds a header to the {@link #headers} list. The value is stored as a <code>String</code>. example: <code>email.addHeader("X-Priority",
* 2)</code>
*
* @param name The name of the header.
* @param value The value of the header, which will be stored using {@link String#valueOf(Object)}.
*/
public EmailBuilder addHeader(@Nonnull final String name, @Nonnull final Object value) {
checkNonEmptyArgument(name, "name");
checkNonEmptyArgument(value, "value");
headers.put(name, String.valueOf(value));
return this;
}
/**
* Adds an attachment to the email message and generates the necessary {@link DataSource} with the given byte data. Then delegates to {@link
* #addAttachment(String, DataSource)}. At this point the datasource is actually a {@link ByteArrayDataSource}.
*
* @param name The name of the extension (eg. filename including extension).
* @param data The byte data of the attachment.
* @param mimetype The content type of the given data (eg. "plain/text", "image/gif" or "application/pdf").
* @see ByteArrayDataSource
* @see #addAttachment(String, DataSource)
*/
public EmailBuilder addAttachment(@Nullable final String name, @Nonnull final byte[] data, @Nonnull final String mimetype) {
checkNonEmptyArgument(data, "data");
checkNonEmptyArgument(mimetype, "mimetype");
final ByteArrayDataSource dataSource = new ByteArrayDataSource(data, mimetype);
dataSource.setName(MiscUtil.encodeText(name));
addAttachment(MiscUtil.encodeText(name), dataSource);
return this;
}
/**
* Overloaded method which sets an attachment on account of name and {@link DataSource}.
*
* @param name The name of the attachment (eg. 'filename.ext').
* @param filedata The attachment data.
*/
public EmailBuilder addAttachment(@Nullable final String name, @Nonnull final DataSource filedata) {
checkNonEmptyArgument(filedata, "filedata");
attachments.add(new AttachmentResource(MiscUtil.encodeText(name), filedata));
return this;
}
/**
* Sets all info needed for DKIM, using a byte array for private key data.
*/
public EmailBuilder signWithDomainKey(@Nonnull final byte[] dkimPrivateKey, @Nonnull final String signingDomain, @Nonnull final String selector) {
this.dkimPrivateKeyInputStream = new ByteArrayInputStream(checkNonEmptyArgument(dkimPrivateKey, "dkimPrivateKey"));
this.signingDomain = checkNonEmptyArgument(signingDomain, "signingDomain");
this.selector = checkNonEmptyArgument(selector, "selector");
return this;
}
/**
* Sets all info needed for DKIM, using a byte array for private key data.
*/
public EmailBuilder signWithDomainKey(@Nonnull final String dkimPrivateKey, @Nonnull final String signingDomain, @Nonnull final String selector) {
checkNonEmptyArgument(dkimPrivateKey, "dkimPrivateKey");
this.dkimPrivateKeyInputStream = new ByteArrayInputStream(dkimPrivateKey.getBytes(UTF_8));
this.signingDomain = checkNonEmptyArgument(signingDomain, "signingDomain");
this.selector = checkNonEmptyArgument(selector, "selector");
return this;
}
/**
* Sets all info needed for DKIM, using a file reference for private key data.
*/
public EmailBuilder signWithDomainKey(@Nonnull final File dkimPrivateKeyFile, @Nonnull final String signingDomain, @Nonnull final String selector) {
this.dkimPrivateKeyFile = checkNonEmptyArgument(dkimPrivateKeyFile, "dkimPrivateKeyFile");
this.signingDomain = checkNonEmptyArgument(signingDomain, "signingDomain");
this.selector = checkNonEmptyArgument(selector, "selector");
return this;
}
/**
* Sets all info needed for DKIM, using an input stream for private key data.
*/
public EmailBuilder signWithDomainKey(@Nonnull final InputStream dkimPrivateKeyInputStream, @Nonnull final String signingDomain,
@Nonnull final String selector) {
this.dkimPrivateKeyInputStream = checkNonEmptyArgument(dkimPrivateKeyInputStream, "dkimPrivateKeyInputStream");
this.signingDomain = checkNonEmptyArgument(signingDomain, "signingDomain");
this.selector = checkNonEmptyArgument(selector, "selector");
return this;
}
/*
SETTERS / GETTERS
*/
public Recipient getFromRecipient() {
return fromRecipient;
}
public Recipient getReplyToRecipient() {
return replyToRecipient;
}
public String getText() {
return text;
}
public String getTextHTML() {
return textHTML;
}
public String getSubject() {
return subject;
}
public List<Recipient> getRecipients() {
return recipients;
}
public List<AttachmentResource> getEmbeddedImages() {
return embeddedImages;
}
public List<AttachmentResource> getAttachments() {
return attachments;
}
public Map<String, String> getHeaders() {
return headers;
}
public File getDkimPrivateKeyFile() {
return dkimPrivateKeyFile;
}
public InputStream getDkimPrivateKeyInputStream() {
return dkimPrivateKeyInputStream;
}
public String getSigningDomain() {
return signingDomain;
}
public String getSelector() {
return selector;
}
}