You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/11/09 02:41:13 UTC

[james-project] branch master updated: [JMAP] Email/set create should add missing mimeMessageId and sentAt (#1286)

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


The following commit(s) were added to refs/heads/master by this push:
     new 40b37a6dbe [JMAP] Email/set create should add missing mimeMessageId and sentAt (#1286)
40b37a6dbe is described below

commit 40b37a6dbe7677481904c78c43638686204cdd02
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Wed Nov 9 09:41:08 2022 +0700

    [JMAP] Email/set create should add missing mimeMessageId and sentAt (#1286)
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 50 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/EmailSet.scala      | 16 ++++---
 2 files changed, 61 insertions(+), 5 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/EmailSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
index 9893e06699..3456cd798f 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
@@ -327,6 +327,56 @@ trait EmailSetMethodContract {
            |}""".stripMargin)
   }
 
+  @Test
+  def emailSetCreateShouldPositionMissingDateAndMessageId(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {
+         |             "${mailboxId.serialize}": true
+         |          },
+         |          "to": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}],
+         |          "from": [{"email": "${BOB.asString}"}]
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["sentAt", "messageId"]
+         |     },
+         |     "c2"]]
+         |}""".stripMargin
+
+    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[1][1].list.[0].sentAt")
+      .isEqualTo("\"${json-unit.ignore}\"")
+    assertThatJson(response)
+      .inPath("methodResponses[1][1].list.[0].messageId")
+      .isEqualTo("[\"${json-unit.ignore}\"]")
+  }
+
   @Test
   def createShouldFailWhenBadJsonPayloadForSpecificHeader(server: GuiceJamesServer): Unit = {
     val bobPath = MailboxPath.inbox(BOB)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
index bc91ceb330..c08fa7301f 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
@@ -35,11 +35,13 @@ import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.{Cid, MessageId}
 import org.apache.james.mime4j.codec.EncoderUtil.Usage
 import org.apache.james.mime4j.codec.{DecodeMonitor, EncoderUtil}
+import org.apache.james.mime4j.dom.address.Mailbox
 import org.apache.james.mime4j.dom.field.{ContentIdField, ContentTypeField, FieldName}
 import org.apache.james.mime4j.dom.{Entity, Message}
 import org.apache.james.mime4j.field.{ContentIdFieldImpl, Fields}
 import org.apache.james.mime4j.message.{BodyPartBuilder, MultipartBuilder}
 import org.apache.james.mime4j.stream.{Field, NameValuePair, RawField}
+import org.apache.james.mime4j.util.MimeUtil
 import org.apache.james.util.html.HtmlTextExtractor
 import play.api.libs.json.JsObject
 
@@ -129,15 +131,16 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
           val builder = Message.Builder.of
           references.flatMap(_.asString).map(new RawField("References", _)).foreach(builder.setField)
           inReplyTo.flatMap(_.asString).map(new RawField("In-Reply-To", _)).foreach(builder.setField)
-          messageId.flatMap(_.asString).map(new RawField(FieldName.MESSAGE_ID, _)).foreach(builder.setField)
           subject.foreach(value => builder.setSubject(value.value))
-          from.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setFrom)
+          val maybeFrom: Option[List[Mailbox]] = from.flatMap(_.asMime4JMailboxList)
+          maybeFrom.map(_.asJava).foreach(builder.setFrom)
           to.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setTo)
           cc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setCc)
           bcc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setBcc)
           sender.flatMap(_.asMime4JMailboxList).map(_.asJava).map(Fields.addressList(FieldName.SENDER, _)).foreach(builder.setField)
           replyTo.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setReplyTo)
-          sentAt.map(_.asUTC).map(_.toInstant).map(Date.from).foreach(builder.setDate)
+          builder.setDate( sentAt.map(_.asUTC).map(_.toInstant).map(Date.from).getOrElse(new Date()))
+          builder.setField(new RawField(FieldName.MESSAGE_ID, messageId.flatMap(_.asString).getOrElse(generateUniqueMessageId(maybeFrom))))
           validateSpecificHeaders(builder)
             .flatMap(_ => {
               specificHeaders.map(_.asField).foreach(builder.addField)
@@ -154,8 +157,11 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
             })
       }
 
-  private def createAlternativeBody(htmlBody: Option[String], textBody: Option[String], htmlTextExtractor: HtmlTextExtractor): MultipartBuilder = {
-    val alternativeBuilder: MultipartBuilder = MultipartBuilder.create(SubType.ALTERNATIVE_SUBTYPE)
+  private def generateUniqueMessageId(fromAddress: Option[List[Mailbox]]): String = 
+    MimeUtil.createUniqueMessageId(fromAddress.flatMap(_.headOption).map(_.getDomain).orNull)
+
+  private def createAlternativeBody(htmlBody: Option[String], textBody: Option[String], htmlTextExtractor: HtmlTextExtractor) = {
+    val alternativeBuilder = MultipartBuilder.create(SubType.ALTERNATIVE_SUBTYPE)
     addBodypart(alternativeBuilder, textBody.getOrElse(htmlTextExtractor.toPlainText(htmlBody.getOrElse(""))), PLAIN_TEXT_UTF_8, StandardCharsets.UTF_8)
     htmlBody.foreach(text => addBodypart(alternativeBuilder, text, HTML_UTF_8, StandardCharsets.UTF_8))
 


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