Skip to content

Commit ff5228b

Browse files
committed
1. Moved determining dispositionNotificationTo and ReturnReceiptTo to EmailGovernanceImpl, since the fall-back options (replyTo and From) can only be considered when the defaults/overrides have been applied. After all, values coming from there could provide different fall-back options or make them obsolete.
2. Replaced the temporary EmailWithDefaultsAndOverridesApplied with Email and switched to a runtime check to make sure defaults/overrides were applied before sending emails 3. Fixes debug logging when recipient fields are not equal in emails, so it only logs when recipient _lists_ are not equal, ignoring order 4. When using dkim property try to load key from file only if the file exists, otherwise assume the property contained the base64 itself 5. Added more specific info on what type of recipient might have failed validation (TO vs CC vs BCC) 6. Value suspected of CRLF injections are now logged with escaped newline characters, rather than the actual newline characters, messing up logging and reporting 7. Added a bunch of junit tests all over the place
1 parent 3b6a786 commit ff5228b

39 files changed

+570
-320
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public class JavadocForCliFormatter extends ContextualCommentFormatter {
3434
super(nestingDepth);
3535
}
3636

37-
@SuppressWarnings("StringConcatenationInLoop")
3837
@Override
3938
@NotNull
4039
public String format(Comment comment) {

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

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ public Email(@NotNull final EmailPopulatingBuilder builder) {
236236

237237
final boolean smimeMerge = builder.isMergeSingleSMIMESignedAttachment() && smimeSignedEmail != null;
238238

239-
this.wasMergedWithSmimeSignedMessage = smimeMerge;
240-
239+
wasMergedWithSmimeSignedMessage = smimeMerge;
241240
recipients = unmodifiableList(builder.getRecipients());
242241
embeddedImages = unmodifiableList((smimeMerge)
243242
? merge(builder.getEmbeddedImages(), smimeSignedEmail.getEmbeddedImages())
@@ -251,7 +250,6 @@ public Email(@NotNull final EmailPopulatingBuilder builder) {
251250
headers = unmodifiableMap((smimeMerge)
252251
? merge(builder.getHeaders(), smimeSignedEmail.getHeaders())
253252
: builder.getHeaders());
254-
255253
id = builder.getId();
256254
fromRecipient = builder.getFromRecipient();
257255
replyToRecipient = builder.getReplyToRecipient();
@@ -262,41 +260,16 @@ public Email(@NotNull final EmailPopulatingBuilder builder) {
262260
textCalendar = builder.getTextCalendar();
263261
contentTransferEncoding = builder.getContentTransferEncoding();
264262
subject = builder.getSubject();
265-
266263
useDispositionNotificationTo = builder.getUseDispositionNotificationTo();
264+
dispositionNotificationTo = builder.getDispositionNotificationTo();
267265
useReturnReceiptTo = builder.getUseReturnReceiptTo();
266+
returnReceiptTo = builder.getReturnReceiptTo();
268267
emailToForward = builder.getEmailToForward();
269-
270268
originalSmimeDetails = builder.getOriginalSmimeDetails();
271-
272269
sentDate = builder.getSentDate();
273-
274270
x509CertificateForSmimeEncryption = builder.getX509CertificateForSmimeEncryption();
275271
pkcs12ConfigForSmimeSigning = builder.getPkcs12ConfigForSmimeSigning();
276-
277-
if (TRUE.equals(useDispositionNotificationTo) && MiscUtil.valueNullOrEmpty(builder.getDispositionNotificationTo())) {
278-
//noinspection IfMayBeConditional
279-
if (builder.getReplyToRecipient() != null) {
280-
dispositionNotificationTo = builder.getReplyToRecipient();
281-
} else {
282-
dispositionNotificationTo = builder.getFromRecipient();
283-
}
284-
} else {
285-
dispositionNotificationTo = builder.getDispositionNotificationTo();
286-
}
287-
288-
if (TRUE.equals(useReturnReceiptTo) && MiscUtil.valueNullOrEmpty(builder.getReturnReceiptTo())) {
289-
//noinspection IfMayBeConditional
290-
if (builder.getReplyToRecipient() != null) {
291-
returnReceiptTo = builder.getReplyToRecipient();
292-
} else {
293-
returnReceiptTo = builder.getFromRecipient();
294-
}
295-
} else {
296-
returnReceiptTo = builder.getReturnReceiptTo();
297-
}
298-
299-
this.dkimConfig = builder.getDkimConfig();
272+
dkimConfig = builder.getDkimConfig();
300273
}
301274

302275
@SuppressWarnings("SameReturnValue")

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public interface EmailPopulatingBuilder {
6868
* will still apply default config (System) properties (files).
6969
*/
7070
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
71-
EmailWithDefaultsAndOverridesApplied buildEmailCompletedWithDefaultsAndOverrides();
71+
Email buildEmailCompletedWithDefaultsAndOverrides();
7272

7373
/**
7474
* Like {@link #buildEmail()}, but returning the final email version right away. Useful if you don't use a Mailer which works with email governance,
@@ -83,7 +83,7 @@ public interface EmailPopulatingBuilder {
8383
* email size is, etc.
8484
*/
8585
@Cli.ExcludeApi(reason = "This API is specifically for Java use")
86-
EmailWithDefaultsAndOverridesApplied buildEmailCompletedWithDefaultsAndOverrides(@NotNull EmailGovernance emailGovernance);
86+
Email buildEmailCompletedWithDefaultsAndOverrides(@NotNull EmailGovernance emailGovernance);
8787

8888
/**
8989
* Indicates that when the email is sent, no default values whatsoever should be applied to the email.
Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
package org.simplejavamail.api.email;
22

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-
}
3+
/**
4+
* Since defaults and overrides are not applied all the way in the beginning anymore when creating an Email instance, at the time of sending we want to
5+
* double-check that defaults and overrides are applied by that time at least. Making this a compile-time check proved to be very cumbersome and confusing
6+
* to the user, so finally we opted for a runtime check behind the scenes.
7+
*/
8+
public interface EmailWithDefaultsAndOverridesApplied {
9+
void markAsDefaultsAndOverridesApplied();
10+
void verifyDefaultsAndOverridesApplied();
2011
}

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

Lines changed: 98 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -19,106 +19,107 @@
1919
@Slf4j
2020
public final class EqualsHelper {
2121

22-
@SuppressWarnings("WeakerAccess")
23-
public static boolean equalsEmail(final Email email1, final Email email2) {
24-
if (!fieldIsEqual(email1.getFromRecipient(), email2.getFromRecipient(), "fromRecipient")) {
25-
return false;
26-
}
27-
if (!fieldIsEqual(email1.getId(), email2.getId(), "id")) {
28-
return false;
29-
}
30-
if (!fieldIsEqual(email1.getSentDate(), email2.getSentDate(), "sendDate")) {
31-
return false;
32-
}
33-
if (!fieldIsEqual(email1.getReplyToRecipient(), email2.getReplyToRecipient(), "replyToRecipient")) {
34-
return false;
35-
}
36-
if (!fieldIsEqual(email1.getBounceToRecipient(), email2.getBounceToRecipient(), "bounceToRecipient")) {
37-
return false;
38-
}
39-
if (!fieldIsEqual(email1.getPlainText(), email2.getPlainText(), "plainText")) {
40-
return false;
41-
}
42-
if (!fieldIsEqual(email1.getCalendarText(), email2.getCalendarText(), "calendarText")) {
43-
return false;
44-
}
45-
if (!fieldIsEqual(email1.getCalendarMethod(), email2.getCalendarMethod(), "calendarMethod")) {
46-
return false;
47-
}
48-
//noinspection SimplifiableConditionalExpression
49-
if (email1.getEmailToForward() != null ? email2.getEmailToForward() == null : email2.getEmailToForward() != null) {
50-
return false;
51-
}
52-
if (email1.getHTMLText() != null ? !normalizeNewlines(email1.getHTMLText()).equals(normalizeNewlines(email2.getHTMLText())) : email2.getHTMLText() != null) {
53-
return false;
54-
}
55-
if (!fieldIsEqual(email1.getSubject(), email2.getSubject(), "subject")) {
56-
return false;
57-
}
22+
@SuppressWarnings("WeakerAccess")
23+
public static boolean equalsEmail(final Email email1, final Email email2) {
24+
if (!fieldIsEqual(email1.getFromRecipient(), email2.getFromRecipient(), "fromRecipient")) {
25+
return false;
26+
}
27+
if (!fieldIsEqual(email1.getId(), email2.getId(), "id")) {
28+
return false;
29+
}
30+
if (!fieldIsEqual(email1.getSentDate(), email2.getSentDate(), "sendDate")) {
31+
return false;
32+
}
33+
if (!fieldIsEqual(email1.getReplyToRecipient(), email2.getReplyToRecipient(), "replyToRecipient")) {
34+
return false;
35+
}
36+
if (!fieldIsEqual(email1.getBounceToRecipient(), email2.getBounceToRecipient(), "bounceToRecipient")) {
37+
return false;
38+
}
39+
if (!fieldIsEqual(email1.getPlainText(), email2.getPlainText(), "plainText")) {
40+
return false;
41+
}
42+
if (!fieldIsEqual(email1.getCalendarText(), email2.getCalendarText(), "calendarText")) {
43+
return false;
44+
}
45+
if (!fieldIsEqual(email1.getCalendarMethod(), email2.getCalendarMethod(), "calendarMethod")) {
46+
return false;
47+
}
48+
//noinspection SimplifiableConditionalExpression
49+
if (email1.getEmailToForward() != null ? email2.getEmailToForward() == null : email2.getEmailToForward() != null) {
50+
return false;
51+
}
52+
if (email1.getHTMLText() != null ? !normalizeNewlines(email1.getHTMLText()).equals(normalizeNewlines(email2.getHTMLText())) : email2.getHTMLText() != null) {
53+
return false;
54+
}
55+
if (!fieldIsEqual(email1.getSubject(), email2.getSubject(), "subject")) {
56+
return false;
57+
}
5858

59-
if (!isEqualRecipientList(email1.getRecipients(), email2.getRecipients())) {
60-
return false;
61-
}
62-
if (!email1.getEmbeddedImages().containsAll(email2.getEmbeddedImages()) || !email2.getEmbeddedImages().containsAll(email1.getEmbeddedImages())) {
63-
return false;
64-
}
65-
if (!email1.getAttachments().containsAll(email2.getAttachments()) || !email2.getAttachments().containsAll(email1.getAttachments())) {
66-
return false;
67-
}
68-
if (!email1.getHeaders().equals(email2.getHeaders())) {
69-
return false;
70-
}
71-
if (!Objects.equals(email1.getUseDispositionNotificationTo(), email2.getUseDispositionNotificationTo())) {
72-
return false;
73-
}
74-
if (!Objects.equals(email1.getUseReturnReceiptTo(), email2.getUseReturnReceiptTo())) {
75-
return false;
76-
}
77-
if (!fieldIsEqual(email1.getDispositionNotificationTo(), email2.getDispositionNotificationTo(), "dispositionNotificationTo")) {
78-
return false;
79-
}
80-
if (!fieldIsEqual(email1.getOriginalSmimeDetails(), email2.getOriginalSmimeDetails(), "originalSmimeDetails")) {
81-
return false;
82-
}
83-
if (!fieldIsEqual(email1.getPkcs12ConfigForSmimeSigning(), email2.getPkcs12ConfigForSmimeSigning(), "pkcs12ConfigForSmimeSigning")) {
84-
return false;
85-
}
86-
if (!fieldIsEqual(email1.getX509CertificateForSmimeEncryption(), email2.getX509CertificateForSmimeEncryption(), "x509CertificateForSmimeEncryption")) {
87-
return false;
88-
}
89-
return fieldIsEqual(email1.getReturnReceiptTo(), email2.getReturnReceiptTo(), "returnReceiptTo");
90-
}
59+
if (!isEqualRecipientList(email1.getRecipients(), email2.getRecipients())) {
60+
return false;
61+
}
62+
if (!email1.getEmbeddedImages().containsAll(email2.getEmbeddedImages()) || !email2.getEmbeddedImages().containsAll(email1.getEmbeddedImages())) {
63+
return false;
64+
}
65+
if (!email1.getAttachments().containsAll(email2.getAttachments()) || !email2.getAttachments().containsAll(email1.getAttachments())) {
66+
return false;
67+
}
68+
if (!email1.getHeaders().equals(email2.getHeaders())) {
69+
return false;
70+
}
71+
if (!Objects.equals(email1.getUseDispositionNotificationTo(), email2.getUseDispositionNotificationTo())) {
72+
return false;
73+
}
74+
if (!Objects.equals(email1.getUseReturnReceiptTo(), email2.getUseReturnReceiptTo())) {
75+
return false;
76+
}
77+
if (!fieldIsEqual(email1.getDispositionNotificationTo(), email2.getDispositionNotificationTo(), "dispositionNotificationTo")) {
78+
return false;
79+
}
80+
if (!fieldIsEqual(email1.getOriginalSmimeDetails(), email2.getOriginalSmimeDetails(), "originalSmimeDetails")) {
81+
return false;
82+
}
83+
if (!fieldIsEqual(email1.getPkcs12ConfigForSmimeSigning(), email2.getPkcs12ConfigForSmimeSigning(), "pkcs12ConfigForSmimeSigning")) {
84+
return false;
85+
}
86+
if (!fieldIsEqual(email1.getX509CertificateForSmimeEncryption(), email2.getX509CertificateForSmimeEncryption(), "x509CertificateForSmimeEncryption")) {
87+
return false;
88+
}
89+
return fieldIsEqual(email1.getReturnReceiptTo(), email2.getReturnReceiptTo(), "returnReceiptTo");
90+
}
9191

92-
private static boolean isEqualRecipientList(final List<Recipient> recipients, final List<Recipient> otherRecipients) {
93-
if (recipients.size() != otherRecipients.size()) {
94-
return false;
95-
}
96-
for (final Recipient otherRecipient : otherRecipients) {
97-
if (!containsRecipient(recipients, otherRecipient)) {
98-
return false;
99-
}
100-
}
101-
return true;
102-
}
92+
private static boolean isEqualRecipientList(final List<Recipient> recipients, final List<Recipient> otherRecipients) {
93+
if (recipients.size() != otherRecipients.size()) {
94+
return false;
95+
}
96+
for (final Recipient otherRecipient : otherRecipients) {
97+
if (!containsRecipient(recipients, otherRecipient)) {
98+
log.debug("Email unqual for recipients: {} vs {}", recipients, otherRecipients);
99+
return false;
100+
}
101+
}
102+
return true;
103+
}
103104

104-
private static boolean containsRecipient(final List<Recipient> recipients, @Nullable final Recipient otherRecipient) {
105-
for (final Recipient recipient : recipients) {
106-
if (fieldIsEqual(recipient, otherRecipient, "recipient")) {
107-
return true;
108-
}
109-
}
110-
return false;
111-
}
105+
private static boolean containsRecipient(final List<Recipient> recipients, @Nullable final Recipient otherRecipient) {
106+
for (final Recipient recipient : recipients) {
107+
if (Objects.equals(recipient, otherRecipient)) {
108+
return true;
109+
}
110+
}
111+
return false;
112+
}
112113

113-
static boolean isEqualDataSource(@Nullable final DataSource a, @Nullable final DataSource b) {
114-
return (a == b) || (a != null && b != null &&
115-
fieldIsEqual(a.getName(), b.getName(), "name") &&
116-
fieldIsEqual(a.getContentType(), b.getContentType(), "contentType"));
117-
}
114+
static boolean isEqualDataSource(@Nullable final DataSource a, @Nullable final DataSource b) {
115+
return (a == b) || (a != null && b != null &&
116+
fieldIsEqual(a.getName(), b.getName(), "name") &&
117+
fieldIsEqual(a.getContentType(), b.getContentType(), "contentType"));
118+
}
118119

119-
private static boolean fieldIsEqual(final Object obj1, final Object obj2, final String name) {
120-
val isEqual = Objects.equals(obj1, obj2);
121-
if (!isEqual) log.debug("Email unqual for {}: {} vs {}", name, obj1, obj2);
122-
return isEqual;
123-
}
120+
private static boolean fieldIsEqual(final Object obj1, final Object obj2, final String name) {
121+
val isEqual = Objects.equals(obj1, obj2);
122+
if (!isEqual) log.debug("Email unqual for {}: {} vs {}", name, obj1, obj2);
123+
return isEqual;
124+
}
124125
}

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.EmailWithDefaultsAndOverridesApplied;
6+
import org.simplejavamail.api.email.Email;
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 EmailWithDefaultsAndOverridesApplied email, @NotNull MimeMessage message);
23+
void sendMessage(@NotNull OperationalConfig operationalConfig, @NotNull Session session, @NotNull Email email, @NotNull MimeMessage message);
2424
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public interface Mailer {
109109
* @see com.sanctionco.jmail.EmailValidator
110110
*/
111111
@SuppressWarnings({"SameReturnValue" })
112-
boolean validate(EmailWithDefaultsAndOverridesApplied email) throws MailException;
112+
boolean validate(Email email) throws MailException;
113113

114114
/**
115115
* 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

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import org.jetbrains.annotations.Nullable;
66
import org.simplejavamail.api.email.Email;
77
import org.simplejavamail.api.email.EmailPopulatingBuilder;
8-
import org.simplejavamail.api.email.EmailWithDefaultsAndOverridesApplied;
98
import org.simplejavamail.api.mailer.MailerGenericBuilder;
109

1110
/**
@@ -45,5 +44,5 @@ public interface EmailGovernance {
4544
* populated with the defaults and overrides.
4645
*/
4746
@NotNull
48-
EmailWithDefaultsAndOverridesApplied produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided);
47+
Email produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided);
4948
}

modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.simplejavamail.api.email.ContentTransferEncoding;
1313
import org.simplejavamail.api.email.Email;
1414
import org.simplejavamail.api.email.EmailPopulatingBuilder;
15-
import org.simplejavamail.api.email.EmailWithDefaultsAndOverridesApplied;
1615
import org.simplejavamail.api.email.OriginalSmimeDetails;
1716
import org.simplejavamail.api.email.OriginalSmimeDetails.SmimeMode;
1817
import org.simplejavamail.api.internal.outlooksupport.model.EmailFromOutlookMessage;
@@ -463,7 +462,7 @@ public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNul
463462
}
464463

465464
/**
466-
* Delegates to {@link MimeMessageProducerHelper#produceMimeMessage(EmailWithDefaultsAndOverridesApplied, Session)}.
465+
* Delegates to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session)}.
467466
*/
468467
public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNull final Session session, @NotNull final EmailGovernance emailGovernance) {
469468
try {

0 commit comments

Comments
 (0)