Skip to content

Commit 7e30d23

Browse files
committed
#93, #95: added support for return notification headers
1 parent dc9eddd commit 7e30d23

File tree

6 files changed

+416
-71
lines changed

6 files changed

+416
-71
lines changed

Diff for: src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageHelper.java

+15
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ private static void setAttachments(final Email email, final MimeMultipart multip
181181
/**
182182
* Sets all headers on the {@link Message} instance. Since we're not using a high-level JavaMail method, the JavaMail library says we need to do
183183
* some encoding and 'folding' manually, to get the value right for the headers (see {@link MimeUtility}.
184+
* <p>
185+
* Furthermore sets the notification flags <code>Disposition-Notification-To</code> and <code>Return-Receipt-To</code> if provided. It used
186+
* JavaMail's built in method for producing an RFC compliant email address (see {@link InternetAddress#toString()}).
184187
*
185188
* @param email The message in which the headers are defined.
186189
* @param message The {@link Message} on which to set the raw, encoded and folded headers.
@@ -198,6 +201,18 @@ private static void setHeaders(final Email email, final Message message)
198201
final String foldedHeaderValue = MimeUtility.fold(headerName.length() + 2, headerValue);
199202
message.addHeader(header.getKey(), foldedHeaderValue);
200203
}
204+
205+
if (email.isUseDispositionNotificationTo()) {
206+
final Address address = new InternetAddress(email.getDispositionNotificationTo().getAddress(),
207+
email.getDispositionNotificationTo().getName(), CHARACTER_ENCODING);
208+
message.setHeader("Disposition-Notification-To", address.toString());
209+
}
210+
211+
if (email.isUseReturnReceiptTo()) {
212+
final Address address = new InternetAddress(email.getReturnReceiptTo().getAddress(),
213+
email.getReturnReceiptTo().getName(), CHARACTER_ENCODING);
214+
message.setHeader("Return-Receipt-To", address.toString());
215+
}
201216
}
202217

203218
/**

Diff for: src/main/java/org/simplejavamail/email/Email.java

+145-7
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,45 @@ public class Email {
9292
* Map of header name and values, such as <code>X-Priority</code> etc.
9393
*/
9494
private final Map<String, String> headers;
95+
96+
/**
97+
* Indicates the new emails should set the <a href="https://tools.ietf.org/html/rfc8098">NPM flag "Disposition-Notification-To"</a>. This flag can
98+
* be used to request a return receipt from the recipient to signal that the recipient has read the email.
99+
* <p>
100+
* This flag may be ignored by SMTP clients (for example gmail ignores it completely, while the Google Apps business suite honors it).
101+
* <p>
102+
* If no address is provided, {@link #dispositionNotificationTo} will default to {@link #replyToRecipient} if available or else
103+
* {@link #fromRecipient}.
104+
*/
105+
private boolean useDispositionNotificationTo;
106+
107+
/**
108+
* @see #useDispositionNotificationTo
109+
*/
110+
private Recipient dispositionNotificationTo;
111+
112+
/**
113+
* Indicates the new emails should set the <a href="https://en.wikipedia.org/wiki/Return_receipt">RRT flag "Return-Receipt-To"</a>. This flag
114+
* can be used to request a notification from the SMTP server recipient to signal that the recipient has read the email.
115+
* <p>
116+
* This flag is rarely used, but your mail server / client might implement this flag to automatically send back a notification that the email
117+
* was received on the mail server or opened in the client, depending on the chosen implementation.
118+
* <p>
119+
* If no address is provided, {@link #returnReceiptTo} will default to {@link #replyToRecipient} if available or else {@link #fromRecipient}.
120+
*/
121+
private boolean useReturnReceiptTo;
122+
123+
/**
124+
* @see #useReturnReceiptTo
125+
*/
126+
private Recipient returnReceiptTo;
95127

96128
/*
97129
DKIM properties
98130
*/
99131
private boolean applyDKIMSignature = false;
100132
private InputStream dkimPrivateKeyInputStream;
101-
private File dkimPrivateKeyFile; // supported seperately, so we don't have to do resource management ourselves for the InputStream
133+
private File dkimPrivateKeyFile; // supported separately, so we don't have to do resource management ourselves for the InputStream
102134
private String dkimSigningDomain;
103135
private String dkimSelector;
104136

@@ -189,16 +221,25 @@ public void signWithDomainKey(@Nonnull final InputStream dkimPrivateKeyInputStre
189221
public void setId(@Nullable final String id) {
190222
this.id = id;
191223
}
192-
224+
193225
/**
194226
* Sets the sender address.
195227
*
196228
* @param name The sender's name.
197-
* @param fromAddress The sender's email address.
229+
* @param fromAddress The sender's email address, mandatory.
198230
*/
199231
public void setFromAddress(@Nullable final String name, @Nonnull final String fromAddress) {
200232
fromRecipient = new Recipient(name, checkNonEmptyArgument(fromAddress, "fromAddress"), null);
201233
}
234+
235+
/**
236+
* Sets the sender address from a preconfigured {@link Recipient} object..
237+
*
238+
* @param recipient The Recipient optional name and mandatory address.
239+
*/
240+
public void setFromAddress(@Nonnull Recipient recipient) {
241+
fromRecipient = new Recipient(recipient.getName(), checkNonEmptyArgument(recipient.getAddress(), "fromAddress"), null);
242+
}
202243

203244
/**
204245
* Sets the reply-to address (optional).
@@ -209,14 +250,51 @@ public void setFromAddress(@Nullable final String name, @Nonnull final String fr
209250
public void setReplyToAddress(@Nullable final String name, @Nonnull final String replyToAddress) {
210251
replyToRecipient = new Recipient(name, checkNonEmptyArgument(replyToAddress, "replyToAddress"), null);
211252
}
253+
254+
/**
255+
* Sets the reply-to address from a preconfigured {@link Recipient} object..
256+
*
257+
* @param recipient The Recipient optional name and mandatory address.
258+
*/
259+
public void setReplyToAddress(@Nonnull Recipient recipient) {
260+
replyToRecipient = new Recipient(recipient.getName(), checkNonEmptyArgument(recipient.getAddress(), "replyToAddress"), null);
261+
}
212262

213263
/**
214264
* Bean setter for {@link #subject}.
215265
*/
216266
public void setSubject(@Nonnull final String subject) {
217267
this.subject = checkNonEmptyArgument(subject, "subject");
218268
}
219-
269+
270+
/**
271+
* Bean setter for {@link #useDispositionNotificationTo}.
272+
*/
273+
public void setUseDispositionNotificationTo(boolean useDispositionNotificationTo) {
274+
this.useDispositionNotificationTo = useDispositionNotificationTo;
275+
}
276+
277+
/**
278+
* Bean setter for {@link #dispositionNotificationTo}.
279+
*/
280+
public void setDispositionNotificationTo(Recipient dispositionNotificationTo) {
281+
this.dispositionNotificationTo = dispositionNotificationTo;
282+
}
283+
284+
/**
285+
* Bean setter for {@link #useReturnReceiptTo}.
286+
*/
287+
public void setUseReturnReceiptTo(boolean useReturnReceiptTo) {
288+
this.useReturnReceiptTo = useReturnReceiptTo;
289+
}
290+
291+
/**
292+
* Bean setter for {@link #returnReceiptTo}.
293+
*/
294+
public void setReturnReceiptTo(Recipient returnReceiptTo) {
295+
this.returnReceiptTo = returnReceiptTo;
296+
}
297+
220298
/**
221299
* Bean setter for {@link #text}.
222300
*/
@@ -394,7 +472,35 @@ public Recipient getReplyToRecipient() {
394472
public String getSubject() {
395473
return subject;
396474
}
397-
475+
476+
/**
477+
* Bean getter for {@link #useDispositionNotificationTo}.
478+
*/
479+
public boolean isUseDispositionNotificationTo() {
480+
return useDispositionNotificationTo;
481+
}
482+
483+
/**
484+
* Bean getter for {@link #dispositionNotificationTo}.
485+
*/
486+
public Recipient getDispositionNotificationTo() {
487+
return dispositionNotificationTo;
488+
}
489+
490+
/**
491+
* Bean getter for {@link #useReturnReceiptTo}.
492+
*/
493+
public boolean isUseReturnReceiptTo() {
494+
return useReturnReceiptTo;
495+
}
496+
497+
/**
498+
* Bean getter for {@link #returnReceiptTo}.
499+
*/
500+
public Recipient getReturnReceiptTo() {
501+
return returnReceiptTo;
502+
}
503+
398504
/**
399505
* Bean getter for {@link #text}.
400506
*/
@@ -467,7 +573,7 @@ public boolean equals(final Object o) {
467573
return (this == o) || (o != null && getClass() == o.getClass() &&
468574
EqualsHelper.equalsEmail(this, (Email) o));
469575
}
470-
576+
471577
@Override
472578
public String toString() {
473579
return "Email{" +
@@ -478,9 +584,16 @@ public String toString() {
478584
",\n\ttextHTML='" + textHTML + '\'' +
479585
",\n\tsubject='" + subject + '\'' +
480586
",\n\trecipients=" + recipients +
587+
",\n\tapplyDKIMSignature=" + applyDKIMSignature +
588+
",\n\t\tdkimSelector=" + dkimSelector +
589+
",\n\t\tdkimSigningDomain=" + dkimSigningDomain +
590+
",\n\tuseDispositionNotificationTo=" + useDispositionNotificationTo +
591+
",\n\t\tdispositionNotificationTo=" + dispositionNotificationTo +
592+
",\n\tuseReturnReceiptTo=" + useReturnReceiptTo +
593+
",\n\t\treturnReceiptTo=" + returnReceiptTo +
594+
",\n\theaders=" + headers +
481595
",\n\tembeddedImages=" + embeddedImages +
482596
",\n\tattachments=" + attachments +
483-
",\n\theaders=" + headers +
484597
"\n}";
485598
}
486599

@@ -503,6 +616,31 @@ public String toString() {
503616
textHTML = builder.getTextHTML();
504617
subject = builder.getSubject();
505618

619+
useDispositionNotificationTo = builder.isUseDispositionNotificationTo();
620+
useReturnReceiptTo = builder.isUseReturnReceiptTo();
621+
dispositionNotificationTo = builder.getDispositionNotificationTo();
622+
returnReceiptTo = builder.getReturnReceiptTo();
623+
624+
if (useDispositionNotificationTo) {
625+
if (valueNullOrEmpty(builder.getDispositionNotificationTo())) {
626+
if (builder.getReplyToRecipient() != null) {
627+
dispositionNotificationTo = builder.getReplyToRecipient();
628+
} else {
629+
dispositionNotificationTo = builder.getFromRecipient();
630+
}
631+
}
632+
}
633+
634+
if (useReturnReceiptTo) {
635+
if (valueNullOrEmpty(builder.getDispositionNotificationTo())) {
636+
if (builder.getReplyToRecipient() != null) {
637+
returnReceiptTo = builder.getReplyToRecipient();
638+
} else {
639+
returnReceiptTo = builder.getFromRecipient();
640+
}
641+
}
642+
}
643+
506644
if (builder.getDkimPrivateKeyFile() != null) {
507645
signWithDomainKey(builder.getDkimPrivateKeyFile(), builder.getSigningDomain(), builder.getDkimSelector());
508646
} else if (builder.getDkimPrivateKeyInputStream() != null) {

0 commit comments

Comments
 (0)