JAMES-1980 Improves for UseHeaderRecipients
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/604a9dae Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/604a9dae Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/604a9dae Branch: refs/heads/master Commit: 604a9dae7c12bb9eb55ea7b7093deaa55b95558e Parents: 398663e Author: Benoit Tellier <btell...@linagora.com> Authored: Wed Dec 28 09:31:54 2016 +0700 Committer: benwa <btell...@linagora.com> Committed: Mon Apr 3 18:09:28 2017 +0700 ---------------------------------------------------------------------- .../mailet/base/test/FakeMailContext.java | 5 + mailet/standard/pom.xml | 5 + .../transport/mailets/UseHeaderRecipients.java | 131 +++++++++---- .../mailets/UseHeaderRecipientsTest.java | 184 +++++++++++++++++++ 4 files changed, 292 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/604a9dae/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java ---------------------------------------------------------------------- diff --git a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java index 4617a94..1b22e44 100644 --- a/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java +++ b/mailet/base/src/test/java/org/apache/mailet/base/test/FakeMailContext.java @@ -122,6 +122,11 @@ public class FakeMailContext implements MailetContext { return this; } + public Builder recipients(MailAddress... recipients) { + this.recipients = Optional.<Collection<MailAddress>>of(ImmutableList.copyOf(recipients)); + return this; + } + public Builder recipient(MailAddress recipient) { Preconditions.checkNotNull(recipient); return recipients(ImmutableList.of(recipient)); http://git-wip-us.apache.org/repos/asf/james-project/blob/604a9dae/mailet/standard/pom.xml ---------------------------------------------------------------------- diff --git a/mailet/standard/pom.xml b/mailet/standard/pom.xml index 04ba9e5..45048ac 100644 --- a/mailet/standard/pom.xml +++ b/mailet/standard/pom.xml @@ -50,6 +50,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.apache.james</groupId> + <artifactId>apache-mime4j-dom</artifactId> + <version>${mime4j.version}</version> + </dependency> + <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/604a9dae/mailet/standard/src/main/java/org/apache/james/transport/mailets/UseHeaderRecipients.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/main/java/org/apache/james/transport/mailets/UseHeaderRecipients.java b/mailet/standard/src/main/java/org/apache/james/transport/mailets/UseHeaderRecipients.java index 603a617..b802d5d 100644 --- a/mailet/standard/src/main/java/org/apache/james/transport/mailets/UseHeaderRecipients.java +++ b/mailet/standard/src/main/java/org/apache/james/transport/mailets/UseHeaderRecipients.java @@ -20,16 +20,29 @@ package org.apache.james.transport.mailets; +import java.io.UnsupportedEncodingException; +import java.util.Collection; + +import javax.mail.MessagingException; +import javax.mail.internet.AddressException; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; + +import org.apache.james.mime4j.dom.address.Address; +import org.apache.james.mime4j.dom.address.AddressList; +import org.apache.james.mime4j.dom.address.Group; +import org.apache.james.mime4j.dom.address.Mailbox; +import org.apache.james.mime4j.field.address.LenientAddressParser; +import org.apache.james.mime4j.util.MimeUtil; import org.apache.mailet.Mail; import org.apache.mailet.MailAddress; import org.apache.mailet.base.GenericMailet; -import javax.mail.MessagingException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; -import java.util.Collection; -import java.util.StringTokenizer; -import java.util.Vector; +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.base.Throwables; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; /** * <p>Mailet designed to process the recipients from the mail headers rather @@ -55,6 +68,17 @@ import java.util.Vector; */ public class UseHeaderRecipients extends GenericMailet { + public static final Function<Mailbox, MailAddress> TO_MAIL_ADDRESS = new Function<Mailbox, MailAddress>() { + @Override + public MailAddress apply(Mailbox input) { + try { + return new MailAddress(input.getAddress()); + } catch (AddressException e) { + throw Throwables.propagate(e); + } + } + }; + /** * Controls certain log messages */ @@ -79,20 +103,10 @@ public class UseHeaderRecipients extends GenericMailet { public void service(Mail mail) throws MessagingException { MimeMessage message = mail.getMessage(); - // Utilise features of Set Collections such that they automatically - // ensure that no two entries are equal using the equality method - // of the element objects. MailAddress objects test equality based - // on equivalent but not necessarily visually identical addresses. - Collection<MailAddress> recipients = mail.getRecipients(); - // Wipe all the exist recipients - recipients.clear(); - recipients.addAll(getHeaderMailAddresses(message, "Mail-For")); - if (recipients.isEmpty()) { - recipients.addAll(getHeaderMailAddresses(message, "To")); - recipients.addAll(getHeaderMailAddresses(message, "Cc")); - } + mail.setRecipients(headersAddresses(message)); + if (isDebug) { - log("All recipients = " + recipients.toString()); + log("All recipients = " + mail.getRecipients()); log("Reprocessing mail using recipients in message headers"); } @@ -101,6 +115,18 @@ public class UseHeaderRecipients extends GenericMailet { mail.setState(Mail.GHOST); } + public Collection<MailAddress> headersAddresses(MimeMessage mimeMessage) throws MessagingException { + Collection<MailAddress> mailForHeaderAddresses = getHeaderMailAddresses(mimeMessage, "Mail-For"); + if (!mailForHeaderAddresses.isEmpty()) { + return mailForHeaderAddresses; + } + return ImmutableList.<MailAddress>builder() + .addAll(getHeaderMailAddresses(mimeMessage, "To")) + .addAll(getHeaderMailAddresses(mimeMessage, "Cc")) + .addAll(getHeaderMailAddresses(mimeMessage, "Bcc")) + .build(); + } + /** * Return a string describing this mailet. @@ -120,28 +146,67 @@ public class UseHeaderRecipients extends GenericMailet { * @return the collection of MailAddress objects. */ private Collection<MailAddress> getHeaderMailAddresses(MimeMessage message, String name) throws MessagingException { - if (isDebug) { log("Checking " + name + " headers"); } - Collection<MailAddress> addresses = new Vector<MailAddress>(); String[] headers = message.getHeader(name); - String addressString; - InternetAddress iAddress; + ImmutableList.Builder<MailAddress> addresses = ImmutableList.builder(); + if (headers != null) { for (String header : headers) { - StringTokenizer st = new StringTokenizer(header, ",", false); - while (st.hasMoreTokens()) { - addressString = st.nextToken(); - iAddress = new InternetAddress(addressString); - if (isDebug) { - log("Address = " + iAddress.toString()); - } - addresses.add(new MailAddress(iAddress)); - } + addresses.addAll(getMailAddressesFromHeaderLine(header)); } } - return addresses; + return addresses.build(); + } + + private ImmutableList<MailAddress> getMailAddressesFromHeaderLine(String header) throws MessagingException { + String unfoldedDecodedString = sanitizeHeaderString(header); + Iterable<String> headerParts = Splitter.on(",").split(unfoldedDecodedString); + return getMailAddressesFromHeadersParts(headerParts); + } + + private ImmutableList<MailAddress> getMailAddressesFromHeadersParts(Iterable<String> headerParts) throws AddressException { + ImmutableList.Builder<MailAddress> result = ImmutableList.builder(); + for (String headerPart : headerParts) { + if (isDebug) { + log("Address = " + headerPart); + } + result.addAll(readMailAddresses(headerPart)); + } + return result.build(); + } + + private Collection<MailAddress> readMailAddresses(String headerPart) throws AddressException { + AddressList addressList = LenientAddressParser.DEFAULT + .parseAddressList(MimeUtil.unfold(headerPart)); + + ImmutableList.Builder<Mailbox> mailboxList = ImmutableList.builder(); + + for (Address address: addressList) { + mailboxList.addAll(convertAddressToMailboxCollection(address)); + } + + return FluentIterable.from(mailboxList.build()) + .transform(TO_MAIL_ADDRESS) + .toList(); + } + + private Collection<Mailbox> convertAddressToMailboxCollection(Address address) { + if (address instanceof Mailbox) { + return ImmutableList.of((Mailbox) address); + } else if (address instanceof Group) { + return ImmutableList.copyOf(((Group) address).getMailboxes()); + } + return ImmutableList.of(); + } + + private String sanitizeHeaderString(String header) throws MessagingException { + try { + return MimeUtility.unfold(MimeUtility.decodeText(header)); + } catch (UnsupportedEncodingException e) { + throw new MessagingException("Can not decode header", e); + } } } http://git-wip-us.apache.org/repos/asf/james-project/blob/604a9dae/mailet/standard/src/test/java/org/apache/james/transport/mailets/UseHeaderRecipientsTest.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/test/java/org/apache/james/transport/mailets/UseHeaderRecipientsTest.java b/mailet/standard/src/test/java/org/apache/james/transport/mailets/UseHeaderRecipientsTest.java new file mode 100644 index 0000000..95d220a --- /dev/null +++ b/mailet/standard/src/test/java/org/apache/james/transport/mailets/UseHeaderRecipientsTest.java @@ -0,0 +1,184 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.transport.mailets; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.mailet.Mail; +import org.apache.mailet.MailAddress; +import org.apache.mailet.base.MailAddressFixture; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.FakeMailContext; +import org.apache.mailet.base.test.FakeMailetConfig; +import org.apache.mailet.base.test.MimeMessageBuilder; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class UseHeaderRecipientsTest { + + public static final String RECIPIENT1 = "a...@apache1.org"; + public static final String RECIPIENT2 = "a...@apache2.org"; + public static final String RECIPIENT3 = "a...@apache3.org"; + private UseHeaderRecipients testee; + private FakeMailContext mailetContext; + private MailAddress mailAddress1; + private MailAddress mailAddress2; + private MailAddress mailAddress3; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() throws Exception { + testee = new UseHeaderRecipients(); + mailetContext = FakeMailContext.defaultContext(); + testee.init(FakeMailetConfig.builder().mailetContext(mailetContext).build()); + + mailAddress1 = new MailAddress(RECIPIENT1); + mailAddress2 = new MailAddress(RECIPIENT2); + mailAddress3 = new MailAddress(RECIPIENT3); + } + + @Test + public void serviceShouldSetMimeMessageRecipients() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(RECIPIENT1, RECIPIENT2) + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getRecipients()) + .containsOnly(mailAddress1, mailAddress2); + } + + @Test + public void serviceShouldSetToCcAndBccSpecifiedInTheMimeMessage() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(RECIPIENT1) + .addCcRecipient(RECIPIENT2) + .addBccRecipient(RECIPIENT3) + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getRecipients()) + .containsOnly(mailAddress1, mailAddress2, mailAddress3); + } + + @Test + public void serviceShouldSetEmptyRecipientWhenNoRecipientsInTheMimeMessage() throws Exception { + + FakeMail fakeMail = FakeMail.builder() + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getRecipients()) + .isEmpty(); + } + + @Test + public void serviceShouldGhostEmail() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getState()) + .isEqualTo(Mail.GHOST); + } + + @Test + public void serviceShouldResendTheEmail() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients(MailAddressFixture.ANY_AT_JAMES) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(RECIPIENT1) + .addCcRecipient(RECIPIENT2) + .addBccRecipient(RECIPIENT3) + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(mailetContext.getSentMails()) + .containsOnly(FakeMailContext.sentMailBuilder() + .recipients(mailAddress1, mailAddress2, mailAddress3) + .build()); + } + + @Test + public void serviceShouldThrowOnInvalidMailAddress() throws Exception { + expectedException.expect(RuntimeException.class); + + FakeMail fakeMail = FakeMail.builder() + .recipients(mailAddress1) + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient("invalid") + .build()) + .build(); + + testee.service(fakeMail); + } + + @Test + public void serviceShouldSupportAddressList() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients() + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient(RECIPIENT1, RECIPIENT2) + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getRecipients()) + .containsOnly(mailAddress1, mailAddress2); + } + + @Test + public void serviceShouldSupportMailboxes() throws Exception { + FakeMail fakeMail = FakeMail.builder() + .recipients() + .mimeMessage(MimeMessageBuilder.mimeMessageBuilder() + .addToRecipient("APACHE" + "<" + UseHeaderRecipientsTest.RECIPIENT1 + ">") + .build()) + .build(); + + testee.service(fakeMail); + + assertThat(fakeMail.getRecipients()) + .containsOnly(mailAddress1); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org