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
The following commit(s) were added to refs/heads/master by this push: new 791833b8f5 [FIX] Email/get full view: hasAttachment should filter out inlined at… (#2709) 791833b8f5 is described below commit 791833b8f5a79191a1b41a361def5f2943ab058e Author: Benoit TELLIER <btell...@linagora.com> AuthorDate: Mon May 12 03:52:14 2025 +0200 [FIX] Email/get full view: hasAttachment should filter out inlined at… (#2709) --- .../resources/eml/simple-inlined-attachment.eml | 121 +++++++++++++++++++++ .../rfc8621/contract/EmailGetMethodContract.scala | 114 +++++++++++++++++++ .../scala/org/apache/james/jmap/mail/Email.scala | 4 +- 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/simple-inlined-attachment.eml b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/simple-inlined-attachment.eml new file mode 100644 index 0000000000..d1568982ee --- /dev/null +++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/simple-inlined-attachment.eml @@ -0,0 +1,121 @@ +Date: Wed, 26 Jan 2022 12:21:37 +0100 +From: Bob <b...@domain.tld> +To: Alice <al...@domain.tld> +MIME-Version: 1.0 +Subject: inlined attachment +Message-ID: <mime4j.16b.d30609c122843f4e.196a380f...@domain.tld> +Content-Type: multipart/related; + boundary="-=Part.16d.b16547c587c987fd.196a380f8d4.b92d6af2f0294389=-" + +---=Part.16d.b16547c587c987fd.196a380f8d4.b92d6af2f0294389=- +Content-Type: multipart/alternative; + boundary="-=Part.16c.d54cec4b999aebb5.196a380f8d2.7189347f48aceda7=-" + +---=Part.16c.d54cec4b999aebb5.196a380f8d2.7189347f48aceda7=- +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable +Accept-Language: fr-FR, en-US, vi-VN, ru-RU, ar-TN, it-IT, de-DE +Content-Language: vi-VN + + +start[Screenshot 2025-05-06 at 09]end + + +---=Part.16c.d54cec4b999aebb5.196a380f8d2.7189347f48aceda7=- +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable +Accept-Language: fr-FR, en-US, vi-VN, ru-RU, ar-TN, it-IT, de-DE +Content-Language: vi-VN + +<div><br></div><div>start</div><div><img src=3D"cid:e612b3e0-2a24-11f0-aacf= +-77a69744ec20" alt=3D"Screenshot 2025-05-06 at 09" style=3D"max-width:770px= +;" data-mimetype=3D"image/png"></div><div>end</div><div><br></div><div><br>= +</div> +---=Part.16c.d54cec4b999aebb5.196a380f8d2.7189347f48aceda7=--- + +---=Part.16d.b16547c587c987fd.196a380f8d4.b92d6af2f0294389=- +Content-Type: image/png; + name="=?US-ASCII?Q?Screenshot_2025-05-06_at_09.50.27.png?="; charset=base64 +Content-Disposition: inline +Content-Transfer-Encoding: base64 +Content-ID: e612b3e0-2a24-11f0-aacf-77a69744ec20 + +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 +2cPcU7IX7FXs/WNf6r6b+8P2Nx9gH6g9aHlw/SHGoeI6pG56XXd9Zn17Q1JD2+HQw82NXo2Hjjge +2dFk2lRxVPfosmPUYwuO9R4vON5zQnqi62TGycfNk5rvnhp36vrp6NOtZ8LOnD8bdPbUOc654+e9 +zzdd8Lxw+CL7Yv0lt0t1La4th351/fVQq1tr3WX3yw1XPK40to1pO3bV9+rJawHXzl7nXb90I+JG +2834m7dvpdxqvy28/fxOzp1Xv+X/9vnu3HuEe8X3Ne+XPjB8UPm77e972t3ajz4MeNjyKPbR3ceC +xy+eyJ986VjwlPa09JnJs+rnzs+bOoM6r/wx/o+OF9IXn7uK/tT6c/1Lm5cH//L7q6V7XHfHK9mr +3tdL3ui/2fHW5W1zT1TPg3e57z6/L/6g/6HqI/vjuU+Jn559nvqF9KXsq+3Xxm9h3+715vb2Svky +ft+nAAaUR5t0AF7vAICWBAADnhup41Xnw76CqM60fQj8J6w6Q/YVNwBq4Td9dBf8urkFwN5tAFhB +fXoKAFE0AOI8ADp69GAdOMv1nTuVhQjPBpt5X9Ny08C/Kaoz6Q9+D22BUtUFDG3/BXkhgywpQj4b +AAAAimVYSWZNTQAqAAAACAAEARoABQAAAAEAAAA+ARsABQAAAAEAAABGASgAAwAAAAEAAgAAh2kA +BAAAAAEAAABOAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAeKACAAQAAAABAAAAsqAD +AAQAAAABAAAATAAAAABBU0NJSQAAAFNjcmVlbnNob3ReQ+6gAAAACXBIWXMAABYlAAAWJQFJUiTw +AAAB1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpu +czptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9 +Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRm +OkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8v +bnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj43 +NjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4x +Nzg8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3Jl +ZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9y +ZGY6UkRGPgo8L3g6eG1wbWV0YT4KYNB7JQAAABxpRE9UAAAAAgAAAAAAAAAmAAAAKAAAACYAAAAm +AAABBBOG5jIAAADQSURBVHgB7NIBEQAgDAMx5l80cLjgL1Owtpl9bzkNfN7AgPz5gt5/DYAMQqIB +kBMzCgEyA4kGQE7MKATIDCQaADkxoxAgM5BoAOTEjEKAzECiAZATMwoBMgOJBkBOzCgEyAwkGgA5 +MaMQIDOQaADkxIxCgMxAogGQEzMKATIDiQZATswoBMgMJBoAOTGjECAzkGgA5MSMQoDMQKIBkBMz +CgEyA4kGQE7MKATIDCQaADkxoxAgM5BoAOTEjEKAzECiAZATMwoBMgOJBkBOzCjEAQAA///X4LVo +AAAAzklEQVTt0gERACAMAzHmXzRwuOAvU7C2mX1vOQ183sCA/PmC3n8NgAxCogGQEzMKATIDiQZA +TswoBMgMJBoAOTGjECAzkGgA5MSMQoDMQKIBkBMzCgEyA4kGQE7MKATIDCQaADkxoxAgM5BoAOTE +jEKAzECiAZATMwoBMgOJBkBOzCgEyAwkGgA5MaMQIDOQaADkxIxCgMxAogGQEzMKATIDiQZATswo +BMgMJBoAOTGjECAzkGgA5MSMQoDMQKIBkBMzCgEyA4kGQE7MKMQBRz8vLLnQPmEAAAAASUVORK5C +YII= + +---=Part.16d.b16547c587c987fd.196a380f8d4.b92d6af2f0294389=--- 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 e2625c020c..2d98222a51 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 @@ -4828,6 +4828,120 @@ trait EmailGetMethodContract { |}""".stripMargin) } + @Test + def inlinedAttachmentMailShouldNotBeCountedAsHasAttachmentWhenEmailFullView(server: GuiceJamesServer): Unit = { + val path = MailboxPath.inbox(BOB) + val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path) + val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl]) + .appendMessage(BOB.asString, path, AppendCommand.from( + ClassLoaderUtils.getSystemResourceAsSharedStream("eml/simple-inlined-attachment.eml"))) + .getMessageId + + val request = + s"""{ + | "using": [ + | "urn:ietf:params:jmap:core", + | "urn:ietf:params:jmap:mail" + | ], + | "methodCalls": [ + | [ + | "Email/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "ids": ["${messageId.serialize}"], + | "properties": [ + | "id", + | "subject", + | "from", + | "to", + | "cc", + | "bcc", + | "keywords", + | "size", + | "receivedAt", + | "sentAt", + | "preview", + | "hasAttachment", + | "attachments", + | "replyTo", + | "mailboxIds" + | ], + | "fetchTextBodyValues": true + | }, + | "c1" + | ] + | ] + |}""".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) + .whenIgnoringPaths("methodResponses[0][1].state") + .isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [ + | [ + | "Email/get", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "state": "33ac468f-7903-4f68-ac3e-8120505b5c3d", + | "list": [ + | { + | "id": "${messageId.serialize}", + | "keywords": {}, + | "mailboxIds": { + | "${mailboxId.serialize}": true + | }, + | "size": 7609, + | "receivedAt": "$${json-unit.ignore}", + | "to": [ + | { + | "name": "Alice", + | "email": "al...@domain.tld" + | } + | ], + | "from": [ + | { + | "name": "Bob", + | "email": "b...@domain.tld" + | } + | ], + | "subject": "inlined attachment", + | "sentAt": "$${json-unit.ignore}", + | "attachments": [ + | { + | "partId": "5", + | "blobId": "${messageId.serialize}_5", + | "size": 4334, + | "name": "Screenshot 2025-05-06 at 09.50.27.png", + | "type": "image/png", + | "charset": "base64", + | "disposition": "inline", + | "cid": "e612b3e0-2a24-11f0-aacf-77a69744ec20" + | } + | ], + | "hasAttachment": false, + | "preview": "start[Screenshot 2025-05-06 at 09]end" + | } + | ], + | "notFound": [] + | }, + | "c1" + | ] + | ] + |}""".stripMargin) + } + @Test def shouldUseFastViewWithAttachmentMetadataWhenSupportedBodyProperties(server: GuiceJamesServer): Unit = { val path = MailboxPath.inbox(BOB) 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 b60b4ae0eb..a0de324aea 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 @@ -38,6 +38,7 @@ import org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperti import org.apache.james.jmap.core.Id.{Id, IdConstraint} import org.apache.james.jmap.core.{Properties, UTCDate} import org.apache.james.jmap.mail.BracketHeader.sanitize +import org.apache.james.jmap.mail.Disposition.ATTACHMENT import org.apache.james.jmap.mail.EmailFullViewFactory.extractBodyValues import org.apache.james.jmap.mail.EmailGetRequest.MaxBodyValueBytes import org.apache.james.jmap.mail.EmailHeaderName.{ADDRESSES_NAMES, DATE, MESSAGE_ID_NAMES} @@ -625,7 +626,8 @@ private class EmailFullViewFactory @Inject()(zoneIdProvider: ZoneIdProvider, pre size = sanitizeSize(firstMessage.getSize)), header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage), bodyMetadata = EmailBodyMetadata( - hasAttachment = HasAttachment(!firstMessage.getLoadedAttachments.isEmpty), + hasAttachment = HasAttachment(bodyStructure.attachments.exists(attachment => + attachment.disposition.contains(ATTACHMENT) && attachment.cid.isEmpty)), preview = preview), body = EmailBody( bodyStructure = bodyStructure, --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org