JAMES-2361 implement a matcher to match Mime-Type Parameters
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/203ad2d1 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/203ad2d1 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/203ad2d1 Branch: refs/heads/master Commit: 203ad2d17efa4603bdf720467c5727a48d6c6f0c Parents: b3e5088 Author: Matthieu Baechler <[email protected]> Authored: Tue Mar 27 11:53:00 2018 +0200 Committer: benwa <[email protected]> Committed: Tue Apr 3 17:01:38 2018 +0700 ---------------------------------------------------------------------- .../matchers/HasMimeTypeParameter.java | 142 +++++++++++++++++ .../matchers/HasMimeTypeParameterTest.java | 157 +++++++++++++++++++ 2 files changed, 299 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/203ad2d1/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeParameter.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeParameter.java b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeParameter.java new file mode 100644 index 0000000..bca7098 --- /dev/null +++ b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeParameter.java @@ -0,0 +1,142 @@ +/**************************************************************** + * 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.matchers; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import javax.activation.MimeType; +import javax.activation.MimeTypeParseException; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.james.core.MailAddress; +import org.apache.mailet.Mail; +import org.apache.mailet.MailetException; +import org.apache.mailet.base.GenericMatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.fge.lambdas.Throwing; +import com.github.steveash.guavate.Guavate; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; + + +/** + * <p>This matcher checks if the content type parameters matches.</p> + * + * use: <pre> + * <code> + * <mailet match="HasMimeTypeParameter=report-type=disposition-notification,report-type=other" class="..." /> + * </code> + * </pre> + */ +public class HasMimeTypeParameter extends GenericMatcher { + + private static final Logger LOGGER = LoggerFactory.getLogger(HasMimeTypeParameter.class); + + @VisibleForTesting List<Pair<String, String>> filteredMimeTypeParameters; + + @Override + public void init() throws MessagingException { + filteredMimeTypeParameters = parseConfigurationString(Optional.ofNullable(getCondition())); + } + + private List<Pair<String, String>> parseConfigurationString(Optional<String> maybeConfiguration) { + return maybeConfiguration + .map(tokenizeConfiguration()) + .orElse(ImmutableList.of()); + } + + private Function<String, List<Pair<String, String>>> tokenizeConfiguration() { + return Throwing.<String, List<Pair<String, String>>>function( + value -> { + try { + return Splitter.on(",") + .trimResults() + .splitToList(value) + .stream() + .map(this::conditionToPair) + .collect(Guavate.toImmutableList()); + } catch (IllegalArgumentException e) { + throw new MailetException("error parsing configuration", e); + } + } + ).sneakyThrow(); + } + + private Pair<String, String> conditionToPair(String s) { + Preconditions.checkArgument(s.contains("=")); + List<String> pairElements = Splitter.on("=") + .limit(2) + .splitToList(s); + validateCondition(pairElements); + + return Pair.of(pairElements.get(0), unQuote(pairElements.get(1))); + } + + private void validateCondition(List<String> pairElements) { + Preconditions.checkArgument(pairElements.stream().noneMatch(Strings::isNullOrEmpty), + "Empty name or value for the parameter argument"); + } + + private String unQuote(String value) { + return StringUtils.unwrap(value, '"'); + } + + @Override + public Collection<MailAddress> match(Mail mail) throws MessagingException { + Optional<MimeType> maybeMimeType = getMimeTypeFromMessage(mail.getMessage()); + if (maybeMimeType.map(this::mimeTypeMatchParameter).orElse(false)) { + return mail.getRecipients(); + } + return ImmutableList.of(); + } + + private Optional<MimeType> getMimeTypeFromMessage(MimeMessage message) throws MessagingException { + try { + return Optional.of(new MimeType(message.getContentType())); + } catch (MimeTypeParseException e) { + LOGGER.warn("Error while parsing message's mimeType {}", message.getContentType(), e); + return Optional.empty(); + } + } + + private boolean mimeTypeMatchParameter(MimeType mimeType) { + return filteredMimeTypeParameters + .stream() + .anyMatch(entry -> mimeTypeContainsParameter(mimeType, entry.getKey(), entry.getValue())); + } + + private boolean mimeTypeContainsParameter(MimeType mimeType, String name, String value) { + return Optional.ofNullable(mimeType.getParameter(name)).map(value::equals).orElse(false); + } + +} + http://git-wip-us.apache.org/repos/asf/james-project/blob/203ad2d1/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeParameterTest.java ---------------------------------------------------------------------- diff --git a/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeParameterTest.java b/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeParameterTest.java new file mode 100644 index 0000000..3817f44 --- /dev/null +++ b/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeParameterTest.java @@ -0,0 +1,157 @@ +/**************************************************************** + * 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.matchers; + +import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1; +import static org.apache.mailet.base.MailAddressFixture.SENDER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import javax.mail.MessagingException; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.james.core.builder.MimeMessageBuilder; +import org.apache.mailet.MailetException; +import org.apache.mailet.base.test.FakeMail; +import org.apache.mailet.base.test.FakeMatcherConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class HasMimeTypeParameterTest { + + private static final String TEST_CONTENT_TYPE = "multipart/report; report-type=\"disposition-notification\"; boundary=\"=-ac61K8KXSRpaQ/eveStc\""; + + private HasMimeTypeParameter matcher; + private FakeMail sampleMail; + + @BeforeEach + void setUp() throws MessagingException { + matcher = new HasMimeTypeParameter(); + sampleMail = FakeMail.builder() + .mimeMessage(MimeMessageBuilder + .mimeMessageBuilder() + .setSubject("Mail read") + .addHeader("Content-Type", TEST_CONTENT_TYPE) + .setText("You email has been read by Bart", TEST_CONTENT_TYPE)) + .sender(SENDER) + .recipient(RECIPIENT1) + .build(); + } + + @Test + void shouldThrowWhenConditionHasNoEquals() { + assertThatThrownBy(() -> + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("abc") + .build())) + .isInstanceOf(MailetException.class); + } + + @Test + void shouldThrowWhenConditionHasNoValue() { + assertThatThrownBy(() -> + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("abc=") + .build())) + .isInstanceOf(MailetException.class); + } + + + @Test + void shouldThrowWhenConditionHasNoName() { + assertThatThrownBy(() -> + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("=abc") + .build())) + .isInstanceOf(MailetException.class); + } + + @Test + void shouldSupportEqualsInValue() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("abc=123,def==--") + .build()); + assertThat(matcher.filteredMimeTypeParameters).contains(Pair.of("def", "=--")); + } + + @Test + void shouldMatchNothingWhenNoCondition() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .build()); + + assertThat(matcher.match(sampleMail)).isEmpty(); + } + + @Test + void shouldNotMatchWhenConditionNotFulfilled() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("no-entry=for-that") + .build()); + + assertThat(matcher.match(sampleMail)).isEmpty(); + } + + @Test + void shouldMatchReportType() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("report-type=\"disposition-notification\"") + .build()); + + assertThat(matcher.match(sampleMail)).contains(RECIPIENT1); + } + + @Test + void shouldMatchAnyConfigurationCondition() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("report-type=\"not found\",boundary=\"=-ac61K8KXSRpaQ/eveStc\"") + .build()); + + assertThat(matcher.match(sampleMail)).contains(RECIPIENT1); + } + + @Test + void shouldMatchConditionWithSameKey() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("report-type=\"not found\",report-type=\"disposition-notification\"") + .build()); + + assertThat(matcher.match(sampleMail)).contains(RECIPIENT1); + } + + @Test + void shouldMatchReportTypeEvenWithoutQuotaInConfiguration() throws MessagingException { + matcher.init(FakeMatcherConfig.builder() + .matcherName("HasMimeType") + .condition("report-type=disposition-notification") + .build()); + + assertThat(matcher.match(sampleMail)).contains(RECIPIENT1); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
