MAILET-147 Introduce a new mailet for mime attribute content extracting
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/6fcd8848 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/6fcd8848 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/6fcd8848 Branch: refs/heads/master Commit: 6fcd884830e0d04d00edd22e74c19f3c682da71f Parents: d7ec83f Author: Antoine Duprat <[email protected]> Authored: Fri Jan 13 15:27:12 2017 +0100 Committer: Antoine Duprat <[email protected]> Committed: Wed Jan 18 14:48:20 2017 +0100 ---------------------------------------------------------------------- .../transport/mailets/AmqpForwardAttribute.java | 26 +--- .../transport/mailets/MimeDecodingMailet.java | 110 +++++++++++++ .../mailets/AmqpForwardAttributeTest.java | 4 +- .../mailets/MimeDecodingMailetTest.java | 154 +++++++++++++++++++ .../mailets/AmqpForwardAttachmentTest.java | 5 + 5 files changed, 278 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/6fcd8848/mailet/standard/src/main/java/org/apache/james/transport/mailets/AmqpForwardAttribute.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/main/java/org/apache/james/transport/mailets/AmqpForwardAttribute.java b/mailet/standard/src/main/java/org/apache/james/transport/mailets/AmqpForwardAttribute.java index 4596c7c..360898d 100644 --- a/mailet/standard/src/main/java/org/apache/james/transport/mailets/AmqpForwardAttribute.java +++ b/mailet/standard/src/main/java/org/apache/james/transport/mailets/AmqpForwardAttribute.java @@ -19,16 +19,11 @@ package org.apache.james.transport.mailets; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; import java.util.Map; import java.util.concurrent.TimeoutException; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; - -import org.apache.commons.io.IOUtils; import org.apache.mailet.Mail; import org.apache.mailet.MailetException; import org.apache.mailet.base.GenericMailet; @@ -70,6 +65,7 @@ public class AmqpForwardAttribute extends GenericMailet { private ConnectionFactory connectionFactory; @VisibleForTesting String routingKey; + @Override public void init() throws MailetException { String uri = getInitParameter(URI_PARAMETER_NAME); if (Strings.isNullOrEmpty(uri)) { @@ -100,6 +96,7 @@ public class AmqpForwardAttribute extends GenericMailet { this.connectionFactory = connectionFactory; } + @Override public void service(Mail mail) throws MailetException { if (mail.getAttribute(attribute) == null) { return; @@ -144,22 +141,15 @@ public class AmqpForwardAttribute extends GenericMailet { } private void sendContentOnChannel(Channel channel, Map<String, byte[]> content) throws IOException { - for (Map.Entry<String, byte[]> entry: content.entrySet()) { - byte[] rawMime = entry.getValue(); - byte[] attachmentContent = extractContent(rawMime); - channel.basicPublish(exchange, routingKey, new AMQP.BasicProperties(), attachmentContent); - } - } - - private byte[] extractContent(byte[] rawMime) throws IOException { - try { - MimeBodyPart mimeBodyPart = new MimeBodyPart(new ByteArrayInputStream(rawMime)); - return IOUtils.toByteArray(mimeBodyPart.getInputStream()); - } catch (MessagingException e) { - throw new IOException(e); + for (byte[] body: content.values()) { + channel.basicPublish(exchange, + routingKey, + new AMQP.BasicProperties(), + body); } } + @Override public String getMailetInfo() { return "AmqpForwardAttribute"; } http://git-wip-us.apache.org/repos/asf/james-project/blob/6fcd8848/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeDecodingMailet.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeDecodingMailet.java b/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeDecodingMailet.java new file mode 100644 index 0000000..78fcf02 --- /dev/null +++ b/mailet/standard/src/main/java/org/apache/james/transport/mailets/MimeDecodingMailet.java @@ -0,0 +1,110 @@ +/**************************************************************** + * 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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.Map; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; + +import org.apache.commons.io.IOUtils; +import org.apache.mailet.Mail; +import org.apache.mailet.MailetException; +import org.apache.mailet.base.GenericMailet; + +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; + +/** + * This mailet replace the mail attribute map of key to MimePart + * by a map of key to the MimePart content (as bytes). + * <br /> + * It takes only one parameter: + * <ul> + * <li>attribute (mandatory): mime content to be decoded, expected to be a Map<String, byte[]> + * </ul> + * + * Then all this map attribute values will be replaced by their content. + */ +public class MimeDecodingMailet extends GenericMailet { + + public static final String ATTRIBUTE_PARAMETER_NAME = "attribute"; + + private String attribute; + + @Override + public void init() throws MessagingException { + attribute = getInitParameter(ATTRIBUTE_PARAMETER_NAME); + if (Strings.isNullOrEmpty(attribute)) { + throw new MailetException("No value for " + ATTRIBUTE_PARAMETER_NAME + + " parameter was provided."); + } + } + + @Override + public void service(Mail mail) throws MessagingException { + if (mail.getAttribute(attribute) == null) { + return; + } + + ImmutableMap.Builder<String, byte[]> extractedMimeContentByName = ImmutableMap.builder(); + for (Map.Entry<String, byte[]> entry: getAttributeContent(mail).entrySet()) { + Optional<byte[]> maybeContent = extractContent(entry.getValue()); + if (maybeContent.isPresent()) { + extractedMimeContentByName.put(entry.getKey(), maybeContent.get()); + } + } + mail.setAttribute(attribute, extractedMimeContentByName.build()); + } + + @SuppressWarnings("unchecked") + private Map<String, byte[]> getAttributeContent(Mail mail) throws MailetException { + Serializable attributeContent = mail.getAttribute(attribute); + if (! (attributeContent instanceof Map)) { + log("Invalid attribute found into attribute " + + attribute + "class Map expected but " + + attributeContent.getClass() + " found."); + return ImmutableMap.of(); + } + return (Map<String, byte[]>) attributeContent; + } + + private Optional<byte[]> extractContent(Object rawMime) throws MessagingException { + try { + MimeBodyPart mimeBodyPart = new MimeBodyPart(new ByteArrayInputStream((byte[]) rawMime)); + return Optional.fromNullable(IOUtils.toByteArray(mimeBodyPart.getInputStream())); + } catch (IOException e) { + log("Error while extracting content from mime part", e); + return Optional.absent(); + } catch (ClassCastException e) { + log("Invalid map attribute types.", e); + return Optional.absent(); + } + } + + @Override + public String getMailetInfo() { + return "MimeDecodingMailet"; + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/6fcd8848/mailet/standard/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttributeTest.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttributeTest.java b/mailet/standard/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttributeTest.java index 5507b5e..dac1fab 100644 --- a/mailet/standard/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttributeTest.java +++ b/mailet/standard/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttributeTest.java @@ -47,7 +47,6 @@ import org.slf4j.Logger; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.primitives.Bytes; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.Channel; @@ -60,9 +59,8 @@ public class AmqpForwardAttributeTest { private static final String EXCHANGE_NAME = "exchangeName"; private static final String ROUTING_KEY = "routingKey"; private static final String AMQP_URI = "amqp://host"; - private static final byte[] ATTACHMENT_HEADER = "Content-Transfer-Encoding: 8bit\r\nContent-Type: application/octet-stream; charset=utf-8\r\n\r\n".getBytes(Charsets.UTF_8); private static final byte[] ATTACHMENT_CONTENT = "Attachment content".getBytes(Charsets.UTF_8); - private static final ImmutableMap<String, byte[]> ATTRIBUTE_CONTENT = ImmutableMap.of("attachment1.txt", Bytes.concat(ATTACHMENT_HEADER, ATTACHMENT_CONTENT)); + private static final ImmutableMap<String, byte[]> ATTRIBUTE_CONTENT = ImmutableMap.of("attachment1.txt", ATTACHMENT_CONTENT); @Rule public ExpectedException expectedException = ExpectedException.none(); http://git-wip-us.apache.org/repos/asf/james-project/blob/6fcd8848/mailet/standard/src/test/java/org/apache/james/transport/mailets/MimeDecodingMailetTest.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/test/java/org/apache/james/transport/mailets/MimeDecodingMailetTest.java b/mailet/standard/src/test/java/org/apache/james/transport/mailets/MimeDecodingMailetTest.java new file mode 100644 index 0000000..c540f44 --- /dev/null +++ b/mailet/standard/src/test/java/org/apache/james/transport/mailets/MimeDecodingMailetTest.java @@ -0,0 +1,154 @@ +/**************************************************************** + * 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 static org.mockito.Mockito.mock; + +import java.util.Map; + +import javax.mail.MessagingException; + +import org.apache.mailet.MailetContext; +import org.apache.mailet.MailetException; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.FakeMailContext; +import org.apache.mailet.base.test.FakeMailetConfig; +import org.assertj.core.data.MapEntry; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.slf4j.Logger; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class MimeDecodingMailetTest { + + private static final String MAIL_ATTRIBUTE = "mime.attachments"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private Logger logger; + private MailetContext mailetContext; + private MimeDecodingMailet testee; + + @Before + public void setUp() throws Exception { + testee = new MimeDecodingMailet(); + logger = mock(Logger.class); + mailetContext = FakeMailContext.builder() + .logger(logger) + .build(); + } + + @Test + public void initShouldThrowWhenNoAttributeParameter() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .build(); + expectedException.expect(MailetException.class); + testee.init(mailetConfig); + } + + @Test + public void initShouldThrowWhenAttributeParameterIsEmpty() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MimeDecodingMailet.ATTRIBUTE_PARAMETER_NAME, "") + .build(); + expectedException.expect(MailetException.class); + testee.init(mailetConfig); + } + + @Test + public void serviceShouldNotThrowWhenAttributeContentIsNotAMap() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MimeDecodingMailet.ATTRIBUTE_PARAMETER_NAME, MAIL_ATTRIBUTE) + .build(); + testee.init(mailetConfig); + + FakeMail mail = FakeMail.defaultFakeMail(); + mail.setAttribute(MAIL_ATTRIBUTE, ImmutableList.of()); + + testee.service(mail); + } + + @Test + public void serviceShouldNotThrowWhenAttributeContentIsAMapOfWrongTypes() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MimeDecodingMailet.ATTRIBUTE_PARAMETER_NAME, MAIL_ATTRIBUTE) + .build(); + testee.init(mailetConfig); + + FakeMail mail = FakeMail.defaultFakeMail(); + mail.setAttribute(MAIL_ATTRIBUTE, ImmutableMap.of("1", "2")); + + testee.service(mail); + } + + @Test + public void serviceShouldNotSetAttributeWhenNone() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MimeDecodingMailet.ATTRIBUTE_PARAMETER_NAME, MAIL_ATTRIBUTE) + .build(); + testee.init(mailetConfig); + + FakeMail mail = FakeMail.defaultFakeMail(); + + testee.service(mail); + assertThat(mail.getAttribute(MAIL_ATTRIBUTE)).isNull(); + } + + @Test + @SuppressWarnings("unchecked") + public void serviceShouldChangeAttributeWhenDefined() throws MessagingException { + FakeMailetConfig mailetConfig = FakeMailetConfig.builder() + .mailetName("Test") + .mailetContext(mailetContext) + .setProperty(MimeDecodingMailet.ATTRIBUTE_PARAMETER_NAME, MAIL_ATTRIBUTE) + .build(); + testee.init(mailetConfig); + + FakeMail mail = FakeMail.defaultFakeMail(); + String text = "Attachment content"; + String content = "Content-Transfer-Encoding: 8bit\r\n" + + "Content-Type: application/octet-stream; charset=utf-8\r\n\r\n" + + text; + String expectedKey = "mimePart1"; + mail.setAttribute(MAIL_ATTRIBUTE, ImmutableMap.of(expectedKey, content.getBytes(Charsets.UTF_8))); + + byte[] expectedValue = text.getBytes(Charsets.UTF_8); + testee.service(mail); + + Map<String, byte[]> processedAttribute = (Map<String, byte[]>) mail.getAttribute(MAIL_ATTRIBUTE); + assertThat(processedAttribute).containsExactly(MapEntry.entry(expectedKey, expectedValue)); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/6fcd8848/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttachmentTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttachmentTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttachmentTest.java index eb94ebf..67f63cd 100644 --- a/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttachmentTest.java +++ b/server/mailet/integration-testing/src/test/java/org/apache/james/transport/mailets/AmqpForwardAttachmentTest.java @@ -125,6 +125,11 @@ public class AmqpForwardAttachmentTest { .build()) .addMailet(MailetConfiguration.builder() .match("All") + .clazz("MimeDecodingMailet") + .addProperty("attribute", MAIL_ATTRIBUTE) + .build()) + .addMailet(MailetConfiguration.builder() + .match("All") .clazz("AmqpForwardAttribute") .addProperty("uri", amqpUri) .addProperty("exchange", EXCHANGE_NAME) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
