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 9465de671e2710bef5ad2416835340f5158ce45b
Author: Benoit TELLIER <[email protected]>
AuthorDate: Thu Nov 28 16:11:02 2024 +0100

    [ENHANCEMENT] Add a PartHasContentType
    
    Rejecting contentTypes is a common policy for content filtering.
    
    James should offer a tool out of the box to do this.
---
 .../servers/partials/PartHasContentType.adoc       |  12 +++
 .../servers/partials/configure/matchers.adoc       |   4 +-
 .../transport/matchers/PartHasContentType.java     |  68 +++++++++++++
 .../james/transport/matchers/utils/MimeWalk.java   |   4 -
 .../transport/matchers/PartHasContentTypeTest.java | 105 +++++++++++++++++++++
 5 files changed, 188 insertions(+), 5 deletions(-)

diff --git a/docs/modules/servers/partials/PartHasContentType.adoc 
b/docs/modules/servers/partials/PartHasContentType.adoc
new file mode 100644
index 0000000000..9d01ad3206
--- /dev/null
+++ b/docs/modules/servers/partials/PartHasContentType.adoc
@@ -0,0 +1,12 @@
+=== PartHasContentType
+
+Checks if at least one attachment has a content type header which matches any
+element of a comma-separated or space-separated list of content type masks.
+
+Syntax: *match="PartHasContentType=[-d] masks"*
+
+The match is case-insensitive.
+
+Multiple content types name masks can be specified, e.g.: 
'application/json,image/png'.
+
+If '*-d*' is coded, some debug info will be logged.
diff --git a/docs/modules/servers/partials/configure/matchers.adoc 
b/docs/modules/servers/partials/configure/matchers.adoc
index 3b787e2a40..cd7d6ff074 100644
--- a/docs/modules/servers/partials/configure/matchers.adoc
+++ b/docs/modules/servers/partials/configure/matchers.adoc
@@ -57,6 +57,8 @@ include::partial$IsSMIMESigned.adoc[]
 
 include::partial$IsX509CertificateSubject.adoc[]
 
+include::partial$PartHasContentType.adoc[]
+
 include::partial$RecipientCountExceeds.adoc[]
 
 include::partial$RecipientDomainIs.adoc[]
@@ -163,4 +165,4 @@ Here is the syntax to adopt in *mailetcontainer.xml*:
         <processor>relay</processor>
     </mailet>
 </processor>
-....
\ No newline at end of file
+....
diff --git 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/PartHasContentType.java
 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/PartHasContentType.java
new file mode 100755
index 0000000000..14a15ddea6
--- /dev/null
+++ 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/PartHasContentType.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * 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 jakarta.mail.MessagingException;
+import jakarta.mail.Part;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.transport.matchers.utils.MimeWalk;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMatcher;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * <P>Checks if at least one attachment has a content type header which 
matches any
+ * element of a comma-separated or space-separated list of content type 
masks.</P>
+ *
+ * <P>Syntax: <CODE>match="PartHasContentType=[-d]<I>masks</I>"</CODE></P>
+ *
+ * <P>The match is case insensitive.</P>
+ * <P>Multiple content types name masks can be specified, e.g.: 
'application/json,image/png'.</P>
+ * <P>If '<CODE>-d</CODE>' is coded, some debug info will be logged.</P>
+ */
+public class PartHasContentType extends GenericMatcher {
+    @VisibleForTesting
+    MimeWalk.Configuration configuration = MimeWalk.Configuration.DEFAULT;
+
+    @Override
+    public void init() throws MessagingException {
+        configuration = MimeWalk.Configuration.parse(getCondition());
+    }
+
+    @Override
+    public Collection<MailAddress> match(Mail mail) throws MessagingException {
+        return new MimeWalk(configuration, this::partMatch)
+            .matchMail(mail);
+    }
+
+    private boolean partMatch(Part part) throws MessagingException {
+        for (MimeWalk.Mask mask : configuration.masks()) {
+            if (part.getContentType().startsWith(mask.getMatchString())) {
+                return true; // matching file found
+            }
+        }
+        return false;
+    }
+}
+
diff --git 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/utils/MimeWalk.java
 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/utils/MimeWalk.java
index 9b2a9a776a..f0336b73a0 100644
--- 
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/utils/MimeWalk.java
+++ 
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/utils/MimeWalk.java
@@ -67,10 +67,6 @@ public class MimeWalk {
             }
         }
 
-        public boolean isSuffixMatch() {
-            return suffixMatch;
-        }
-
         public String getMatchString() {
             return matchString;
         }
diff --git 
a/mailet/standard/src/test/java/org/apache/james/transport/matchers/PartHasContentTypeTest.java
 
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/PartHasContentTypeTest.java
new file mode 100644
index 0000000000..ad17b38a74
--- /dev/null
+++ 
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/PartHasContentTypeTest.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * 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.ANY_AT_JAMES;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collection;
+
+import org.apache.james.core.MailAddress;
+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.Test;
+
+class PartHasContentTypeTest {
+    @Test
+    void shouldNotMatchWhenNoContentType() throws Exception {
+        PartHasContentType testee = new PartHasContentType();
+        testee.init(FakeMatcherConfig.builder()
+            .matcherName("PartHasContentType")
+            .condition("image/png")
+            .build());
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .recipient(ANY_AT_JAMES)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setMultipartWithBodyParts(
+                    MimeMessageBuilder.bodyPartBuilder()
+                        .disposition("attachment")
+                        .filename("xxx.zip")))
+            .build();
+        Collection<MailAddress> matched = testee.match(mail);
+
+        assertThat(matched)
+            .isNull();
+    }
+
+    @Test
+    void shouldNotMatchWhenWrongContentType() throws Exception {
+        PartHasContentType testee = new PartHasContentType();
+        testee.init(FakeMatcherConfig.builder()
+            .matcherName("PartHasContentType")
+            .condition("image/png")
+            .build());
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .recipient(ANY_AT_JAMES)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setMultipartWithBodyParts(
+                    MimeMessageBuilder.bodyPartBuilder()
+                        .type("text/plain")
+                        .disposition("attachment")
+                        .filename("xxx.zip")))
+            .build();
+        Collection<MailAddress> matched = testee.match(mail);
+
+        assertThat(matched)
+            .isNull();
+    }
+
+    @Test
+    void shouldMatchWhenGoodContentType() throws Exception {
+        PartHasContentType testee = new PartHasContentType();
+        testee.init(FakeMatcherConfig.builder()
+            .matcherName("PartHasContentType")
+            .condition("image/png")
+            .build());
+
+        Mail mail = FakeMail.builder()
+            .name("mail")
+            .recipient(ANY_AT_JAMES)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setMultipartWithBodyParts(
+                    MimeMessageBuilder.bodyPartBuilder()
+                        .type("image/png")
+                        .disposition("attachment")
+                        .filename("xxx.zip")))
+            .build();
+        Collection<MailAddress> matched = testee.match(mail);
+
+        assertThat(matched)
+            .isNotEmpty();
+    }
+}
\ No newline at end of file


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

Reply via email to