From eaa767778fa97f4f889de23c884af9abdba2d93c Mon Sep 17 00:00:00 2001 From: bbottema Date: Sun, 24 Dec 2017 14:12:11 +0100 Subject: [PATCH] #115: overhauled how Mailers are created and initialized. Everything now works exclusively through builders, including property defaults. --- .../org/simplejavamail/mailer/Mailer.java | 333 ++--------- .../simplejavamail/mailer/MailerBuilder.java | 284 +++++++++ .../mailer/MailerFromSessionBuilder.java | 51 ++ .../mailer/MailerGenericBuiler.java | 551 ++++++++++++++++++ .../simplejavamail/mailer/ServerConfig.java | 54 ++ .../mailer/config/ProxyConfig.java | 141 ----- .../mailer/config/ServerConfig.java | 96 --- .../internal/mailsender/MailSender.java | 183 ++---- .../mailsender/OperationalConfig.java | 73 +++ .../internal/mailsender/ProxyConfig.java | 75 +++ .../internal/socks/SocksProxyConfig.java | 35 +- .../SimpleJavaMailSpringSupport.java | 3 +- src/test/java/demo/MailTestDemoApp.java | 33 +- .../mailer/MailerBuilderProxyConfigTest.java | 97 +++ .../simplejavamail/mailer/MailerLiveTest.java | 8 +- .../org/simplejavamail/mailer/MailerTest.java | 106 ++-- .../mailer/ProxyConfigTest.java | 168 ------ .../mailer/ServerConfigTest.java | 84 --- .../internal/mailsender/MailSenderTest.java | 61 +- .../testutil/testrules/TestSmtpServer.java | 25 +- 20 files changed, 1435 insertions(+), 1026 deletions(-) create mode 100644 src/main/java/org/simplejavamail/mailer/MailerBuilder.java create mode 100644 src/main/java/org/simplejavamail/mailer/MailerFromSessionBuilder.java create mode 100644 src/main/java/org/simplejavamail/mailer/MailerGenericBuiler.java create mode 100644 src/main/java/org/simplejavamail/mailer/ServerConfig.java delete mode 100644 src/main/java/org/simplejavamail/mailer/config/ProxyConfig.java delete mode 100644 src/main/java/org/simplejavamail/mailer/config/ServerConfig.java create mode 100644 src/main/java/org/simplejavamail/mailer/internal/mailsender/OperationalConfig.java create mode 100644 src/main/java/org/simplejavamail/mailer/internal/mailsender/ProxyConfig.java create mode 100644 src/test/java/org/simplejavamail/mailer/MailerBuilderProxyConfigTest.java delete mode 100644 src/test/java/org/simplejavamail/mailer/ProxyConfigTest.java diff --git a/src/main/java/org/simplejavamail/mailer/Mailer.java b/src/main/java/org/simplejavamail/mailer/Mailer.java index 77f24c2e8..64ffdfcd7 100644 --- a/src/main/java/org/simplejavamail/mailer/Mailer.java +++ b/src/main/java/org/simplejavamail/mailer/Mailer.java @@ -7,16 +7,16 @@ import org.simplejavamail.email.AttachmentResource; import org.simplejavamail.email.Email; import org.simplejavamail.email.Recipient; -import org.simplejavamail.mailer.config.ProxyConfig; -import org.simplejavamail.mailer.config.ServerConfig; +import org.simplejavamail.mailer.MailerBuilder.MailerRegularBuilder; import org.simplejavamail.mailer.config.TransportStrategy; import org.simplejavamail.mailer.internal.mailsender.MailSender; -import org.simplejavamail.util.ConfigLoader.Property; +import org.simplejavamail.mailer.internal.mailsender.OperationalConfig; +import org.simplejavamail.mailer.internal.mailsender.ProxyConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import javax.mail.Authenticator; -import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.internet.MimeMessage; @@ -25,14 +25,8 @@ import java.util.Properties; import static java.lang.String.format; -import static org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria.RFC_COMPLIANT; import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; import static org.simplejavamail.mailer.config.TransportStrategy.findStrategyForSession; -import static org.simplejavamail.util.ConfigLoader.Property.JAVAXMAIL_DEBUG; -import static org.simplejavamail.util.ConfigLoader.Property.TRANSPORT_STRATEGY; -import static org.simplejavamail.util.ConfigLoader.getProperty; -import static org.simplejavamail.util.ConfigLoader.hasProperty; -import static org.simplejavamail.util.ConfigLoader.valueOrProperty; /** * Mailing tool aimed for simplicity, for sending e-mails of any complexity. This includes e-mails with plain text and/or html content, embedded @@ -46,7 +40,6 @@ * applications. *

* Technically, the resulting email structure is as follows:
- *

*

  * - root
  * 	- related
@@ -56,9 +49,8 @@
  * 		- embedded images
  * 	- attachments
  * 
- *

- *
Usage example:
- *

+ *

+ * Usage example:
*

  * Email email = new Email();
  * email.setFromAddress("lollypop", "lolly.pop@somemail.com");
@@ -72,13 +64,11 @@
  * 
*

* simplejavamail.org - *

- *


+ *
*

* On a technical note, the {@link Mailer} class is the front facade for the public API. It limits itself to creating Session objects, offering * various constructors, sorting missing arguments using available properties and finally email validation. The actual sending and proxy configuration - * is done by the internal {@link MailSender}. Some internal api is made public through this class for uses other than directly sending emails, such - * as {@link #setDebug(boolean)} and {@link #signMessageWithDKIM(MimeMessage, Email)}. + * is done by the internal {@link MailSender}. * * @author Benny Bottema * @see MimeMessageHelper.MimeEmailMessageWrapper @@ -86,157 +76,37 @@ */ @SuppressWarnings("WeakerAccess") public class Mailer { - + private static final Logger LOGGER = LoggerFactory.getLogger(Mailer.class); - + private final MailSender mailSender; - - /** - * Email address restriction flags set to {@link EmailAddressCriteria#RFC_COMPLIANT} or overridden by by user with {@link - * #setEmailAddressCriteria(EnumSet)}. - */ - private EnumSet emailAddressCriteria = RFC_COMPLIANT; - - /** - * Custom Session constructor, stores the given mail session for later use. Assumes that *all* properties used to make a connection are configured - * (host, port, authentication and transport protocol settings). Will skip proxy. - *

- * Uses {@link TransportStrategy#findStrategyForSession} to determine the right connection mode based on already available properties in the - * {@link Session}. - * - * @param session A preconfigured mail {@link Session} object with which a {@link Message} can be produced. - * @see #Mailer(Session, ProxyConfig) - */ - public Mailer(final Session session) { - this(session, new ProxyConfig()); - } - - /** - * Custom Session constructor with proxy, stores the given mail session for later use. Assumes that *all* properties (except session - * timeouts) used to make a connection are configured (host, port, authentication and transport protocol settings). - *

- * Only proxy settings are always added if details are provided. - *

- * Also set javax.mail debug mode if a config file was provided for this. Uses {@link TransportStrategy#findStrategyForSession} to determine the right - * connection mode based on already available properties in the {@link Session}. - * - * @param session A preconfigured mail {@link Session} object with which a {@link Message} can be produced. - * @param proxyConfig Remote proxy server details, if the connection should be run through a SOCKS proxy. - */ - @SuppressWarnings("SameParameterValue") - public Mailer(final Session session, final ProxyConfig proxyConfig) { - if (hasProperty(JAVAXMAIL_DEBUG)) { - setDebug((Boolean) getProperty(JAVAXMAIL_DEBUG)); - } - this.mailSender = new MailSender(session, proxyConfig, findStrategyForSession(session)); - } - - /** - * No-arg constructor that only works with properly populated config file ("simplejavamail.properties") on the classpath. - *

- * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and populates as much as possible from the config file (smtp server - * details, proxy details, transport strategy) and otherwise defaults to {@link TransportStrategy#SMTP} and skipping proxy. - * - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer() { - this(new ServerConfig(null, null, null, null), null, null); - } - - /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and populates as much as possible from the config file (proxy details, - * transport strategy) and otherwise defaults to {@link TransportStrategy#SMTP} and skipping proxy. - * - * @param host The address URL of the SMTP server to be used. - * @param port The port of the SMTP server. - * @param username An optional username, may be null. - * @param password An optional password, may be null, but only if username is null as well. - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer(final String host, final Integer port, final String username, final String password) { - this(new ServerConfig(host, port, username, password), null, null); - } - - /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and populates as much as possible from the config file (proxy details, - * transport strategy) and otherwise defaults to {@link TransportStrategy#SMTP} and skipping proxy. - * - * @param serverConfig Remote SMTP server details. - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer(final ServerConfig serverConfig) { - this(serverConfig, null, null); - } - - /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and tries to populates proxy details from config file and otherwise skips - * proxy. - * - * @param host The address URL of the SMTP server to be used. - * @param port The port of the SMTP server. - * @param username An optional username, may be null. - * @param password An optional password, may be null, but only if username is null as well. - * @param transportStrategy The transport protocol configuration type for handling SSL or TLS (or vanilla SMTP) - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer(final String host, final Integer port, final String username, final String password, final TransportStrategy transportStrategy) { - this(new ServerConfig(host, port, username, password), transportStrategy, null); - } /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and tries to populates server config and proxy details from config - * file and otherwise skips proxy. + * See {@link MailerGenericBuiler#withEmailAddressCriteria(EnumSet)}. * - * @param transportStrategy The transport protocol configuration type for handling SSL or TLS (or vanilla SMTP) - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) + * @see EmailAddressCriteria */ - public Mailer(final TransportStrategy transportStrategy) { - this(null, transportStrategy, null); - } + private final EnumSet emailAddressCriteria; - /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and tries to populates proxy details from config file and otherwise skips - * proxy. - * - * @param serverConfig Remote SMTP server details. - * @param transportStrategy The transport protocol configuration type for handling SSL or TLS (or vanilla SMTP) - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer(final ServerConfig serverConfig, final TransportStrategy transportStrategy) { - this(serverConfig, transportStrategy, null); + Mailer(@Nonnull final MailerFromSessionBuilder fromSessionBuilder) { + emailAddressCriteria = fromSessionBuilder.getEmailAddressCriteria(); + final Session session = fromSessionBuilder.getSession(); + final ProxyConfig proxyConfig = fromSessionBuilder.buildProxyConfig(); + this.mailSender = initFromGenericBuilder(findStrategyForSession(session), proxyConfig, session, fromSessionBuilder); } - - /** - * Delegates to {@link #Mailer(ServerConfig, TransportStrategy, ProxyConfig)} and tries to populates transport strategy from config file and otherwise - * defaults to {@link TransportStrategy#SMTP}. - * - * @param serverConfig Remote SMTP server details. - * @param proxyConfig Remote proxy server details, if the connection should be run through a SOCKS proxy. - * @see #Mailer(ServerConfig, TransportStrategy, ProxyConfig) - */ - public Mailer(final ServerConfig serverConfig, final ProxyConfig proxyConfig) { - this(serverConfig, null, proxyConfig); + + Mailer(@Nonnull final MailerRegularBuilder regularBuilder) { + emailAddressCriteria = regularBuilder.getEmailAddressCriteria(); + final TransportStrategy transportStrategy = regularBuilder.getTransportStrategy(); + final ServerConfig serverConfig = regularBuilder.buildServerConfig(); + final ProxyConfig proxyConfig = regularBuilder.buildProxyConfig(); + final Session session = createMailSession(serverConfig, transportStrategy); + this.mailSender = initFromGenericBuilder(transportStrategy, proxyConfig, session, regularBuilder); } - - /** - * Main constructor which produces a new {@link Session} on the fly. Use this if you don't have a mail session configured in your web container, - * or Spring context etc. - *

- * Also sets javax.mail debug mode if a config file was provided for this. - * - * @param serverConfig Remote SMTP server details. - * @param transportStrategy The transport protocol configuration type for handling SSL or TLS (or vanilla SMTP) - * @param proxyConfig Remote proxy server details, if the connection should be run through a SOCKS proxy. - */ - public Mailer(final ServerConfig serverConfig, final TransportStrategy transportStrategy, final ProxyConfig proxyConfig) { - final TransportStrategy effectiveTransportStrategy = valueOrProperty(transportStrategy, TRANSPORT_STRATEGY, TransportStrategy.SMTP); - final Session session = createMailSession(serverConfig, effectiveTransportStrategy); - this.mailSender = new MailSender(session, proxyConfig, effectiveTransportStrategy); - this.emailAddressCriteria = null; - - if (hasProperty(JAVAXMAIL_DEBUG)) { - setDebug((Boolean) getProperty(JAVAXMAIL_DEBUG)); - } + + private MailSender initFromGenericBuilder(@Nonnull TransportStrategy transportStrategy, @Nonnull ProxyConfig proxyConfig, @Nonnull Session session, @Nonnull final MailerGenericBuiler genericBuiler) { + OperationalConfig operationalConfig = genericBuiler.buildOperationalConfig(); + return new MailSender(session, operationalConfig, proxyConfig, transportStrategy); } /** @@ -251,6 +121,7 @@ public Mailer(final ServerConfig serverConfig, final TransportStrategy transport * @param transportStrategy The transport protocol strategy enum that actually handles the session configuration. Session configuration meaning * setting the right properties for the appropriate transport type (ie. "mail.smtp.host" for SMTP, * "mail.smtps.host" for SMTPS). + * * @return A fully configured Session instance complete with transport protocol settings. * @see TransportStrategy#generateProperties() * @see TransportStrategy#propertyNameHost() @@ -259,25 +130,23 @@ public Mailer(final ServerConfig serverConfig, final TransportStrategy transport * @see TransportStrategy#propertyNameAuthenticate() */ @SuppressWarnings("WeakerAccess") - public static Session createMailSession(final ServerConfig serverConfig, final TransportStrategy transportStrategy) { - final ServerConfig effectiveServerConfig = serverConfig != null ? serverConfig : new ServerConfig(null, null, null, null); - + public static Session createMailSession(@Nonnull final ServerConfig serverConfig, @Nonnull final TransportStrategy transportStrategy) { final Properties props = transportStrategy.generateProperties(); - props.put(transportStrategy.propertyNameHost(), effectiveServerConfig.getHost()); - props.put(transportStrategy.propertyNamePort(), String.valueOf(effectiveServerConfig.getPort())); - - if (effectiveServerConfig.getUsername() != null) { - props.put(transportStrategy.propertyNameUsername(), effectiveServerConfig.getUsername()); + props.put(transportStrategy.propertyNameHost(), serverConfig.getHost()); + props.put(transportStrategy.propertyNamePort(), String.valueOf(serverConfig.getPort())); + + if (serverConfig.getUsername() != null) { + props.put(transportStrategy.propertyNameUsername(), serverConfig.getUsername()); } - - if (effectiveServerConfig.getPassword() != null) { + + if (serverConfig.getPassword() != null) { props.put(transportStrategy.propertyNameAuthenticate(), "true"); - return Session.getInstance(props, new SmtpAuthenticator(effectiveServerConfig)); + return Session.getInstance(props, new SmtpAuthenticator(serverConfig)); } else { return Session.getInstance(props); } } - + /** * In case Simple Java Mail falls short somehow, you can get a hold of the internal {@link Session} instance to debug or tweak. Please let us know * why you are needing this on https://github.com/bbottema/simple-java-mail/issues. @@ -287,62 +156,12 @@ public Session getSession() { LOGGER.warn("\t\t> https://github.com/bbottema/simple-java-mail/issues"); return mailSender.getSession(); } - - /** - * Calls {@link Session#setDebug(boolean)} so that it generates debug information. To get more information out of the underlying JavaMail - * framework or out of Simple Java Mail, increase logging config of your chosen logging framework (examples here). - * - * @param debug Flag to indicate debug mode yes/no. - * @see Property#JAVAXMAIL_DEBUG - */ - public void setDebug(final boolean debug) { - mailSender.setDebug(debug); - } - - /** - * Sets the transport mode for this mail sender to logging only, which means no mail will be actually sent out. - * - * @param transportModeLoggingOnly Flag to indicate logging mode yes/no. - */ - public synchronized void setTransportModeLoggingOnly(final boolean transportModeLoggingOnly) { - mailSender.setTransportModeLoggingOnly(transportModeLoggingOnly); - } - - /** - * @return Whether this Mailer is set to only log or also actually send emails through an SMTP server (which is the default). - */ - public boolean isTransportModeLoggingOnly() { - return mailSender.isTransportModeLoggingOnly(); - } - + /** - * Configures the current session to trust all hosts and don't validate any SSL keys. The property "mail.smtp(s).ssl.trust" is set to "*". - *

- * Refer to https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html#mail.smtp.ssl.trust + * @return The operational parameters defined using a mailer builder. Includes general things like session timeouts, debug mode, SSL config etc. */ - public void trustAllSSLHosts(final boolean trustAllHosts) { - mailSender.trustAllHosts(trustAllHosts); - } - - /** - * Configures the current session to only accept server certificates issued to one of the provided hostnames, - * and disables certificate issuer validation. - *

- * Passing an empty list resets the current session's trust behavior to the default, and is equivalent to never - * calling this method in the first place. - *

- * Security warning: Any certificate matching any of the provided host names will be accepted, - * regardless of the certificate issuer; attackers can abuse this behavior by serving a matching self-signed - * certificate during a man-in-the-middle attack. - *

- * This method sets the property {@code mail.smtp.ssl.trust} to a space-separated list of the provided - * {@code hosts}. If the provided list is empty, {@code mail.smtp.ssl.trust} is unset. - * - * @see mail.smtp.ssl.trust - */ - public void trustSSLHosts(final String... hosts) { - mailSender.trustHosts(hosts); + public OperationalConfig getOperationalConfig() { + return mailSender.getOperationalConfig(); } /** @@ -354,31 +173,6 @@ public void testConnection() { mailSender.testConnection(); } - /** - * Copies all property entries into the {@link Session} using {@link Session#getProperties()}. - * - * @param properties The source properties to add or override in the internal {@link Session} instance. - */ - public void applyProperties(final Properties properties) { - mailSender.applyProperties(properties); - } - - /** - * @param poolSize The maximum number of threads when sending emails in async fashion. - * @see Property#DEFAULT_POOL_SIZE - */ - public void setThreadPoolSize(final int poolSize) { - mailSender.setThreadPoolSize(poolSize); - } - - /** - * @param sessionTimeout The timeout to use when sending emails (affects socket connect-, read- and write timeouts). - * @see Property#DEFAULT_SESSION_TIMEOUT_MILLIS - */ - public void setSessionTimeout(final int sessionTimeout) { - mailSender.setSessionTimeout(sessionTimeout); - } - /** * Delegates to {@link #sendMail(Email, boolean)}, with async = false. This method returns only when the email has been processed by * the target SMTP server. @@ -386,7 +180,7 @@ public void setSessionTimeout(final int sessionTimeout) { public final void sendMail(final Email email) { sendMail(email, false); } - + /** * @see MailSender#send(Email, boolean) * @see #validate(Email) @@ -402,20 +196,21 @@ public final synchronized void sendMail(final Email email, @SuppressWarnings("Sa * the addresses are missing for NPM notification flags. *

* It also checks for illegal characters that would facilitate injection attacks: - * + *

*

* * @param email The email that needs to be configured correctly. + * * @return Always true (throws a {@link MailException} exception if validation fails). * @throws MailException Is being thrown in any of the above causes. * @see EmailAddressValidator */ - @SuppressWarnings({ "SameReturnValue", "WeakerAccess" }) + @SuppressWarnings({"SameReturnValue", "WeakerAccess"}) public boolean validate(final Email email) throws MailException { // check for mandatory values @@ -485,7 +280,7 @@ public boolean validate(final Email email) } /** - * @param value Value checked for suspicious newline characters "\n", "\r" and "%0A" (as acknowledged by SMTP servers). + * @param value Value checked for suspicious newline characters "\n", "\r" and "%0A" (as acknowledged by SMTP servers). * @param valueLabel The name of the field being checked, used for reporting exceptions. */ private static void scanForInjectionAttack(final String value, final String valueLabel) { @@ -493,32 +288,24 @@ private static void scanForInjectionAttack(final String value, final String valu throw new MailerException(format(MailerException.INJECTION_SUSPECTED, valueLabel, value)); } } - + /** * Refer to {@link MimeMessageHelper#signMessageWithDKIM(MimeMessage, Email)} */ - public static MimeMessage signMessageWithDKIM(final MimeMessage message, final Email email) { - return MimeMessageHelper.signMessageWithDKIM(message, email); + public static MimeMessage signMessageWithDKIM(final MimeMessage messageToSign, final Email emailContainingSigningDetails) { + return MimeMessageHelper.signMessageWithDKIM(messageToSign, emailContainingSigningDetails); } - - /** - * Overrides the default email address validation restrictions {@link #emailAddressCriteria} when validating and sending emails using the current - * Mailer instance. - */ - public void setEmailAddressCriteria(final EnumSet emailAddressCriteria) { - this.emailAddressCriteria = emailAddressCriteria; - } - + /** * Simple Authenticator used to create a {@link Session} object with in {@link #createMailSession(ServerConfig, TransportStrategy)}. */ private static class SmtpAuthenticator extends Authenticator { private final ServerConfig serverConfig; - + public SmtpAuthenticator(final ServerConfig serverConfig) { this.serverConfig = serverConfig; } - + @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(serverConfig.getUsername(), serverConfig.getPassword()); diff --git a/src/main/java/org/simplejavamail/mailer/MailerBuilder.java b/src/main/java/org/simplejavamail/mailer/MailerBuilder.java new file mode 100644 index 000000000..3a2259836 --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/MailerBuilder.java @@ -0,0 +1,284 @@ +package org.simplejavamail.mailer; + +import org.simplejavamail.mailer.config.TransportStrategy; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.mail.Session; + +import static org.simplejavamail.util.ConfigLoader.Property.SMTP_HOST; +import static org.simplejavamail.util.ConfigLoader.Property.SMTP_PASSWORD; +import static org.simplejavamail.util.ConfigLoader.Property.SMTP_PORT; +import static org.simplejavamail.util.ConfigLoader.Property.SMTP_USERNAME; +import static org.simplejavamail.util.ConfigLoader.Property.TRANSPORT_STRATEGY; +import static org.simplejavamail.util.ConfigLoader.getProperty; +import static org.simplejavamail.util.ConfigLoader.hasProperty; + +/** + * Entry builder used to start a {@link MailerGenericBuiler} and fully configure a Mailer. + *

+ * Any of these methods return a specialized builder, of which there are two: + *

+ * + * @see MailerFromSessionBuilder + * @see MailerRegularBuilder + */ +public class MailerBuilder { + + /** + * Delegates to {@link MailerFromSessionBuilder#usingSession(Session)}. + */ + public static MailerFromSessionBuilder usingSession(@Nonnull final Session session) { + return new MailerFromSessionBuilder().usingSession(session); + } + + /** + * Delegates to {@link MailerRegularBuilder#withTransportStrategy(TransportStrategy)}. + */ + public static MailerRegularBuilder withTransportStrategy(@Nonnull final TransportStrategy transportStrategy) { + return new MailerRegularBuilder().withTransportStrategy(transportStrategy); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServer(ServerConfig)}. + */ + public static MailerRegularBuilder withSMTPServer(@Nonnull final ServerConfig serverConfig) { + return new MailerRegularBuilder().withSMTPServer(serverConfig); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServer(String, Integer, String, String)}. + */ + public static MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port, @Nullable final String username, @Nullable final String password) { + return new MailerRegularBuilder().withSMTPServer(host, port, username, password); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServer(String, Integer, String)}. + */ + public static MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port, @Nullable final String username) { + return new MailerRegularBuilder().withSMTPServer(host, port, username); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServer(String, Integer)}. + */ + public static MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port) { + return new MailerRegularBuilder().withSMTPServer(host, port); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServerHost(String)}. + */ + public static MailerRegularBuilder withSMTPServerHost(@Nullable final String host) { + return new MailerRegularBuilder().withSMTPServerHost(host); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServerPort(Integer)}. + */ + public static MailerRegularBuilder withSMTPServerPort(@Nullable final Integer port) { + return new MailerRegularBuilder().withSMTPServerPort(port); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServerUsername(String)}. + */ + public static MailerRegularBuilder withSMTPServerUsername(@Nullable final String username) { + return new MailerRegularBuilder().withSMTPServerUsername(username); + } + + /** + * Delegates to {@link MailerRegularBuilder#withSMTPServerPassword(String)}. + */ + public static MailerRegularBuilder withSMTPServerPassword(@Nullable final String password) { + return new MailerRegularBuilder().withSMTPServerPassword(password); + } + + /** + * Delegates to {@link MailerRegularBuilder#withDebugLogging(Boolean)} + *

+ * Note: Assumes you don't want to use your own {@link Session} object (otherwise start with {@link #usingSession(Session)} + * instead). + */ + public static MailerRegularBuilder withDebugLogging(Boolean debugLogging) { + return new MailerRegularBuilder().withDebugLogging(debugLogging); + } + + /** + * Shortcuts to {@link MailerRegularBuilder#buildMailer()}. This means that none of the builder methods are used and the configuration completely + * depends on defaults being configured from property file ("simplejavamail.properties") on the classpath or through programmatic defaults. + */ + public static Mailer buildMailer() { + return new MailerRegularBuilder().buildMailer(); + } + + /** + * Default builder for generating Mailer instances. + *

+ * In addition on generic Mailer setting, this builder is used to configure SMTP server details and transport strategy needed to produce a valid + * {@link Session} instance. + * + * @see TransportStrategy + */ + public static class MailerRegularBuilder extends MailerGenericBuiler { + + private String host; + private Integer port; + private String username; + private String password; + private TransportStrategy transportStrategy; + + /** + * Sets defaults configured for SMTP host, SMTP port, SMTP username, SMTP password and transport strategy. + *

+ * Note: Any builder methods invoked after this will override the default value. + */ + MailerRegularBuilder() { + if (hasProperty(SMTP_HOST)) { + withSMTPServerHost((String) getProperty(SMTP_HOST)); + } + if (hasProperty(SMTP_PORT)) { + withSMTPServerPort((Integer) getProperty(SMTP_PORT)); + } + if (hasProperty(SMTP_USERNAME)) { + withSMTPServerUsername((String) getProperty(SMTP_USERNAME)); + } + if (hasProperty(SMTP_PASSWORD)) { + withSMTPServerPassword((String) getProperty(SMTP_PASSWORD)); + } + withTransportStrategy(TransportStrategy.SMTP); + if (hasProperty(TRANSPORT_STRATEGY)) { + withTransportStrategy((TransportStrategy) getProperty(TRANSPORT_STRATEGY)); + } + } + + /** + * Sets the optional transport strategy of this mailer. Will default to {@link TransportStrategy#SMTP} is left empty. + */ + public MailerRegularBuilder withTransportStrategy(@Nonnull final TransportStrategy transportStrategy) { + this.transportStrategy = transportStrategy; + return this; + } + + /** + * @param host Delegates to {@link #withSMTPServerHost(String)}. + * @param port Delegates to {@link #withSMTPServerPort(Integer)}. + * @param username Delegates to {@link #withSMTPServerUsername(String)}. + * @param password Delegates to {@link #withSMTPServerPassword(String)}. + */ + public MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port, @Nullable final String username, @Nullable final String password) { + return withSMTPServerHost(host) + .withSMTPServerPort(port) + .withSMTPServerUsername(username) + .withSMTPServerPassword(password); + } + + /** + * @param host Delegates to {@link #withSMTPServerHost(String)}. + * @param port Delegates to {@link #withSMTPServerPort(Integer)}. + * @param username Delegates to {@link #withSMTPServerUsername(String)}. + */ + public MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port, @Nullable final String username) { + return withSMTPServerHost(host) + .withSMTPServerPort(port) + .withSMTPServerUsername(username); + } + + /** + * @param host Delegates to {@link #withSMTPServerHost(String)}. + * @param port Delegates to {@link #withSMTPServerPort(Integer)}. + */ + public MailerRegularBuilder withSMTPServer(@Nullable final String host, @Nullable final Integer port) { + return withSMTPServerHost(host) + .withSMTPServerPort(port); + } + + /** + * Delegates for each contained property respectively to: + *

+ */ + public MailerRegularBuilder withSMTPServer(@Nonnull final ServerConfig serverConfig) { + return withSMTPServerHost(serverConfig.getHost()) + .withSMTPServerPort(serverConfig.getPort()) + .withSMTPServerUsername(serverConfig.getUsername()) + .withSMTPServerPassword(serverConfig.getPassword()); + } + + /** + * Sets the optional SMTP host. Will default to pre-configured property if left empty. + */ + public MailerRegularBuilder withSMTPServerHost(@Nullable final String host) { + this.host = host; + return this; + } + + /** + * Sets the optional SMTP port. Will default to pre-configured property if left empty. + */ + public MailerRegularBuilder withSMTPServerPort(@Nullable final Integer port) { + this.port = port; + return this; + } + + /** + * Sets the optional SMTP username. Will default to pre-configured property if left empty. + */ + public MailerRegularBuilder withSMTPServerUsername(@Nullable final String username) { + this.username = username; + return this; + } + + /** + * Sets the optional SMTP password. Will default to pre-configured property if left empty. + */ + public MailerRegularBuilder withSMTPServerPassword(@Nullable final String password) { + this.password = password; + return this; + } + + /** + * Builds the actual {@link Mailer} instance with everything configured on this builder instance. + *

+ * For all configurable values: if omitted, a default value will be attempted by looking at property files or manually defined defauls. + */ + public Mailer buildMailer() { + return new Mailer(this); + } + + /** + * For internal use. + */ + ServerConfig buildServerConfig() { + return new ServerConfig(getHost(), getPort(), getUsername(), getPassword()); + } + + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public TransportStrategy getTransportStrategy() { + return transportStrategy; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/MailerFromSessionBuilder.java b/src/main/java/org/simplejavamail/mailer/MailerFromSessionBuilder.java new file mode 100644 index 000000000..5b265ef77 --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/MailerFromSessionBuilder.java @@ -0,0 +1,51 @@ +package org.simplejavamail.mailer; + +import javax.annotation.Nonnull; +import javax.mail.Message; +import javax.mail.Session; + +/** + * Builder that supports a fixed {@link Session} instance. Allows configuring all generic Mailer settings, but not SMTP and transport strategy + * details. + *

+ * Note: Any SMTP server properties that can be set on the {@link Session} object by are presumed to be already present in the passed + * {@link Session} instance. + * + * @see org.simplejavamail.mailer.config.TransportStrategy + */ +public class MailerFromSessionBuilder extends MailerGenericBuiler { + + /** + * @see #usingSession(Session) + */ + private Session session; + + /** + * Only use this API if you must use your own {@link Session} instance. Assumes that all properties (except session timeout) used to make + * a connection are configured (host, port, authentication and transport protocol settings). + *

+ * Only proxy can be configured optionally and general connection settings. + * + * @param session A mostly preconfigured mail {@link Session} object with which a {@link Message} can be produced. + */ + public MailerFromSessionBuilder usingSession(@Nonnull final Session session) { + this.session = session; + return this; + } + + /** + * Builds the actual {@link Mailer} instance with everything configured on this builder instance. + *

+ * For all configurable values: if omitted, a default value will be attempted by looking at property files or manually defined defauls. + */ + public Mailer buildMailer() { + return new Mailer(this); + } + + /** + * @see #usingSession(Session) + */ + public Session getSession() { + return session; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/MailerGenericBuiler.java b/src/main/java/org/simplejavamail/mailer/MailerGenericBuiler.java new file mode 100644 index 000000000..912052892 --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/MailerGenericBuiler.java @@ -0,0 +1,551 @@ +package org.simplejavamail.mailer; + +import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.simplejavamail.mailer.internal.mailsender.OperationalConfig; +import org.simplejavamail.mailer.internal.mailsender.ProxyConfig; +import org.simplejavamail.util.ConfigLoader; +import org.simplejavamail.util.ConfigLoader.Property; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.mail.Session; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty; +import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; +import static org.simplejavamail.util.ConfigLoader.Property.PROXY_HOST; +import static org.simplejavamail.util.ConfigLoader.Property.PROXY_PASSWORD; +import static org.simplejavamail.util.ConfigLoader.Property.PROXY_PORT; +import static org.simplejavamail.util.ConfigLoader.Property.PROXY_USERNAME; +import static org.simplejavamail.util.ConfigLoader.getProperty; +import static org.simplejavamail.util.ConfigLoader.hasProperty; + +/** + * Builder API that takes care of all generic Mailer properties unrelated to the SMTP server (host, port, username, password and transport strategy). + *

+ * To start a new Mailer builder, refer to {@link MailerBuilder}. + */ +public abstract class MailerGenericBuiler { + + /** + * The default maximum timeout value for the transport socket is {@value #DEFAULT_SESSION_TIMEOUT_MILLIS} milliseconds (affects socket connect-, + * read- and write timeouts). Can be overridden from a config file or through System variable. + */ + @SuppressWarnings("JavaDoc") + public static final int DEFAULT_SESSION_TIMEOUT_MILLIS = 60_000; + + /** + * For multi-threaded scenario's where a batch of emails sent asynchronously, the default maximum number of threads is {@value + * #DEFAULT_POOL_SIZE}. Can be overridden from a config file or through System variable. + */ + @SuppressWarnings("JavaDoc") + public static final int DEFAULT_POOL_SIZE = 10; + + /** + * The temporary intermediary SOCKS5 relay server bridge is a server that sits in between JavaMail and the remote proxy. Default port is {@value + * #DEFAULT_PROXY_BRIDGE_PORT}. + */ + @SuppressWarnings("JavaDoc") + public static final int DEFAULT_PROXY_BRIDGE_PORT = 1081; + + /** + * Defaults to {@value DEFAULT_TRANSPORT_MODE_LOGGING_ONLY}, sending mails rather than just only logging the mails. + */ + public static final boolean DEFAULT_TRANSPORT_MODE_LOGGING_ONLY = false; + + /** + * @see #withProxyHost(String) + */ + private String proxyHost; + + /** + * @see #withProxyPort(Integer) + */ + private Integer proxyPort; + + /** + * @see #withProxyUsername(String) + */ + private String proxyUsername; + + /** + * @see #withProxyPassword(String) + */ + private String proxyPassword; + + /** + * @see #withProxyBridgePort(Integer) + */ + private Integer proxyBridgePort; + + /** + * @see #withDebugLogging(Boolean) + */ + private Boolean debugLogging; + + /** + * @see #withSessionTimeout(Integer) + */ + private Integer sessionTimeout; + + /** + * @see #withEmailAddressCriteria(EnumSet) + */ + private EnumSet emailAddressCriteria; + + /** + * @see #withThreadPoolSize(Integer) + */ + private Integer threadPoolSize; + + /** + * @see #trustingSSLHosts(String...) + */ + private List sslHostsToTrust = new ArrayList<>(); + + /** + * @see #trustingAllHosts(Boolean) + */ + private Boolean trustAllSSLHost; + + /** + * @see #withProperties(Properties) + */ + private Properties properties = new Properties(); + + /** + * Determines whether at the very last moment an email is sent out using JavaMail's native API or whether the email is simply only logged. + */ + private Boolean transportModeLoggingOnly; + + /** + * Sets defaults configured for proxy host, proxy port, proxy username, proxy password and proxy bridge port (used in authenticated proxy). + *

+ * Note: Any builder methods invoked after this will override the default value. + */ + MailerGenericBuiler() { + if (hasProperty(PROXY_HOST)) { + withProxyHost((String) getProperty(PROXY_HOST)); + } + if (hasProperty(PROXY_PORT)) { + withProxyPort((Integer) getProperty(PROXY_PORT)); + } + if (hasProperty(PROXY_USERNAME)) { + withProxyUsername((String) getProperty(PROXY_USERNAME)); + } + if (hasProperty(PROXY_PASSWORD)) { + withProxyPassword((String) getProperty(PROXY_PASSWORD)); + } + + withProxyBridgePort(ConfigLoader.valueOrProperty(null, Property.PROXY_SOCKS5BRIDGE_PORT, DEFAULT_PROXY_BRIDGE_PORT)); + withDebugLogging(ConfigLoader.valueOrProperty(null, Property.JAVAXMAIL_DEBUG, false)); + withSessionTimeout(ConfigLoader.valueOrProperty(null, Property.DEFAULT_SESSION_TIMEOUT_MILLIS, DEFAULT_SESSION_TIMEOUT_MILLIS)); + withThreadPoolSize(ConfigLoader.valueOrProperty(null, Property.DEFAULT_POOL_SIZE, DEFAULT_POOL_SIZE)); + withTransportModeLoggingOnly(ConfigLoader.valueOrProperty(null, Property.TRANSPORT_MODE_LOGGING_ONLY, DEFAULT_TRANSPORT_MODE_LOGGING_ONLY)); + + withEmailAddressCriteria(EmailAddressCriteria.RFC_COMPLIANT); + trustingAllHosts(true); + } + + /** + * For internal use. + */ + ProxyConfig buildProxyConfig() { + validateProxy(); + return new ProxyConfig(getProxyHost(), getProxyPort(), getProxyUsername(), getProxyPassword(), getProxyBridgePort()); + } + + private void validateProxy() { + if (!valueNullOrEmpty(proxyHost)) { + checkArgumentNotEmpty(proxyPort, "proxyHost provided, but not a proxyPort"); + + if (!valueNullOrEmpty(proxyUsername) && valueNullOrEmpty(proxyPassword)) { + throw new IllegalArgumentException("Proxy username provided but not a password"); + } + if (valueNullOrEmpty(proxyUsername) && !valueNullOrEmpty(proxyPassword)) { + throw new IllegalArgumentException("Proxy password provided but not a username"); + } + } + } + + /** + * For internal use. + */ + OperationalConfig buildOperationalConfig() { + return new OperationalConfig(getProperties(), getSessionTimeout(), getThreadPoolSize(), getTransportModeLoggingOnly(), getDebugLogging(), + getSslHostsToTrust(), getTrustAllSSLHost()); + } + + /** + * Delegates to {@link #withProxyHost(String)} and {@link #withProxyPort(Integer)}. + */ + public T withProxy(@Nullable final String proxyHost, @Nullable final Integer proxyPort) { + return (T) withProxyHost(proxyHost) + .withProxyPort(proxyPort); + } + + /** + * Delegates to: + *

    + *
  1. {@link #withProxyHost(String)}
  2. + *
  3. {@link #withProxyPort(Integer)}
  4. + *
  5. {@link #withProxyUsername(String)}
  6. + *
  7. {@link #withProxyPassword(String)}
  8. + *
+ */ + public T withProxy(@Nullable final String proxyHost, @Nullable final Integer proxyPort, @Nullable final String proxyUsername, @Nullable final String proxyPassword) { + return (T) withProxyHost(proxyHost) + .withProxyPort(proxyPort) + .withProxyUsername(proxyUsername) + .withProxyPassword(proxyPassword); + } + + /** + * Sets the optional proxy host, which will override any default that might have been set (through properties file or programmatically). + */ + public T withProxyHost(@Nullable final String proxyHost) { + this.proxyHost = proxyHost; + return (T) this; + } + + /** + * Sets the proxy port, which will override any default that might have been set (through properties file or programmatically). + *

+ * Proxy port is required if a proxyHost has been configured. + */ + // TODO take default port from transport strategy + public T withProxyPort(@Nullable final Integer proxyPort) { + this.proxyPort = proxyPort; + return (T) this; + } + + /** + * Sets the optional username to authenticate with the proxy. + *

+ * If set, Simple Java Mail will use its built in proxy bridge to perform the SOCKS authentication, as the underlying JavaMail framework doesn't + * support this directly. + *

+ * The path will be:
+ * {@code Simple Java Mail -> JavaMail -> anonymous authentication with local proxy bridge -> full authentication with remote SOCKS + * proxy}. + */ + public T withProxyUsername(@Nullable final String proxyUsername) { + this.proxyUsername = proxyUsername; + return (T) this; + } + + /** + * Sets the optional password to authenticate with the proxy. + * + * @see #withProxyUsername(String) + */ + public T withProxyPassword(@Nullable final String proxyPassword) { + this.proxyPassword = proxyPassword; + return (T) this; + } + + /** + * Relevant only when using username authentication with a proxy. + *

+ * Overrides the default for the intermediary SOCKS5 relay server bridge, which is a server that sits in between JavaMail and the remote proxy. + * Defaults to {@value DEFAULT_PROXY_BRIDGE_PORT} if no custom default property was configured. + * + * @see #withProxyUsername(String) + */ + public T withProxyBridgePort(@Nullable final Integer proxyBridgePort) { + this.proxyBridgePort = proxyBridgePort; + return (T) this; + } + + /** + * This flag is set on the Session instance through {@link Session#setDebug(boolean)} so that it generates debug information. To get more + * information out of the underlying JavaMail framework or out of Simple Java Mail, increase logging config of your chosen logging framework. + */ + public T withDebugLogging(@Nullable final Boolean debugLogging) { + this.debugLogging = debugLogging; + return (T) this; + } + + /** + * Controls the timeout to use when sending emails (affects socket connect-, read- and write timeouts). + */ + public T withSessionTimeout(@Nullable final Integer sessionTimeout) { + this.sessionTimeout = sessionTimeout; + return (T) this; + } + + /** + * Sets the email address validation restrictions when validating and sending emails using the current Mailer instance. + *

+ * Defaults to {@link EmailAddressCriteria#RFC_COMPLIANT} if not overridden with a ({@code null}) value. + * + * @see EmailAddressCriteria + * @see #clearEmailAddressCriteria() + * @see #resetEmailAddressCriteria() + */ + public T withEmailAddressCriteria(@Nonnull final EnumSet emailAddressCriteria) { + this.emailAddressCriteria = emailAddressCriteria.clone(); + return (T) this; + } + + /** + * Controls the maximum number of threads when sending emails in async fashion. Defaults to {@link #DEFAULT_POOL_SIZE}. + * + * @see #resetThreadpoolSize() + */ + public T withThreadPoolSize(@Nonnull final Integer defaultPoolSize) { + this.threadPoolSize = defaultPoolSize; + return (T) this; + } + + /** + * Determines whether at the very last moment an email is sent out using JavaMail's native API or whether the email is simply only logged. + * + * @see #resetTransportModeLoggingOnly() + */ + public T withTransportModeLoggingOnly(@Nonnull final Boolean transportModeLoggingOnly) { + this.transportModeLoggingOnly = transportModeLoggingOnly; + return (T) this; + } + + /** + * Configures the new session to only accept server certificates issued to one of the provided hostnames, and disables certificate issuer + * validation. + *

+ * Passing an empty list resets the current session's trust behavior to the default, and is equivalent to never calling this method in the first + * place. + *

+ * Security warning: Any certificate matching any of the provided host names will be accepted, regardless of the certificate + * issuer; attackers can abuse this behavior by serving a matching self-signed certificate during a man-in-the-middle attack. + *

+ * This method sets the property {@code mail.smtp.ssl.trust} to a space-separated list of the provided {@code hosts}. If the provided list is + * empty, {@code mail.smtp.ssl.trust} is unset. + * + * @see mail.smtp.ssl.trust + * @see #trustingAllHosts(Boolean) + */ + public T trustingSSLHosts(String... sslHostsToTrust) { + this.sslHostsToTrust = Arrays.asList(sslHostsToTrust); + return (T) this; + } + + /** + * Configures the current session to trust all hosts and don't validate any SSL keys. The property "mail.smtp(s).ssl.trust" is set to "*". + *

+ * Refer to https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html#mail.smtp.ssl.trust + * + * @see #trustingSSLHosts(String...) + */ + private T trustingAllHosts(@Nonnull final Boolean trustAllHosts) { + this.trustAllSSLHost = trustAllHosts; + return (T) this; + } + + /** + * Adds the given properties to the total list applied to the {@link Session} when building a mailer. + * + * @see #withProperties(Map) + * @see #withProperty(String, String) + * @see #clearProperties() + */ + public T withProperties(@Nonnull final Properties properties) { + for (Map.Entry property : properties.entrySet()) { + this.properties.put(property.getKey(), property.getValue()); + } + return (T) this; + } + + /** + * @see #withProperties(Properties) + * @see #clearProperties() + */ + public T withProperties(@Nonnull final Map properties) { + for (Map.Entry property : properties.entrySet()) { + this.properties.put(property.getKey(), property.getValue()); + } + return (T) this; + } + + /** + * @see #withProperties(Properties) + * @see #clearProperties() + */ + public T withProperty(@Nonnull final String propertyName, @Nullable final String propertyValue) { + if (propertyValue == null) { + this.properties.remove(propertyName); + } else { + this.properties.put(propertyName, propertyValue); + } + return (T) this; + } + + /** + * Resets session time to its default ({@value DEFAULT_SESSION_TIMEOUT_MILLIS}). + * + * @see #withSessionTimeout(Integer) + */ + public T resetSessionTimeout() { + return withSessionTimeout(DEFAULT_SESSION_TIMEOUT_MILLIS); + } + + /** + * Resets emailAddressCriteria to {@link EmailAddressCriteria#RFC_COMPLIANT}. + * + * @see #withEmailAddressCriteria(EnumSet) + * @see #clearEmailAddressCriteria() + */ + public T resetEmailAddressCriteria() { + return withEmailAddressCriteria(EmailAddressCriteria.RFC_COMPLIANT); + } + + /** + * Resets threadPoolSize to its default ({@value #DEFAULT_POOL_SIZE}). + * + * @see #withThreadPoolSize(Integer) + */ + public T resetThreadpoolSize() { + return withThreadPoolSize(DEFAULT_POOL_SIZE); + } + + /** + * Resets transportModeLoggingOnly to {@value #DEFAULT_TRANSPORT_MODE_LOGGING_ONLY}. + * + * @see #withTransportModeLoggingOnly(Boolean) + */ + public T resetTransportModeLoggingOnly() { + return withTransportModeLoggingOnly(DEFAULT_TRANSPORT_MODE_LOGGING_ONLY); + } + + /** + * Empties all proxy configuration. + */ + public T clearProxy() { + return (T) withProxy(null, null, null, null) + .withProxyBridgePort(null); + } + + /** + * Removes all email address criteria, meaning validation won't take place. + * + * @see #withEmailAddressCriteria(EnumSet) + * @see #resetEmailAddressCriteria() + */ + public T clearEmailAddressCriteria() { + return withEmailAddressCriteria(EnumSet.noneOf(EmailAddressCriteria.class)); + } + + /** + * Removes all trusted hosts from the list. + * + * @see #trustingSSLHosts(String...) + */ + public T clearTrustesSSLHosts() { + return trustingSSLHosts(); + } + + /** + * Removes all properties. + * + * @see #withProperties(Properties) + */ + public T clearProperties() { + properties.clear(); + return (T) this; + } + + /** + * @see #withProxyHost(String) + */ + public String getProxyHost() { + return proxyHost; + } + + /** + * @see #withProxyPort(Integer) + */ + public Integer getProxyPort() { + return proxyPort; + } + + /** + * @see #withProxyUsername(String) + */ + public String getProxyUsername() { + return proxyUsername; + } + + /** + * @see #withProxyPassword(String) + */ + public String getProxyPassword() { + return proxyPassword; + } + + /** + * @see #withProxyBridgePort(Integer) + */ + public Integer getProxyBridgePort() { + return proxyBridgePort; + } + + /** + * @see #withDebugLogging(Boolean) + */ + public Boolean getDebugLogging() { + return debugLogging; + } + + /** + * @see #withSessionTimeout(Integer) + */ + public Integer getSessionTimeout() { + return sessionTimeout; + } + + /** + * @see #withEmailAddressCriteria(EnumSet) + */ + public EnumSet getEmailAddressCriteria() { + return emailAddressCriteria; + } + + /** + * @see #withThreadPoolSize(Integer) + */ + public Integer getThreadPoolSize() { + return threadPoolSize; + } + + /** + * @see #trustingSSLHosts(String...) + */ + public List getSslHostsToTrust() { + return sslHostsToTrust; + } + + /** + * @see #trustingAllHosts(Boolean) + */ + public Boolean getTrustAllSSLHost() { + return trustAllSSLHost; + } + + /** + * @see #withTransportModeLoggingOnly(Boolean) + */ + public boolean getTransportModeLoggingOnly() { + return transportModeLoggingOnly; + } + + /** + * @see #withProperties(Properties) + */ + public Properties getProperties() { + return properties; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/ServerConfig.java b/src/main/java/org/simplejavamail/mailer/ServerConfig.java new file mode 100644 index 000000000..e4432af30 --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/ServerConfig.java @@ -0,0 +1,54 @@ +package org.simplejavamail.mailer; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static java.lang.String.format; +import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty; +import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; + +class ServerConfig { + private final String host; + private final Integer port; + private final String username; + private final String password; + + ServerConfig(@Nonnull final String host, @Nonnull final Integer port, @Nullable final String username, @Nullable final String password) { + this.host = checkArgumentNotEmpty(host, "smtp host address not given and not configured in config file"); + this.port = checkArgumentNotEmpty(port, "smtp host port not given and not configured in config file"); + this.username = username; + this.password = password; + + if (valueNullOrEmpty(this.username) && !valueNullOrEmpty(this.password)) { + throw new IllegalArgumentException("Password provided but no username given as argument or in config file"); + } + } + + @Override + public String toString() { + String str = format("%s:%s", host, port); + if (username != null) { + str += format(", username: %s", username); + } + if (password != null) { + str += " (authenticated)"; + } + return str; + } + + public String getHost() { + return host; + } + + public Integer getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/config/ProxyConfig.java b/src/main/java/org/simplejavamail/mailer/config/ProxyConfig.java deleted file mode 100644 index cc885f002..000000000 --- a/src/main/java/org/simplejavamail/mailer/config/ProxyConfig.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.simplejavamail.mailer.config; - -import org.simplejavamail.mailer.internal.socks.SocksProxyConfig; - -import static java.lang.String.format; -import static org.simplejavamail.util.ConfigLoader.Property.*; -import static org.simplejavamail.util.ConfigLoader.valueOrProperty; -import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty; -import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; - -/** - * The proxy configuration that indicates whether the connections should be routed through a proxy. - *

- * In case a proxy is required, the properties "mail.smtp(s).socks.host" and "mail.smtp(s).socks.port" will be set. - *

- * As the underlying JavaMail framework only support anonymous SOCKS proxy servers for non-ssl connections, authenticated SOCKS5 proxy is made - * possible using an intermediary anonymous proxy server which relays the connection through an authenticated remote proxy server. Anonymous proxies - * are still handled by JavaMail's own time-tested proxy client implementation. - *

- * Attempting to use a proxy and SSL SMTP authentication will result in an error, as the underlying JavaMail framework ignores any proxy settings for - * SSL connections. - */ -public class ProxyConfig extends SocksProxyConfig { - - /** - * The temporary intermediary SOCKS5 relay server bridge is a server that sits in between JavaMail and the remote proxy. Default port is {@value - * #DEFAULT_PROXY_BRIDGE_PORT}. - */ - @SuppressWarnings("JavaDoc") - public static final int DEFAULT_PROXY_BRIDGE_PORT = 1081; - - /** - * 'Skip proxy' constructor short-cut. - * - * @see #ProxyConfig(String, Integer, String, String) - */ - public ProxyConfig() { - this(null, null, null, null); - } - - /** - * 'Anonymous proxy' constructor short-cut. - * - * @param remoteProxyHost The host of the remote proxy. - * @param remoteProxyPort The port of the remote proxy. - * @see #ProxyConfig(String, Integer, String, String) - */ - public ProxyConfig(final String remoteProxyHost, final Integer remoteProxyPort) { - this(remoteProxyHost, remoteProxyPort, null, null); - } - - /** - * Creates an proxy configuration, which can be anonymous or authenticated. Host and port are required and either both or none of username and - * password should be provided. All arguments can be empty if the related properties are configured in a config file. - * - * @param remoteProxyHost The host of the remote proxy. - * @param remoteProxyPort The port of the remote proxy. - * @param username Username is mandatory when authentication is required. - * @param password Password is mandatory when authentication is required. - */ - @SuppressWarnings({ "WeakerAccess", "SameParameterValue" }) - public ProxyConfig(final String remoteProxyHost, final Integer remoteProxyPort, final String username, final String password) { - super( - valueOrProperty(remoteProxyHost, PROXY_HOST), - valueOrProperty(remoteProxyPort, PROXY_PORT), - valueOrProperty(username, PROXY_USERNAME), - valueOrProperty(password, PROXY_PASSWORD), - valueOrProperty(null, PROXY_SOCKS5BRIDGE_PORT, DEFAULT_PROXY_BRIDGE_PORT) - ); - - if (!valueNullOrEmpty(this.remoteProxyHost)) { - checkArgumentNotEmpty(this.remoteProxyPort, "remoteProxyPort not given and not configured in config file"); - - if (!valueNullOrEmpty(this.username) && valueNullOrEmpty(this.password)) { - throw new IllegalArgumentException("Proxy username provided but no password given as argument or in config file"); - } - if (valueNullOrEmpty(this.username) && !valueNullOrEmpty(this.password)) { - throw new IllegalArgumentException("Proxy password provided but no username given as argument or in config file"); - } - } - } - - /** - * If a host was provided then proxy is required. - */ - public boolean requiresProxy() { - return remoteProxyHost != null; - } - - /** - * If a username was provided, we will need to authenticate with the proxy. - */ - public boolean requiresAuthentication() { - return username != null; - } - - @Override - public String toString() { - String str = ""; - if (remoteProxyHost == null) { - return "no-proxy"; - } else { - str += format("%s:%s", remoteProxyHost, remoteProxyPort); - } - if (username != null) { - str += format(", username: %s", username); - } - if (proxyBridgePort != DEFAULT_PROXY_BRIDGE_PORT) { - str += format(", proxy bridge @ localhost:%s", proxyBridgePort); - } - return str; - } - - public int getProxyBridgePort() { - return proxyBridgePort; - } - - /** - * @param proxyBridgePort Port override for the temporary intermediary SOCKS5 relay server bridge (default is {@value - * #DEFAULT_PROXY_BRIDGE_PORT}). - */ - public void setProxyBridgePort(@SuppressWarnings("SameParameterValue") final int proxyBridgePort) { - this.proxyBridgePort = proxyBridgePort; - } - - public String getRemoteProxyHost() { - return remoteProxyHost; - } - - public Integer getRemoteProxyPort() { - return remoteProxyPort; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} diff --git a/src/main/java/org/simplejavamail/mailer/config/ServerConfig.java b/src/main/java/org/simplejavamail/mailer/config/ServerConfig.java deleted file mode 100644 index d4b4b41cf..000000000 --- a/src/main/java/org/simplejavamail/mailer/config/ServerConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.simplejavamail.mailer.config; - -import static java.lang.String.format; -import static org.simplejavamail.util.ConfigLoader.Property.*; -import static org.simplejavamail.util.ConfigLoader.valueOrProperty; -import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty; -import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; - -public class ServerConfig { - private final String host; - private final Integer port; - private final String username; - private final String password; - - /** - * No-arg constructor short-cut. Works only with populated config files. - * - * @see #ServerConfig(String, Integer, String, String) - */ - public ServerConfig() { - this(null, null, null, null); - } - - /** - * 'Anonymous smtp' constructor short-cut. If provided, username and / or password is loaded from config file. - * - * @param host The address URL of the SMTP server to be used. - * @param port The port of the SMTP server. - * @see #ServerConfig(String, Integer, String, String) - */ - public ServerConfig(final String host, final Integer port) { - this(host, port, null, null); - } - - /** - * 'Non-authenticated smtp' constructor short-cut. If provided, password is loaded from config file. - * - * @param host The address URL of the SMTP server to be used. - * @param port The port of the SMTP server. - * @param username An optional username, may be null. - * @see #ServerConfig(String, Integer, String, String) - */ - public ServerConfig(final String host, final Integer port, final String username) { - this(host, port, username, null); - } - - /** - * Main constructor, overrides any relevant values that may have been provided in config file. - * - * @param host The address URL of the SMTP server to be used. - * @param port The port of the SMTP server. - * @param username An optional username, may be null. - * @param password An optional password, may be null. - */ - public ServerConfig(final String host, final Integer port, final String username, final String password) { - this.host = valueOrProperty(host, SMTP_HOST); - this.port = valueOrProperty(port, SMTP_PORT); - this.username = valueOrProperty(username, SMTP_USERNAME); - this.password = valueOrProperty(password, SMTP_PASSWORD); - - checkArgumentNotEmpty(this.host, "smtp host address not given and not configured in config file"); - checkArgumentNotEmpty(this.port, "smtp host port not given and not configured in config file"); - - if (valueNullOrEmpty(this.username) && !valueNullOrEmpty(this.password)) { - throw new IllegalArgumentException("Password provided but no username given as argument or in config file"); - } - } - - @Override - public String toString() { - String str = format("%s:%s", host, port); - if (username != null) { - str += format(", username: %s", username); - } - if (password != null) { - str += " (authenticated)"; - } - return str; - } - - public String getHost() { - return host; - } - - public Integer getPort() { - return port; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} diff --git a/src/main/java/org/simplejavamail/mailer/internal/mailsender/MailSender.java b/src/main/java/org/simplejavamail/mailer/internal/mailsender/MailSender.java index 88bcbcc56..4d80d1d3e 100644 --- a/src/main/java/org/simplejavamail/mailer/internal/mailsender/MailSender.java +++ b/src/main/java/org/simplejavamail/mailer/internal/mailsender/MailSender.java @@ -4,11 +4,10 @@ import org.simplejavamail.converter.internal.mimemessage.MimeMessageHelper; import org.simplejavamail.email.Email; import org.simplejavamail.email.Recipient; -import org.simplejavamail.mailer.config.ProxyConfig; import org.simplejavamail.mailer.config.TransportStrategy; import org.simplejavamail.mailer.internal.socks.AuthenticatingSocks5Bridge; +import org.simplejavamail.mailer.internal.socks.SocksProxyConfig; import org.simplejavamail.mailer.internal.socks.socks5server.AnonymousSocks5Server; -import org.simplejavamail.util.ConfigLoader; import org.simplejavamail.util.ConfigLoader.Property; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,6 +21,7 @@ import javax.mail.Transport; import javax.mail.internet.MimeMessage; import java.io.UnsupportedEncodingException; +import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -36,8 +36,7 @@ *

* Refer to {@link #send(Email, boolean)} for details. *

- *


- *

+ *


* On a technical note, this is the most complex class in the library (aside from the SOCKS5 bridging server), because it deals with optional * asynchronous mailing requests and an optional proxy server that needs to be started and stopped on the fly depending on how many emails are (still) * being sent. Especially the combination of asynchronous emails and synchronous emails needs to be managed properly. @@ -45,18 +44,6 @@ public class MailSender { private static final Logger LOGGER = LoggerFactory.getLogger(MailSender.class); - - /** - * Defaults to {@code false}, sending mails rather than just only logging the mails. - */ - private static final boolean DEFAULT_MODE_LOGGING_ONLY = false; - - /** - * The default maximum timeout value for the transport socket is {@value #DEFAULT_SESSION_TIMEOUT_MILLIS} - * milliseconds. Can be overridden from a config file or through System variable. - */ - @SuppressWarnings("JavaDoc") - private static final int DEFAULT_SESSION_TIMEOUT_MILLIS = 60_000; /** * For multi-threaded scenario's where a batch of emails sent asynchronously, the default maximum number of threads is {@value @@ -86,6 +73,9 @@ public class MailSender { @Nullable private final TransportStrategy transportStrategy; + // FIXME update JavaDoc + private final OperationalConfig operationalConfig; + /** * Intermediary SOCKS5 relay server that acts as bridge between JavaMail and remote proxy (since JavaMail only supports anonymous SOCKS proxies). * Only set when {@link ProxyConfig} is provided with authentication details. @@ -109,42 +99,29 @@ public class MailSender { */ private Phaser smtpRequestsPhaser; - /** - * The timeout to use when sending emails (affects socket connect-, read- and write timeouts). Defaults to - * {@value #DEFAULT_SESSION_TIMEOUT_MILLIS}. - */ - private int sessionTimeout; - - /** - * The number of concurrent threads sending an email each. Used only when sending emails asynchronously (batch job mode). Defaults to - * {@value #DEFAULT_POOL_SIZE}. - */ - private int threadPoolSize; - - /** - * Determines whether at the very last moment an email is sent out using JavaMail's native API or whether the email is simply only logged. - */ - private boolean transportModeLoggingOnly; - - /** - * Configures proxy: {@link #configureSessionWithProxy(ProxyConfig, Session, TransportStrategy)}(ProxyConfig, Session, TransportStrategy) - *

- * Also initializes: - *

- */ - public MailSender(final Session session, final ProxyConfig proxyConfig, @Nullable final TransportStrategy transportStrategy) { + public MailSender(@Nonnull final Session session, + @Nonnull final OperationalConfig operationalConfig, + @Nonnull final ProxyConfig proxyConfig, + @Nullable final TransportStrategy transportStrategy) { this.session = session; + this.operationalConfig = operationalConfig; this.transportStrategy = transportStrategy; this.proxyServer = configureSessionWithProxy(proxyConfig, session, transportStrategy); - this.threadPoolSize = ConfigLoader.valueOrProperty(null, Property.DEFAULT_POOL_SIZE, DEFAULT_POOL_SIZE); - this.sessionTimeout = ConfigLoader.valueOrProperty(null, Property.DEFAULT_SESSION_TIMEOUT_MILLIS, DEFAULT_SESSION_TIMEOUT_MILLIS); - this.transportModeLoggingOnly = ConfigLoader.valueOrProperty(null, Property.TRANSPORT_MODE_LOGGING_ONLY, DEFAULT_MODE_LOGGING_ONLY); + init(operationalConfig); } - + + private void init(@Nonnull OperationalConfig operationalConfig) { + session.setDebug(operationalConfig.isDebugLogging()); + session.getProperties().putAll(operationalConfig.getProperties()); + if (transportStrategy != null) { + if (operationalConfig.isTrustAllSSLHost()) { + trustAllHosts(true); + } else { + trustHosts(operationalConfig.getSslHostsToTrust()); + } + } + } + /** * If a {@link ProxyConfig} was provided with a host address, then the appropriate properties are set on the {@link Session}, overriding any SOCKS * properties already there. @@ -155,13 +132,13 @@ public MailSender(final Session session, final ProxyConfig proxyConfig, @Nullabl * @param proxyConfig Proxy server details, optionally with username / password. * @param session The session with properties to add the new configuration to. * @param transportStrategy Used to verify if the current combination with proxy is allowed (SMTP with SSL trategy doesn't support any proxy, - * virtue of the underlying JavaMail framework). + * virtue of the underlying JavaMail framework). Can be omitted if the Session is presumed preconfigured. * @return null in case of no proxy or anonymous proxy, or a AnonymousSocks5Server proxy bridging server instance in case of authenticated proxy. */ - private static AnonymousSocks5Server configureSessionWithProxy(final ProxyConfig proxyConfig, final Session session, - final TransportStrategy transportStrategy) { - final ProxyConfig effectiveProxyConfig = (proxyConfig != null) ? proxyConfig : new ProxyConfig(); - if (!effectiveProxyConfig.requiresProxy()) { + private static AnonymousSocks5Server configureSessionWithProxy(@Nonnull final ProxyConfig proxyConfig, + @Nonnull final Session session, + @Nullable final TransportStrategy transportStrategy) { + if (!proxyConfig.requiresProxy()) { LOGGER.debug("No proxy set, skipping proxy."); } else { if (transportStrategy == TransportStrategy.SMTPS) { @@ -169,23 +146,24 @@ private static AnonymousSocks5Server configureSessionWithProxy(final ProxyConfig } final Properties sessionProperties = session.getProperties(); if (transportStrategy != null) { - sessionProperties.put(transportStrategy.propertyNameSocksHost(), effectiveProxyConfig.getRemoteProxyHost()); - sessionProperties.put(transportStrategy.propertyNameSocksPort(), String.valueOf(effectiveProxyConfig.getRemoteProxyPort())); + sessionProperties.put(transportStrategy.propertyNameSocksHost(), proxyConfig.getRemoteProxyHost()); + sessionProperties.put(transportStrategy.propertyNameSocksPort(), String.valueOf(proxyConfig.getRemoteProxyPort())); } else { LOGGER.debug("no transport strategy provided, expecting mail.smtp(s).socks.host and .port properties to be set to proxy " + "config on Session"); } - if (effectiveProxyConfig.requiresAuthentication()) { + if (proxyConfig.requiresAuthentication()) { if (transportStrategy != null) { // wire anonymous proxy request to our own proxy bridge so we can perform authentication to the actual proxy sessionProperties.put(transportStrategy.propertyNameSocksHost(), "localhost"); - sessionProperties.put(transportStrategy.propertyNameSocksPort(), String.valueOf(effectiveProxyConfig.getProxyBridgePort())); + sessionProperties.put(transportStrategy.propertyNameSocksPort(), String.valueOf(proxyConfig.getProxyBridgePort())); } else { LOGGER.debug("no transport strategy provided but authenticated proxy required, expecting mail.smtp(s).socks.host and .port " + - "properties to be set to localhost and port " + effectiveProxyConfig.getProxyBridgePort()); + "properties to be set to localhost and port " + proxyConfig.getProxyBridgePort()); } - return new AnonymousSocks5Server(new AuthenticatingSocks5Bridge(effectiveProxyConfig), - effectiveProxyConfig.getProxyBridgePort()); + SocksProxyConfig socksProxyConfig = new SocksProxyConfig(proxyConfig.getRemoteProxyHost(), proxyConfig.getRemoteProxyPort(), + proxyConfig.getUsername(), proxyConfig.getPassword(), proxyConfig.getProxyBridgePort()); + return new AnonymousSocks5Server(new AuthenticatingSocks5Bridge(socksProxyConfig), proxyConfig.getProxyBridgePort()); } } return null; @@ -193,11 +171,11 @@ private static AnonymousSocks5Server configureSessionWithProxy(final ProxyConfig /** * Processes an {@link Email} instance into a completely configured {@link Message}. - *

+ *

* Sends the Sun JavaMail {@link Message} object using {@link Session#getTransport()}. It will call {@link Transport#connect()} assuming all * connection details have been configured in the provided {@link Session} instance and finally {@link Transport#sendMessage(Message, * Address[])}. - *

+ *

* Performs a call to {@link Message#saveChanges()} as the Sun JavaMail API indicates it is needed to configure the message headers and providing * a message id. *

@@ -226,9 +204,9 @@ the proxy bridge server (or connection pool in async mode) while a non-async ema if (async) { // start up thread pool if necessary if (executor == null || executor.isTerminated()) { - executor = Executors.newFixedThreadPool(threadPoolSize); + executor = Executors.newFixedThreadPool(operationalConfig.getThreadPoolSize()); } - configureSessionWithTimeout(session, sessionTimeout); + configureSessionWithTimeout(session, operationalConfig.getSessionTimeout()); executor.execute(new Runnable() { @Override public void run() { @@ -290,7 +268,7 @@ private void sendMailClosure(@Nonnull final Session session, @Nonnull final Emai } } - if (!transportModeLoggingOnly) { + if (!operationalConfig.isTransportModeLoggingOnly()) { LOGGER.trace("\t\nEmail: {}", email); LOGGER.trace("\t\nMimeMessage: {}\n", mimeMessageToEML(message)); @@ -365,18 +343,9 @@ private static void logSession(final Session session) { } /** - * Refer to Session{@link Session#setDebug(boolean)} - */ - public void setDebug(final boolean debug) { - session.setDebug(debug); - } - - /** - * Configures the current session to trust all hosts and don't validate any SSL keys. The property "mail.smtp(s).ssl.trust" is set to "*". - *

- * Refer to https://javamail.java.net/nonav/docs/api/com/sun/mail/smtp/package-summary.html#mail.smtp.ssl.trust + * @see org.simplejavamail.mailer.MailerGenericBuiler#trustingAllHosts(Boolean) */ - public void trustAllHosts(final boolean trustAllHosts) { + private void trustAllHosts(final boolean trustAllHosts) { if (transportStrategy != null) { session.getProperties().remove(transportStrategy.propertyNameSSLTrust()); if (trustAllHosts) { @@ -388,30 +357,17 @@ public void trustAllHosts(final boolean trustAllHosts) { } /** - * Configures the current session to only accept server certificates issued to one of the provided hostnames, - * and disables certificate issuer validation. - *

- * Passing an empty list resets the current session's trust behavior to the default, and is equivalent to never - * calling this method in the first place. - *

- * Security warning: Any certificate matching any of the provided host names will be accepted, - * regardless of the certificate issuer; attackers can abuse this behavior by serving a matching self-signed - * certificate during a man-in-the-middle attack. - *

- * This method sets the property {@code mail.smtp(s).ssl.trust} to a space-separated list of the provided - * {@code hosts}. If the provided list is empty, {@code mail.smtp(s).ssl.trust} is unset. - * - * @see mail.smtp.ssl.trust + * @see org.simplejavamail.mailer.MailerGenericBuiler#trustingSSLHosts(String...) */ - public void trustHosts(final String... hosts) { + private void trustHosts(@Nonnull final List hosts) { trustAllHosts(false); - if (hosts.length > 0) { + if (!hosts.isEmpty()) { if (transportStrategy == null) { throw new MailSenderException(MailSenderException.CANNOT_SET_TRUST_WITHOUT_TRANSPORTSTRATEGY); } - final StringBuilder builder = new StringBuilder(hosts[0]); - for (int i = 1; i < hosts.length; i++) { - builder.append(" ").append(hosts[i]); + final StringBuilder builder = new StringBuilder(hosts.get(0)); + for (int i = 1; i < hosts.size(); i++) { + builder.append(" ").append(hosts.get(i)); } session.getProperties().setProperty(transportStrategy.propertyNameSSLTrust(), builder.toString()); } @@ -450,13 +406,6 @@ private boolean needsAuthenticatedProxy() { return proxyServer != null; } - /** - * @param properties Properties which will be added to the current {@link Session} instance. - */ - public void applyProperties(final Properties properties) { - session.getProperties().putAll(properties); - } - /** * For emergencies, when a client really wants access to the internally created {@link Session} instance. */ @@ -464,33 +413,7 @@ public Session getSession() { return session; } - /** - * @param threadPoolSize The maximum number of threads when sending emails in async fashion. - * @see Property#DEFAULT_POOL_SIZE - */ - public synchronized void setThreadPoolSize(final int threadPoolSize) { - this.threadPoolSize = threadPoolSize; - } - - /** - * @param sessionTimeout The timeout to use when sending emails (affects socket connect-, read- and write timeouts). - * @see Property#DEFAULT_SESSION_TIMEOUT_MILLIS - */ - public synchronized void setSessionTimeout(final int sessionTimeout) { - this.sessionTimeout = sessionTimeout; - } - - /** - * Sets the transport mode for this mail sender to logging only, which means no mail will be actually sent out. - */ - public synchronized void setTransportModeLoggingOnly(final boolean transportModeLoggingOnly) { - this.transportModeLoggingOnly = transportModeLoggingOnly; - } - - /** - * @return {@link #transportModeLoggingOnly} - */ - public boolean isTransportModeLoggingOnly() { - return transportModeLoggingOnly; + public OperationalConfig getOperationalConfig() { + return operationalConfig; } } \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/internal/mailsender/OperationalConfig.java b/src/main/java/org/simplejavamail/mailer/internal/mailsender/OperationalConfig.java new file mode 100644 index 000000000..3b4c8337e --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/internal/mailsender/OperationalConfig.java @@ -0,0 +1,73 @@ +package org.simplejavamail.mailer.internal.mailsender; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +public class OperationalConfig { + /** + * Refer to {@link org.simplejavamail.mailer.MailerGenericBuiler#DEFAULT_SESSION_TIMEOUT_MILLIS}. + */ + private final int sessionTimeout; + + /** + * Refer to {@link org.simplejavamail.mailer.MailerGenericBuiler#withProperties(Properties)}. + */ + private final Properties properties; + + /** + * The number of concurrent threads sending an email each. Used only when sending emails asynchronously (batch job mode). + */ + private final int threadPoolSize; + + /** + * Determines whether at the very last moment an email is sent out using JavaMail's native API or whether the email is simply only logged. + */ + private final boolean transportModeLoggingOnly; + + // FIXME JavaDoc + private final boolean debugLogging; + // FIXME JavaDoc + private final List sslHostsToTrust; + // FIXME JavaDoc + private final boolean trustAllSSLHost; + + public OperationalConfig(@Nonnull Properties properties, int sessionTimeout, int threadPoolSize, boolean transportModeLoggingOnly, boolean debugLogging, List sslHostsToTrust, boolean trustAllSSLHost) { + this.properties = properties; + this.sessionTimeout = sessionTimeout; + this.threadPoolSize = threadPoolSize; + this.transportModeLoggingOnly = transportModeLoggingOnly; + this.debugLogging = debugLogging; + this.sslHostsToTrust = Collections.unmodifiableList(sslHostsToTrust); + this.trustAllSSLHost = trustAllSSLHost; + } + + public int getSessionTimeout() { + return sessionTimeout; + } + + public int getThreadPoolSize() { + return threadPoolSize; + } + + public boolean isTransportModeLoggingOnly() { + return transportModeLoggingOnly; + } + + public boolean isDebugLogging() { + return debugLogging; + } + + public List getSslHostsToTrust() { + return sslHostsToTrust; + } + + public boolean isTrustAllSSLHost() { + return trustAllSSLHost; + } + + public Properties getProperties() { + return properties; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/internal/mailsender/ProxyConfig.java b/src/main/java/org/simplejavamail/mailer/internal/mailsender/ProxyConfig.java new file mode 100644 index 000000000..d692e546a --- /dev/null +++ b/src/main/java/org/simplejavamail/mailer/internal/mailsender/ProxyConfig.java @@ -0,0 +1,75 @@ +package org.simplejavamail.mailer.internal.mailsender; + +import javax.annotation.Nullable; + +import static java.lang.String.format; + +/** + * The proxy configuration that indicates whether the connections should be routed through a proxy. + *

+ * In case a proxy is required, the properties "mail.smtp(s).socks.host" and "mail.smtp(s).socks.port" will be set. + *

+ * As the underlying JavaMail framework only support anonymous SOCKS proxy servers for non-ssl connections, authenticated SOCKS5 proxy is made + * possible using an intermediary anonymous proxy server which relays the connection through an authenticated remote proxy server. Anonymous proxies + * are still handled by JavaMail's own time-tested proxy client implementation. + *

+ * NOTE: Attempting to use a proxy and SSL SMTP authentication will result in an error, as the underlying JavaMail framework ignores any proxy + * settings for SSL connections. + */ +public class ProxyConfig { + + private final String remoteProxyHost; + private final Integer remoteProxyPort; + private final String username; + private final String password; + private final Integer proxyBridgePort; + + public ProxyConfig(@Nullable final String remoteProxyHost, @Nullable final Integer remoteProxyPort, @Nullable final String username, @Nullable final String password, @Nullable final Integer proxyBridgePort) { + this.remoteProxyHost = remoteProxyHost; + this.remoteProxyPort = remoteProxyPort; + this.username = username; + this.password = password; + this.proxyBridgePort = proxyBridgePort; + } + + public boolean requiresProxy() { + return remoteProxyHost != null; + } + + public boolean requiresAuthentication() { + return username != null; + } + + @Override + public String toString() { + if (!requiresProxy()) { + return "no-proxy"; + } + String str = format("%s:%s", remoteProxyHost, remoteProxyPort); + if (requiresAuthentication()) { + str += format(", username: %s", username); + str += format(", proxy bridge @ localhost:%s", proxyBridgePort); + } + return str; + } + + public int getProxyBridgePort() { + return proxyBridgePort; + } + + public String getRemoteProxyHost() { + return remoteProxyHost; + } + + public Integer getRemoteProxyPort() { + return remoteProxyPort; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/mailer/internal/socks/SocksProxyConfig.java b/src/main/java/org/simplejavamail/mailer/internal/socks/SocksProxyConfig.java index b25b201d8..aab92c4e2 100644 --- a/src/main/java/org/simplejavamail/mailer/internal/socks/SocksProxyConfig.java +++ b/src/main/java/org/simplejavamail/mailer/internal/socks/SocksProxyConfig.java @@ -1,22 +1,37 @@ package org.simplejavamail.mailer.internal.socks; -public class SocksProxyConfig { - - protected final String remoteProxyHost; - protected final Integer remoteProxyPort; - protected final String username; - protected final String password; - protected int proxyBridgePort; +import static java.lang.String.format; - protected SocksProxyConfig(final String remoteProxyHost, final Integer remoteProxyPort, final String username, final String password, final int proxyBridgePort) { +/** + * Proxy config needed for authenticating SOCKS proxy. Used in conjunction with the SOCK bridging server. + */ +public class SocksProxyConfig { + + final String remoteProxyHost; + final Integer remoteProxyPort; + final String username; + final String password; + final int proxyBridgePort; + + public SocksProxyConfig(final String remoteProxyHost, final Integer remoteProxyPort, final String username, final String password, final int proxyBridgePort) { this.remoteProxyHost = remoteProxyHost; this.remoteProxyPort = remoteProxyPort; this.username = username; this.password = password; this.proxyBridgePort = proxyBridgePort; } - + boolean requiresAuthentication() { return username != null; } -} + + @Override + public String toString() { + String str = format("%s:%s", remoteProxyHost, remoteProxyPort); + if (requiresAuthentication()) { + str += format(", username: %s", username); + str += format(", proxy bridge @ localhost:%s", proxyBridgePort); + } + return str; + } +} \ No newline at end of file diff --git a/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java b/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java index 0e06eada5..53c97dd3c 100644 --- a/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java +++ b/src/main/java/org/simplejavamail/springsupport/SimpleJavaMailSpringSupport.java @@ -1,6 +1,7 @@ package org.simplejavamail.springsupport; import org.simplejavamail.mailer.Mailer; +import org.simplejavamail.mailer.MailerBuilder; import org.simplejavamail.util.ConfigLoader; import org.simplejavamail.util.ConfigLoader.Property; import org.springframework.beans.factory.annotation.Value; @@ -78,7 +79,7 @@ public Mailer loadGlobalConfigAndCreateDefaultMailer( // this will configure itself with the global config and is read to use // ofcourse this is optional simply as a convenience default - return new Mailer(); + return MailerBuilder.buildMailer(); } private static void setNullableProperty(final Properties emailProperties, final String key, final String value) { diff --git a/src/test/java/demo/MailTestDemoApp.java b/src/test/java/demo/MailTestDemoApp.java index da25ad230..5a1081236 100644 --- a/src/test/java/demo/MailTestDemoApp.java +++ b/src/test/java/demo/MailTestDemoApp.java @@ -5,7 +5,7 @@ import org.simplejavamail.email.EmailBuilder; import org.simplejavamail.email.EmailPopulatingBuilder; import org.simplejavamail.mailer.Mailer; -import org.simplejavamail.mailer.config.ServerConfig; +import org.simplejavamail.mailer.MailerBuilder; import org.simplejavamail.mailer.config.TransportStrategy; import testutil.ConfigLoaderTestHelper; @@ -28,15 +28,23 @@ public class MailTestDemoApp { // if you have 2-factor login turned on, you need to generate a once-per app password // https://security.google.com/settings/security/apppasswords private static final String YOUR_GMAIL_PASSWORD = "your_gmail_password"; - - private static final ServerConfig serverConfigSMTP = new ServerConfig("smtp.gmail.com", 25, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD); - private static final ServerConfig serverConfigTLS = new ServerConfig("smtp.gmail.com", 587, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD); - private static final ServerConfig serverConfigSSL = new ServerConfig("smtp.gmail.com", 465, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD); - + + private static final Mailer mailerSMTP = buildMailer("smtp.gmail.com", 25, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD, TransportStrategy.SMTP); + private static final Mailer mailerSSL = buildMailer("smtp.gmail.com", 465, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD, TransportStrategy.SMTPS); + private static final Mailer mailerTLS = buildMailer("smtp.gmail.com", 587, YOUR_GMAIL_ADDRESS, YOUR_GMAIL_PASSWORD, TransportStrategy.SMTP_TLS); + /** * If you just want to see what email is being sent, just set this to true. It won't actually connect to an SMTP server then. */ private static final boolean LOGGING_MODE = false; + + private static final Mailer buildMailer(String host, int port, String gMailAddress, String gMailPassword, TransportStrategy strategy) { + return MailerBuilder + .withSMTPServer(host, port, gMailAddress, gMailPassword) + .withTransportStrategy(strategy) + .withTransportModeLoggingOnly(LOGGING_MODE) + .buildMailer(); + } public static void main(final String[] args) throws Exception { @@ -68,15 +76,8 @@ public static void main(final String[] args) } private static void sendMail(final Email email) { - // ProxyConfig proxyconfig = new ProxyConfig("localhost", 1030); - sendMail(serverConfigSMTP, TransportStrategy.SMTP_TLS, email); - sendMail(serverConfigTLS, TransportStrategy.SMTP_TLS, email); - sendMail(serverConfigSSL, TransportStrategy.SMTPS, email); - } - - private static void sendMail(ServerConfig serverConfigSMTP, TransportStrategy smtpTls, Email email) { - Mailer mailer = new Mailer(serverConfigSMTP, smtpTls); - mailer.setTransportModeLoggingOnly(LOGGING_MODE); - mailer.sendMail(email); + mailerSMTP.sendMail(email); + mailerTLS.sendMail(email); + mailerSSL.sendMail(email); } } \ No newline at end of file diff --git a/src/test/java/org/simplejavamail/mailer/MailerBuilderProxyConfigTest.java b/src/test/java/org/simplejavamail/mailer/MailerBuilderProxyConfigTest.java new file mode 100644 index 000000000..ac2e39b7b --- /dev/null +++ b/src/test/java/org/simplejavamail/mailer/MailerBuilderProxyConfigTest.java @@ -0,0 +1,97 @@ +package org.simplejavamail.mailer; + +import org.junit.Before; +import org.junit.Test; +import org.simplejavamail.mailer.internal.mailsender.ProxyConfig; +import org.simplejavamail.util.ConfigLoader; +import testutil.ConfigLoaderTestHelper; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +public class MailerBuilderProxyConfigTest { + + @Before + public void restoreOriginalStaticProperties() + throws IOException { + String s = "simplejavamail.proxy.host=proxy.default.com\n" + + "simplejavamail.proxy.port=1080\n" + + "simplejavamail.proxy.username=username proxy\n" + + "simplejavamail.proxy.password=password proxy\n" + + "simplejavamail.proxy.socks5bridge.port=1081\n"; + ConfigLoader.loadProperties(new ByteArrayInputStream(s.getBytes()), false); + } + + @Test + public void NoArgconstructor_WithoutConfigFile_WithoutHost() + throws Exception { + ConfigLoaderTestHelper.clearConfigProperties(); + ProxyConfig emptyProxyConfig = new ProxyConfig(null, null, null, null, -1); + verifyProxyConfig(emptyProxyConfig, null, null, null, null, -1); + assertThat(emptyProxyConfig.requiresProxy()).isFalse(); + assertThat(emptyProxyConfig.requiresAuthentication()).isFalse(); + } + + @Test + public void NoArgconstructor_WithoutConfigFile_WithoutPort() + throws Exception { + ConfigLoaderTestHelper.clearConfigProperties(); + try { + MailerBuilder + .withSMTPServerHost("host") + .withSMTPServerPort(1234) + .withProxy("host", null) + .buildMailer(); + fail("IllegalArgumentException expected for proxy port"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).containsIgnoringCase("proxyHost provided, but not a proxyPort"); + } + try { + MailerBuilder + .withSMTPServerHost("host") + .withSMTPServerPort(1234) + .withProxy("host", null, null, null) + .buildMailer(); + fail("IllegalArgumentException expected for proxy port"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).containsIgnoringCase("proxyHost provided, but not a proxyPort"); + } + } + + @Test + public void NoArgconstructor_WithoutConfigFile_MissingPasswordOrUsername() + throws Exception { + ConfigLoaderTestHelper.clearConfigProperties(); + + try { + MailerBuilder.withSMTPServerHost("host") + .withSMTPServerPort(123) + .withProxy("host", 1234, "username", null) + .buildMailer(); + fail("IllegalArgumentException expected for password"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).containsIgnoringCase("password"); + } + try { + MailerBuilder + .withSMTPServerHost("host") + .withSMTPServerPort(1234) + .withProxy("host", 1234, null, "password") + .buildMailer(); + fail("IllegalArgumentException expected for username"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).containsIgnoringCase("username"); + } + } + + private void verifyProxyConfig(ProxyConfig proxyConfig, String host, Integer port, String username, String password, int defaultProxyBridgePort) { + assertThat(proxyConfig.getRemoteProxyHost()).isEqualTo(host); + assertThat(proxyConfig.getRemoteProxyPort()).isEqualTo(port); + assertThat(proxyConfig.getUsername()).isEqualTo(username); + assertThat(proxyConfig.getPassword()).isEqualTo(password); + assertThat(proxyConfig.getProxyBridgePort()).isEqualTo(defaultProxyBridgePort); + } +} \ No newline at end of file diff --git a/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java b/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java index a1e4977df..ea4084d02 100644 --- a/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java +++ b/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java @@ -9,7 +9,6 @@ import org.simplejavamail.email.EmailBuilder; import org.simplejavamail.email.EmailPopulatingBuilder; import org.simplejavamail.email.Recipient; -import org.simplejavamail.mailer.config.ServerConfig; import org.simplejavamail.util.ConfigLoader; import testutil.EmailHelper; import testutil.testrules.SmtpServerRule; @@ -31,17 +30,18 @@ @SuppressWarnings("unused") public class MailerLiveTest { - private static final ServerConfig SERVER_CONFIG = new ServerConfig("localhost", 251); + private static final String SERVER_HOST = "localhost"; + private static final Integer SERVER_PORT = 251; @Rule - public final SmtpServerRule smtpServerRule = new SmtpServerRule(new TestSmtpServer(SERVER_CONFIG)); + public final SmtpServerRule smtpServerRule = new SmtpServerRule(new TestSmtpServer(SERVER_HOST, SERVER_PORT)); private Mailer mailer; @Before public void setup() { ConfigLoader.loadProperties(new Properties(), false); // clear out defaults - mailer = new Mailer(SERVER_CONFIG); + mailer = MailerBuilder.withSMTPServer(SERVER_HOST, SERVER_PORT).buildMailer(); } @Test diff --git a/src/test/java/org/simplejavamail/mailer/MailerTest.java b/src/test/java/org/simplejavamail/mailer/MailerTest.java index 9ecf82c52..9de472db1 100644 --- a/src/test/java/org/simplejavamail/mailer/MailerTest.java +++ b/src/test/java/org/simplejavamail/mailer/MailerTest.java @@ -3,12 +3,10 @@ import net.markenwerk.utils.mail.dkim.DkimMessage; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import org.simplejavamail.converter.EmailConverter; import org.simplejavamail.email.Email; import org.simplejavamail.email.EmailPopulatingBuilder; -import org.simplejavamail.mailer.config.ProxyConfig; -import org.simplejavamail.mailer.config.ServerConfig; +import org.simplejavamail.mailer.MailerBuilder.MailerRegularBuilder; import org.simplejavamail.mailer.config.TransportStrategy; import org.simplejavamail.util.ConfigLoader; import testutil.ConfigLoaderTestHelper; @@ -23,14 +21,13 @@ import static javax.xml.bind.DatatypeConverter.parseBase64Binary; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; import static org.simplejavamail.mailer.config.TransportStrategy.SMTPS; import static org.simplejavamail.mailer.config.TransportStrategy.SMTP_TLS; import static org.simplejavamail.util.ConfigLoader.Property.OPPORTUNISTIC_TLS; @SuppressWarnings("unused") public class MailerTest { - + @Before public void restoreOriginalStaticProperties() throws IOException { @@ -47,15 +44,15 @@ public void restoreOriginalStaticProperties() + "simplejavamail.proxy.socks5bridge.port=1081"; ConfigLoader.loadProperties(new ByteArrayInputStream(s.getBytes()), false); } - + @Test public void createMailSession_MinimalConstructor_WithoutConfig() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); - - Mailer mailer = new Mailer("host", 25, null, null); + + Mailer mailer = MailerBuilder.withSMTPServer("host", 25, null, null).buildMailer(); Session session = mailer.getSession(); - + assertThat(session.getDebug()).isFalse(); assertThat(session.getProperty("mail.smtp.host")).isEqualTo("host"); assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25"); @@ -70,32 +67,24 @@ public void createMailSession_MinimalConstructor_WithoutConfig() assertThat(session.getProperty("mail.smtp.auth")).isNull(); assertThat(session.getProperty("mail.smtp.socks.host")).isNull(); assertThat(session.getProperty("mail.smtp.socks.port")).isNull(); - + // all constructors, providing the same minimal information - Mailer alternative1 = new Mailer(new ServerConfig("host", 25)); - Mailer alternative2 = new Mailer(new ServerConfig("host", 25), (TransportStrategy) null); - Mailer alternative3 = new Mailer(new ServerConfig("host", 25), (ProxyConfig) null); - Mailer alternative4 = new Mailer(new ServerConfig("host", 25), null, null); - Mailer alternative5 = new Mailer(session); - Mailer alternative6 = new Mailer(session, null); - + Mailer alternative1 = MailerBuilder.withSMTPServer("host", 25).buildMailer(); + Mailer alternative2 = MailerBuilder.usingSession(session).buildMailer(); + assertThat(session.getProperties()).isEqualTo(alternative1.getSession().getProperties()); assertThat(session.getProperties()).isEqualTo(alternative2.getSession().getProperties()); - assertThat(session.getProperties()).isEqualTo(alternative3.getSession().getProperties()); - assertThat(session.getProperties()).isEqualTo(alternative4.getSession().getProperties()); - assertThat(session.getProperties()).isEqualTo(alternative5.getSession().getProperties()); - assertThat(session.getProperties()).isEqualTo(alternative6.getSession().getProperties()); } - + @Test public void createMailSession_AnonymousProxyConstructor_WithoutConfig() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); - + Mailer mailer = createFullyConfiguredMailer(false, "", SMTP_TLS); - + Session session = mailer.getSession(); - + assertThat(session.getDebug()).isTrue(); assertThat(session.getProperty("mail.smtp.host")).isEqualTo("smtp host"); assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25"); @@ -112,16 +101,16 @@ public void createMailSession_AnonymousProxyConstructor_WithoutConfig() assertThat(session.getProperty("extra1")).isEqualTo("value1"); assertThat(session.getProperty("extra2")).isEqualTo("value2"); } - + @Test public void createMailSession_MaximumConstructor_WithoutConfig() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); - + Mailer mailer = createFullyConfiguredMailer(true, "", SMTP_TLS); - + Session session = mailer.getSession(); - + assertThat(session.getDebug()).isTrue(); assertThat(session.getProperty("mail.smtp.host")).isEqualTo("smtp host"); assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25"); @@ -140,7 +129,7 @@ public void createMailSession_MaximumConstructor_WithoutConfig() @Test public void createMailSession_MinimalConstructor_WithConfig() { - Mailer mailer = new Mailer(); + Mailer mailer = MailerBuilder.buildMailer(); Session session = mailer.getSession(); assertThat(session.getDebug()).isTrue(); @@ -163,7 +152,7 @@ public void createMailSession_MinimalConstructor_WithConfig_OPPORTUNISTIC_TLS() properties.setProperty(OPPORTUNISTIC_TLS.key(), "false"); ConfigLoader.loadProperties(properties, true); - Mailer mailer = new Mailer(TransportStrategy.SMTP); + Mailer mailer = MailerBuilder.withTransportStrategy(TransportStrategy.SMTP).buildMailer(); Session session = mailer.getSession(); assertThat(session.getDebug()).isTrue(); @@ -190,7 +179,7 @@ public void createMailSession_MinimalConstructor_WithConfig_OPPORTUNISTIC_TLS_Ma TransportStrategy.SMTP.setOpportunisticTLS(true); - Mailer mailer = new Mailer(TransportStrategy.SMTP); + Mailer mailer = MailerBuilder.withTransportStrategy(TransportStrategy.SMTP).buildMailer(); Session session = mailer.getSession(); assertThat(session.getDebug()).isTrue(); @@ -250,12 +239,12 @@ public void createMailSession_MaximumConstructor_WithConfig_TLS() assertThat(session.getProperty("extra1")).isEqualTo("overridden value1"); assertThat(session.getProperty("extra2")).isEqualTo("overridden value2"); } - + @Test public void testDKIMPriming() throws IOException, MessagingException { final EmailPopulatingBuilder emailPopulatingBuilder = EmailHelper.createDummyEmailBuilder(true, false, false); - + // System.out.println(printBase64Binary(Files.readAllBytes(Paths.get("D:\\keys\\dkim.der")))); // needs jdk 1.7 String privateDERkeyBase64 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMYuC7ZjFBSWJtP6JH8w1deJE+5sLwkUacZcW4MTVQXTM33BzN8Ec64KO1Hk2B9oxkpdunKt" @@ -265,18 +254,18 @@ public void testDKIMPriming() + "pYpqAWJbbR+8scBgVxS+9NLLeHhlx/EvkaZRdLhwRyHAkEAtr1ThkqrFIXHxt9Wczd20HCG+qlgF5gv3WHYx4bSTx2/pBCHgWjzyxtqst1HN7+l5nicdrxsDJVVv+vYJ7FtlQJAWPgG" + "Zwgvs3Rvv7k5NwifQOEbhbZAigAGCF5Jk/Ijpi6zaUn7754GSn2FOzWgxDguUKe/fcgdHBLai/1jIRVZQQJAXF2xzWMwP+TmX44QxK52QHVI8mhNzcnH7A311gWns6AbLcuLA9quwjU" + "YJMRlfXk67lJXCleZL15EpVPrQ34KlA=="; - + emailPopulatingBuilder.signWithDomainKey(new ByteArrayInputStream(parseBase64Binary(privateDERkeyBase64)), "somemail.com", "select"); MimeMessage mimeMessage = EmailConverter.emailToMimeMessage(emailPopulatingBuilder.buildEmail()); // success, signing did not produce an error assertThat(mimeMessage).isInstanceOf(DkimMessage.class); } - + @Test public void testParser() throws Exception { final EmailPopulatingBuilder emailPopulatingBuilderNormal = EmailHelper.createDummyEmailBuilder(true, false, false); - + // let's try producing and then consuming a MimeMessage -> // (bounce recipient is not part of the Mimemessage, but the Envelope and is configured on the Session, so just ignore this) emailPopulatingBuilderNormal.clearBounceTo(); @@ -284,28 +273,31 @@ public void testParser() final MimeMessage mimeMessage = EmailConverter.emailToMimeMessage(emailNormal); final Email emailFromMimeMessage = EmailConverter.mimeMessageToEmail(mimeMessage); - + assertThat(emailFromMimeMessage).isEqualTo(emailNormal); } - + private Mailer createFullyConfiguredMailer(boolean authenticateProxy, String prefix, TransportStrategy transportStrategy) { - ServerConfig serverConfig = new ServerConfig(prefix + "smtp host", 25, prefix + "username smtp", prefix + "password smtp"); - ProxyConfig proxyConfigAnon = new ProxyConfig(prefix + "proxy host", 1080); - ProxyConfig proxyConfigAuth = new ProxyConfig(prefix + "proxy host", 1080, prefix + "username proxy", prefix + "password proxy"); - proxyConfigAuth.setProxyBridgePort(999); - - ProxyConfig proxyBypassingMock = Mockito.mock(ProxyConfig.class); - when(proxyBypassingMock.requiresProxy()).thenReturn(false); - - Mailer mailer = transportStrategy == SMTP_TLS - ? new Mailer(serverConfig, transportStrategy, authenticateProxy ? proxyConfigAuth : proxyConfigAnon) - : new Mailer(serverConfig, transportStrategy, proxyBypassingMock); // SLL doesn't support proxy and defaults include proxy - - mailer.setDebug(true); - Properties extraProperties = new Properties(); - extraProperties.put("extra1", prefix + "value1"); - extraProperties.put("extra2", prefix + "value2"); - mailer.applyProperties(extraProperties); - return mailer; + MailerRegularBuilder mailerBuilder = MailerBuilder + .withSMTPServer(prefix + "smtp host", 25, prefix + "username smtp", prefix + "password smtp") + .withTransportStrategy(transportStrategy) + .withDebugLogging(true); + + if (transportStrategy == SMTP_TLS) { + if (authenticateProxy) { + mailerBuilder + .withProxy(prefix + "proxy host", 1080, prefix + "username proxy", prefix + "password proxy") + .withProxyBridgePort(999); + } else { + mailerBuilder.withProxy(prefix + "proxy host", 1080); + } + } else if (transportStrategy == SMTPS) { + mailerBuilder.clearProxy(); + } + + return mailerBuilder + .withProperty("extra1", prefix + "value1") + .withProperty("extra2", prefix + "value2") + .buildMailer(); } } \ No newline at end of file diff --git a/src/test/java/org/simplejavamail/mailer/ProxyConfigTest.java b/src/test/java/org/simplejavamail/mailer/ProxyConfigTest.java deleted file mode 100644 index e8c206866..000000000 --- a/src/test/java/org/simplejavamail/mailer/ProxyConfigTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package org.simplejavamail.mailer; - -import org.junit.Before; -import org.junit.Test; -import org.simplejavamail.util.ConfigLoader; -import org.simplejavamail.mailer.config.ProxyConfig; -import testutil.ConfigLoaderTestHelper; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.simplejavamail.mailer.config.ProxyConfig.DEFAULT_PROXY_BRIDGE_PORT; - -public class ProxyConfigTest { - - @Before - public void restoreOriginalStaticProperties() - throws IOException { - String s = "simplejavamail.proxy.host=proxy.default.com\n" - + "simplejavamail.proxy.port=1080\n" - + "simplejavamail.proxy.username=username proxy\n" - + "simplejavamail.proxy.password=password proxy\n" - + "simplejavamail.proxy.socks5bridge.port=1081\n"; - ConfigLoader.loadProperties(new ByteArrayInputStream(s.getBytes()), false); - } - - @Test - public void NoArgconstructor_WithoutConfigFile_WithoutHost() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - ProxyConfig emptyProxyConfig = new ProxyConfig(); - verifyProxyConfig(emptyProxyConfig, null, null, null, null, DEFAULT_PROXY_BRIDGE_PORT); - assertThat(emptyProxyConfig.requiresProxy()).isFalse(); - assertThat(emptyProxyConfig.requiresAuthentication()).isFalse(); - } - - @Test - public void NoArgconstructor_WithoutConfigFile_WithoutPort() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - try { - new ProxyConfig("host", null); - fail("IllegalArgumentException expected for proxy port"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("remoteProxyPort"); - } - try { - new ProxyConfig("host", null, null, null); - fail("IllegalArgumentException expected for proxy port"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("remoteProxyPort"); - } - } - - @Test - public void NoArgconstructor_WithoutConfigFile_AnonymousLogin() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - ProxyConfig proxyConfig = new ProxyConfig("host", 1234); - ProxyConfig proxyConfigAlternative = new ProxyConfig("host", 1234, null, null); - assertThat(proxyConfig).isEqualToComparingFieldByField(proxyConfigAlternative); - verifyProxyConfig(proxyConfig, "host", 1234, null, null, DEFAULT_PROXY_BRIDGE_PORT); - assertThat(proxyConfig.requiresProxy()).isTrue(); - assertThat(proxyConfig.requiresAuthentication()).isFalse(); - } - - @Test - public void NoArgconstructor_WithoutConfigFile_MissingPasswordOrUsername() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - - try { - new ProxyConfig("host", 1234, "username", null); - fail("IllegalArgumentException expected for password"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("password"); - } - try { - new ProxyConfig("host", 1234, null, "password"); - fail("IllegalArgumentException expected for username"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("username"); - } - } - - @Test - public void NoArgconstructor_WithoutConfigFile_Authenticated() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - ProxyConfig proxyConfig = new ProxyConfig("host", 1234, "username", "password"); - verifyProxyConfig(proxyConfig, "host", 1234, "username", "password", DEFAULT_PROXY_BRIDGE_PORT); - assertThat(proxyConfig.requiresProxy()).isTrue(); - assertThat(proxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void NoArgconstructor_WithConfigFile_WithoutHost() { - ProxyConfig emptyProxyConfig = new ProxyConfig(); - verifyProxyConfig(emptyProxyConfig, "proxy.default.com", 1080, "username proxy", "password proxy", 1081); - assertThat(emptyProxyConfig.requiresProxy()).isTrue(); - assertThat(emptyProxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void NoArgconstructor_WithConfigFile_WithoutPort() { - ProxyConfig emptyProxyConfig = new ProxyConfig("host", null); - verifyProxyConfig(emptyProxyConfig, "host", 1080, "username proxy", "password proxy", 1081); - emptyProxyConfig = new ProxyConfig("host", null, null, null); - verifyProxyConfig(emptyProxyConfig, "host", 1080, "username proxy", "password proxy", 1081); - assertThat(emptyProxyConfig.requiresProxy()).isTrue(); - assertThat(emptyProxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void NoArgconstructor_WithConfigFile_AnonymousLogin() - throws Exception { - ProxyConfig proxyConfig = new ProxyConfig("host", 1234); - ProxyConfig proxyConfigAlternative = new ProxyConfig("host", 1234, null, null); - assertThat(proxyConfig).isEqualToComparingFieldByField(proxyConfigAlternative); - verifyProxyConfig(proxyConfig, "host", 1234, "username proxy", "password proxy", 1081); - assertThat(proxyConfig.requiresProxy()).isTrue(); - assertThat(proxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void NoArgconstructor_WithConfigFile_MissingPasswordOrUsername() { - ProxyConfig proxyConfig = new ProxyConfig("host", 1234, "username", null); - verifyProxyConfig(proxyConfig, "host", 1234, "username", "password proxy", 1081); - proxyConfig = new ProxyConfig("host", 1234, null, "password"); - verifyProxyConfig(proxyConfig, "host", 1234, "username proxy", "password", 1081); - assertThat(proxyConfig.requiresProxy()).isTrue(); - assertThat(proxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void NoArgconstructor_WithConfigFile_Authenticated() - throws Exception { - ProxyConfig proxyConfig = new ProxyConfig("host", 1234, "username", "password"); - verifyProxyConfig(proxyConfig, "host", 1234, "username", "password", 1081); - assertThat(proxyConfig.requiresProxy()).isTrue(); - assertThat(proxyConfig.requiresAuthentication()).isTrue(); - } - - @Test - public void testToString() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - ProxyConfig proxyConfig = new ProxyConfig(); - assertThat(proxyConfig.toString()).isEqualTo("no-proxy"); - proxyConfig = new ProxyConfig("host", 1234, null, null); - assertThat(proxyConfig.toString()).isEqualTo("host:1234"); - proxyConfig = new ProxyConfig("host", 1234, "username", "password"); - assertThat(proxyConfig.toString()).isEqualTo("host:1234, username: username"); - proxyConfig.setProxyBridgePort(999); - assertThat(proxyConfig.toString()).isEqualTo("host:1234, username: username, proxy bridge @ localhost:999"); - } - - private void verifyProxyConfig(ProxyConfig proxyConfig, String host, Integer port, String username, String password, int defaultProxyBridgePort) { - assertThat(proxyConfig.getRemoteProxyHost()).isEqualTo(host); - assertThat(proxyConfig.getRemoteProxyPort()).isEqualTo(port); - assertThat(proxyConfig.getUsername()).isEqualTo(username); - assertThat(proxyConfig.getPassword()).isEqualTo(password); - assertThat(proxyConfig.getProxyBridgePort()).isEqualTo(defaultProxyBridgePort); - } - -} \ No newline at end of file diff --git a/src/test/java/org/simplejavamail/mailer/ServerConfigTest.java b/src/test/java/org/simplejavamail/mailer/ServerConfigTest.java index ab9f9b4d8..9034216b6 100644 --- a/src/test/java/org/simplejavamail/mailer/ServerConfigTest.java +++ b/src/test/java/org/simplejavamail/mailer/ServerConfigTest.java @@ -3,7 +3,6 @@ import org.junit.Before; import org.junit.Test; import org.simplejavamail.util.ConfigLoader; -import org.simplejavamail.mailer.config.ServerConfig; import testutil.ConfigLoaderTestHelper; import java.io.ByteArrayInputStream; @@ -28,24 +27,6 @@ public void restoreOriginalStaticProperties() public void NoArgconstructor_WithoutConfigFile_WithoutHost() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); - try { - new ServerConfig(); - fail("IllegalArgumentException expected for host"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("host address"); - } - try { - new ServerConfig(null, null); - fail("IllegalArgumentException expected for host"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("host address"); - } - try { - new ServerConfig(null, null, null); - fail("IllegalArgumentException expected for host"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("host address"); - } try { new ServerConfig(null, null, null, null); fail("IllegalArgumentException expected for host"); @@ -58,18 +39,6 @@ public void NoArgconstructor_WithoutConfigFile_WithoutHost() public void NoArgconstructor_WithoutConfigFile_WithoutPort() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); - try { - new ServerConfig("host", null); - fail("IllegalArgumentException expected for port"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("host port"); - } - try { - new ServerConfig("host", null, null); - fail("IllegalArgumentException expected for port"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).containsIgnoringCase("host port"); - } try { new ServerConfig("host", null, null, null); fail("IllegalArgumentException expected for port"); @@ -78,24 +47,11 @@ public void NoArgconstructor_WithoutConfigFile_WithoutPort() } } - @Test - public void NoArgconstructor_WithoutConfigFile_AnonymousLogin() - throws Exception { - ConfigLoaderTestHelper.clearConfigProperties(); - ServerConfig serverConfig = new ServerConfig("host", 1234); - ServerConfig serverConfigAlternative1 = new ServerConfig("host", 1234, null); - ServerConfig serverConfigAlternative2 = new ServerConfig("host", 1234, null, null); - assertThat(serverConfig).isEqualToComparingFieldByField(serverConfigAlternative1).isEqualToComparingFieldByField(serverConfigAlternative2); - verifyServerConfig(serverConfig, "host", 1234, null, null); - } - @Test public void NoArgconstructor_WithoutConfigFile_MissingPasswordOrUsername() throws Exception { ConfigLoaderTestHelper.clearConfigProperties(); ServerConfig serverConfig = new ServerConfig("host", 1234, "username", null); - ServerConfig serverConfigAlternative = new ServerConfig("host", 1234, "username"); - assertThat(serverConfig).isEqualToComparingFieldByField(serverConfigAlternative); verifyServerConfig(serverConfig, "host", 1234, "username", null); try { @@ -114,45 +70,6 @@ public void NoArgconstructor_WithoutConfigFile_Authenticated() verifyServerConfig(serverConfig, "host", 1234, "username", "password"); } - @Test - public void NoArgconstructor_WithConfigFile_WithoutHost() { - ServerConfig emptyServerConfig = new ServerConfig(); - verifyServerConfig(emptyServerConfig, "smtp.default.com", 25, "username smtp", "password smtp"); - } - - @Test - public void NoArgconstructor_WithConfigFile_WithoutPort() { - ServerConfig emptyServerConfig = new ServerConfig("host", null); - verifyServerConfig(emptyServerConfig, "host", 25, "username smtp", "password smtp"); - emptyServerConfig = new ServerConfig("host", null, null, null); - verifyServerConfig(emptyServerConfig, "host", 25, "username smtp", "password smtp"); - } - - @Test - public void NoArgconstructor_WithConfigFile_AnonymousLogin() - throws Exception { - ServerConfig serverConfig = new ServerConfig("host", 1234); - ServerConfig serverConfigAlternative1 = new ServerConfig("host", 1234, null); - ServerConfig serverConfigAlternative2 = new ServerConfig("host", 1234, null, null); - assertThat(serverConfig).isEqualToComparingFieldByField(serverConfigAlternative1).isEqualToComparingFieldByField(serverConfigAlternative2); - verifyServerConfig(serverConfig, "host", 1234, "username smtp", "password smtp"); - } - - @Test - public void NoArgconstructor_WithConfigFile_MissingPasswordOrUsername() { - ServerConfig serverConfig = new ServerConfig("host", 1234, "username", null); - verifyServerConfig(serverConfig, "host", 1234, "username", "password smtp"); - serverConfig = new ServerConfig("host", 1234, null, "password"); - verifyServerConfig(serverConfig, "host", 1234, "username smtp", "password"); - } - - @Test - public void NoArgconstructor_WithConfigFile_Authenticated() - throws Exception { - ServerConfig serverConfig = new ServerConfig("host", 1234, "username", "password"); - verifyServerConfig(serverConfig, "host", 1234, "username", "password"); - } - @Test public void testToString() throws Exception { @@ -171,5 +88,4 @@ private void verifyServerConfig(ServerConfig serverConfig, String host, Integer assertThat(serverConfig.getUsername()).isEqualTo(username); assertThat(serverConfig.getPassword()).isEqualTo(password); } - } \ No newline at end of file diff --git a/src/test/java/org/simplejavamail/mailer/internal/mailsender/MailSenderTest.java b/src/test/java/org/simplejavamail/mailer/internal/mailsender/MailSenderTest.java index 835b6bcf6..c7814759a 100644 --- a/src/test/java/org/simplejavamail/mailer/internal/mailsender/MailSenderTest.java +++ b/src/test/java/org/simplejavamail/mailer/internal/mailsender/MailSenderTest.java @@ -1,15 +1,16 @@ package org.simplejavamail.mailer.internal.mailsender; -import org.assertj.core.api.ThrowableAssert; import org.junit.Before; import org.junit.Test; -import org.simplejavamail.mailer.config.ProxyConfig; +import javax.annotation.Nonnull; import javax.mail.Session; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Properties; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.simplejavamail.mailer.config.TransportStrategy.SMTP; @@ -19,39 +20,23 @@ public class MailSenderTest { private Session session; + private static final List EMPTY_LIST = Collections.emptyList(); + @Before public void setup() { session = Session.getDefaultInstance(new Properties()); } - @Test - public void setDebug() { - MailSender mailSender = new MailSender(session, null, null); - mailSender.setDebug(true); - assertThat(session.getDebug()).isTrue(); - mailSender.setDebug(false); - assertThat(session.getDebug()).isFalse(); - } - - @Test - public void trustHosts_WithoutTransportStrategy() { - assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override - public void call() throws Throwable { - new MailSender(session, null, null).trustHosts(); - } - }) - .isInstanceOf(MailSenderException.class) - .hasMessage("Cannot determine the trust properties to set without a provided transport strategy"); - + @Nonnull + private ProxyConfig createEmptyProxyConfig() { + return new ProxyConfig(null, null, null, null, -1); } @Test public void trustAllHosts_PLAIN() { - MailSender mailSender = new MailSender(session, null, SMTP); - mailSender.trustAllHosts(true); + new MailSender(session, createDummyOperationalConfig(EMPTY_LIST, true), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("*"); - mailSender.trustAllHosts(false); + new MailSender(session, createDummyOperationalConfig(EMPTY_LIST, false), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isNull(); } @@ -59,23 +44,31 @@ public void trustAllHosts_PLAIN() { public void trustAllHosts_SMTPS() { ProxyConfig proxyBypassingMock = mock(ProxyConfig.class); when(proxyBypassingMock.requiresProxy()).thenReturn(false); - MailSender mailSender = new MailSender(session, proxyBypassingMock, SMTPS); - mailSender.trustAllHosts(true); + new MailSender(session, createDummyOperationalConfig(EMPTY_LIST, true), proxyBypassingMock, SMTPS); assertThat(session.getProperties().getProperty("mail.smtps.ssl.trust")).isEqualTo("*"); - mailSender.trustAllHosts(false); + new MailSender(session, createDummyOperationalConfig(EMPTY_LIST, false), proxyBypassingMock, SMTPS); assertThat(session.getProperties().getProperty("mail.smtps.ssl.trust")).isNull(); } @Test public void trustHosts() { - MailSender mailSender = new MailSender(session, null, SMTP); - mailSender.trustHosts(); + new MailSender(session, createDummyOperationalConfig(asList(), false), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isNull(); - mailSender.trustHosts("a"); + new MailSender(session, createDummyOperationalConfig(asList("a"), false), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a"); - mailSender.trustHosts("a", "b"); + new MailSender(session, createDummyOperationalConfig(asList("a", "b"), false), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a b"); - mailSender.trustHosts("a", "b", "c"); + new MailSender(session, createDummyOperationalConfig(asList("a", "b", "c"), false), createEmptyProxyConfig(), SMTP); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a b c"); } + + @Nonnull + private List asList(String... args) { + return Arrays.asList(args); + } + + @Nonnull + private OperationalConfig createDummyOperationalConfig(List hostsToTrust, boolean trustAllSSLHost) { + return new OperationalConfig(new Properties(), 0, 0, false, false, hostsToTrust, trustAllSSLHost); + } } \ No newline at end of file diff --git a/src/test/java/testutil/testrules/TestSmtpServer.java b/src/test/java/testutil/testrules/TestSmtpServer.java index ed5532f0f..28943a33c 100644 --- a/src/test/java/testutil/testrules/TestSmtpServer.java +++ b/src/test/java/testutil/testrules/TestSmtpServer.java @@ -1,24 +1,25 @@ package testutil.testrules; -import org.simplejavamail.mailer.config.ServerConfig; - import javax.annotation.Nonnull; public class TestSmtpServer implements SmtpServerSupport { - private final ServerConfig serverConfig; - - public TestSmtpServer(final ServerConfig serverConfig) { - this.serverConfig = serverConfig; + + private final String host; + private final Integer port; + + public TestSmtpServer(final String host, final Integer port) { + this.host = host; + this.port = port; } - + + @Nonnull @Override - public int getPort() { - return serverConfig.getPort(); + public String getHostname() { + return host; } - @Nonnull @Override - public String getHostname() { - return serverConfig.getHost(); + public int getPort() { + return port; } } \ No newline at end of file