This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit c44feefb7020f58f41bd4b3623da7094e71485af
Author: Benoit Tellier <[email protected]>
AuthorDate: Fri May 28 17:30:51 2021 +0700

    JAMES-2157 Introduce a HasMimeTypeAnySubPart matcher
    
    Behaves like HasMimeType mailet but do walk down the mime tree
    to look up all body parts content type.
---
 docs/modules/servers/partials/HasMimeType.adoc     |   4 +-
 .../servers/partials/HasMimeTypeAnySubPart.adoc    |  11 ++
 .../james/transport/matchers/HasMimeType.java      |   3 +-
 ...HasMimeType.java => HasMimeTypeAnySubPart.java} |  60 +++++--
 .../matchers/HasMimeTypeAnySubPartTest.java        | 181 +++++++++++++++++++++
 5 files changed, 244 insertions(+), 15 deletions(-)

diff --git a/docs/modules/servers/partials/HasMimeType.adoc 
b/docs/modules/servers/partials/HasMimeType.adoc
index 985f9a7..64049a0 100644
--- a/docs/modules/servers/partials/HasMimeType.adoc
+++ b/docs/modules/servers/partials/HasMimeType.adoc
@@ -2,10 +2,10 @@
 
 This matcher checks if the content type matches.
 
-This matcher do not walk down the mime tree and stops at the top level mime 
part.
+This matcher does not walk down the mime tree and stops at the top level mime 
part.
 
 use:
 
 ....
 <mailet match="HasMimeType=text/plain,text/html" class="..." />
-....
\ No newline at end of file
+....
diff --git a/docs/modules/servers/partials/HasMimeTypeAnySubPart.adoc 
b/docs/modules/servers/partials/HasMimeTypeAnySubPart.adoc
new file mode 100644
index 0000000..0afaaf4
--- /dev/null
+++ b/docs/modules/servers/partials/HasMimeTypeAnySubPart.adoc
@@ -0,0 +1,11 @@
+=== HasMimeTypeAnySubPart
+
+This matcher checks if the content type matches.
+
+This matcher walks down the mime tree and will look up at all the mime parts 
of this message.
+
+use:
+
+....
+<mailet match="HasMimeTypeAnySubPart=text/plain,text/html" class="..." />
+....
diff --git 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
index 8ca40b7..a343f60 100644
--- 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
+++ 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
@@ -42,7 +42,7 @@ import com.google.common.collect.ImmutableSet;
 /**
  * <p>This matcher checks if the content type matches.</p>
  *
- * <p>This matcher do not walk down the mime tree and stops at the top level 
mime part.</p>
+ * <p>This matcher does not walk down the mime tree and stops at the top level 
mime part.</p>
  *
  * use: <pre><code><mailet match="HasMimeType=text/plain,text/html" 
class="..." /></code></pre>
  */
@@ -77,4 +77,3 @@ public class HasMimeType extends GenericMatcher {
     }
 
 }
-
diff --git 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPart.java
similarity index 55%
copy from 
mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
copy to 
mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPart.java
index 8ca40b7..f66f36b 100644
--- 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeType.java
+++ 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPart.java
@@ -17,23 +17,30 @@
  * under the License.                                           *
  ****************************************************************/
 
-
 package org.apache.james.transport.matchers;
 
 import static org.apache.mailet.base.RFC2822Headers.CONTENT_TYPE;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.Set;
 import java.util.stream.Stream;
 
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Part;
+
 import org.apache.james.core.MailAddress;
+import org.apache.james.javax.MultipartUtil;
 import org.apache.james.mime4j.field.Fields;
 import org.apache.james.util.StreamUtils;
 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.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -42,31 +49,63 @@ import com.google.common.collect.ImmutableSet;
 /**
  * <p>This matcher checks if the content type matches.</p>
  *
- * <p>This matcher do not walk down the mime tree and stops at the top level 
mime part.</p>
+ * <p>This matcher walks down the mime tree and will try to match any of the 
mime parts of this message.</p>
  *
- * use: <pre><code><mailet match="HasMimeType=text/plain,text/html" 
class="..." /></code></pre>
+ * use: <pre><code><mailet match="HasMimeTypeAnySubPart=text/plain,text/html" 
class="..." /></code></pre>
  */
-public class HasMimeType extends GenericMatcher {
+public class HasMimeTypeAnySubPart extends GenericMatcher {
 
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(HasMimeType.class);
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(HasMimeTypeAnySubPart.class);
+    private static final String MULTIPART_MIME_TYPE = "multipart/*";
 
     private Set<String> acceptedContentTypes;
 
     @Override
-    public void init() throws javax.mail.MessagingException {
+    public void init() throws MessagingException {
         acceptedContentTypes = 
ImmutableSet.copyOf(Splitter.on(",").trimResults().split(getCondition()));
     }
 
     @Override
-    public Collection<MailAddress> match(Mail mail) throws 
javax.mail.MessagingException {
-        return 
StreamUtils.ofNullable(mail.getMessage().getHeader(CONTENT_TYPE))
-            .flatMap(this::getMimeType)
-            .filter(acceptedContentTypes::contains)
+    public Collection<MailAddress> match(Mail mail) throws MessagingException {
+        return detectMatchingMimeTypes(mail.getMessage())
             .findAny()
             .map(any -> mail.getRecipients())
             .orElse(ImmutableList.of());
     }
 
+
+    private boolean isMultipart(Part part) throws MailetException {
+        try {
+            return part.isMimeType(MULTIPART_MIME_TYPE);
+        } catch (MessagingException e) {
+            throw new MailetException("Could not retrieve contenttype of 
MimePart.", e);
+        }
+    }
+
+    private Stream<String> detectMatchingMimeTypes(Part part) throws 
MailetException {
+        try {
+            if (isMultipart(part)) {
+                Multipart multipart = (Multipart) part.getContent();
+
+                return Stream.concat(
+                    MultipartUtil.retrieveBodyParts(multipart)
+                        .stream()
+                        
.flatMap(Throwing.function(this::detectMatchingMimeTypes).sneakyThrow()),
+                    detectMatchingMimeTypesNoRecursion(part));
+            }
+
+            return detectMatchingMimeTypesNoRecursion(part);
+        } catch (MessagingException | IOException e) {
+            throw new MailetException("Could not retrieve contenttype of 
MimePart.", e);
+        }
+    }
+
+    private Stream<String> detectMatchingMimeTypesNoRecursion(Part part) 
throws MessagingException {
+        return StreamUtils.ofNullable(part.getHeader(CONTENT_TYPE))
+            .flatMap(this::getMimeType)
+            .filter(acceptedContentTypes::contains);
+    }
+
     private Stream<String> getMimeType(String rawValue) {
         try {
             return Stream.of(Fields.contentType(rawValue).getMimeType());
@@ -77,4 +116,3 @@ public class HasMimeType extends GenericMatcher {
     }
 
 }
-
diff --git 
a/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPartTest.java
 
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPartTest.java
new file mode 100644
index 0000000..2efd68c
--- /dev/null
+++ 
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/HasMimeTypeAnySubPartTest.java
@@ -0,0 +1,181 @@
+/****************************************************************
+ * 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.RFC2822Headers.CONTENT_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.mailet.Mail;
+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 HasMimeTypeAnySubPartTest {
+    private static final String RECIPIENT = "[email protected]";
+    private static final String FROM = "[email protected]";
+    private static final String MIME_TYPES = "multipart/mixed";
+
+    private HasMimeTypeAnySubPart matcher;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        matcher = new HasMimeTypeAnySubPart();
+    }
+
+    @Test
+    void shouldMatchWhenHasMimeTypeTopLevel() throws Exception {
+        matcher.init(FakeMatcherConfig.builder()
+            .matcherName("HasMimeType")
+            .condition(MIME_TYPES)
+            .build());
+
+        MimeMessageBuilder message = MimeMessageBuilder.mimeMessageBuilder()
+            .setMultipartWithBodyParts(
+                MimeMessageBuilder.bodyPartBuilder()
+                    .data("simple text")
+                    .disposition("text"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .filename("text_file.txt")
+                    .disposition("attachment"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .type("application/zip")
+                    .filename("zip_file.zip")
+                    .disposition("attachment"))
+            .setSubject("test");
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .mimeMessage(message)
+            .sender(FROM)
+            .recipient(RECIPIENT)
+            .build();
+
+        assertThat(matcher.match(mail)).containsAll(mail.getRecipients());
+    }
+
+    @Test
+    void shouldNotMatchWhenTypeDoNotAppearInParts() throws Exception {
+        matcher.init(FakeMatcherConfig.builder()
+            .matcherName("HasMimeType")
+            .condition("application/zip")
+            .build());
+
+        MimeMessageBuilder message = MimeMessageBuilder.mimeMessageBuilder()
+            .setMultipartWithBodyParts(
+                MimeMessageBuilder.bodyPartBuilder()
+                    .data("simple text")
+                    .disposition("text"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .filename("text_file.txt")
+                    .disposition("attachment"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .type("image/png")
+                    .filename("file.png")
+                    .disposition("attachment"))
+            .setSubject("test");
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .mimeMessage(message)
+            .sender(FROM)
+            .recipient(RECIPIENT)
+            .build();
+
+        assertThat(matcher.match(mail)).isEmpty();
+    }
+
+    @Test
+    void shouldMatchWhenTypeDoAppearInParts() throws Exception {
+        matcher.init(FakeMatcherConfig.builder()
+            .matcherName("HasMimeType")
+            .condition("text/plain, application/zip")
+            .build());
+
+        MimeMessageBuilder message = MimeMessageBuilder.mimeMessageBuilder()
+            .setMultipartWithBodyParts(
+                MimeMessageBuilder.bodyPartBuilder()
+                    .data("simple text")
+                    .disposition("text"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .filename("text_file.txt")
+                    .disposition("attachment"),
+                MimeMessageBuilder.bodyPartBuilder()
+                    .type("application/zip")
+                    .filename("zip_file.zip")
+                    .disposition("attachment"))
+            .setSubject("test");
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .mimeMessage(message)
+            .sender(FROM)
+            .recipient(RECIPIENT)
+            .build();
+
+        assertThat(matcher.match(mail)).containsAll(mail.getRecipients());
+    }
+
+    @Test
+    void matchShouldReturnRecipientsWhenAtLeastOneMimeTypeMatch() throws 
Exception {
+        matcher.init(FakeMatcherConfig.builder()
+            .matcherName("HasMimeType")
+            .condition("text/md, text/html")
+            .build());
+
+        MimeMessageBuilder message = MimeMessageBuilder.mimeMessageBuilder()
+            .setText("content <b>in</b> <i>HTML</i>", "text/html")
+            .setSubject("test");
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .mimeMessage(message)
+            .sender(FROM)
+            .recipient(RECIPIENT)
+            .build();
+
+        
assertThat(matcher.match(mail)).containsExactlyElementsOf(mail.getRecipients());
+    }
+
+    @Test
+    void matchShouldNotFailOnEmptyCharset() throws Exception {
+        matcher.init(FakeMatcherConfig.builder()
+            .matcherName("HasMimeType")
+            .condition("text/html")
+            .build());
+
+        MimeMessage message = mock(MimeMessage.class);
+        when(message.getHeader(CONTENT_TYPE)).thenReturn(new String[] 
{"text/html; charset="});
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .mimeMessage(message)
+            .sender(FROM)
+            .recipient(RECIPIENT)
+            .build();
+
+        
assertThat(matcher.match(mail)).containsExactlyElementsOf(mail.getRecipients());
+    }
+}
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to