Skip to content

Commit 349cd24

Browse files
committed
WIP. Completely moved defaults/overrides to Mailer level in EmailGovernanceImpl. Added option to Email to completely ignore defaults/overrides when sending emails (or resolving it manually using a EmailGovernance instance). Added builder api to build email as normal but also to build an email resolving defaults/overrides right away. Added temporary type EmailWithDefaultsAndOverridesApplied to make sure during this refactoring at which point we internally completed the email (this will be removed again). Moved some non-user Email api to new sub class InternalEmail
1 parent a3b1a9f commit 349cd24

File tree

45 files changed

+784
-447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+784
-447
lines changed

modules/cli-module/src/main/java/org/simplejavamail/internal/clisupport/CliCommandLineConsumerResultHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private static void processCliTestConnection(List<CliReceivedOptionData> receive
5050
private static void processCliValidate(List<CliReceivedOptionData> receivedOptions) {
5151
final EmailPopulatingBuilder emailBuilder = invokeBuilderApi(receivedOptions, CliBuilderApiType.EMAIL, new EmailStartingBuilderImpl());
5252
final MailerGenericBuilder<?> mailerBuilder = invokeBuilderApi(receivedOptions, CliBuilderApiType.MAILER, new MailerRegularBuilderImpl());
53-
mailerBuilder.buildMailer().validate(emailBuilder.buildEmail());
53+
mailerBuilder.buildMailer().validate(emailBuilder.buildEmailCompletedWithDefaultsAndOverrides());
5454
}
5555

5656
@SuppressWarnings("unchecked")

modules/core-module/src/main/java/org/simplejavamail/api/email/ContentTransferEncoding.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ public static ContentTransferEncoding byEncoder(@NotNull final String encoder) {
4040
public String toString() {
4141
return encoder;
4242
}
43-
}
43+
44+
public static ContentTransferEncoding getDefault() {
45+
return QUOTED_PRINTABLE;
46+
}
47+
}

modules/core-module/src/main/java/org/simplejavamail/api/email/Email.java

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ public class Email implements Serializable {
4141

4242
private static final long serialVersionUID = 1234567L;
4343

44+
/**
45+
* @see EmailPopulatingBuilder#ignoringDefaults(boolean)
46+
*/
47+
private final boolean ignoreDefaults;
48+
49+
/**
50+
* @see EmailPopulatingBuilder#ignoringOverrides(boolean)
51+
*/
52+
private final boolean ignoreOverrides;
53+
4454
/**
4555
* @see EmailPopulatingBuilder#dontApplyDefaultValueFor(EmailProperty...)
4656
*/
@@ -54,7 +64,7 @@ public class Email implements Serializable {
5464
/**
5565
* @see EmailPopulatingBuilder#fixingMessageId(String)
5666
*/
57-
private String id;
67+
protected String id;
5868

5969
/**
6070
* @see EmailPopulatingBuilder#from(Recipient)
@@ -94,7 +104,7 @@ public class Email implements Serializable {
94104
/**
95105
* @see EmailPopulatingBuilder#withContentTransferEncoding(ContentTransferEncoding)
96106
*/
97-
@NotNull
107+
@Nullable
98108
private final ContentTransferEncoding contentTransferEncoding;
99109

100110
/**
@@ -200,9 +210,9 @@ public class Email implements Serializable {
200210
private final OriginalSmimeDetails originalSmimeDetails;
201211

202212
/**
203-
* @see Email#wasMergedWithSmimeSignedMessage()
213+
* @see "ExtendedEmail.wasMergedWithSmimeSignedMessage()"
204214
*/
205-
private final boolean wasMergedWithSmimeSignedMessage;
215+
protected final boolean wasMergedWithSmimeSignedMessage;
206216

207217
/**
208218
* @see EmailPopulatingBuilder#fixingSentDate(Date)
@@ -218,6 +228,8 @@ public class Email implements Serializable {
218228
public Email(@NotNull final EmailPopulatingBuilder builder) {
219229
checkNonEmptyArgument(builder, "builder");
220230

231+
ignoreDefaults = builder.isIgnoreDefaults();
232+
ignoreOverrides = builder.isIgnoreOverrides();
221233
propertiesNotToApplyDefaultValueFor = builder.getPropertiesNotToApplyDefaultValueFor();
222234
propertiesNotToApplyOverrideValueFor = builder.getPropertiesNotToApplyOverrideValueFor();
223235
smimeSignedEmail = builder.getSmimeSignedEmail();
@@ -287,25 +299,6 @@ public Email(@NotNull final EmailPopulatingBuilder builder) {
287299
this.dkimConfig = builder.getDkimConfig();
288300
}
289301

290-
/**
291-
* @deprecated Don't use this method, refer to {@link EmailPopulatingBuilder#fixingMessageId(String)} instead. This method is used internally to
292-
* update the message id once a mail has been sent.
293-
*/
294-
@Deprecated
295-
public void internalSetId(@NotNull final String id) {
296-
this.id = id;
297-
}
298-
299-
/**
300-
* @deprecated Don't use this method. This method is used internally when using the builder API to copy an email that
301-
* contains an S/MIME signed message. Without this method, we don't know if the copy should also be merged to match the
302-
* copied email.
303-
*/
304-
@Deprecated
305-
public boolean wasMergedWithSmimeSignedMessage() {
306-
return wasMergedWithSmimeSignedMessage;
307-
}
308-
309302
@SuppressWarnings("SameReturnValue")
310303
@Override
311304
public int hashCode() {
@@ -328,7 +321,7 @@ public String toString() {
328321
",\n\ttext='" + text + '\'' +
329322
",\n\ttextHTML='" + textHTML + '\'' +
330323
",\n\ttextCalendar='" + format("%s (method: %s)", textCalendar, calendarMethod) + '\'' +
331-
",\n\tcontentTransferEncoding='" + contentTransferEncoding + '\'' +
324+
",\n\tcontentTransferEncoding='" + (contentTransferEncoding != null ? contentTransferEncoding : ContentTransferEncoding.getDefault()) + '\'' +
332325
",\n\tsubject='" + subject + '\'' +
333326
",\n\trecipients=" + recipients);
334327
if (!MiscUtil.valueNullOrEmpty(dkimConfig)) {
@@ -388,6 +381,20 @@ private String formatDate(@Nullable Date date) {
388381
return sdf.format(date);
389382
}
390383

384+
/**
385+
* @see EmailPopulatingBuilder#ignoringDefaults(boolean)
386+
*/
387+
public boolean isIgnoreDefaults() {
388+
return ignoreDefaults;
389+
}
390+
391+
/**
392+
* @see EmailPopulatingBuilder#ignoringOverrides(boolean)
393+
*/
394+
public boolean isIgnoreOverrides() {
395+
return ignoreOverrides;
396+
}
397+
391398
/**
392399
* @see EmailPopulatingBuilder#dontApplyDefaultValueFor(EmailProperty...)
393400
*/
@@ -647,7 +654,7 @@ public Date getSentDate() {
647654
/**
648655
* @see EmailPopulatingBuilder#withContentTransferEncoding(ContentTransferEncoding)
649656
*/
650-
@NotNull
657+
@Nullable
651658
public ContentTransferEncoding getContentTransferEncoding() {
652659
return contentTransferEncoding;
653660
}

modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import org.simplejavamail.api.email.config.DkimConfig;
1111
import org.simplejavamail.api.internal.clisupport.model.Cli;
1212
import org.simplejavamail.api.internal.clisupport.model.CliBuilderApiType;
13+
import org.simplejavamail.api.mailer.Mailer;
14+
import org.simplejavamail.api.mailer.config.EmailGovernance;
1315
import org.simplejavamail.api.mailer.config.Pkcs12Config;
1416
import org.simplejavamail.internal.config.EmailProperty;
1517

@@ -44,11 +46,61 @@ public interface EmailPopulatingBuilder {
4446
Pattern IMG_SRC_PATTERN = compile("(?<imageTagStart><[Ii][Mm][Gg]\\s*[^>]*?\\s+[Ss][Rr][Cc]\\s*=\\s*[\"'])(?<src>[^\"']+?)(?<imageSrcEnd>[\"'])");
4547

4648
/**
47-
* Validated DKIM values and then delegates to {@link Email#Email(EmailPopulatingBuilder)} with <code>this</code> as argument.
49+
* Validated DKIM values and then delegates to {@link Email#Email(EmailPopulatingBuilder)} with <code>this</code> as argument. This results in an Email instance with
50+
* just the values set on this builder by the user. <strong>This is the regular use case and the common way to send emails using a {@link Mailer} instance.</strong>
51+
* <p>
52+
* If you don't have a Mailer instance or you just want to call helper methods that only accept an {@link EmailWithDefaultsAndOverridesApplied}, there are two ways
53+
* to complete this Email with defaults and overrides that you may have configured as (system) properties (files):
54+
* <ol>
55+
* <li>Construct a new {@code EmailGovernanceImpl} or use {@code EmailGovernanceImpl.NO_GOVERNANCE()}, and then use {@link EmailGovernance#produceEmailApplyingDefaultsAndOverrides(Email)}</li>
56+
* <li>Don't use {@link #buildEmail()}, but use {@link #buildEmailCompletedWithDefaultsAndOverrides()} or {@link #buildEmailCompletedWithDefaultsAndOverrides(EmailGovernance)} instead</li>
57+
* </ol>
58+
* It depends on whether you like fine-grained control over email governance (validation, max email size, defaults, overrides, etc.) or not.
59+
*
60+
* @see #buildEmailCompletedWithDefaultsAndOverrides(EmailGovernance)
4861
*/
62+
@SuppressWarnings("JavadocDeclaration")
4963
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
5064
Email buildEmail();
5165

66+
/**
67+
* Delegates to {@link #buildEmailCompletedWithDefaultsAndOverrides(EmailGovernance)} with an empty default email governance, which
68+
* will still apply default config (System) properties (files).
69+
*/
70+
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
71+
EmailWithDefaultsAndOverridesApplied buildEmailCompletedWithDefaultsAndOverrides();
72+
73+
/**
74+
* Like {@link #buildEmail()}, but returning the final email version right away. Useful if you don't use a Mailer which works with email governance,
75+
* or just want to call a helper method that only accepts {@link EmailWithDefaultsAndOverridesApplied}. For regular cases of just sending with a {@link Mailer}, this
76+
* completion stage happens automatically when converting to MimeMessage. In that case use {@link #buildEmail()} instead.
77+
* <p>
78+
* Why not always apply defaults and overrides? Because it would be a waste of resources to do so, especially when you are sending multiple emails resuing a mailer instance.
79+
* Another use case is that when using a server cluster with the batch-module (multiple mailer instances), defaults set during sending ensure that the defaults set for a
80+
* specific mailer are used. This is sometimes important if an SMTP server needs a specific sender address, or if you want to use a specific DKIM signature bound to that server.
81+
*
82+
* @param emailGovernance The email governance to use for this email. It determines which default values and overrides to apply, what the maximum
83+
* email size is, etc.
84+
*/
85+
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
86+
EmailWithDefaultsAndOverridesApplied buildEmailCompletedWithDefaultsAndOverrides(@NotNull EmailGovernance emailGovernance);
87+
88+
/**
89+
* Indicates that when the email is sent, no default values whatsoever should be applied to the email.
90+
*
91+
* @see #dontApplyDefaultValueFor(EmailProperty...)
92+
* @see org.simplejavamail.api.mailer.MailerRegularBuilder#withEmailDefaults(Email)
93+
*/
94+
EmailPopulatingBuilder ignoringDefaults(boolean ignoreDefaults);
95+
96+
/**
97+
* Indicates that when the email is sent, no override values whatsoever should be applied to the email.
98+
*
99+
* @see #dontApplyOverrideValueFor(EmailProperty...)
100+
* @see org.simplejavamail.api.mailer.MailerRegularBuilder#withEmailOverrides(Email)
101+
*/
102+
EmailPopulatingBuilder ignoringOverrides(boolean ignoreDefaults);
103+
52104
/**
53105
* Allows you to prevent a property to be configured with default values. This might be useful if you have defined defaults (either through (system) properties,
54106
* config files, or on mailer level on the {@link org.simplejavamail.api.mailer.config.EmailGovernance}), but for a specific case or under certain conditions
@@ -1536,6 +1588,16 @@ public interface EmailPopulatingBuilder {
15361588
*/
15371589
EmailPopulatingBuilder clearSMIMESignedAttachmentMergingBehavior();
15381590

1591+
/**
1592+
* @see #ignoringDefaults(boolean)
1593+
*/
1594+
boolean isIgnoreDefaults();
1595+
1596+
/**
1597+
* @see #ignoringOverrides(boolean)
1598+
*/
1599+
boolean isIgnoreOverrides();
1600+
15391601
/**
15401602
* @see #dontApplyDefaultValueFor(EmailProperty...)
15411603
*/
@@ -1600,6 +1662,7 @@ public interface EmailPopulatingBuilder {
16001662
* @see #withContentTransferEncoding(ContentTransferEncoding)
16011663
* @see #clearContentTransferEncoding()
16021664
*/
1665+
@Nullable
16031666
ContentTransferEncoding getContentTransferEncoding();
16041667

16051668
/**

modules/core-module/src/main/java/org/simplejavamail/api/email/EmailStartingBuilder.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,30 @@ public interface EmailStartingBuilder {
3535
*/
3636
String DEFAULT_QUOTING_MARKUP = "<blockquote style=\"color: gray; border-left: 1px solid #4f4f4f; padding-left: " +
3737
"1cm\">%s</blockquote>";
38-
38+
3939
/**
4040
* Configures this builder to create an email ignoring the all defaults from (System) properties, config files or defaults email on
41-
* Mailer level in the {@link org.simplejavamail.api.mailer.config.EmailGovernance}.
41+
* Mailer level in the {@link org.simplejavamail.api.mailer.config.EmailGovernance}. You can make individual exceptions with
42+
* * {@link EmailPopulatingBuilder#dontApplyDefaultValueFor(EmailProperty...)}
4243
* <br>
4344
* <strong>Note:</strong> This is irrelevant for Email instances used to set on {@link org.simplejavamail.api.mailer.config.EmailGovernance}
4445
* as defaults or overrides reference.
4546
*
4647
* @see EmailPopulatingBuilder#dontApplyDefaultValueFor(EmailProperty...)
4748
*/
4849
EmailStartingBuilder ignoringDefaults();
50+
51+
/**
52+
* Configures this builder to create an email ignoring the all overrides from (System) properties, config files or defaults email on
53+
* Mailer level in the {@link org.simplejavamail.api.mailer.config.EmailGovernance}. You can make individual exceptions with
54+
* {@link EmailPopulatingBuilder#dontApplyOverrideValueFor(EmailProperty...)}
55+
* <br>
56+
* <strong>Note:</strong> This is irrelevant for Email instances used to set on {@link org.simplejavamail.api.mailer.config.EmailGovernance}
57+
* as defaults or overrides reference.
58+
*
59+
* @see EmailPopulatingBuilder#dontApplyOverrideValueFor(EmailProperty...)
60+
*/
61+
EmailStartingBuilder ignoringOverrides();
4962

5063
/**
5164
* Most common use case for creating a new email. Starts with an empty email, populated with defaults when set through config properties (if
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.simplejavamail.api.email;
2+
3+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4+
import lombok.Getter;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.experimental.Delegate;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
@RequiredArgsConstructor
10+
@Getter
11+
// FIXME temporary workaround to make sure we update all the calls that require a final Email, with a fcall to EmailGovernance.produceFinalEmail() first
12+
@SuppressFBWarnings("EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WI")
13+
public class EmailWithDefaultsAndOverridesApplied {
14+
@Delegate
15+
@NotNull Email delegate;
16+
17+
public int hashCode() {
18+
return 0;
19+
}
20+
}

modules/core-module/src/main/java/org/simplejavamail/api/email/config/DkimConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.simplejavamail.api.email.config;
22

3+
import lombok.EqualsAndHashCode;
34
import lombok.Getter;
45
import lombok.ToString;
56
import org.jetbrains.annotations.Nullable;
@@ -10,7 +11,6 @@
1011
import java.io.IOException;
1112
import java.io.InputStream;
1213
import java.io.Serializable;
13-
import java.util.Arrays;
1414
import java.util.HashSet;
1515
import java.util.Set;
1616

@@ -25,6 +25,7 @@
2525
*/
2626
@ToString(exclude = "dkimPrivateKeyData")
2727
@Getter
28+
@EqualsAndHashCode
2829
public class DkimConfig implements Serializable {
2930

3031
private static final long serialVersionUID = 1234567L;

modules/core-module/src/main/java/org/simplejavamail/api/mailer/CustomMailer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import jakarta.mail.Session;
44
import jakarta.mail.internet.MimeMessage;
55
import org.jetbrains.annotations.NotNull;
6-
import org.simplejavamail.api.email.Email;
6+
import org.simplejavamail.api.email.EmailWithDefaultsAndOverridesApplied;
77
import org.simplejavamail.api.mailer.config.OperationalConfig;
88

99
/**
@@ -20,5 +20,5 @@
2020
*/
2121
public interface CustomMailer {
2222
void testConnection(@NotNull OperationalConfig operationalConfig, @NotNull Session session);
23-
void sendMessage(@NotNull OperationalConfig operationalConfig, @NotNull Session session, @NotNull Email email, @NotNull MimeMessage message);
23+
void sendMessage(@NotNull OperationalConfig operationalConfig, @NotNull Session session, @NotNull EmailWithDefaultsAndOverridesApplied email, @NotNull MimeMessage message);
2424
}

modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.jetbrains.annotations.Nullable;
88
import org.simplejavamail.MailException;
99
import org.simplejavamail.api.email.Email;
10+
import org.simplejavamail.api.email.EmailWithDefaultsAndOverridesApplied;
1011
import org.simplejavamail.api.mailer.config.EmailGovernance;
1112
import org.simplejavamail.api.mailer.config.OperationalConfig;
1213
import org.simplejavamail.api.mailer.config.ProxyConfig;
@@ -62,7 +63,9 @@ public interface Mailer {
6263
@NotNull CompletableFuture<Void> sendMail(Email email);
6364

6465
/**
65-
* Processes an {@link Email} instance into a completely configured {@link Message}.
66+
* Processes an {@link Email} instance into a completely configured {@link Message}. First, it will apply all defaults and overrides to the email
67+
* instance using {@link EmailGovernance#produceEmailApplyingDefaultsAndOverrides(Email)} . Then it will validate the email. Finally, it will process
68+
* the email into a JavaMail {@link Message} object.
6669
* <p>
6770
* Sends the JavaMail {@link Message} object using {@link Session#getTransport()}. It will call {@link Transport#connect()} assuming all
6871
* connection details have been configured in the provided {@link Session} instance and finally {@link Transport#sendMessage(Message,
@@ -83,7 +86,7 @@ public interface Mailer {
8386
* @return A {@link CompletableFuture} that is completed immediately if not <em>async</em>.
8487
* @throws MailException Can be thrown if an email isn't validating correctly, or some other problem occurs during connection, sending etc.
8588
* @see java.util.concurrent.Executors#newFixedThreadPool(int)
86-
* @see #validate(Email)
89+
* @see #validate(EmailWithDefaultsAndOverridesApplied)
8790
*/
8891
@NotNull CompletableFuture<Void> sendMail(Email email, @SuppressWarnings("SameParameterValue") boolean async);
8992

@@ -93,10 +96,10 @@ public interface Mailer {
9396
* <p>
9497
* It also checks for illegal characters that would facilitate injection attacks:
9598
* <ul>
96-
* <li>http://www.cakesolutions.net/teamblogs/2008/05/08/email-header-injection-security</li>
97-
* <li>https://security.stackexchange.com/a/54100/110048</li>
98-
* <li>https://www.owasp.org/index.php/Testing_for_IMAP/SMTP_Injection_(OTG-INPVAL-011)</li>
99-
* <li>http://cwe.mitre.org/data/definitions/93.html</li>
99+
* <li><a href="http://www.cakesolutions.net/teamblogs/2008/05/08/email-header-injection-security">http://www.cakesolutions.net/teamblogs/2008/05/08/email-header-injection-security</a></li>
100+
* <li><a href="https://security.stackexchange.com/a/54100/110048">https://security.stackexchange.com/a/54100/110048</a></li>
101+
* <li><a href="https://www.owasp.org/index.php/Testing_for_IMAP/SMTP_Injection_(OTG-INPVAL-011)">https://www.owasp.org/index.php/Testing_for_IMAP/SMTP_Injection_(OTG-INPVAL-011)</a></li>
102+
* <li><a href="http://cwe.mitre.org/data/definitions/93.html">http://cwe.mitre.org/data/definitions/93.html</a></li>
100103
* </ul>
101104
*
102105
* @param email The email that needs to be configured correctly.
@@ -106,7 +109,7 @@ public interface Mailer {
106109
* @see com.sanctionco.jmail.EmailValidator
107110
*/
108111
@SuppressWarnings({"SameReturnValue" })
109-
boolean validate(Email email) throws MailException;
112+
boolean validate(EmailWithDefaultsAndOverridesApplied email) throws MailException;
110113

111114
/**
112115
* Shuts down the connection pool associated with this {@link Mailer} instance and closes remaining open connections. Waits until all connections still in use become available again

0 commit comments

Comments
 (0)