Skip to content

Commit

Permalink
JAMES-2662 Centralize and test Mime4J envelope parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Mar 8, 2019
1 parent 9a59a54 commit 0e2cfc0
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 72 deletions.
4 changes: 4 additions & 0 deletions mailbox/plugin/deleted-messages-vault/pom.xml
Expand Up @@ -65,6 +65,10 @@
<artifactId>apache-mailet-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
<artifactId>james-server-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
Expand Down
Expand Up @@ -23,30 +23,24 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

import javax.mail.internet.AddressException;
import java.util.Set;

import org.apache.james.core.MailAddress;
import org.apache.james.core.MaybeSender;
import org.apache.james.core.User;
import org.apache.james.mime4j.MimeIOException;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.address.AddressList;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.mime4j.message.DefaultMessageBuilder;
import org.apache.james.mime4j.stream.MimeConfig;
import org.apache.james.util.StreamUtils;
import org.apache.james.server.core.Envelope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.steveash.guavate.Guavate;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;

class DeletedMessageConverter {

Expand Down Expand Up @@ -103,29 +97,9 @@ private MaybeSender retrieveSender(Optional<Message> mimeMessage) {
.orElse(MaybeSender.nullSender());
}

private List<MailAddress> retrieveRecipients(Optional<Message> message) {
return StreamUtils.flatten(combineRecipients(message)
.map(AddressList::flatten)
.flatMap(MailboxList::stream)
.map(Mailbox::getAddress)
.map(this::retrieveAddress))
.collect(Guavate.toImmutableList());
}

private Stream<MailAddress> retrieveAddress(String address) {
try {
return Stream.of(new MailAddress(address));
} catch (AddressException e) {
LOGGER.warn("Can not create the mailAddress from {}", address, e);
return Stream.of();
}
}

private Stream<AddressList> combineRecipients(Optional<Message> message) {
return message.map(mimeMessage -> Stream.of(mimeMessage.getTo(),
mimeMessage.getCc(),
mimeMessage.getBcc())
.filter(Objects::nonNull))
.orElse(Stream.of());
private Set<MailAddress> retrieveRecipients(Optional<Message> maybeMessage) {
return maybeMessage.map(message -> Envelope.fromMime4JMessage(message, Envelope.ValidationPolicy.IGNORE))
.map(Envelope::getRecipients)
.orElse(ImmutableSet.of());
}
}
Expand Up @@ -17,84 +17,96 @@
* under the License. *
****************************************************************/

package org.apache.james.jmap.model;
package org.apache.james.server.core;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import javax.mail.internet.AddressException;

import org.apache.james.core.MailAddress;
import org.apache.james.core.MaybeSender;
import org.apache.james.mime4j.dom.address.AddressList;
import org.apache.james.mime4j.dom.address.Mailbox;
import org.apache.james.mime4j.dom.address.MailboxList;
import org.apache.james.util.OptionalUtils;
import org.apache.james.util.StreamUtils;
import org.slf4j.LoggerFactory;

import com.github.fge.lambdas.Throwing;
import com.github.steveash.guavate.Guavate;
import com.google.common.base.Preconditions;

public class Envelope {

public static Envelope fromMessage(Message jmapMessage) {
MailAddress sender = jmapMessage.getFrom()
.map(Emailer::toMailAddress)
.orElseThrow(() -> new RuntimeException("Sender is mandatory"));

Stream<MailAddress> to = emailersToMailAddresses(jmapMessage.getTo());
Stream<MailAddress> cc = emailersToMailAddresses(jmapMessage.getCc());
Stream<MailAddress> bcc = emailersToMailAddresses(jmapMessage.getBcc());

return new Envelope(sender,
StreamUtils.flatten(Stream.of(to, cc, bcc))
.collect(Guavate.toImmutableSet()));
public interface ValidationPolicy {
ValidationPolicy THROW = e -> {
throw new RuntimeException(e);
};
ValidationPolicy IGNORE = e -> {
LoggerFactory.getLogger(Envelope.class).info("Failed to parse a mail address", e);
return Optional.empty();
};

Optional<MailAddress> handleParsingException(AddressException e);
}

public static Envelope fromMime4JMessage(org.apache.james.mime4j.dom.Message mime4JMessage) {
MailAddress sender = mime4JMessage.getFrom()
.stream()
return fromMime4JMessage(mime4JMessage, ValidationPolicy.THROW);
}

public static Envelope fromMime4JMessage(org.apache.james.mime4j.dom.Message mime4JMessage, ValidationPolicy validationPolicy) {
MaybeSender sender = Optional.ofNullable(mime4JMessage.getFrom())
.map(MailboxList::stream)
.orElse(Stream.empty())
.findAny()
.map(Mailbox::getAddress)
.map(Throwing.function(MailAddress::new))
.orElseThrow(() -> new RuntimeException("Sender is mandatory"));
.flatMap(addressAsString -> newMailAddress(validationPolicy, addressAsString))
.map(MaybeSender::of)
.orElse(MaybeSender.nullSender());

Stream<MailAddress> to = emailersToMailAddresses(mime4JMessage.getTo());
Stream<MailAddress> cc = emailersToMailAddresses(mime4JMessage.getCc());
Stream<MailAddress> bcc = emailersToMailAddresses(mime4JMessage.getBcc());
Stream<MailAddress> to = emailersToMailAddresses(mime4JMessage.getTo(), validationPolicy);
Stream<MailAddress> cc = emailersToMailAddresses(mime4JMessage.getCc(), validationPolicy);
Stream<MailAddress> bcc = emailersToMailAddresses(mime4JMessage.getBcc(), validationPolicy);

return new Envelope(sender,
StreamUtils.flatten(Stream.of(to, cc, bcc))
.collect(Guavate.toImmutableSet()));
}

private static Stream<MailAddress> emailersToMailAddresses(List<Emailer> emailers) {
return emailers.stream()
.map(Emailer::toMailAddress);
private static Optional<MailAddress> newMailAddress(ValidationPolicy validationPolicy, String addressAsString) {
try {
if (addressAsString.isEmpty()) {
return Optional.empty();
}
return Optional.of(new MailAddress(addressAsString));
} catch (AddressException e) {
return validationPolicy.handleParsingException(e);
}
}

private static Stream<MailAddress> emailersToMailAddresses(AddressList addresses) {
private static Stream<MailAddress> emailersToMailAddresses(AddressList addresses, ValidationPolicy validationPolicy) {
return Optional.ofNullable(addresses)
.map(AddressList::flatten)
.map(MailboxList::stream)
.orElse(Stream.of())
.map(Mailbox::getAddress)
.map(Throwing.function(MailAddress::new));
.map(addressAsString -> newMailAddress(validationPolicy, addressAsString))
.flatMap(OptionalUtils::toStream);
}


private final MailAddress from;
private final MaybeSender from;
private final Set<MailAddress> recipients;

private Envelope(MailAddress from, Set<MailAddress> recipients) {
public Envelope(MaybeSender from, Set<MailAddress> recipients) {
Preconditions.checkNotNull(from);
Preconditions.checkNotNull(recipients);

this.from = from;
this.recipients = recipients;
}

public MailAddress getFrom() {
public MaybeSender getFrom() {
return from;
}

Expand Down

0 comments on commit 0e2cfc0

Please sign in to comment.