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

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

commit 7ce20ab746f043b62c200bb0a557a452f2ed4217
Author: LanKhuat <[email protected]>
AuthorDate: Wed Sep 23 11:37:12 2020 +0700

    JAMES-3379 Email/get specific parsed headers: asURLs
---
 .../rfc8621/contract/EmailGetMethodContract.scala  | 162 +++++++++++++++++++++
 .../james/jmap/json/EmailGetSerializer.scala       |   5 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  16 +-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  20 ++-
 4 files changed, 200 insertions(+), 3 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/EmailGetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index 59b4a52..61fb12f 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -6522,4 +6522,166 @@ trait EmailGetMethodContract {
            |    "header:Date:asDate": null
            }""".stripMargin)
   }
+
+  @Test
+  def emailGetShouldReturnSpecificHeaderAsURLs(server: GuiceJamesServer): Unit 
= {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .addField(new RawField("List-Help", "<http://www.host.com/list/>, 
<mailto:[email protected]>"))
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:List-Help:asURLs"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:List-Help:asURLs": [
+           |      "http://www.host.com/list/";,
+           |      "mailto:[email protected]";
+           |    ]
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnSpecificHeaderAsURLsAndIgnoresComments(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .addField(new RawField("List-Help", "<http://www.host.com/list/>, (FTP) 
<mailto:[email protected]>"))
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:List-Help:asURLs"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:List-Help:asURLs": [
+           |      "http://www.host.com/list/";,
+           |      "mailto:[email protected]";
+           |    ]
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnNullWhenInvalidSpecificHeaderAsURLs(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .addField(new RawField("List-Help", "Invalid"))
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:List-Help:asURLs"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:List-Help:asURLs": null
+           }""".stripMargin)
+  }
 }
\ No newline at end of file
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index 6712ef1..9ec87f6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.json
 
 import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, 
Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, 
EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, 
EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, 
EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, 
EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, 
FetchHTMLBodyValues, FetchTextBodyValu [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, 
Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, 
EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, 
EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, 
EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, 
EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, 
FetchHTMLBodyValues, FetchTextBodyValu [...]
 import org.apache.james.jmap.model._
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
@@ -65,6 +65,8 @@ object EmailGetSerializer {
   private implicit val groupedAddressesHeaderWrites: 
Writes[GroupedAddressesHeaderValue] = 
Json.valueWrites[GroupedAddressesHeaderValue]
   private implicit val messageIdsHeaderWrites: Writes[MessageIdsHeaderValue] = 
Json.valueWrites[MessageIdsHeaderValue]
   private implicit val dateHeaderWrites: Writes[DateHeaderValue] = 
Json.valueWrites[DateHeaderValue]
+  private implicit val headerURLWrites: Writes[HeaderURL] = 
Json.valueWrites[HeaderURL]
+  private implicit val urlsHeaderWrites: Writes[URLsHeaderValue] = 
Json.valueWrites[URLsHeaderValue]
   private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
     case headerValue: RawHeaderValue => 
Json.toJson[RawHeaderValue](headerValue)
     case headerValue: TextHeaderValue => 
Json.toJson[TextHeaderValue](headerValue)
@@ -72,6 +74,7 @@ object EmailGetSerializer {
     case headerValue: GroupedAddressesHeaderValue => 
Json.toJson[GroupedAddressesHeaderValue](headerValue)
     case headerValue: MessageIdsHeaderValue => 
Json.toJson[MessageIdsHeaderValue](headerValue)
     case headerValue: DateHeaderValue => 
Json.toJson[DateHeaderValue](headerValue)
+    case headerValue: URLsHeaderValue => 
Json.toJson[URLsHeaderValue](headerValue)
   }
   private implicit val headersWrites: Writes[EmailHeader] = 
Json.writes[EmailHeader]
   private implicit val bodyValueWrites: Writes[EmailBodyValue] = 
Json.writes[EmailBodyValue]
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 62d225c..0653d80 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -34,6 +34,7 @@ import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.api.model.Preview
 import 
org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperties, 
MessageFastViewProjection}
+import org.apache.james.jmap.mail.BracketHeader.sanitize
 import org.apache.james.jmap.mail.Email.{Size, sanitizeSize}
 import org.apache.james.jmap.method.ZoneIdProvider
 import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
@@ -149,8 +150,14 @@ case object FullReadLevel extends ReadLevel
 
 object HeaderMessageId {
   def from(string: String): HeaderMessageId = HeaderMessageId(sanitize(string))
+}
+
+object HeaderURL {
+  def from(string: String): HeaderURL = HeaderURL(sanitize(string))
+}
 
-  private def sanitize(string: String): String = string match {
+object BracketHeader {
+  def sanitize(string: String): String = string match {
     case s if s.startsWith("<") => sanitize(s.substring(1))
     case s if s.endsWith(">") => sanitize(s.substring(0, s.length - 1))
     case s => s
@@ -169,6 +176,7 @@ object ParseOptions {
       case "asGroupedAddresses" => Some(AsGroupedAddresses)
       case "asMessageIds" => Some(AsMessageIds)
       case "asDate" => Some(AsDate)
+      case "asURLs" => Some(AsURLs)
       case _ => None
   }
 }
@@ -196,6 +204,10 @@ case object AsDate extends ParseOption {
 
   def extractHeaderValue(field: Field, zoneId: ZoneId): 
Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, zoneId))
 }
+case object AsURLs extends ParseOption {
+  override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(URLsHeaderValue.from(field))
+}
+
 case class HeaderMessageId(value: String) extends AnyVal
 
 case class Subject(value: String) extends AnyVal
@@ -206,6 +218,8 @@ case class ThreadId(value: String) extends AnyVal
 
 case class HasAttachment(value: Boolean) extends AnyVal
 
+case class HeaderURL(value: String) extends AnyVal
+
 case class EmailMetadata(id: MessageId,
                          blobId: BlobId,
                          threadId: ThreadId,
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 343f6dd..7f05aec 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -26,7 +26,7 @@ import org.apache.commons.lang3.StringUtils
 import org.apache.james.jmap.model.UTCDate
 import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
 import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => 
Mime4jAddress, Mailbox => Mime4jMailbox}
-import org.apache.james.mime4j.field.{AddressListFieldImpl, DateTimeFieldImpl}
+import org.apache.james.mime4j.field.{AddressListFieldImpl, 
ContentLocationFieldImpl, DateTimeFieldImpl}
 import org.apache.james.mime4j.stream.Field
 import org.apache.james.mime4j.util.MimeUtil
 
@@ -102,6 +102,23 @@ object DateHeaderValue extends EmailHeaderValue {
       .getOrElse(DateHeaderValue(None))
 }
 
+object URLsHeaderValue extends EmailHeaderValue {
+  def from(field: Field): URLsHeaderValue = {
+    val url: Option[List[HeaderURL]] = 
Option(ContentLocationFieldImpl.PARSER.parse(field, 
DecodeMonitor.SILENT).getLocation)
+      .map(urls => urls.split(',')
+        .toList
+        .flatMap(url => {
+          if(url.startsWith("<") && url.endsWith(">")) {
+            scala.Right(HeaderURL.from(url))
+          } else {
+            Left()
+          }
+        }.toOption))
+
+      URLsHeaderValue(url.filter(_.nonEmpty))
+  }
+}
+
 case class EmailHeaderName(value: String) extends AnyVal
 
 sealed trait EmailHeaderValue
@@ -111,5 +128,6 @@ case class AddressesHeaderValue(value: List[EmailAddress]) 
extends EmailHeaderVa
 case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends 
EmailHeaderValue
 case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends 
EmailHeaderValue
 case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
+case class URLsHeaderValue(value: Option[List[HeaderURL]]) extends 
EmailHeaderValue
 
 case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)


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

Reply via email to