Because Greenmail won't tell us who got Bcc'd, we have to determine it ourselves by the process of elimination.
For the given email
- Find out the email addresses that have received the email (based on
message id).
- Remove the toRecipients from this list.
- Remove the ccRecipients from this list.
import com.icegreen.greenmail.spring.GreenMailBean;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.store.StoredMessage;
import com.icegreen.greenmail.user.GreenMailUser;
import jakarta.mail.BodyPart;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.springframework.stereotype.Component;
@Component
public class MailExtractor {
private final GreenMailBean greenMailBean;
public MailExtractor(GreenMailBean greenMailBean) {
this.greenMailBean = greenMailBean;
}
public Email getEmailForFirstSentMessage() {
MimeMessage mimeMessage = greenMailBean.getReceivedMessages()[0];
Email email = MailExtractor.extractEmail(mimeMessage, getUserMailFolderList());
System.out.printf("From %s%n", email.from());
System.out.printf("To %s%n", email.toRecipients());
System.out.printf("Cc %s%n", email.ccRecipients());
System.out.printf("Bcc %s%n", email.bccRecipients());
System.out.printf("Body %s%n", email.body());
System.out.printf("Message ID %s%n", email.messageId());
return email;
}
private Map<GreenMailUser, MailFolder> getUserMailFolderList() {
Collection<GreenMailUser> greenMailUsers = greenMailBean.getGreenMail().getUserManager().listUser();
Map<GreenMailUser, MailFolder> userMailFolder = new HashMap<>();
for (GreenMailUser greenMailUser : greenMailUsers) {
try {
userMailFolder.put(greenMailUser, greenMailBean.getGreenMail().getManagers().getImapHostManager().getInbox(greenMailUser));
} catch (FolderException e) {
throw new RuntimeException(e);
}
}
return userMailFolder;
}
private static Email extractEmail(MimeMessage mimeMessage, Map<GreenMailUser, MailFolder> userMailFolderList) {
return new Email(extractFrom(mimeMessage),
extractTo(mimeMessage),
extractCc(mimeMessage),
extractBcc(mimeMessage, userMailFolderList),
extractSubject(mimeMessage),
extractBody(mimeMessage),
extractMessageId(mimeMessage));
}
private static String extractFrom(MimeMessage mimeMessage) {
return getValueForField(mimeMessage, "From");
}
private static List<String> extractTo(MimeMessage mimeMessage) {
return getValuesForField(mimeMessage, "To");
}
private static List<String> extractCc(MimeMessage mimeMessage) {
return getValuesForField(mimeMessage, "Cc");
}
/* Because Greenmail won't tell us who got Bcc'd, we have to determine it ourselves by the process of elimination.
*
* For the given email
* -> Find out the email addresses that have received the email (based on message id)
* -> Remove the toRecipients from this list
* -> Remove the ccRecipients from this list
*/
private static List<String> extractBcc(MimeMessage mimeMessage, Map<GreenMailUser, MailFolder> userMailFolder) {
List<String> bccList = new ArrayList<>();
for (Entry<GreenMailUser, MailFolder> mailFolder : userMailFolder.entrySet()) {
mailFolder.getValue().getMessages().stream()
.filter(storedMessage -> messageIdsMatch(storedMessage, extractMessageId(mimeMessage)))
.findFirst()
.map(it -> bccList.add(mailFolder.getKey().getEmail()));
}
bccList.removeAll(extractTo(mimeMessage));
bccList.removeAll(extractCc(mimeMessage));
return bccList;
}
private static boolean messageIdsMatch(StoredMessage storedMessage, String messageId) {
try {
return storedMessage.getMimeMessage().getMessageID().equals(messageId);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
private static String extractSubject(MimeMessage mimeMessage) {
return getValueForField(mimeMessage, "Subject");
}
private static String extractMessageId(MimeMessage mimeMessage) {
return getValueForField(mimeMessage, "Message-ID");
}
private static String extractBody(MimeMessage mimeMessage) {
try {
if (mimeMessage.getContent() instanceof MimeMultipart mimeMultipart) {
return findHtmlBody(mimeMultipart).orElseThrow(() -> new IllegalStateException("Could not find html-body"));
}
throw new IllegalArgumentException("Unexpected e-mail content of type " + mimeMessage.getContent().getClass());
} catch (IOException | MessagingException e) {
throw new IllegalArgumentException(e);
}
}
private static Optional<String> findHtmlBody(MimeMultipart multipart) {
try {
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if ("text/html;charset=UTF-8".equals(bodyPart.getContentType())) {
return Optional.of(((String) bodyPart.getContent()).replace("\r", ""));
}
if (bodyPart.getContent() instanceof MimeMultipart subMultipart) {
return findHtmlBody(subMultipart);
}
}
} catch (MessagingException | IOException e) {
throw new RuntimeException(e);
}
return Optional.empty();
}
private static List<String> getValuesForField(MimeMessage mimeMessage, String headerName) {
String value = getValueForField(mimeMessage, headerName);
return value != null ? Arrays.stream(value.split(", ")).toList() : List.of();
}
private static String getValueForField(MimeMessage mimeMessage, String headerName) {
try {
return Optional.ofNullable(mimeMessage.getHeader(headerName)).map(content -> content[0]).orElse(null);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
private record Email(String from,
Collection<String> toRecipients,
Collection<String> ccRecipients,
Collection<String> bccRecipients,
String subject,
String body,
String messageId
) {}
}