Skip to content

Commit

Permalink
#107: implemented replyTo functionality in EmailBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
bbottema committed Nov 5, 2017
1 parent 502e25b commit 898719c
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class MimeMessageParser {
static {
// taken from: protected javax.mail.internet.InternetHeaders constructor
/*
* When extracting information to create an Email, we're not interested in the following headers:
* When extracting information to create an Email, we're NOT interested in the following headers:
*/
DEFAULT_HEADERS.add("Return-Path");
DEFAULT_HEADERS.add("Received");
Expand All @@ -59,8 +59,9 @@ public class MimeMessageParser {
DEFAULT_HEADERS.add("Cc");
DEFAULT_HEADERS.add("Bcc");
DEFAULT_HEADERS.add("Message-Id");
DEFAULT_HEADERS.add("In-Reply-To");
DEFAULT_HEADERS.add("References");
// The next two are needed for replying to
// DEFAULT_HEADERS.add("In-Reply-To");
// DEFAULT_HEADERS.add("References");
DEFAULT_HEADERS.add("Subject");
DEFAULT_HEADERS.add("Comments");
DEFAULT_HEADERS.add("Keywords");
Expand Down
84 changes: 82 additions & 2 deletions src/main/java/org/simplejavamail/email/EmailBuilder.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package org.simplejavamail.email;

import org.simplejavamail.converter.EmailConverter;
import org.simplejavamail.internal.util.MiscUtil;

import javax.activation.DataSource;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static org.simplejavamail.internal.util.MiscUtil.extractEmailAddresses;
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument;
Expand Down Expand Up @@ -52,6 +59,9 @@ public class EmailBuilder {
*/
private String id;

/**
* The sender of the email. Can be used in conjunction with {@link #replyToRecipient}.
*/
private Recipient fromRecipient;

/**
Expand Down Expand Up @@ -82,7 +92,7 @@ public class EmailBuilder {
/**
* List of {@link Recipient}.
*/
private final List<Recipient> recipients;
private final Set<Recipient> recipients;

/**
* List of {@link AttachmentResource}.
Expand Down Expand Up @@ -152,7 +162,7 @@ public class EmailBuilder {
private Recipient returnReceiptTo;

public EmailBuilder() {
recipients = new ArrayList<>();
recipients = new HashSet<>();
embeddedImages = new ArrayList<>();
attachments = new ArrayList<>();
headers = new HashMap<>();
Expand Down Expand Up @@ -204,6 +214,15 @@ public EmailBuilder id(@Nullable final String id) {
return this;
}

/**
* Sets the sender address {@link #fromRecipient}.
*
* @param fromAddress The sender's email address.
*/
public EmailBuilder from(@Nonnull final String fromAddress) {
return from(null, fromAddress);
}

/**
* Sets the sender address {@link #fromRecipient}.
*
Expand Down Expand Up @@ -305,6 +324,17 @@ public EmailBuilder textHTML(@Nullable final String textHTML) {
* @see Recipient
*/
public EmailBuilder to(@Nonnull final Recipient... recipientsToAdd) {
return to(asList(recipientsToAdd));
}

/**
* Adds new {@link Recipient} instances to the list on account of name, address with recipient type {@link Message.RecipientType#TO}.
*
* @param recipientsToAdd The recipients whose name and address to use
* @see #recipients
* @see Recipient
*/
public EmailBuilder to(@Nonnull final Collection<Recipient> recipientsToAdd) {
for (final Recipient recipient : checkNonEmptyArgument(recipientsToAdd, "recipientsToAdd")) {
recipients.add(new Recipient(recipient.getName(), recipient.getAddress(), Message.RecipientType.TO));
}
Expand Down Expand Up @@ -495,6 +525,11 @@ public EmailBuilder embedImage(@Nullable final String name, @Nonnull final DataS
return this;
}

public EmailBuilder withHeaders(@Nonnull final Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}

/**
* Adds a header to the {@link #headers} list. The value is stored as a <code>String</code>. example: <code>email.addHeader("X-Priority",
* 2)</code>
Expand Down Expand Up @@ -656,6 +691,51 @@ public EmailBuilder withReturnReceiptTo(@Nonnull Recipient recipient) {
return this;
}

/**
* Delegates to {@link #asReplyTo(MimeMessage, boolean)} with replyToAll set to <code>true</code>.
*/
public EmailBuilder asReplyTo(@Nonnull final Email email) {
return asReplyTo(EmailConverter.emailToMimeMessage(email), true);
}

/**
* Delegates to {@link #asReplyTo(MimeMessage, boolean)}.
*/
public EmailBuilder asReplyTo(@Nonnull final Email email, boolean repyToAll) {
return asReplyTo(EmailConverter.emailToMimeMessage(email), repyToAll);
}

/**
* Delegates to {@link #asReplyTo(MimeMessage, boolean)} with replyToAll set to <code>true</code>.
*/
public EmailBuilder asReplyTo(MimeMessage email) {
return asReplyTo(email, true);
}

/**
* Primes the email with all subject, headers and recipients needed for a valid RFC reply.
* <p>
* Note: replaces subject.
*
* @see javax.mail.internet.MimeMessage#reply(boolean)
*/
public EmailBuilder asReplyTo(MimeMessage emailMessage, boolean repyToAll) {
final Email reply;
try {
MimeMessage replyMessage = (MimeMessage) emailMessage.reply(repyToAll);
replyMessage.setText("ignore");
replyMessage.setFrom("[email protected]");
reply = EmailConverter.mimeMessageToEmail(replyMessage);
} catch (MessagingException e) {
throw new EmailException("was unable to parse mimemessage to produce a reply for", e);
}

return this
.subject(reply.getSubject())
.to(reply.getRecipients())
.withHeaders(reply.getHeaders());
}

/*
SETTERS / GETTERS
*/
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/simplejavamail/email/EmailException.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ class EmailException extends MailException {
EmailException(@SuppressWarnings("SameParameterValue") final String message) {
super(message);
}

EmailException(@SuppressWarnings("SameParameterValue") final String message, final Exception cause) {
super(message, cause);
}
}
65 changes: 63 additions & 2 deletions src/test/java/org/simplejavamail/mailer/MailerLiveTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.simplejavamail.email.AttachmentResource;
import org.simplejavamail.email.Email;
import org.simplejavamail.email.EmailAssert;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.email.Recipient;
import org.simplejavamail.mailer.config.ServerConfig;
import org.simplejavamail.util.ConfigLoader;
Expand All @@ -20,6 +21,7 @@

import static javax.mail.Message.RecipientType.TO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.MapEntry.entry;
import static org.simplejavamail.converter.EmailConverter.mimeMessageToEmail;
import static testutil.EmailHelper.normalizeText;
import static testutil.EmailHelper.readOutlookMessage;
Expand Down Expand Up @@ -69,7 +71,7 @@ public void createMailSession_StandardDummyMailWithId()
throws IOException, MessagingException {
assertSendingEmail(EmailHelper.createDummyEmail("<123@456>", true, false, false));
}

@Test
public void createMailSession_OutlookMessageTest()
throws IOException, MessagingException {
Expand Down Expand Up @@ -122,7 +124,66 @@ private Email assertSendingEmail(final Email originalEmail)
assertThat(receivedEmail).isEqualTo(originalEmail);
return receivedEmail;
}


@Test
public void createMailSession_ReplyToMessage()
throws IOException, MessagingException {
// send initial mail
mailer.sendMail(readOutlookMessage("test-messages/HTML mail with replyto and attachment and embedded image.msg"));
MimeMessage receivedMimeMessage = smtpServerRule.getOnlyMessage();
Email receivedEmail = mimeMessageToEmail(receivedMimeMessage);

// send reply to initial mail
Email reply = new EmailBuilder()
.asReplyTo(assertSendingEmail(receivedEmail))
.from("[email protected]")
.text("This is the reply")
.build();

// test received reply to initial mail
mailer.sendMail(reply);
MimeMessage receivedMimeMessageReply1 = smtpServerRule.getMessage("[email protected]");
MimeMessage receivedMimeMessageReply2 = smtpServerRule.getMessage("[email protected]");
Email receivedReply1 = mimeMessageToEmail(receivedMimeMessageReply1);
Email receivedReply2 = mimeMessageToEmail(receivedMimeMessageReply2);

assertThat(receivedReply1).isEqualTo(receivedReply2);
EmailAssert.assertThat(receivedReply1).hasSubject("Re: hey");
EmailAssert.assertThat(receivedReply1).hasOnlyRecipients(
new Recipient("lollypop-replyto", "[email protected]", TO),
new Recipient("Bottema, Benny", "[email protected]", TO)
);
assertThat(receivedReply1.getHeaders()).contains(entry("In-Reply-To", receivedEmail.getId()));
assertThat(receivedReply1.getHeaders()).contains(entry("References", receivedEmail.getId()));
}

@Test
public void createMailSession_ReplyToMessage_NotAll_AndCustomReferences()
throws IOException, MessagingException {
// send initial mail
mailer.sendMail(readOutlookMessage("test-messages/HTML mail with replyto and attachment and embedded image.msg"));
MimeMessage receivedMimeMessage = smtpServerRule.getOnlyMessage();
Email receivedEmail = mimeMessageToEmail(receivedMimeMessage);

// send reply to initial mail
Email reply = new EmailBuilder()
.asReplyTo(assertSendingEmail(receivedEmail), false)
.addHeader("References", "dummy-references")
.from("[email protected]")
.text("This is the reply")
.build();

// test received reply to initial mail
mailer.sendMail(reply);
MimeMessage receivedMimeMessageReply1 = smtpServerRule.getOnlyMessage("[email protected]");
Email receivedReply = mimeMessageToEmail(receivedMimeMessageReply1);

EmailAssert.assertThat(receivedReply).hasSubject("Re: hey");
EmailAssert.assertThat(receivedReply).hasOnlyRecipients(new Recipient("lollypop-replyto", "[email protected]", TO));
assertThat(receivedReply.getHeaders()).contains(entry("In-Reply-To", receivedEmail.getId()));
assertThat(receivedReply.getHeaders()).contains(entry("References", "dummy-references"));
}

private void assertAttachmentMetadata(AttachmentResource embeddedImg, String mimeType, String filename) {
assertThat(embeddedImg.getDataSource().getContentType()).isEqualTo(mimeType);
assertThat(embeddedImg.getName()).isEqualTo(filename);
Expand Down
38 changes: 36 additions & 2 deletions src/test/java/testutil/testrules/SmtpServerRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.List;

import static java.lang.String.format;
Expand Down Expand Up @@ -62,16 +63,49 @@ public List<WiserMessage> getMessages() {
checkState("getMessages()");
return wiser.getMessages();
}


@Nonnull
public MimeMessage getOnlyMessage(String envelopeReceiver)
throws MessagingException {
checkState("getMessages()");
List<WiserMessage> messages = getMessages();
assertThat(messages).hasSize(1);
Iterator<WiserMessage> iterator = messages.iterator();
WiserMessage wiserMessage = iterator.next();
assertThat(wiserMessage.getEnvelopeReceiver()).isEqualTo(envelopeReceiver);
MimeMessage mimeMessage = wiserMessage.getMimeMessage();
iterator.remove();
return mimeMessage;
}

@Nonnull
public MimeMessage getOnlyMessage()
throws MessagingException {
checkState("getMessages()");
List<WiserMessage> messages = getMessages();
assertThat(messages).hasSize(1);
MimeMessage mimeMessage = messages.iterator().next().getMimeMessage();
Iterator<WiserMessage> iterator = messages.iterator();
MimeMessage mimeMessage = iterator.next().getMimeMessage();
iterator.remove();
return mimeMessage;
}

@Nonnull
public MimeMessage getMessage(String envelopeReceiver)
throws MessagingException {
checkState("getMessages()");
List<WiserMessage> messages = getMessages();
Iterator<WiserMessage> iterator = messages.iterator();
while (iterator.hasNext()) {
WiserMessage wiserMessage = iterator.next();
if (wiserMessage.getEnvelopeReceiver().equals(envelopeReceiver)) {
MimeMessage mimeMessage = wiserMessage.getMimeMessage();
iterator.remove();
return mimeMessage;
}
}
throw new AssertionError("message not found for recipient " + envelopeReceiver);
}

@Nonnull
public SMTPServer getServer() {
Expand Down

0 comments on commit 898719c

Please sign in to comment.