JAMES-2341 Specify user to SpamAssassin This enables per-user spam reporting customization.
Note that for SpamAssassin, the rejection being global to all of the recipients, Spam is checked against the general DB and not one of the users (the result being different for each of them). Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/3b997148 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/3b997148 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/3b997148 Branch: refs/heads/master Commit: 3b997148b20ad2f0fed1c0e96a25fbce16f931ac Parents: 37435ee Author: benwa <[email protected]> Authored: Mon Mar 5 18:12:13 2018 +0700 Committer: Antoine Duprat <[email protected]> Committed: Thu Mar 8 10:36:35 2018 +0100 ---------------------------------------------------------------------- .../mailbox/spamassassin/SpamAssassin.java | 4 +-- .../spamassassin/SpamAssassinListener.java | 2 +- .../spamassassin/SpamAssassinListenerTest.java | 2 +- .../james/util/scanner/SpamAssassinInvoker.java | 31 +++++++++++++++----- .../util/scanner/SpamAssassinInvokerTest.java | 15 +++++----- server/mailet/mailets/pom.xml | 5 ++++ .../james/transport/mailets/SpamAssassin.java | 25 +++++++++++++++- .../transport/mailets/SpamAssassinTest.java | 3 +- 8 files changed, 66 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassin.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassin.java b/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassin.java index 864cba0..7eecfe9 100644 --- a/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassin.java +++ b/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassin.java @@ -37,12 +37,12 @@ public class SpamAssassin { this.spamAssassinConfiguration = spamAssassinConfiguration; } - public void learnSpam(List<InputStream> messages) { + public void learnSpam(List<InputStream> messages, String user) { if (spamAssassinConfiguration.isEnable()) { Host host = spamAssassinConfiguration.getHost().get(); SpamAssassinInvoker invoker = new SpamAssassinInvoker(host.getHostName(), host.getPort()); messages - .forEach(Throwing.consumer(message -> invoker.learnAsSpam(message))); + .forEach(Throwing.consumer(message -> invoker.learnAsSpam(message, user))); } } } http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassinListener.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassinListener.java b/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassinListener.java index bccf22d..f2de03e 100644 --- a/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassinListener.java +++ b/mailbox/plugin/spamassassin/src/main/java/org/apache/james/mailbox/spamassassin/SpamAssassinListener.java @@ -67,7 +67,7 @@ public class SpamAssassinListener implements SpamEventListener { .stream() .map(Throwing.function(Message::getFullContent)) .collect(Guavate.toImmutableList()); - spamAssassin.learnSpam(messages); + spamAssassin.learnSpam(messages, event.getMailboxPath().getUser()); } } } http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java ---------------------------------------------------------------------- diff --git a/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java b/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java index c6fe083..72761da 100644 --- a/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java +++ b/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java @@ -100,6 +100,6 @@ public class SpamAssassinListenerTest { listener.event(added); - verify(spamAssassin).learnSpam(any()); + verify(spamAssassin).learnSpam(any(), any()); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/server/container/util/src/main/java/org/apache/james/util/scanner/SpamAssassinInvoker.java ---------------------------------------------------------------------- diff --git a/server/container/util/src/main/java/org/apache/james/util/scanner/SpamAssassinInvoker.java b/server/container/util/src/main/java/org/apache/james/util/scanner/SpamAssassinInvoker.java index 3e6f856..22a1431 100644 --- a/server/container/util/src/main/java/org/apache/james/util/scanner/SpamAssassinInvoker.java +++ b/server/container/util/src/main/java/org/apache/james/util/scanner/SpamAssassinInvoker.java @@ -27,6 +27,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; +import java.util.Arrays; import java.util.List; import javax.mail.MessagingException; @@ -83,16 +84,30 @@ public class SpamAssassinInvoker { * @throws MessagingException * if an error on scanning is detected */ + public SpamAssassinResult scanMail(MimeMessage message, String user) throws MessagingException { + return scanMailWithAdditionalHeaders(message, + "User: " + user); + } + public SpamAssassinResult scanMail(MimeMessage message) throws MessagingException { + return scanMailWithAdditionalHeaders(message); + } + + public SpamAssassinResult scanMailWithAdditionalHeaders(MimeMessage message, String... additionalHeaders) throws MessagingException { try (Socket socket = new Socket(spamdHost, spamdPort); - OutputStream out = socket.getOutputStream(); - PrintWriter writer = new PrintWriter(out); - BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { + OutputStream out = socket.getOutputStream(); + PrintWriter writer = new PrintWriter(out); + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { writer.write("CHECK SPAMC/1.2"); writer.write(CRLF); - writer.write("User: [email protected]"); - writer.write(CRLF); + + Arrays.stream(additionalHeaders) + .forEach(header -> { + writer.write(header); + writer.write(CRLF); + }); + writer.write(CRLF); writer.flush(); @@ -153,7 +168,7 @@ public class SpamAssassinInvoker { * @throws MessagingException * if an error occured during learning. */ - public boolean learnAsSpam(InputStream message) throws MessagingException { + public boolean learnAsSpam(InputStream message, String user) throws MessagingException { try (Socket socket = new Socket(spamdHost, spamdPort); OutputStream out = socket.getOutputStream(); PrintWriter writer = new PrintWriter(out); @@ -164,12 +179,12 @@ public class SpamAssassinInvoker { writer.write(CRLF); writer.write("Content-length: " + byteArray.length); writer.write(CRLF); - writer.write("User: [email protected]"); - writer.write(CRLF); writer.write("Message-class: spam"); writer.write(CRLF); writer.write("Set: local, remote"); writer.write(CRLF); + writer.write("User: " + user); + writer.write(CRLF); writer.write(CRLF); writer.flush(); http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/server/container/util/src/test/java/org/apache/james/util/scanner/SpamAssassinInvokerTest.java ---------------------------------------------------------------------- diff --git a/server/container/util/src/test/java/org/apache/james/util/scanner/SpamAssassinInvokerTest.java b/server/container/util/src/test/java/org/apache/james/util/scanner/SpamAssassinInvokerTest.java index ffa796a..4dbd72d 100644 --- a/server/container/util/src/test/java/org/apache/james/util/scanner/SpamAssassinInvokerTest.java +++ b/server/container/util/src/test/java/org/apache/james/util/scanner/SpamAssassinInvokerTest.java @@ -34,6 +34,7 @@ public class SpamAssassinInvokerTest { private SpamAssassin spamAssassin; private SpamAssassinInvoker testee; + @BeforeEach public void setup(SpamAssassin spamAssassin) throws Exception { this.spamAssassin = spamAssassin; @@ -44,7 +45,7 @@ public class SpamAssassinInvokerTest { public void scanMailShouldModifyHitsField() throws Exception { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("eml/spam.eml")); - SpamAssassinResult result = testee.scanMail(mimeMessage); + SpamAssassinResult result = testee.scanMail(mimeMessage, "any@james"); // The result is varying from 0.4 to 0.0 assertThat(result.getHits()).startsWith("0."); @@ -54,7 +55,7 @@ public class SpamAssassinInvokerTest { public void scanMailShouldModifyRequiredHitsField() throws Exception { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("eml/spam.eml")); - SpamAssassinResult result = testee.scanMail(mimeMessage); + SpamAssassinResult result = testee.scanMail(mimeMessage, "any@james"); assertThat(result.getRequiredHits()).isEqualTo("5.0"); } @@ -63,7 +64,7 @@ public class SpamAssassinInvokerTest { public void scanMailShouldModifyHeadersField() throws Exception { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("eml/spam.eml")); - SpamAssassinResult result = testee.scanMail(mimeMessage); + SpamAssassinResult result = testee.scanMail(mimeMessage, "any@james"); assertThat(result.getHeadersAsAttribute()).isNotEmpty(); } @@ -75,7 +76,7 @@ public class SpamAssassinInvokerTest { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam1")); - SpamAssassinResult result = testee.scanMail(mimeMessage); + SpamAssassinResult result = testee.scanMail(mimeMessage, "any@james"); assertThat(result.getHeadersAsAttribute().get(SpamAssassinInvoker.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("YES"); } @@ -85,7 +86,7 @@ public class SpamAssassinInvokerTest { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam1")); - boolean result = testee.learnAsSpam(mimeMessage.getInputStream()); + boolean result = testee.learnAsSpam(mimeMessage.getInputStream(), "any@james"); assertThat(result).isTrue(); } @@ -95,9 +96,9 @@ public class SpamAssassinInvokerTest { MimeMessage mimeMessage = MimeMessageUtil.mimeMessageFromStream( ClassLoader.getSystemResourceAsStream("spamassassin_db/spam/spam1")); - testee.learnAsSpam(mimeMessage.getInputStream()); + testee.learnAsSpam(mimeMessage.getInputStream(), "any@james"); - SpamAssassinResult result = testee.scanMail(mimeMessage); + SpamAssassinResult result = testee.scanMail(mimeMessage, "any@james"); assertThat(result.getHeadersAsAttribute().get(SpamAssassinInvoker.FLAG_MAIL_ATTRIBUTE_NAME)).isEqualTo("YES"); } http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/server/mailet/mailets/pom.xml ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/pom.xml b/server/mailet/mailets/pom.xml index b95770f..fdbe73a 100644 --- a/server/mailet/mailets/pom.xml +++ b/server/mailet/mailets/pom.xml @@ -113,6 +113,11 @@ </dependency> <dependency> <groupId>${project.groupId}</groupId> + <artifactId>james-server-data-memory</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> <artifactId>james-server-dnsservice-api</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java index fd7b15c..f07bdcf 100644 --- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java +++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SpamAssassin.java @@ -19,11 +19,17 @@ package org.apache.james.transport.mailets; +import java.util.Arrays; import java.util.Optional; +import javax.inject.Inject; import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; +import org.apache.james.core.MailAddress; +import org.apache.james.user.api.UsersRepository; +import org.apache.james.user.api.UsersRepositoryException; import org.apache.james.util.Port; import org.apache.james.util.scanner.SpamAssassinInvoker; import org.apache.james.util.scanner.SpamAssassinResult; @@ -31,6 +37,7 @@ import org.apache.mailet.Mail; import org.apache.mailet.base.GenericMailet; import org.apache.mailet.base.MailetUtil; +import com.github.fge.lambdas.Throwing; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; @@ -64,9 +71,16 @@ public class SpamAssassin extends GenericMailet { public static final String DEFAULT_HOST = "127.0.0.1"; public static final int DEFAULT_PORT = 783; + private final UsersRepository usersRepository; + private String spamdHost; private int spamdPort; + @Inject + public SpamAssassin(UsersRepository usersRepository) { + this.usersRepository = usersRepository; + } + /** * @see org.apache.mailet.base.GenericMailet#init() */ @@ -87,7 +101,16 @@ public class SpamAssassin extends GenericMailet { // Invoke SpamAssassin connection and scan the message SpamAssassinInvoker sa = new SpamAssassinInvoker(spamdHost, spamdPort); - SpamAssassinResult result = sa.scanMail(message); + Arrays.stream(message.getAllRecipients()) + .map(InternetAddress.class::cast) + .forEach( + Throwing.consumer( + (InternetAddress recipient) -> querySpamAssassin(mail, message, sa, recipient)) + .sneakyThrow()); + } + + private void querySpamAssassin(Mail mail, MimeMessage message, SpamAssassinInvoker sa, InternetAddress recipient) throws MessagingException, UsersRepositoryException { + SpamAssassinResult result = sa.scanMail(message, usersRepository.getUser(new MailAddress(recipient))); // Add headers as attribute to mail object for (String key : result.getHeadersAsAttribute().keySet()) { http://git-wip-us.apache.org/repos/asf/james-project/blob/3b997148/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java ---------------------------------------------------------------------- diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java index 9fd2090..af1836c 100644 --- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java +++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SpamAssassinTest.java @@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import javax.mail.MessagingException; import org.apache.james.core.builder.MimeMessageBuilder; +import org.apache.james.user.memory.MemoryUsersRepository; import org.apache.james.util.Port; import org.apache.james.util.scanner.SpamAssassinInvoker; import org.apache.james.utils.MockSpamd; @@ -40,7 +41,7 @@ public class SpamAssassinTest { @Rule public MockSpamdTestRule spamd = new MockSpamdTestRule(); - private SpamAssassin mailet = new SpamAssassin(); + private SpamAssassin mailet = new SpamAssassin(MemoryUsersRepository.withVirtualHosting()); @Test public void initShouldSetDefaultSpamdHostWhenNone() throws Exception { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
