Skip to content

Commit

Permalink
#93, #95: added support for return notification headers
Browse files Browse the repository at this point in the history
  • Loading branch information
bbottema committed Aug 15, 2017
1 parent dc9eddd commit 7e30d23
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ private static void setAttachments(final Email email, final MimeMultipart multip
/**
* 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
* some encoding and 'folding' manually, to get the value right for the headers (see {@link MimeUtility}.
* <p>
* Furthermore sets the notification flags <code>Disposition-Notification-To</code> and <code>Return-Receipt-To</code> if provided. It used
* JavaMail's built in method for producing an RFC compliant email address (see {@link InternetAddress#toString()}).
*
* @param email The message in which the headers are defined.
* @param message The {@link Message} on which to set the raw, encoded and folded headers.
Expand All @@ -198,6 +201,18 @@ private static void setHeaders(final Email email, final Message message)
final String foldedHeaderValue = MimeUtility.fold(headerName.length() + 2, headerValue);
message.addHeader(header.getKey(), foldedHeaderValue);
}

if (email.isUseDispositionNotificationTo()) {
final Address address = new InternetAddress(email.getDispositionNotificationTo().getAddress(),
email.getDispositionNotificationTo().getName(), CHARACTER_ENCODING);
message.setHeader("Disposition-Notification-To", address.toString());
}

if (email.isUseReturnReceiptTo()) {
final Address address = new InternetAddress(email.getReturnReceiptTo().getAddress(),
email.getReturnReceiptTo().getName(), CHARACTER_ENCODING);
message.setHeader("Return-Receipt-To", address.toString());
}
}

/**
Expand Down
152 changes: 145 additions & 7 deletions src/main/java/org/simplejavamail/email/Email.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,45 @@ public class Email {
* Map of header name and values, such as <code>X-Priority</code> etc.
*/
private final Map<String, String> headers;

/**
* Indicates the new emails should set the <a href="https://tools.ietf.org/html/rfc8098">NPM flag "Disposition-Notification-To"</a>. This flag can
* be used to request a return receipt from the recipient to signal that the recipient has read the email.
* <p>
* This flag may be ignored by SMTP clients (for example gmail ignores it completely, while the Google Apps business suite honors it).
* <p>
* If no address is provided, {@link #dispositionNotificationTo} will default to {@link #replyToRecipient} if available or else
* {@link #fromRecipient}.
*/
private boolean useDispositionNotificationTo;

/**
* @see #useDispositionNotificationTo
*/
private Recipient dispositionNotificationTo;

/**
* Indicates the new emails should set the <a href="https://en.wikipedia.org/wiki/Return_receipt">RRT flag "Return-Receipt-To"</a>. This flag
* can be used to request a notification from the SMTP server recipient to signal that the recipient has read the email.
* <p>
* This flag is rarely used, but your mail server / client might implement this flag to automatically send back a notification that the email
* was received on the mail server or opened in the client, depending on the chosen implementation.
* <p>
* If no address is provided, {@link #returnReceiptTo} will default to {@link #replyToRecipient} if available or else {@link #fromRecipient}.
*/
private boolean useReturnReceiptTo;

/**
* @see #useReturnReceiptTo
*/
private Recipient returnReceiptTo;

/*
DKIM properties
*/
private boolean applyDKIMSignature = false;
private InputStream dkimPrivateKeyInputStream;
private File dkimPrivateKeyFile; // supported seperately, so we don't have to do resource management ourselves for the InputStream
private File dkimPrivateKeyFile; // supported separately, so we don't have to do resource management ourselves for the InputStream
private String dkimSigningDomain;
private String dkimSelector;

Expand Down Expand Up @@ -189,16 +221,25 @@ public void signWithDomainKey(@Nonnull final InputStream dkimPrivateKeyInputStre
public void setId(@Nullable final String id) {
this.id = id;
}

/**
* Sets the sender address.
*
* @param name The sender's name.
* @param fromAddress The sender's email address.
* @param fromAddress The sender's email address, mandatory.
*/
public void setFromAddress(@Nullable final String name, @Nonnull final String fromAddress) {
fromRecipient = new Recipient(name, checkNonEmptyArgument(fromAddress, "fromAddress"), null);
}

/**
* Sets the sender address from a preconfigured {@link Recipient} object..
*
* @param recipient The Recipient optional name and mandatory address.
*/
public void setFromAddress(@Nonnull Recipient recipient) {
fromRecipient = new Recipient(recipient.getName(), checkNonEmptyArgument(recipient.getAddress(), "fromAddress"), null);
}

/**
* Sets the reply-to address (optional).
Expand All @@ -209,14 +250,51 @@ public void setFromAddress(@Nullable final String name, @Nonnull final String fr
public void setReplyToAddress(@Nullable final String name, @Nonnull final String replyToAddress) {
replyToRecipient = new Recipient(name, checkNonEmptyArgument(replyToAddress, "replyToAddress"), null);
}

/**
* Sets the reply-to address from a preconfigured {@link Recipient} object..
*
* @param recipient The Recipient optional name and mandatory address.
*/
public void setReplyToAddress(@Nonnull Recipient recipient) {
replyToRecipient = new Recipient(recipient.getName(), checkNonEmptyArgument(recipient.getAddress(), "replyToAddress"), null);
}

/**
* Bean setter for {@link #subject}.
*/
public void setSubject(@Nonnull final String subject) {
this.subject = checkNonEmptyArgument(subject, "subject");
}


/**
* Bean setter for {@link #useDispositionNotificationTo}.
*/
public void setUseDispositionNotificationTo(boolean useDispositionNotificationTo) {
this.useDispositionNotificationTo = useDispositionNotificationTo;
}

/**
* Bean setter for {@link #dispositionNotificationTo}.
*/
public void setDispositionNotificationTo(Recipient dispositionNotificationTo) {
this.dispositionNotificationTo = dispositionNotificationTo;
}

/**
* Bean setter for {@link #useReturnReceiptTo}.
*/
public void setUseReturnReceiptTo(boolean useReturnReceiptTo) {
this.useReturnReceiptTo = useReturnReceiptTo;
}

/**
* Bean setter for {@link #returnReceiptTo}.
*/
public void setReturnReceiptTo(Recipient returnReceiptTo) {
this.returnReceiptTo = returnReceiptTo;
}

/**
* Bean setter for {@link #text}.
*/
Expand Down Expand Up @@ -394,7 +472,35 @@ public Recipient getReplyToRecipient() {
public String getSubject() {
return subject;
}


/**
* Bean getter for {@link #useDispositionNotificationTo}.
*/
public boolean isUseDispositionNotificationTo() {
return useDispositionNotificationTo;
}

/**
* Bean getter for {@link #dispositionNotificationTo}.
*/
public Recipient getDispositionNotificationTo() {
return dispositionNotificationTo;
}

/**
* Bean getter for {@link #useReturnReceiptTo}.
*/
public boolean isUseReturnReceiptTo() {
return useReturnReceiptTo;
}

/**
* Bean getter for {@link #returnReceiptTo}.
*/
public Recipient getReturnReceiptTo() {
return returnReceiptTo;
}

/**
* Bean getter for {@link #text}.
*/
Expand Down Expand Up @@ -467,7 +573,7 @@ public boolean equals(final Object o) {
return (this == o) || (o != null && getClass() == o.getClass() &&
EqualsHelper.equalsEmail(this, (Email) o));
}

@Override
public String toString() {
return "Email{" +
Expand All @@ -478,9 +584,16 @@ public String toString() {
",\n\ttextHTML='" + textHTML + '\'' +
",\n\tsubject='" + subject + '\'' +
",\n\trecipients=" + recipients +
",\n\tapplyDKIMSignature=" + applyDKIMSignature +
",\n\t\tdkimSelector=" + dkimSelector +
",\n\t\tdkimSigningDomain=" + dkimSigningDomain +
",\n\tuseDispositionNotificationTo=" + useDispositionNotificationTo +
",\n\t\tdispositionNotificationTo=" + dispositionNotificationTo +
",\n\tuseReturnReceiptTo=" + useReturnReceiptTo +
",\n\t\treturnReceiptTo=" + returnReceiptTo +
",\n\theaders=" + headers +
",\n\tembeddedImages=" + embeddedImages +
",\n\tattachments=" + attachments +
",\n\theaders=" + headers +
"\n}";
}

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

useDispositionNotificationTo = builder.isUseDispositionNotificationTo();
useReturnReceiptTo = builder.isUseReturnReceiptTo();
dispositionNotificationTo = builder.getDispositionNotificationTo();
returnReceiptTo = builder.getReturnReceiptTo();

if (useDispositionNotificationTo) {
if (valueNullOrEmpty(builder.getDispositionNotificationTo())) {
if (builder.getReplyToRecipient() != null) {
dispositionNotificationTo = builder.getReplyToRecipient();
} else {
dispositionNotificationTo = builder.getFromRecipient();
}
}
}

if (useReturnReceiptTo) {
if (valueNullOrEmpty(builder.getDispositionNotificationTo())) {
if (builder.getReplyToRecipient() != null) {
returnReceiptTo = builder.getReplyToRecipient();
} else {
returnReceiptTo = builder.getFromRecipient();
}
}
}

if (builder.getDkimPrivateKeyFile() != null) {
signWithDomainKey(builder.getDkimPrivateKeyFile(), builder.getSigningDomain(), builder.getDkimSelector());
} else if (builder.getDkimPrivateKeyInputStream() != null) {
Expand Down
Loading

0 comments on commit 7e30d23

Please sign in to comment.