JAMES-2341 Demonstrate that Spam classification also works over IMAP COPY to "Spam" mailbox should be supported as some clients uses a COPY + EXPUNGE mechanism to implement the MOVE. MOVE extension should also be supported
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/04b3d79d Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/04b3d79d Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/04b3d79d Branch: refs/heads/master Commit: 04b3d79de77da83e70b26e1d9eb9986c51a25cc2 Parents: ce46d33 Author: benwa <[email protected]> Authored: Wed Mar 7 11:44:40 2018 +0700 Committer: Antoine Duprat <[email protected]> Committed: Thu Mar 8 11:01:18 2018 +0100 ---------------------------------------------------------------------- .../integration/SpamAssassinContract.java | 103 +++++++++++++++++++ .../apache/james/utils/IMAPMessageReader.java | 4 + 2 files changed, 107 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/04b3d79d/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java index dc3f7be..6c75381 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SpamAssassinContract.java @@ -37,6 +37,7 @@ import org.apache.james.jmap.HttpJmapAuthentication; import org.apache.james.jmap.api.access.AccessToken; import org.apache.james.mailbox.Role; import org.apache.james.utils.DataProbeImpl; +import org.apache.james.utils.IMAPMessageReader; import org.apache.james.utils.JmapGuiceProbe; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -60,6 +61,8 @@ public interface SpamAssassinContract { String ALICES_DOMAIN = "angels.org"; String ALICE = "alice@" + ALICES_DOMAIN; String ALICE_PASSWORD = "alicePassword"; + String LOCALHOST = "127.0.0.1"; + int IMAP_PORT = 1143; @BeforeEach default void setup(JamesWithSpamAssassin james) throws Throwable { @@ -150,6 +153,106 @@ public interface SpamAssassinContract { calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2)); } + @Test + default void imapCopiesToSpamMailboxShouldBeConsideredAsSpam(JamesWithSpamAssassin james) throws Exception { + Duration slowPacedPollInterval = Duration.FIVE_HUNDRED_MILLISECONDS; + ConditionFactory calmlyAwait = Awaitility.with().pollInterval(slowPacedPollInterval).and().with().pollDelay(slowPacedPollInterval).await(); + + james.getSpamAssassinExtension().getSpamAssassin().train(ALICE); + AccessToken aliceAccessToken = accessTokenFor(james.getJmapServer(), ALICE, ALICE_PASSWORD); + AccessToken bobAccessToken = accessTokenFor(james.getJmapServer(), BOB, BOB_PASSWORD); + + // Bob is sending a message to Alice + given() + .header("Authorization", bobAccessToken.serialize()) + .body(setMessageCreate(bobAccessToken)) + .when() + .post("/jmap"); + calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getInboxId(aliceAccessToken), 1)); + + // Alice is moving this message to Spam -> learning in SpamAssassin + with() + .header("Authorization", aliceAccessToken.serialize()) + .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + getInboxId(aliceAccessToken) + "\"]}}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("messageList")) + .body(ARGUMENTS + ".messageIds", hasSize(1)) + .extract() + .path(ARGUMENTS + ".messageIds"); + + try (IMAPMessageReader imapMessageReader = new IMAPMessageReader()) { + imapMessageReader.connect(LOCALHOST, IMAP_PORT) + .login(ALICE, ALICE_PASSWORD) + .select(IMAPMessageReader.INBOX); + + imapMessageReader.copyFirstMessage("Spam"); + } + calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 1)); + + // Bob is sending again the same message to Alice + given() + .header("Authorization", bobAccessToken.serialize()) + .body(setMessageCreate(bobAccessToken)) + .when() + .post("/jmap"); + + // This message is delivered in Alice Spam mailbox (she now must have 2 messages in her Spam mailbox) + calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2)); + } + + @Test + default void imapMovesToSpamMailboxShouldBeConsideredAsSpam(JamesWithSpamAssassin james) throws Exception { + Duration slowPacedPollInterval = Duration.FIVE_HUNDRED_MILLISECONDS; + ConditionFactory calmlyAwait = Awaitility.with().pollInterval(slowPacedPollInterval).and().with().pollDelay(slowPacedPollInterval).await(); + + james.getSpamAssassinExtension().getSpamAssassin().train(ALICE); + AccessToken aliceAccessToken = accessTokenFor(james.getJmapServer(), ALICE, ALICE_PASSWORD); + AccessToken bobAccessToken = accessTokenFor(james.getJmapServer(), BOB, BOB_PASSWORD); + + // Bob is sending a message to Alice + given() + .header("Authorization", bobAccessToken.serialize()) + .body(setMessageCreate(bobAccessToken)) + .when() + .post("/jmap"); + calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getInboxId(aliceAccessToken), 1)); + + // Alice is moving this message to Spam -> learning in SpamAssassin + with() + .header("Authorization", aliceAccessToken.serialize()) + .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + getInboxId(aliceAccessToken) + "\"]}}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("messageList")) + .body(ARGUMENTS + ".messageIds", hasSize(1)) + .extract() + .path(ARGUMENTS + ".messageIds"); + + try (IMAPMessageReader imapMessageReader = new IMAPMessageReader()) { + imapMessageReader.connect(LOCALHOST, IMAP_PORT) + .login(ALICE, ALICE_PASSWORD) + .select(IMAPMessageReader.INBOX); + + imapMessageReader.moveFirstMessage("Spam"); + } + calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 1)); + + // Bob is sending again the same message to Alice + given() + .header("Authorization", bobAccessToken.serialize()) + .body(setMessageCreate(bobAccessToken)) + .when() + .post("/jmap"); + + // This message is delivered in Alice Spam mailbox (she now must have 2 messages in her Spam mailbox) + calmlyAwait.atMost(10, TimeUnit.SECONDS).until(() -> areMessagesFoundInMailbox(aliceAccessToken, getSpamId(aliceAccessToken), 2)); + } + default boolean areMessagesFoundInMailbox(AccessToken accessToken, String mailboxId, int expectedNumberOfMessages) { try { with() http://git-wip-us.apache.org/repos/asf/james-project/blob/04b3d79d/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java ---------------------------------------------------------------------- diff --git a/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java b/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java index 277fe6d..2b7a7f0 100644 --- a/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java +++ b/server/testing/src/main/java/org/apache/james/utils/IMAPMessageReader.java @@ -168,4 +168,8 @@ public class IMAPMessageReader extends ExternalResource implements Closeable { public void copyFirstMessage(String destMailbox) throws IOException { imapClient.copy("1", destMailbox); } + + public void moveFirstMessage(String destMailbox) throws IOException { + imapClient.sendCommand("MOVE 1 " + destMailbox); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
