You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2020/10/01 03:54:11 UTC

[james-project] branch master updated (60f8428 -> 63667e4)

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

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


    from 60f8428  JAMES-3393 wrap exception into Mono instead of using Throwing
     new b0d172b  JAMES-3377 Email/query allow filtering by text
     new 1f067f7  JAMES-3377 Email/query allow filtering by text should ignore attachment
     new 46d83c4  JAMES-1717 AutomaticallySentMailDetector should handle auto-generated and auto-notified
     new 8f92e35  JAMES-1717 VacationMailet should not send notification to senders with '-noreply' suffix
     new ffd53fd  JAMES-3391 And integration test for bodyFiltering
     new 63667e4  JAMES-3391 Implement for bodyFiltering

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../mailet/base/AutomaticallySentMailDetector.java |   2 +
 .../AutomaticallySentMailDetectorImpl.java         |   8 +-
 .../AutomaticallySentMailDetectorImplTest.java     |  24 +
 .../apache/james/jmap/mailet/VacationMailet.java   |  17 +-
 .../james/jmap/mailet/VacationMailetTest.java      |  20 +
 .../contract/EmailQueryMethodContract.scala        | 560 ++++++++++++++++++++-
 .../rfc8621/memory/MemoryEmailQueryMethodTest.java |   6 +
 .../james/jmap/utils/search/MailboxFilter.scala    |  21 +-
 8 files changed, 647 insertions(+), 11 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 03/06: JAMES-1717 AutomaticallySentMailDetector should handle auto-generated and auto-notified

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 46d83c4a2f8612a46579a575ead58fc91ef439e4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Sep 30 10:51:11 2020 +0700

    JAMES-1717 AutomaticallySentMailDetector should handle auto-generated and auto-notified
    
    See https://www.iana.org/assignments/auto-submitted-keywords/auto-submitted-keywords.xhtml
---
 .../mailet/base/AutomaticallySentMailDetector.java |  2 ++
 .../AutomaticallySentMailDetectorImpl.java         |  8 +++++++-
 .../AutomaticallySentMailDetectorImplTest.java     | 24 ++++++++++++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/mailet/base/src/main/java/org/apache/mailet/base/AutomaticallySentMailDetector.java b/mailet/base/src/main/java/org/apache/mailet/base/AutomaticallySentMailDetector.java
index c90fc44..e81992e 100644
--- a/mailet/base/src/main/java/org/apache/mailet/base/AutomaticallySentMailDetector.java
+++ b/mailet/base/src/main/java/org/apache/mailet/base/AutomaticallySentMailDetector.java
@@ -27,6 +27,8 @@ public interface AutomaticallySentMailDetector {
 
     String AUTO_SUBMITTED_HEADER = "Auto-Submitted";
     String AUTO_REPLIED_VALUE = "auto-replied";
+    String AUTO_GENERATED_VALUE = "auto-generated";
+    String AUTO_NOTIFIED_VALUE = "auto-notified";
 
     boolean isAutomaticallySent(Mail mail) throws MessagingException;
 
diff --git a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImpl.java b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImpl.java
index ab9f557..6f91048 100644
--- a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImpl.java
+++ b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImpl.java
@@ -85,11 +85,17 @@ public class AutomaticallySentMailDetectorImpl implements AutomaticallySentMailD
         String[] headers = mail.getMessage().getHeader(AUTO_SUBMITTED_HEADER);
         if (headers != null) {
             return Arrays.stream(headers)
-                .anyMatch(header -> header.equalsIgnoreCase(AUTO_REPLIED_VALUE));
+                .anyMatch(this::isAutoSubmitted);
         }
         return false;
     }
 
+    private boolean isAutoSubmitted(String header) {
+        return header.equalsIgnoreCase(AUTO_REPLIED_VALUE)
+            || header.equalsIgnoreCase(AUTO_GENERATED_VALUE)
+            || header.equalsIgnoreCase(AUTO_NOTIFIED_VALUE);
+    }
+
     @Override
     public boolean isMdnSentAutomatically(Mail mail) throws MessagingException {
         ResultCollector resultCollector = new ResultCollector(false);
diff --git a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImplTest.java b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImplTest.java
index b827b57..52d0071 100644
--- a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImplTest.java
+++ b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/AutomaticallySentMailDetectorImplTest.java
@@ -235,6 +235,30 @@ public class AutomaticallySentMailDetectorImplTest {
     }
 
     @Test
+    public void isAutoSubmittedShouldBeDetectedWhenAutoGenerated() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+                .name("mail")
+                .sender("any@any.com")
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addHeader("Auto-Submitted", "auto-generated"))
+                .build();
+
+        assertThat(new AutomaticallySentMailDetectorImpl().isAutoSubmitted(fakeMail)).isTrue();
+    }
+
+    @Test
+    public void isAutoSubmittedShouldBeDetectedWhenAutoNotified() throws Exception {
+        FakeMail fakeMail = FakeMail.builder()
+                .name("mail")
+                .sender("any@any.com")
+                .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                    .addHeader("Auto-Submitted", "auto-notified"))
+                .build();
+
+        assertThat(new AutomaticallySentMailDetectorImpl().isAutoSubmitted(fakeMail)).isTrue();
+    }
+
+    @Test
     public void isMdnSentAutomaticallyShouldBeDetected() throws Exception {
         MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
         MimeMultipart multipart = new MimeMultipart();


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 06/06: JAMES-3391 Implement for bodyFiltering

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 63667e42115119aaaaa7148956e22aa26fdfbd80
Author: duc91 <vd...@linagora.com>
AuthorDate: Tue Sep 29 18:01:12 2020 +0700

    JAMES-3391 Implement for bodyFiltering
---
 .../contract/EmailQueryMethodContract.scala        | 245 ++-------------------
 .../james/jmap/utils/search/MailboxFilter.scala    |  10 +-
 2 files changed, 23 insertions(+), 232 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index fdd2d2a..750eab4 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -592,13 +592,13 @@ trait EmailQueryMethodContract {
     }
   }
 
-  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenBody(server: GuiceJamesServer): Unit = {
+  def bodyFilterShouldMatchTextPlainOnBody(server: GuiceJamesServer): Unit = {
     val message: Message = simpleMessage("this message body are uniqueeee")
     val mailboxPath = MailboxPath.inbox(BOB)
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
     val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
     val messageId1: MessageId = sendMessageToBobInbox(server, message, requestDate)
-    server.getProbe(classOf[MailboxProbeImpl])
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, mailboxPath, AppendCommand.from(
         ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
       .getMessageId
@@ -632,27 +632,14 @@ trait EmailQueryMethodContract {
         .body
         .asString
 
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId1)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId1.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(s"""["${messageId1.serialize}"]}""")
     }
   }
 
   @Test
-  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenAttachmentsContent(server: GuiceJamesServer): Unit = {
+  def bodyFilterShouldMatchTextPlainOnAttachmentsContent(server: GuiceJamesServer): Unit = {
     val message: Message = simpleMessage("this message body are uniqueeee")
     val mailboxPath = MailboxPath.inbox(BOB)
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
@@ -692,27 +679,14 @@ trait EmailQueryMethodContract {
         .body
         .asString
 
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId2)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId2.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(s"""["${messageId2.serialize}"]}""")
     }
   }
 
   @Test
-  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenAttachmentsFileName(server: GuiceJamesServer): Unit = {
+  def bodyFilterShouldMatchTextPlainOnAttachmentsFileName(server: GuiceJamesServer): Unit = {
     val message: Message = simpleMessage("this message body are uniqueeee")
     val mailboxPath = MailboxPath.inbox(BOB)
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
@@ -752,197 +726,10 @@ trait EmailQueryMethodContract {
         .body
         .asString
 
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId2)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId2.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
-    }
-  }
-
-  @Test
-  def bodyMessageShouldSupportContains(server: GuiceJamesServer): Unit = {
-    val message: Message = buildTestMessage
-    val mailboxPath = MailboxPath.inbox(BOB)
-    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
-    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
-    val messageId1: MessageId = sendMessageToBobInbox(server, message, requestDate)
-    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, mailboxPath, AppendCommand.from(message))
-      .getMessageId
-
-    val request =
-      s"""{
-         |  "using": [
-         |    "urn:ietf:params:jmap:core",
-         |    "urn:ietf:params:jmap:mail"],
-         |  "methodCalls": [[
-         |    "Email/query",
-         |    {
-         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-         |      "filter": {
-         |        "body":"test"
-         |       }
-         |    },
-         |    "c1"]]
-         |}""".stripMargin
-
-    awaitAtMostTenSeconds.untilAsserted { () =>
-      val response = `given`
-        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
-        .body(request)
-      .when
-        .post
-      .`then`
-        .statusCode(SC_OK)
-        .contentType(JSON)
-        .extract
-        .body
-        .asString
-
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId2, messageId1)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId2.serialize}", "${messageId1.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
-    }
-  }
-
-  @Test
-  def attachmentsContentShouldSupportContains(server: GuiceJamesServer): Unit = {
-    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
-    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
-    sendMessageToBobInbox(server, buildTestMessage, requestDate)
-    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
-        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
-      .getMessageId
-
-    val request =
-      s"""{
-         |  "using": [
-         |    "urn:ietf:params:jmap:core",
-         |    "urn:ietf:params:jmap:mail"],
-         |  "methodCalls": [[
-         |    "Email/query",
-         |    {
-         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-         |      "filter": {
-         |        "body":"RSA PRIVATE"
-         |       }
-         |    },
-         |    "c1"]]
-         |}""".stripMargin
-
-    awaitAtMostTenSeconds.untilAsserted { () =>
-      val response = `given`
-        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
-        .body(request)
-      .when
-        .post
-      .`then`
-        .statusCode(SC_OK)
-        .contentType(JSON)
-        .extract
-        .body
-        .asString
-
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId2)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId2.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
-    }
-  }
-
-  @Test
-  def attachmentFileNameShouldSupportContains(server: GuiceJamesServer): Unit = {
-    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
-    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
-    sendMessageToBobInbox(server, buildTestMessage, requestDate)
-    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
-        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
-      .getMessageId
-
-    val request =
-      s"""{
-         |  "using": [
-         |    "urn:ietf:params:jmap:core",
-         |    "urn:ietf:params:jmap:mail"],
-         |  "methodCalls": [[
-         |    "Email/query",
-         |    {
-         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-         |      "filter": {
-         |        "body":"text2"
-         |       }
-         |    },
-         |    "c1"]]
-         |}""".stripMargin
-
-    awaitAtMostTenSeconds.untilAsserted { () =>
-      val response = `given`
-        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
-        .body(request)
-      .when
-        .post
-      .`then`
-        .statusCode(SC_OK)
-        .contentType(JSON)
-        .extract
-        .body
-        .asString
-
-      assertThatJson(response).isEqualTo(
-        s"""{
-           |    "sessionState": "75128aab4b1b",
-           |    "methodResponses": [[
-           |            "Email/query",
-           |            {
-           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-           |                "queryState": "${generateQueryState(messageId2)}",
-           |                "canCalculateChanges": false,
-           |                "position": 0,
-           |                "limit": 256,
-           |                "ids": ["${messageId2.serialize}"]
-           |            },
-           |            "c1"
-           |        ]]
-           |}""".stripMargin)
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+        s"""["${messageId2.serialize}"]""")
     }
   }
 
@@ -1120,7 +907,6 @@ trait EmailQueryMethodContract {
             .setSubject("test")
             .setBody("testmail", StandardCharsets.UTF_8)
             .build))
-
       .getMessageId
 
     val request =
@@ -2339,8 +2125,7 @@ trait EmailQueryMethodContract {
   @ValueSource(strings = Array(
     "allInThreadHaveKeyword",
     "someInThreadHaveKeyword",
-    "noneInThreadHaveKeyword",
-    "body"
+    "noneInThreadHaveKeyword"
   ))
   def listMailsShouldReturnUnsupportedFilterWhenValidButUnsupported(unsupportedFilter: String): Unit = {
     val request =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index 6142886..c0580ed 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -27,7 +27,7 @@ import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery.{AccessibleNamespace, Namespace, PersonalNamespace}
 import org.apache.james.mailbox.model.SearchQuery.DateResolution.Second
-import org.apache.james.mailbox.model.SearchQuery.{AddressType, DateComparator, DateOperator, DateResolution, InternalDateCriterion}
+import org.apache.james.mailbox.model.SearchQuery.{Criterion, AddressType, DateComparator, DateOperator, DateResolution, InternalDateCriterion}
 import org.apache.james.mailbox.model.{MultimailboxesSearchQuery, SearchQuery}
 
 import scala.jdk.CollectionConverters._
@@ -237,7 +237,13 @@ object MailboxFilter {
   case object Body extends QueryFilter {
     override def toQuery(builder: SearchQuery.Builder, request: EmailQueryRequest): Either[UnsupportedFilterException, SearchQuery.Builder] =
       request.filter.flatMap(_.body) match {
-        case Some(_) => Left(UnsupportedFilterException("body"))
+        case Some(text) =>
+           val bodyFilterOr: List[Criterion] = List(
+            SearchQuery.attachmentContains(text.value),
+            SearchQuery.bodyContains(text.value),
+            SearchQuery.attachmentFileName(text.value))
+
+          Right(builder.andCriteria(SearchQuery.or(bodyFilterOr.asJava)))
         case None => Right(builder)
       }
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/06: JAMES-3377 Email/query allow filtering by text

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b0d172be411edbd6d58dc7bdcf02d3b5449a8cd3
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Sep 30 14:00:46 2020 +0700

    JAMES-3377 Email/query allow filtering by text
---
 .../contract/EmailQueryMethodContract.scala        | 330 ++++++++++++++++++++-
 .../rfc8621/memory/MemoryEmailQueryMethodTest.java |   6 +
 .../james/jmap/utils/search/MailboxFilter.scala    |   2 +-
 3 files changed, 334 insertions(+), 4 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index e954bf5..3fdd803 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -46,13 +46,13 @@ import org.apache.james.mailbox.model.MailboxPath.inbox
 import org.apache.james.mailbox.model.{MailboxACL, MailboxPath, MessageId}
 import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.field.address.DefaultAddressParser
-import org.apache.james.mime4j.message.DefaultMessageWriter
+import org.apache.james.mime4j.message.{DefaultMessageWriter, MultipartBuilder}
 import org.apache.james.mime4j.stream.RawField
 import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl}
 import org.apache.james.utils.DataProbeImpl
 import org.awaitility.Awaitility
 import org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS
-import org.junit.jupiter.api.{BeforeEach, Test}
+import org.junit.jupiter.api.{BeforeEach, Disabled, Test}
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource}
 import org.threeten.extra.Seconds
@@ -1985,7 +1985,6 @@ trait EmailQueryMethodContract {
     "allInThreadHaveKeyword",
     "someInThreadHaveKeyword",
     "noneInThreadHaveKeyword",
-    "text",
     "body"
   ))
   def listMailsShouldReturnUnsupportedFilterWhenValidButUnsupported(unsupportedFilter: String): Unit = {
@@ -4505,6 +4504,331 @@ trait EmailQueryMethodContract {
     }
   }
 
+  @Test
+  def emailQueryShouldSupportTextFilterForHeaders(server: GuiceJamesServer): Unit = {
+    val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
+
+    mailboxProbe.createMailbox(inbox(BOB))
+
+    val messageId1: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("a mail")
+        .setFrom("bloblah@domain.tld")
+        .setFrom("test@domain.tld")
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val messageId2: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("another mail")
+        .setTo("bloblah@domain.tld")
+        .setTo("test@gmail.com")
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val messageId3: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("another mail")
+        .addField(new RawField("Cc", "<bl...@domain.tld>, <te...@gmail.com>"))
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val messageId4: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("another mail")
+        .addField(new RawField("Bcc", "<bl...@domain.tld>, <te...@gmail.com>"))
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val messageId5: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("Subject with test word")
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    mailboxProbe
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("should not be found mail")
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "test"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[
+             |  "${messageId5.serialize}",
+             |  "${messageId4.serialize}",
+             |  "${messageId3.serialize}",
+             |  "${messageId2.serialize}",
+             |  "${messageId1.serialize}"
+             |]""".stripMargin)
+    }
+  }
+
+  @Test
+  def emailQueryShouldSupportTextFilterForTextBody(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+    val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("a mail")
+        .setBody("This is a test body", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("should not be found mail")
+        .setBody("lorem ipsum", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "test"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[
+             |  "${messageId1.serialize}"
+             |]""".stripMargin)
+    }
+  }
+
+  @Test
+  def emailQueryShouldSupportTextFilterForHtmlBody(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+    val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("a mail")
+        .setBody("<body>This is a test body</body>", "html", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("should not be found mail")
+        .setBody("<body>This is another body</body>", "html", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "test"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[
+             |  "${messageId1.serialize}"
+             |]""".stripMargin)
+    }
+  }
+
+  @Test
+  def emailQueryShouldSupportTextFilterForMultipartMessage(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+
+    val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("a mail")
+        .setBody(MultipartBuilder.create()
+          .addTextPart("This is a test body", StandardCharsets.UTF_8)
+          .build())
+        .build))
+      .getMessageId
+
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("should not be found mail")
+        .setBody(MultipartBuilder.create()
+          .addTextPart("This is another body", StandardCharsets.UTF_8)
+          .build())
+        .build))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "test"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[
+             |  "${messageId1.serialize}"
+             |]""".stripMargin)
+    }
+  }
+
+  @Test
+  def emailQueryFilterByTextShouldIgnoreMarkupsInHtmlBody(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, inbox(BOB), AppendCommand.from(Message.Builder
+        .of
+        .setSubject("A mail")
+        .setBody("<body><test>This is a html body<test></body>", "html", StandardCharsets.UTF_8)
+        .build))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "test"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[]""".stripMargin)
+    }
+  }
+
   private def sendMessageToBobInbox(server: GuiceJamesServer, message: Message, requestDate: Date): MessageId = {
     server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
index 78b50d1..474f93a 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
@@ -26,6 +26,8 @@ import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.jmap.rfc8621.contract.EmailQueryMethodContract;
 import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 public class MemoryEmailQueryMethodTest implements EmailQueryMethodContract {
@@ -36,4 +38,8 @@ public class MemoryEmailQueryMethodTest implements EmailQueryMethodContract {
             .overrideWith(new TestJMAPServerModule()))
         .build();
 
+    @Test
+    @Override
+    @Disabled("JAMES-3377 Not supported for in-memory test")
+    public void emailQueryFilterByTextShouldIgnoreMarkupsInHtmlBody(GuiceJamesServer server) {}
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index 30a1022..27e72c9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -178,7 +178,7 @@ object MailboxFilter {
   case object Text extends QueryFilter {
     override def toQuery(builder: SearchQuery.Builder, request: EmailQueryRequest): Either[UnsupportedFilterException, SearchQuery.Builder] =
       request.filter.flatMap(_.text) match {
-        case Some(_) => Left(UnsupportedFilterException("text"))
+        case Some(text) => Right(builder.andCriteria(SearchQuery.textContains(text.value)))
         case None => Right(builder)
       }
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 04/06: JAMES-1717 VacationMailet should not send notification to senders with '-noreply' suffix

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8f92e35937b4ad7ea4145d54aed252e2ceaed9d8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Sep 30 11:58:25 2020 +0700

    JAMES-1717 VacationMailet should not send notification to senders with '-noreply' suffix
    
    This additional heuristic allow to better handle automatic sender that did not set there headers correctly
    for notifying that a mail is sent automatically.
---
 .../org/apache/james/jmap/mailet/VacationMailet.java | 17 ++++++++++++++---
 .../apache/james/jmap/mailet/VacationMailetTest.java | 20 ++++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
index d9c2588..aea13e6 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/VacationMailet.java
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.mailet;
 
 import java.time.ZonedDateTime;
+import java.util.Locale;
 
 import javax.inject.Inject;
 import javax.mail.MessagingException;
@@ -93,14 +94,24 @@ public class VacationMailet extends GenericMailet {
     }
 
     private void sendNotificationIfRequired(MailAddress recipient, Mail processedMail, ZonedDateTime processingDate, Vacation vacation, Boolean alreadySent) {
-        if (shouldSendNotification(vacation, processingDate, alreadySent)) {
+        if (shouldSendNotification(processedMail, vacation, processingDate, alreadySent)) {
             sendNotification(recipient, processedMail, vacation);
         }
     }
 
-    private boolean shouldSendNotification(Vacation vacation, ZonedDateTime processingDate, boolean alreadySent) {
+    private boolean shouldSendNotification(Mail processedMail, Vacation vacation, ZonedDateTime processingDate, boolean alreadySent) {
         return vacation.isActiveAtDate(processingDate)
-            && ! alreadySent;
+            && !alreadySent
+            && !isNoReplySender(processedMail);
+    }
+
+    private Boolean isNoReplySender(Mail processedMail) {
+        return processedMail.getMaybeSender()
+            .asOptional()
+            .map(address -> address.getLocalPart()
+                .toLowerCase(Locale.US)
+                .endsWith("-noreply"))
+            .orElse(true);
     }
 
     private void sendNotification(MailAddress recipient, Mail processedMail, Vacation vacation) {
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
index d03b751..4d36816 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/mailet/VacationMailetTest.java
@@ -241,6 +241,26 @@ public class VacationMailetTest {
     }
 
     @Test
+    public void serviceShouldNotSendNotificationToSenderWithNoReplySuffix() throws Exception {
+        FakeMail mail = FakeMail.builder()
+            .name("name")
+            .fileName("spamMail.eml")
+            .recipient(originalRecipient)
+            .sender(new MailAddress("distant-noreply@apache.org"))
+            .build();
+
+        when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
+            .thenReturn(Mono.just(VACATION));
+        when(zonedDateTimeProvider.get()).thenReturn(DATE_TIME_2017);
+        when(notificationRegistry.isRegistered(any(), any())).thenReturn(Mono.just(false));
+        when(automaticallySentMailDetector.isAutomaticallySent(mail)).thenReturn(false);
+
+        testee.service(mail);
+
+        verifyNoMoreInteractions(mailetContext);
+    }
+
+    @Test
     public void serviceShouldNotPropagateExceptionIfSendFails() throws Exception {
         when(vacationRepository.retrieveVacation(AccountId.fromString(USERNAME)))
             .thenReturn(Mono.just(VACATION));


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 05/06: JAMES-3391 And integration test for bodyFiltering

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ffd53fde61bfa852d8c62cd1618e0c6b560d5083
Author: duc91 <vd...@linagora.com>
AuthorDate: Tue Sep 29 17:32:34 2020 +0700

    JAMES-3391 And integration test for bodyFiltering
---
 .../contract/EmailQueryMethodContract.scala        | 355 +++++++++++++++++++++
 1 file changed, 355 insertions(+)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index fd78e0e..fdd2d2a 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -592,6 +592,360 @@ trait EmailQueryMethodContract {
     }
   }
 
+  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenBody(server: GuiceJamesServer): Unit = {
+    val message: Message = simpleMessage("this message body are uniqueeee")
+    val mailboxPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    val messageId1: MessageId = sendMessageToBobInbox(server, message, requestDate)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, mailboxPath, AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"this message"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId1)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId1.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  @Test
+  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenAttachmentsContent(server: GuiceJamesServer): Unit = {
+    val message: Message = simpleMessage("this message body are uniqueeee")
+    val mailboxPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    sendMessageToBobInbox(server, message, requestDate)
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"RSA PRIVATE"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId2)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId2.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  @Test
+  def bodyFilterTextShouldSupportContainsOnBodyOrAttachmentsContentOrAttachmentsFileNameWhenAttachmentsFileName(server: GuiceJamesServer): Unit = {
+    val message: Message = simpleMessage("this message body are uniqueeee")
+    val mailboxPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    sendMessageToBobInbox(server, message, requestDate)
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"text2"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId2)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId2.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  @Test
+  def bodyMessageShouldSupportContains(server: GuiceJamesServer): Unit = {
+    val message: Message = buildTestMessage
+    val mailboxPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(mailboxPath)
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    val messageId1: MessageId = sendMessageToBobInbox(server, message, requestDate)
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, mailboxPath, AppendCommand.from(message))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"test"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId2, messageId1)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId2.serialize}", "${messageId1.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  @Test
+  def attachmentsContentShouldSupportContains(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    sendMessageToBobInbox(server, buildTestMessage, requestDate)
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"RSA PRIVATE"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId2)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId2.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
+  @Test
+  def attachmentFileNameShouldSupportContains(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
+    sendMessageToBobInbox(server, buildTestMessage, requestDate)
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "body":"text2"
+         |       }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [[
+           |            "Email/query",
+           |            {
+           |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |                "queryState": "${generateQueryState(messageId2)}",
+           |                "canCalculateChanges": false,
+           |                "position": 0,
+           |                "limit": 256,
+           |                "ids": ["${messageId2.serialize}"]
+           |            },
+           |            "c1"
+           |        ]]
+           |}""".stripMargin)
+    }
+  }
+
   @Test
   def headerExistsShouldBeCaseInsentive(server: GuiceJamesServer): Unit = {
     val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
@@ -766,6 +1120,7 @@ trait EmailQueryMethodContract {
             .setSubject("test")
             .setBody("testmail", StandardCharsets.UTF_8)
             .build))
+
       .getMessageId
 
     val request =


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/06: JAMES-3377 Email/query allow filtering by text should ignore attachment

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1f067f7ad627947ccc1ef20368dc61201dc0e26e
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Sep 30 15:41:28 2020 +0700

    JAMES-3377 Email/query allow filtering by text should ignore attachment
---
 .../contract/EmailQueryMethodContract.scala        | 90 +++++++++++++++++++++-
 .../james/jmap/utils/search/MailboxFilter.scala    | 11 ++-
 2 files changed, 99 insertions(+), 2 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index 3fdd803..fd78e0e 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -52,7 +52,7 @@ import org.apache.james.modules.{ACLProbeImpl, MailboxProbeImpl}
 import org.apache.james.utils.DataProbeImpl
 import org.awaitility.Awaitility
 import org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS
-import org.junit.jupiter.api.{BeforeEach, Disabled, Test}
+import org.junit.jupiter.api.{BeforeEach, Test}
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource}
 import org.threeten.extra.Seconds
@@ -4829,6 +4829,94 @@ trait EmailQueryMethodContract {
     }
   }
 
+  @Test
+  def emailQueryFilterByTextShouldIgnoreAttachmentName(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "text2"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[]""".stripMargin)
+    }
+  }
+
+  @Test
+  def emailQueryFilterByTextShouldIgnoreAttachmentContent(server: GuiceJamesServer): Unit = {
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+        ClassLoader.getSystemResourceAsStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "text": "RSA PRIVATE"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(
+          s"""[]""".stripMargin)
+    }
+  }
+
   private def sendMessageToBobInbox(server: GuiceJamesServer, message: Message, requestDate: Date): MessageId = {
     server.getProbe(classOf[MailboxProbeImpl])
       .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index 27e72c9..6142886 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -178,7 +178,16 @@ object MailboxFilter {
   case object Text extends QueryFilter {
     override def toQuery(builder: SearchQuery.Builder, request: EmailQueryRequest): Either[UnsupportedFilterException, SearchQuery.Builder] =
       request.filter.flatMap(_.text) match {
-        case Some(text) => Right(builder.andCriteria(SearchQuery.textContains(text.value)))
+        case Some(text) =>
+          val textFilterOrCondition: List[SearchQuery.Criterion] = List(SearchQuery.headerContains("From", text.value),
+            SearchQuery.headerContains("To", text.value),
+            SearchQuery.headerContains("Cc", text.value),
+            SearchQuery.headerContains("Bcc", text.value),
+            SearchQuery.headerContains("Subject", text.value),
+            SearchQuery.bodyContains(text.value))
+
+          Right(builder.andCriteria(SearchQuery.or(textFilterOrCondition.asJava))
+            .andCriteria(SearchQuery.not(SearchQuery.attachmentContains(text.value))))
         case None => Right(builder)
       }
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org