Skip to content

Commit 5e5130c

Browse files
committed
#473: add support for multiple reply-to addresses
1 parent 00d241b commit 5e5130c

File tree

15 files changed

+99
-75
lines changed

15 files changed

+99
-75
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
[![Latest Release](https://img.shields.io/maven-central/v/org.simplejavamail/simple-java-mail.svg?style=flat)](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.simplejavamail%22%20AND%20v%3A%228.1.3%22)
33
[![Javadocs](https://img.shields.io/badge/javadoc-8.1.3-brightgreen.svg?color=brightgreen)](https://www.javadoc.io/doc/org.simplejavamail/maven-master-project)
44
[![Codacy](https://img.shields.io/codacy/grade/c7506663a4ab41e49b9675d87cd900b7.svg?style=flat)](https://app.codacy.com/gh/bbottema/simple-java-mail)
5-
![Java 1.7+](https://img.shields.io/badge/java-1.8+-lightgray.svg)
5+
![Java 1.7+](https://img.shields.io/badge/java-1.8+-lightgray.sv
66

77
# Simple Java Mail #
88

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ public class Email implements Serializable {
7474
/**
7575
* @see EmailPopulatingBuilder#withReplyTo(Recipient)
7676
*/
77-
private final Recipient replyToRecipient;
78-
77+
@NotNull
78+
private final List<Recipient> replyToRecipients;
79+
7980
/**
8081
* @see EmailPopulatingBuilder#withBounceTo(Recipient)
8182
*/
@@ -252,7 +253,7 @@ public Email(@NotNull final EmailPopulatingBuilder builder) {
252253
: builder.getHeaders());
253254
id = builder.getId();
254255
fromRecipient = builder.getFromRecipient();
255-
replyToRecipient = builder.getReplyToRecipient();
256+
replyToRecipients = unmodifiableList(builder.getReplyToRecipients());
256257
bounceToRecipient = builder.getBounceToRecipient();
257258
text = smimeMerge ? smimeSignedEmail.getPlainText() : builder.getText();
258259
textHTML = smimeMerge ? smimeSignedEmail.getHTMLText() : builder.getTextHTML();
@@ -289,7 +290,7 @@ public String toString() {
289290
String s = "Email{" +
290291
"\n\tid=" + id + ("\n\tsentDate=" + formatDate(sentDate) +
291292
"\n\tfromRecipient=" + fromRecipient +
292-
",\n\treplyToRecipient=" + replyToRecipient +
293+
",\n\treplyToRecipients=" + replyToRecipients +
293294
",\n\tbounceToRecipient=" + bounceToRecipient +
294295
",\n\ttext='" + text + '\'' +
295296
",\n\ttextHTML='" + textHTML + '\'' +
@@ -403,9 +404,9 @@ public Recipient getFromRecipient() {
403404
/**
404405
* @see EmailPopulatingBuilder#withReplyTo(Recipient)
405406
*/
406-
@Nullable
407-
public Recipient getReplyToRecipient() {
408-
return replyToRecipient;
407+
@NotNull
408+
public List<Recipient> getReplyToRecipients() {
409+
return replyToRecipients;
409410
}
410411

411412
/**

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public interface EmailPopulatingBuilder {
4949
* Validated DKIM values and then delegates to {@link Email#Email(EmailPopulatingBuilder)} with <code>this</code> as argument. This results in an Email instance with
5050
* 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>
5151
* <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
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
5353
* to complete this Email with defaults and overrides that you may have configured as (system) properties (files):
5454
* <ol>
5555
* <li>Construct a new {@code EmailGovernanceImpl} or use {@code EmailGovernanceImpl.NO_GOVERNANCE()}, and then use {@link EmailGovernance#produceEmailApplyingDefaultsAndOverrides(Email)}</li>
@@ -181,12 +181,12 @@ public interface EmailPopulatingBuilder {
181181
EmailPopulatingBuilder from(@NotNull Recipient recipient);
182182

183183
/**
184-
* Delegates to {@link #withReplyTo(Recipient)} with a new {@link Recipient} wrapped around the given email address (or null if missing).
184+
* Delegates to {@link #withReplyTo(Recipient)} with a new {@link Recipient} wrapped around the given email address.
185185
*
186186
* @param replyToAddress The address that receivers will get when they reply to the email.
187187
*/
188188
@Cli.ExcludeApi(reason = "API is subset of another API")
189-
EmailPopulatingBuilder withReplyTo(@Nullable String replyToAddress);
189+
EmailPopulatingBuilder withReplyTo(@NotNull String replyToAddress);
190190

191191
/**
192192
* Delegates to {@link #withReplyTo(Recipient)} with a new {@link Recipient} wrapped around the given fixed name and email address.
@@ -205,17 +205,22 @@ public interface EmailPopulatingBuilder {
205205
* Delegates to {@link #withReplyTo(Recipient)} with a new {@link Recipient} wrapped around the given fixed name and address.
206206
*/
207207
EmailPopulatingBuilder withReplyTo(@Nullable String fixedName, @NotNull InternetAddress replyToAddress);
208-
208+
209+
/**
210+
* Delegates to #withReplyTo(Recipient...)
211+
*/
212+
EmailPopulatingBuilder withReplyTo(@NotNull Recipient recipient);
213+
209214
/**
210215
* Sets the <em>replyTo</em> address of this email with given {@link Recipient} (ignoring its {@link Message.RecipientType} if provided).
211216
* <p>
212217
* If provided, email clients should prioritize the <em>replyTo</em> recipient over the <em>from</em> recipient when replying to this email.
213218
*
214-
* @param recipient Preconfigured recipient which includes optional name and mandatory email address.
219+
* @param recipients Preconfigured recipients which each includes optional name and mandatory email address.
215220
*
216221
* @see #withReplyTo(String, String)
217222
*/
218-
EmailPopulatingBuilder withReplyTo(@Nullable Recipient recipient);
223+
EmailPopulatingBuilder withReplyTo(@NotNull List<Recipient> recipients);
219224

220225
/**
221226
* Delegates to {@link #withBounceTo(Recipient)} with a new {@link Recipient} wrapped around the email address (or null if missing).
@@ -399,7 +404,7 @@ public interface EmailPopulatingBuilder {
399404
* {@link #withPlainText(String)} and {@link #withHTMLText(String)}.
400405
*
401406
* @param calendarMethod An RFC-2446 VEVENT calendar component method. Example: {@code PUBLISH, REQUEST, REPLY, ADD, CANCEL, REFRESH, COUNTER, DECLINECOUNTER}
402-
* @param textCalendar free form text, which you should can produce with a library such as
407+
* @param textCalendar free form text, which you can produce with a library such as
403408
* <a href="https://github.com/ical4j/ical4j/wiki/Examples">ical4j</a>.
404409
*
405410
* @see "The Test demo app in Simple Java Mail's source for a working example."
@@ -1340,8 +1345,8 @@ public interface EmailPopulatingBuilder {
13401345
EmailPopulatingBuilder encryptWithSmime(@NotNull X509Certificate x509Certificate);
13411346

13421347
/**
1343-
* When the S/MIME module is loaded, S/MIME signed / encrypted attachments are decrypted and kept in a separate list. However
1344-
* if it is a single attachment and the actual attachment has mimetype "message/rfc822", it is assumes to be the message
1348+
* When the S/MIME module is loaded, S/MIME signed / encrypted attachments are decrypted and kept in a separate list. However,
1349+
* if it is a single attachment and the actual attachment has mimetype "message/rfc822", it is assumed to be the message
13451350
* itself and by default will be merged with the top level email (basically overriding body, headers and attachments).
13461351
* <br>
13471352
* This API disables this behavior and stricly keeps all attachments as-is (still decrypted, but not merged with the email).
@@ -1471,7 +1476,7 @@ public interface EmailPopulatingBuilder {
14711476
EmailPopulatingBuilder clearFromRecipient();
14721477

14731478
/**
1474-
* Resets <em>replyToRecipient</em> to empty.
1479+
* Resets <em>replyToRecipients</em> to empty.
14751480
*/
14761481
@SuppressWarnings("unused")
14771482
EmailPopulatingBuilder clearReplyTo();
@@ -1631,8 +1636,8 @@ public interface EmailPopulatingBuilder {
16311636
/**
16321637
* @see #withReplyTo(Recipient)
16331638
*/
1634-
@Nullable
1635-
Recipient getReplyToRecipient();
1639+
@NotNull
1640+
List<Recipient> getReplyToRecipients();
16361641

16371642
/**
16381643
* @see #withBounceTo(Recipient)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static boolean equalsEmail(final Email email1, final Email email2) {
3030
if (!fieldIsEqual(email1.getSentDate(), email2.getSentDate(), "sendDate")) {
3131
return false;
3232
}
33-
if (!fieldIsEqual(email1.getReplyToRecipient(), email2.getReplyToRecipient(), "replyToRecipient")) {
33+
if (!isEqualRecipientList(email1.getReplyToRecipients(), email2.getReplyToRecipients())) {
3434
return false;
3535
}
3636
if (!fieldIsEqual(email1.getBounceToRecipient(), email2.getBounceToRecipient(), "bounceToRecipient")) {

modules/core-module/src/main/java/org/simplejavamail/internal/config/EmailProperty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public enum EmailProperty {
3030
USE_DISPOSITION_NOTIFICATION_TO (Email::getUseDispositionNotificationTo, false),
3131
CONTENT_TRANSFER_ENCODING(Email::getContentTransferEncoding, false),
3232
FROM_RECIPIENT(Email::getFromRecipient, false),
33-
REPLYTO_RECIPIENT(Email::getReplyToRecipient, false),
33+
REPLYTO_RECIPIENT(Email::getReplyToRecipients, true),
3434
BOUNCETO_RECIPIENT(Email::getBounceToRecipient, false),
3535
ALL_RECIPIENTS(Email::getRecipients, true),
3636
TO_RECIPIENTS(Email::getToRecipients, true),

modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageHelper.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,22 @@ static void setRecipients(final Email email, final Message message)
8080
}
8181

8282
/**
83-
* Fills the {@link Message} instance with reply-to address.
83+
* Fills the {@link Message} instance with reply-to address(es).
8484
*
8585
* @param email The message in which the recipients are defined.
86-
* @param message The javax message that needs to be filled with reply-to address.
86+
* @param message The javax message that needs to be filled with reply-to addresses.
8787
* @throws UnsupportedEncodingException See {@link InternetAddress#InternetAddress(String, String)}.
8888
* @throws MessagingException See {@link Message#setReplyTo(Address[])}
8989
*/
9090
static void setReplyTo(@NotNull final Email email, final Message message)
9191
throws UnsupportedEncodingException, MessagingException {
92-
if (email.getReplyToRecipient() != null) {
93-
message.setReplyTo(new Address[] {
94-
new InternetAddress(email.getReplyToRecipient().getAddress(), email.getReplyToRecipient().getName(), CHARACTER_ENCODING)
95-
});
92+
if (!email.getReplyToRecipients().isEmpty()) {
93+
val replyToAddresses = new Address[email.getReplyToRecipients().size()];
94+
int i = 0;
95+
for (val replyToRecipient : email.getReplyToRecipients()) {
96+
replyToAddresses[i++] = new InternetAddress(replyToRecipient.getAddress(), replyToRecipient.getName(), CHARACTER_ENCODING);
97+
}
98+
message.setReplyTo(replyToAddresses);
9699
}
97100
}
98101

modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ public class EmailPopulatingBuilderImpl implements InternalEmailPopulatingBuilde
126126
/**
127127
* @see #withReplyTo(Recipient)
128128
*/
129-
@Nullable
130-
private Recipient replyToRecipient;
129+
@NotNull
130+
private final List<Recipient> replyToRecipients = new ArrayList<>();
131131

132132
/**
133133
* @see #withBounceTo(Recipient)
@@ -559,8 +559,8 @@ public EmailPopulatingBuilder from(@NotNull final Recipient recipient) {
559559
*/
560560
@Override
561561
@Cli.ExcludeApi(reason = "API is subset of another API")
562-
public EmailPopulatingBuilder withReplyTo(@Nullable final String replyToAddress) {
563-
return withReplyTo(replyToAddress != null ? new Recipient(null, replyToAddress, null) : null);
562+
public EmailPopulatingBuilder withReplyTo(@NotNull final String replyToAddress) {
563+
return withReplyTo(new Recipient(null, replyToAddress, null));
564564
}
565565

566566
/**
@@ -589,13 +589,24 @@ public EmailPopulatingBuilder withReplyTo(@Nullable final String fixedName, @Not
589589
checkNonEmptyArgument(replyToAddress, "replyToAddress");
590590
return withReplyTo(new Recipient(fixedName, replyToAddress.getAddress(), null));
591591
}
592-
592+
593593
/**
594594
* @see EmailPopulatingBuilder#withReplyTo(Recipient)
595595
*/
596596
@Override
597-
public EmailPopulatingBuilder withReplyTo(@Nullable final Recipient recipient) {
598-
this.replyToRecipient = recipient != null ? new Recipient(recipient.getName(), recipient.getAddress(), null) : null;
597+
public EmailPopulatingBuilder withReplyTo(@NotNull final Recipient recipient) {
598+
this.replyToRecipients.add(new Recipient(recipient.getName(), recipient.getAddress(), null));
599+
return this;
600+
}
601+
602+
/**
603+
* @see EmailPopulatingBuilder#withReplyTo(List)
604+
*/
605+
@Override
606+
public EmailPopulatingBuilder withReplyTo(@NotNull final List<Recipient> recipients) {
607+
for (Recipient recipient : recipients) {
608+
this.replyToRecipients.add(new Recipient(recipient.getName(), recipient.getAddress(), null));
609+
}
599610
return this;
600611
}
601612

@@ -2129,7 +2140,7 @@ public EmailPopulatingBuilder clearFromRecipient() {
21292140
*/
21302141
@Override
21312142
public EmailPopulatingBuilder clearReplyTo() {
2132-
this.replyToRecipient = null;
2143+
this.replyToRecipients.clear();
21332144
return this;
21342145
}
21352146

@@ -2362,12 +2373,12 @@ public Recipient getFromRecipient() {
23622373
}
23632374

23642375
/**
2365-
* @see EmailPopulatingBuilder#getReplyToRecipient()
2376+
* @see EmailPopulatingBuilder#getReplyToRecipients()
23662377
*/
23672378
@Override
2368-
@Nullable
2369-
public Recipient getReplyToRecipient() {
2370-
return replyToRecipient;
2379+
@NotNull
2380+
public List<Recipient> getReplyToRecipients() {
2381+
return replyToRecipients;
23712382
}
23722383

23732384
/**

modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailStartingBuilderImpl.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,7 @@ public EmailPopulatingBuilder copying(@NotNull final Email email) {
207207
if (email.getFromRecipient() != null) {
208208
builder.from(email.getFromRecipient());
209209
}
210-
if (email.getReplyToRecipient() != null) {
211-
builder.withReplyTo(email.getReplyToRecipient());
212-
}
210+
builder.withReplyTo(email.getReplyToRecipients());
213211
if (email.getBounceToRecipient() != null) {
214212
builder.withBounceTo(email.getBounceToRecipient());
215213
}

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static boolean validateLenient(@NotNull final Email email, @Nullable fina
115115
*/
116116
public static void validateCompleteness(final @NotNull Email email) {
117117
// check for mandatory values
118-
if (email.getRecipients().size() == 0) {
118+
if (email.getRecipients().isEmpty()) {
119119
throw new MailCompletenessException(MailCompletenessException.MISSING_RECIPIENT);
120120
} else if (email.getFromRecipient() == null) {
121121
throw new MailCompletenessException(MailCompletenessException.MISSING_SENDER);
@@ -144,7 +144,9 @@ public static void validateAddresses(final @NotNull Email email, final @Nullable
144144
default: validateAddress(emailValidator, recipient, MailInvalidAddressException.INVALID_TO_RECIPIENT); break;
145145
}
146146
}
147-
validateAddress(emailValidator, email.getReplyToRecipient(), MailInvalidAddressException.INVALID_REPLYTO);
147+
for (final Recipient recipient : email.getReplyToRecipients()) {
148+
validateAddress(emailValidator, recipient, MailInvalidAddressException.INVALID_REPLYTO);
149+
}
148150
validateAddress(emailValidator, email.getBounceToRecipient(), MailInvalidAddressException.INVALID_BOUNCETO);
149151
if (TRUE.equals(email.getUseDispositionNotificationTo()) && email.getDispositionNotificationTo() != null) {
150152
validateAddress(emailValidator, email.getDispositionNotificationTo(), MailInvalidAddressException.INVALID_DISPOSITIONNOTIFICATIONTO);
@@ -202,9 +204,9 @@ public static void scanForInjectionAttacks(final @NotNull Email email) {
202204
scanForInjectionAttack(email.getFromRecipient().getName(), "email.fromRecipient.name");
203205
scanForInjectionAttack(email.getFromRecipient().getAddress(), "email.fromRecipient.address");
204206
}
205-
if (!valueNullOrEmpty(email.getReplyToRecipient())) {
206-
scanForInjectionAttack(email.getReplyToRecipient().getName(), "email.replyToRecipient.name");
207-
scanForInjectionAttack(email.getReplyToRecipient().getAddress(), "email.replyToRecipient.address");
207+
for (final Recipient recipient : email.getReplyToRecipients()) {
208+
scanForInjectionAttack(recipient.getName(), "email.replyToRecipient.name");
209+
scanForInjectionAttack(recipient.getAddress(), "email.replyToRecipient.address");
208210
}
209211
if (!valueNullOrEmpty(email.getBounceToRecipient())) {
210212
scanForInjectionAttack(email.getBounceToRecipient().getName(), "email.bounceToRecipient.name");

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/EmailGovernanceImpl.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,10 @@ public Email produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided)
192192
: EmailBuilder.forwarding(provided.getEmailToForward());
193193

194194
final Recipient fromRecipient = resolveEmailProperty(provided, EmailProperty.FROM_RECIPIENT);
195-
final Recipient replyToRecipient = resolveEmailProperty(provided, EmailProperty.REPLYTO_RECIPIENT);
195+
final List<Recipient> replyToRecipients = resolveEmailCollectionProperty(provided, EmailProperty.REPLYTO_RECIPIENT);
196196

197197
ofNullable(fromRecipient).ifPresent(builder::from);
198-
builder.withReplyTo(replyToRecipient);
198+
builder.withReplyTo(replyToRecipients);
199199
builder.to(resolveEmailCollectionProperty(provided, EmailProperty.TO_RECIPIENTS));
200200
builder.cc(resolveEmailCollectionProperty(provided, EmailProperty.CC_RECIPIENTS));
201201
builder.bcc(resolveEmailCollectionProperty(provided, EmailProperty.BCC_RECIPIENTS));
@@ -216,8 +216,8 @@ public Email produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided)
216216
Recipient returnReceiptToRecipient = resolveEmailProperty(provided, EmailProperty.RETURN_RECEIPT_TO);
217217
if (returnReceiptToRecipient != null) {
218218
builder.withReturnReceiptTo(returnReceiptToRecipient);
219-
} else if (replyToRecipient != null) {
220-
builder.withReturnReceiptTo(replyToRecipient);
219+
} else if (!replyToRecipients.isEmpty()) {
220+
builder.withReturnReceiptTo(replyToRecipients.get(0));
221221
} else if (fromRecipient != null) {
222222
builder.withReturnReceiptTo(fromRecipient);
223223
} else {
@@ -230,8 +230,8 @@ public Email produceEmailApplyingDefaultsAndOverrides(@Nullable Email provided)
230230
Recipient dispositionNotificationToRecipient = resolveEmailProperty(provided, EmailProperty.DISPOSITION_NOTIFICATION_TO);
231231
if (dispositionNotificationToRecipient != null) {
232232
builder.withDispositionNotificationTo(dispositionNotificationToRecipient);
233-
} else if (replyToRecipient != null) {
234-
builder.withDispositionNotificationTo(replyToRecipient);
233+
} else if (!replyToRecipients.isEmpty()) {
234+
builder.withDispositionNotificationTo(replyToRecipients.get(0));
235235
} else if (fromRecipient != null) {
236236
builder.withDispositionNotificationTo(fromRecipient);
237237
} else {

0 commit comments

Comments
 (0)