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 403f8d63b0907581037c1b22ec98064318d6098e
Author: Tung Van TRAN <[email protected]>
AuthorDate: Wed Jul 27 07:37:33 2022 +0700

    JAMES-3775 Mailet for RSpamD
---
 .../docs/modules/ROOT/partials/IsMarkedAsSpam.adoc |   5 +-
 .../transport/matchers/IsMarkedAsSpamTest.java     |   4 +-
 third-party/rspamd/pom.xml                         |  10 +-
 .../org/apache/james/rspamd/RSpamDScanner.java     |  83 +++++++++++
 .../apache/james/rspamd/DockerRSpamDExtension.java |   8 +-
 .../org/apache/james/rspamd/RSpamDScannerTest.java | 151 +++++++++++++++++++++
 6 files changed, 254 insertions(+), 7 deletions(-)

diff --git 
a/server/apps/distributed-app/docs/modules/ROOT/partials/IsMarkedAsSpam.adoc 
b/server/apps/distributed-app/docs/modules/ROOT/partials/IsMarkedAsSpam.adoc
index 1f2bf1ee1a..1c743eab22 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/partials/IsMarkedAsSpam.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/partials/IsMarkedAsSpam.adoc
@@ -16,12 +16,13 @@ As an example, here is a part of a mailet pipeline which 
can be used in your Loc
 <!-- End of SpamAssassing mailets pipeline -->
 ....
 
-In order to use this with `rspamd`, we need to declare a condition for the 
matcher.
+In order to use this with `rspamd`, we need to declare a condition for the 
matcher
+and drop the RSpamD jar (*third-party/rspamd*) in the James extensions-jars 
folder.
 Eg: With the recipient header for RSpamD being 
*org.apache.james.rspamd.status*,
 then the configuration would be:
 
 ....
-<!-- SpamAssassing mailets pipeline -->
+<!-- RSpamD mailets pipeline -->
     <mailet match="IsMarkedAsSpam=org.apache.james.rspamd.status" 
class="WithStorageDirective">
         <targetFolderName>Spam</targetFolderName>
     </mailet>
diff --git 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/IsMarkedAsSpamTest.java
 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/IsMarkedAsSpamTest.java
index 5841ee54c9..3ea229ad1f 100644
--- 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/IsMarkedAsSpamTest.java
+++ 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/IsMarkedAsSpamTest.java
@@ -168,10 +168,10 @@ class IsMarkedAsSpamTest {
             .recipient("[email protected]")
             .addHeaderForRecipient(PerRecipientHeaders.Header.builder()
                     .name("custom.package")
-                    .value("Yes, hits=6.8 required=5.0")
+                    .value("Yes, actions=reject score=14.225 
requiredScore=14.0 desiredRewriteSubject=")
                     .build(),
                 new MailAddress("[email protected]"))
-            .attribute(Attribute.convertToAttribute("custom.package", "Yes, 
hits=6.8 required=5.0"))
+            .attribute(Attribute.convertToAttribute("custom.package", "Yes, 
actions=reject score=14.225 requiredScore=14.0 desiredRewriteSubject="))
             .build();
 
         Collection<MailAddress> matches = matcher.match(mail);
diff --git a/third-party/rspamd/pom.xml b/third-party/rspamd/pom.xml
index a8a3beb13a..51142d3bf6 100644
--- a/third-party/rspamd/pom.xml
+++ b/third-party/rspamd/pom.xml
@@ -58,6 +58,10 @@
             <groupId>${james.groupId}</groupId>
             <artifactId>apache-mailet-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-mailet-base</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>event-bus-api</artifactId>
@@ -75,6 +79,10 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-core</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-data-api</artifactId>
@@ -143,4 +151,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git 
a/third-party/rspamd/src/main/java/org/apache/james/rspamd/RSpamDScanner.java 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/RSpamDScanner.java
new file mode 100644
index 0000000000..4f25e8f497
--- /dev/null
+++ 
b/third-party/rspamd/src/main/java/org/apache/james/rspamd/RSpamDScanner.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.rspamd;
+
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.mail.MessagingException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.rspamd.client.RSpamDHttpClient;
+import org.apache.james.rspamd.model.AnalysisResult;
+import org.apache.james.server.core.MimeMessageInputStream;
+import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
+import org.apache.mailet.AttributeValue;
+import org.apache.mailet.Mail;
+import org.apache.mailet.PerRecipientHeaders;
+import org.apache.mailet.base.GenericMailet;
+
+import com.google.common.collect.ImmutableList;
+
+public class RSpamDScanner extends GenericMailet {
+    public static final AttributeName FLAG_MAIL = 
AttributeName.of("org.apache.james.rspamd.flag");
+    public static final AttributeName STATUS_MAIL = 
AttributeName.of("org.apache.james.rspamd.status");
+
+    private final RSpamDHttpClient rSpamDHttpClient;
+
+    @Inject
+    public RSpamDScanner(RSpamDHttpClient rSpamDHttpClient) {
+        this.rSpamDHttpClient = rSpamDHttpClient;
+    }
+
+    @Override
+    public void service(Mail mail) throws MessagingException {
+        AnalysisResult rSpamDResult = rSpamDHttpClient.checkV2(new 
MimeMessageInputStream(mail.getMessage())).block();
+
+        mail.getRecipients()
+            .forEach(recipient -> appendRSpamDResultHeader(mail, recipient, 
rSpamDResult));
+    }
+
+    private void appendRSpamDResultHeader(Mail mail, MailAddress recipient, 
AnalysisResult rSpamDResult) {
+        for (Attribute attribute : getHeadersAsAttributes(rSpamDResult)) {
+            
mail.addSpecificHeaderForRecipient(PerRecipientHeaders.Header.builder()
+                .name(attribute.getName().asString())
+                .value((String) attribute.getValue().value())
+                .build(), recipient);
+        }
+    }
+
+    private List<Attribute> getHeadersAsAttributes(AnalysisResult 
rSpamDResult) {
+        String defaultFlagMailAttributeValue = "NO";
+        String defaultStatusMailAttributeValue = "No";
+        if (rSpamDResult.getAction().equals(AnalysisResult.Action.REJECT)) {
+            defaultFlagMailAttributeValue = "YES";
+            defaultStatusMailAttributeValue = "Yes";
+        }
+
+        return ImmutableList.of(new Attribute(FLAG_MAIL, 
AttributeValue.of(defaultFlagMailAttributeValue)),
+            new Attribute(STATUS_MAIL, 
AttributeValue.of(defaultStatusMailAttributeValue + ","
+                + " actions=" + rSpamDResult.getAction().getDescription()
+                + " score=" + rSpamDResult.getScore()
+                + " requiredScore=" + rSpamDResult.getRequiredScore())));
+    }
+}
diff --git 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/DockerRSpamDExtension.java
 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/DockerRSpamDExtension.java
index 0dc22cccd8..47fe2b74b5 100644
--- 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/DockerRSpamDExtension.java
+++ 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/DockerRSpamDExtension.java
@@ -59,7 +59,11 @@ public class DockerRSpamDExtension implements 
GuiceModuleTestExtension {
         return dockerRSpamD();
     }
 
-    public URL getBaseUrl() throws MalformedURLException {
-        return new URL("http://127.0.0.1:"; + dockerRSpamD().getPort());
+    public URL getBaseUrl() {
+        try {
+            return new URL("http://127.0.0.1:"; + dockerRSpamD().getPort());
+        } catch (MalformedURLException e) {
+            throw new RuntimeException(e);
+        }
     }
 }
diff --git 
a/third-party/rspamd/src/test/java/org/apache/james/rspamd/RSpamDScannerTest.java
 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/RSpamDScannerTest.java
new file mode 100644
index 0000000000..3079a84116
--- /dev/null
+++ 
b/third-party/rspamd/src/test/java/org/apache/james/rspamd/RSpamDScannerTest.java
@@ -0,0 +1,151 @@
+/****************************************************************
+ * 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.rspamd;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.builder.MimeMessageBuilder;
+import org.apache.james.rspamd.client.RSpamDClientConfiguration;
+import org.apache.james.rspamd.client.RSpamDHttpClient;
+import org.apache.james.util.MimeMessageUtil;
+import org.apache.mailet.Mail;
+import org.apache.mailet.PerRecipientHeaders;
+import org.apache.mailet.base.test.FakeMail;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.google.common.collect.ImmutableList;
+
+class RSpamDScannerTest {
+
+    @RegisterExtension
+    static DockerRSpamDExtension rSpamDExtension = new DockerRSpamDExtension();
+    static final String rSpamDPassword = "admin";
+
+    private RSpamDScanner mailet;
+
+    @BeforeEach
+    void setup() {
+        RSpamDClientConfiguration configuration = new 
RSpamDClientConfiguration(rSpamDExtension.getBaseUrl(), rSpamDPassword, 
Optional.empty());
+        RSpamDHttpClient client = new RSpamDHttpClient(configuration);
+        mailet = new RSpamDScanner(client);
+    }
+
+    @Test
+    void serviceShouldWriteSpamAttributeOnMail() throws Exception {
+        Mail mail = FakeMail.builder()
+            .name("name")
+            .recipient("[email protected]")
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .addToRecipient("[email protected]")
+                .addFrom("[email protected]")
+                .setSubject("testing")
+                .setText("Please!")
+                .build())
+            .build();
+
+        mailet.service(mail);
+
+        assertThat(
+            mail.getPerRecipientSpecificHeaders()
+                .getHeadersByRecipient()
+                .get(new MailAddress("[email protected]"))
+                .stream()
+                .map(PerRecipientHeaders.Header::getName)
+                .collect(ImmutableList.toImmutableList()))
+            .contains(RSpamDScanner.FLAG_MAIL.asString(), 
RSpamDScanner.STATUS_MAIL.asString());
+    }
+
+    @Test
+    void serviceShouldWriteMessageAsNotSpamWhenNotSpam() throws Exception {
+        Mail mail = FakeMail.builder()
+            .name("name")
+            .recipient("[email protected]")
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .addToRecipient("[email protected]")
+                .addFrom("[email protected]")
+                .setSubject("testing")
+                .setText("Please!")
+                .build())
+            .build();
+
+        mailet.service(mail);
+
+        Collection<PerRecipientHeaders.Header> headersForRecipient = 
mail.getPerRecipientSpecificHeaders()
+            .getHeadersForRecipient(new MailAddress("[email protected]"));
+
+        SoftAssertions.assertSoftly(softly -> {
+            softly.assertThat(headersForRecipient.stream()
+                    .filter(header -> 
header.getName().equals(RSpamDScanner.FLAG_MAIL.asString()))
+                    .filter(header -> header.getValue().startsWith("NO"))
+                    .findAny())
+                .isPresent();
+
+            softly.assertThat(headersForRecipient.stream()
+                    .filter(header -> 
header.getName().equals(RSpamDScanner.STATUS_MAIL.asString()))
+                    .filter(header -> header.getValue().startsWith("No, 
actions=no action"))
+                    .findAny())
+                .isPresent();
+
+        });
+    }
+
+    @Test
+    void serviceShouldWriteMessageAsSpamWhenSpam() throws Exception {
+        MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream(
+            ClassLoader.getSystemResourceAsStream("mail/spam/spam8.eml"));
+
+        Mail mail = FakeMail.builder()
+            .name("name")
+            .recipient("[email protected]")
+            .mimeMessage(mimeMessage)
+            .build();
+
+        mailet.service(mail);
+
+
+        Collection<PerRecipientHeaders.Header> headersForRecipient = 
mail.getPerRecipientSpecificHeaders()
+            .getHeadersForRecipient(new MailAddress("[email protected]"));
+
+        SoftAssertions.assertSoftly(softly -> {
+            softly.assertThat(headersForRecipient.stream()
+                    .filter(header -> 
header.getName().equals(RSpamDScanner.FLAG_MAIL.asString()))
+                    .filter(header -> header.getValue().startsWith("YES"))
+                    .findAny())
+                .isPresent();
+
+            softly.assertThat(headersForRecipient.stream()
+                    .filter(header -> 
header.getName().equals(RSpamDScanner.STATUS_MAIL.asString()))
+                    .filter(header -> header.getValue().startsWith("Yes, 
actions=reject"))
+                    .findAny())
+                .isPresent();
+
+        });
+    }
+}
\ No newline at end of file


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

Reply via email to