org.simplejavamail.mailer.internal.EmailGovernanceImpl Maven / Gradle / Ivy
/*
* Copyright © 2009 Benny Bottema ([email protected])
*
* Licensed 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.simplejavamail.mailer.internal;
import com.sanctionco.jmail.EmailValidator;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.ToString;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simplejavamail.api.email.CalendarMethod;
import org.simplejavamail.api.email.ContentTransferEncoding;
import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.email.EmailPopulatingBuilder;
import org.simplejavamail.api.email.Recipient;
import org.simplejavamail.api.email.config.DkimConfig;
import org.simplejavamail.api.email.config.SmimeEncryptionConfig;
import org.simplejavamail.api.email.config.SmimeSigningConfig;
import org.simplejavamail.api.mailer.MailerGenericBuilder;
import org.simplejavamail.api.mailer.config.EmailGovernance;
import org.simplejavamail.api.mailer.config.Pkcs12Config;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.email.internal.InternalEmail;
import org.simplejavamail.internal.config.EmailProperty;
import java.io.File;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static java.lang.Boolean.TRUE;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_BCC_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_BCC_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_BOUNCETO_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_BOUNCETO_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_CC_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_CC_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_CONTENT_TRANSFER_ENCODING;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_FROM_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_FROM_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_REPLYTO_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_REPLYTO_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_SUBJECT;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_TO_ADDRESS;
import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_TO_NAME;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_EXCLUDED_HEADERS_FROM_DEFAULT_SIGNING_LIST;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_PRIVATE_KEY_FILE_OR_DATA;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SELECTOR;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SIGNING_ALGORITHM;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SIGNING_BODY_CANONICALIZATION;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SIGNING_DOMAIN;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SIGNING_HEADER_CANONICALIZATION;
import static org.simplejavamail.config.ConfigLoader.Property.DKIM_SIGNING_USE_LENGTH_PARAM;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_ENCRYPTION_CERTIFICATE;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_ENCRYPTION_CIPHER;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_ENCRYPTION_KEY_ENCAPSULATION_ALGORITHM;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_ALGORITHM;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE_PASSWORD;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_ALIAS;
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_PASSWORD;
import static org.simplejavamail.config.ConfigLoader.getBooleanProperty;
import static org.simplejavamail.config.ConfigLoader.getProperty;
import static org.simplejavamail.config.ConfigLoader.getStringProperty;
import static org.simplejavamail.config.ConfigLoader.hasProperty;
import static org.simplejavamail.internal.util.MiscUtil.overrideAndOrProvideAndOrDefaultCollection;
import static org.simplejavamail.internal.util.MiscUtil.overrideAndOrProvideAndOrDefaultHeaders;
import static org.simplejavamail.internal.util.MiscUtil.overrideOrProvideOrDefaultProperty;
import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument;
import static org.simplejavamail.internal.util.Preconditions.verifyNonnullOrEmpty;
/**
* Governance for all emails being sent through the current {@link org.simplejavamail.api.mailer.Mailer} instance. That is, this class represents actions
* taken or configuration used by default for each individual email sent through the current mailer. For example, you might want to S/MIME sign all emails
* by default. You can do it manually on each email of course, but then the keystore used for this is not reused.
*
* Also, you can supply a custom {@link Email email} instance which will be used for defaults or overrides. For example,
* you can set a default from address or subject. Any fields that are not set on the email will be taken from the defaults (properties). Any fields that are set on the
* email will be used instead of the defaults.
*/
@ToString
@Getter
public class EmailGovernanceImpl implements EmailGovernance {
// for internal convenience in junit tests
public static EmailGovernance NO_GOVERNANCE() {
return new EmailGovernanceImpl(null, null, null, null);
}
/**
* The effective email validator used for email validation. Can be null
if no validation should be done.
* @see MailerGenericBuilder#withEmailValidator(EmailValidator)
* @see EmailValidator
*/
@Nullable private final EmailValidator emailValidator;
/**
* Reference email used for defaults if no fields are not filled in the email but are on this instance.
* Can be null
if no defaults should be used.
* @see MailerGenericBuilder#withEmailDefaults(Email)
*/
@Getter(AccessLevel.NONE)
@NotNull private final Email emailDefaults;
/**
* Reference email used for overrides. Values from this email will trump the incoming email.
* Can be null
if no overrides should be used.
* @see MailerGenericBuilder#withEmailOverrides(Email)
*/
@Getter(AccessLevel.NONE)
@NotNull private final Email emailOverrides;
/**
* Determines at what size Simple Java Mail should reject a MimeMessage. Useful if you know your SMTP server has a limit.
* @see MailerGenericBuilder#withMaximumEmailSize(int)
*/
@Nullable private final Integer maximumEmailSize;
public EmailGovernanceImpl(@Nullable EmailValidator emailValidator, @Nullable Email emailDefaults, @Nullable Email emailOverrides, @Nullable Integer maximumEmailSize) {
this.emailValidator = emailValidator;
this.emailDefaults = emailDefaults != null ? emailDefaults : newDefaultsEmailWithDefaultDefaults();
this.emailOverrides = emailOverrides != null ? emailOverrides : EmailBuilder.startingBlank().buildEmail();
this.maximumEmailSize = maximumEmailSize;
}
// FIXME default notificationTo is missing
// The name is a bit cryptic, but succinct (and it's only used internally)
private Email newDefaultsEmailWithDefaultDefaults() {
final EmailPopulatingBuilder allDefaults = EmailBuilder.startingBlank();
if (hasProperty(DEFAULT_FROM_ADDRESS)) {
allDefaults.from(getStringProperty(DEFAULT_FROM_NAME), verifyNonnullOrEmpty(getStringProperty(DEFAULT_FROM_ADDRESS)));
}
if (hasProperty(DEFAULT_REPLYTO_ADDRESS)) {
allDefaults.withReplyTo(getStringProperty(DEFAULT_REPLYTO_NAME), verifyNonnullOrEmpty(getStringProperty(DEFAULT_REPLYTO_ADDRESS)));
}
if (hasProperty(DEFAULT_BOUNCETO_ADDRESS)) {
allDefaults.withBounceTo(getStringProperty(DEFAULT_BOUNCETO_NAME), verifyNonnullOrEmpty(getStringProperty(DEFAULT_BOUNCETO_ADDRESS)));
}
if (hasProperty(DEFAULT_TO_ADDRESS)) {
if (hasProperty(DEFAULT_TO_NAME)) {
allDefaults.to(getStringProperty(DEFAULT_TO_NAME), getStringProperty(DEFAULT_TO_ADDRESS));
} else {
allDefaults.to(verifyNonnullOrEmpty(getStringProperty(DEFAULT_TO_ADDRESS)));
}
}
if (hasProperty(DEFAULT_CC_ADDRESS)) {
if (hasProperty(DEFAULT_CC_NAME)) {
allDefaults.cc(getStringProperty(DEFAULT_CC_NAME), getStringProperty(DEFAULT_CC_ADDRESS));
} else {
allDefaults.cc(verifyNonnullOrEmpty(getStringProperty(DEFAULT_CC_ADDRESS)));
}
}
if (hasProperty(DEFAULT_BCC_ADDRESS)) {
if (hasProperty(DEFAULT_BCC_NAME)) {
allDefaults.bcc(getStringProperty(DEFAULT_BCC_NAME), getStringProperty(DEFAULT_BCC_ADDRESS));
} else {
allDefaults.bcc(verifyNonnullOrEmpty(getStringProperty(DEFAULT_BCC_ADDRESS)));
}
}
if (hasProperty(DEFAULT_CONTENT_TRANSFER_ENCODING)) {
allDefaults.withContentTransferEncoding(verifyNonnullOrEmpty(getProperty(DEFAULT_CONTENT_TRANSFER_ENCODING)));
}
if (hasProperty(DEFAULT_SUBJECT)) {
allDefaults.withSubject(getProperty(DEFAULT_SUBJECT));
}
if (allDefaults.getSmimeSignedEmail() == null && hasProperty(SMIME_SIGNING_KEYSTORE)) {
allDefaults.signWithSmime(SmimeSigningConfig.builder()
.pkcs12Config(Pkcs12Config.builder()
.pkcs12Store(verifyNonnullOrEmpty(getStringProperty(SMIME_SIGNING_KEYSTORE)))
.storePassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEYSTORE_PASSWORD), "Keystore password property"))
.keyAlias(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_ALIAS), "Key alias property"))
.keyPassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_PASSWORD), "Key password property"))
.build())
.signatureAlgorithm(hasProperty(SMIME_SIGNING_ALGORITHM) ? getStringProperty(SMIME_SIGNING_ALGORITHM) : null)
.build());
}
if (allDefaults.getSmimeEncryptionConfig() == null && hasProperty(SMIME_ENCRYPTION_CERTIFICATE)) {
allDefaults.encryptWithSmime(SmimeEncryptionConfig.builder()
.x509Certificate(verifyNonnullOrEmpty(getStringProperty(SMIME_ENCRYPTION_CERTIFICATE)))
.keyEncapsulationAlgorithm(hasProperty(SMIME_ENCRYPTION_KEY_ENCAPSULATION_ALGORITHM) ? getStringProperty(SMIME_ENCRYPTION_KEY_ENCAPSULATION_ALGORITHM) : null)
.cipherAlgorithm(hasProperty(SMIME_ENCRYPTION_CIPHER) ? getStringProperty(SMIME_ENCRYPTION_CIPHER) : null)
.build());
}
if (allDefaults.getDkimConfig() == null && hasProperty(DKIM_PRIVATE_KEY_FILE_OR_DATA)) {
val dkimConfigBuilder = DkimConfig.builder()
.dkimSelector(verifyNonnullOrEmpty(getStringProperty(DKIM_SELECTOR)))
.dkimSigningDomain(verifyNonnullOrEmpty(getStringProperty(DKIM_SIGNING_DOMAIN)))
.useLengthParam(hasProperty(DKIM_SIGNING_USE_LENGTH_PARAM) ? getBooleanProperty(DKIM_SIGNING_USE_LENGTH_PARAM) : null)
.excludedHeadersFromDkimDefaultSigningList(verifyNonnullOrEmpty(getStringProperty(DKIM_EXCLUDED_HEADERS_FROM_DEFAULT_SIGNING_LIST)))
.headerCanonicalization(hasProperty(DKIM_SIGNING_HEADER_CANONICALIZATION) ? getProperty(DKIM_SIGNING_HEADER_CANONICALIZATION) : null)
.bodyCanonicalization(hasProperty(DKIM_SIGNING_BODY_CANONICALIZATION) ? getProperty(DKIM_SIGNING_BODY_CANONICALIZATION) : null)
.signingAlgorithm(hasProperty(DKIM_SIGNING_ALGORITHM) ? getStringProperty(DKIM_SIGNING_ALGORITHM) : null);
val dkimPrivateKeyFileOrData = verifyNonnullOrEmpty(getStringProperty(DKIM_PRIVATE_KEY_FILE_OR_DATA));
if (new File(dkimPrivateKeyFileOrData).exists()) {
dkimConfigBuilder.dkimPrivateKeyPath(dkimPrivateKeyFileOrData);
} else {
dkimConfigBuilder.dkimPrivateKeyData(dkimPrivateKeyFileOrData);
}
allDefaults.signWithDomainKey(dkimConfigBuilder.build());
}
return allDefaults.buildEmail();
}
@NotNull
public Email produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided) {
val builder = (provided == null || provided.getEmailToForward() == null)
? EmailBuilder.startingBlank()
: EmailBuilder.forwarding(provided.getEmailToForward());
final Recipient fromRecipient = resolveEmailProperty(provided, EmailProperty.FROM_RECIPIENT);
final List replyToRecipients = resolveEmailCollectionProperty(provided, EmailProperty.REPLYTO_RECIPIENT);
ofNullable(fromRecipient).ifPresent(builder::from);
builder.withReplyTo(replyToRecipients);
builder.to(resolveEmailCollectionProperty(provided, EmailProperty.TO_RECIPIENTS));
builder.cc(resolveEmailCollectionProperty(provided, EmailProperty.CC_RECIPIENTS));
builder.bcc(resolveEmailCollectionProperty(provided, EmailProperty.BCC_RECIPIENTS));
builder.withSubject(resolveEmailProperty(provided, EmailProperty.SUBJECT));
builder.withPlainText(this.resolveEmailProperty(provided, EmailProperty.BODY_TEXT));
builder.withHTMLText(this.resolveEmailProperty(provided, EmailProperty.BODY_HTML));
val calendarText = this.resolveEmailProperty(provided, EmailProperty.CALENDAR_TEXT);
if (calendarText != null) {
val calendarMethod = this.resolveEmailProperty(provided, EmailProperty.CALENDAR_METHOD);
builder.withCalendarText(requireNonNull(calendarMethod, "calendarMethod"), calendarText);
}
builder.withHeaders(resolveEmailHeadersProperty(provided));
builder.withAttachments(resolveEmailCollectionProperty(provided, EmailProperty.ATTACHMENTS));
builder.withEmbeddedImages(resolveEmailCollectionProperty(provided, EmailProperty.EMBEDDED_IMAGES));
val useReturnReceiptTo = resolveEmailProperty(provided, EmailProperty.USE_RETURN_RECEIPT_TO);
if (TRUE.equals(useReturnReceiptTo)) {
Recipient returnReceiptToRecipient = resolveEmailProperty(provided, EmailProperty.RETURN_RECEIPT_TO);
if (returnReceiptToRecipient != null) {
builder.withReturnReceiptTo(returnReceiptToRecipient);
} else if (!replyToRecipients.isEmpty()) {
builder.withReturnReceiptTo(replyToRecipients.get(0));
} else if (fromRecipient != null) {
builder.withReturnReceiptTo(fromRecipient);
} else {
builder.withReturnReceiptTo();
}
}
val useDispositionNotificationTo = resolveEmailProperty(provided, EmailProperty.USE_DISPOSITION_NOTIFICATION_TO);
if (TRUE.equals(useDispositionNotificationTo)) {
Recipient dispositionNotificationToRecipient = resolveEmailProperty(provided, EmailProperty.DISPOSITION_NOTIFICATION_TO);
if (dispositionNotificationToRecipient != null) {
builder.withDispositionNotificationTo(dispositionNotificationToRecipient);
} else if (!replyToRecipients.isEmpty()) {
builder.withDispositionNotificationTo(replyToRecipients.get(0));
} else if (fromRecipient != null) {
builder.withDispositionNotificationTo(fromRecipient);
} else {
builder.withDispositionNotificationTo();
}
}
val overrideReceivers = this.resolveEmailCollectionProperty(provided, EmailProperty.OVERRIDE_RECEIVERS);
if (!overrideReceivers.isEmpty()) {
builder.withOverrideReceivers(overrideReceivers);
}
ofNullable(this.resolveEmailProperty(provided, EmailProperty.CONTENT_TRANSFER_ENCODING)).ifPresent(builder::withContentTransferEncoding);
ofNullable(this.resolveEmailProperty(provided, EmailProperty.SMIME_SIGNING_CONFIG)).ifPresent(builder::signWithSmime);
ofNullable(this.resolveEmailProperty(provided, EmailProperty.SMIME_ENCRYPTION_CONFIG)).ifPresent(builder::encryptWithSmime);
ofNullable(this.resolveEmailProperty(provided, EmailProperty.DKIM_SIGNING_CONFIG)).ifPresent(builder::signWithDomainKey);
builder.withBounceTo(this.resolveEmailProperty(provided, EmailProperty.BOUNCETO_RECIPIENT));
ofNullable(this.resolveEmailProperty(provided, EmailProperty.SENT_DATE)).ifPresent(builder::fixingSentDate);
builder.fixingMessageId(resolveEmailProperty(provided, EmailProperty.ID));
val email = builder.buildEmail();
// we need to update the user's email instance with the generated ID when sending
if (provided != null) {
//noinspection deprecation
((InternalEmail) email).setUserProvidedEmail(provided);
}
//noinspection deprecation
((InternalEmail) email).markAsDefaultsAndOverridesApplied();
return email;
}
@Nullable
private T resolveEmailProperty(@Nullable Email email, @NotNull EmailProperty emailProperty) {
return overrideOrProvideOrDefaultProperty(email, emailDefaults, emailOverrides, emailProperty);
}
@NotNull
private List resolveEmailCollectionProperty(@Nullable Email email, @NotNull EmailProperty emailProperty) {
return overrideAndOrProvideAndOrDefaultCollection(email, emailDefaults, emailOverrides, emailProperty);
}
@NotNull
private Map> resolveEmailHeadersProperty(@Nullable Email email) {
return overrideAndOrProvideAndOrDefaultHeaders(email, emailDefaults, emailOverrides);
}
}