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 c80ea10788 [FIX] Attachment filename should be indexed when missing in
the `Content-Disposition` header but exists in the `Content-Type` header
c80ea10788 is described below
commit c80ea107886e8798f44c7e348e10f66371206b58
Author: Quan Tran <[email protected]>
AuthorDate: Tue May 20 16:32:20 2025 +0700
[FIX] Attachment filename should be indexed when missing in the
`Content-Disposition` header but exists in the `Content-Type` header
---
.../opensearch/json/IndexableMessageTest.java | 35 ++++++++++++++++
.../src/test/resources/eml/alternative.json | 4 +-
.../eml/attachments-filename-in-content-type.eml | 48 ++++++++++++++++++++++
.../mailbox/store/search/mime/MimePartParser.java | 8 +++-
.../eml/attachment-filename-in-content-type.eml | 43 +++++++++++++++++++
.../contract/EmailQueryMethodContract.scala | 44 ++++++++++++++++++++
6 files changed, 178 insertions(+), 4 deletions(-)
diff --git
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/json/IndexableMessageTest.java
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/json/IndexableMessageTest.java
index 04385ae896..3836ae127f 100644
---
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/json/IndexableMessageTest.java
+++
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/json/IndexableMessageTest.java
@@ -250,6 +250,41 @@ class IndexableMessageTest {
assertThat(indexableMessage.getAttachments()).isNotEmpty();
}
+ @Test
+ void
attachmentsFilenameShouldFallbackToContentTypeWhenFilenameIsMissingInContentDisposition()
throws Exception {
+ //Given
+ MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+ TestId mailboxId = TestId.of(1);
+ when(mailboxMessage.getMailboxId())
+ .thenReturn(mailboxId);
+ when(mailboxMessage.getModSeq())
+ .thenReturn(ModSeq.first());
+ when(mailboxMessage.getMessageId())
+ .thenReturn(InMemoryMessageId.of(42));
+ when(mailboxMessage.getFullContent())
+
.thenReturn(ClassLoader.getSystemResourceAsStream("eml/attachments-filename-in-content-type.eml"));
+ when(mailboxMessage.createFlags())
+ .thenReturn(new Flags());
+ when(mailboxMessage.getUid())
+ .thenReturn(MESSAGE_UID);
+
+ // When
+ IndexableMessage indexableMessage = IndexableMessage.builder()
+ .message(mailboxMessage)
+ .extractor(new DefaultTextExtractor())
+ .zoneId(ZoneId.of("Europe/Paris"))
+ .indexAttachments(IndexAttachments.YES)
+ .indexHeaders(IndexHeaders.YES)
+ .build()
+ .block();
+
+ // Then
+
assertThat(indexableMessage.getAttachments().getFirst().fileName()).isEqualTo(Optional.of("1.txt"));
+
assertThat(indexableMessage.getAttachments().getFirst().fileExtension()).isEqualTo(Optional.of("txt"));
+
assertThat(indexableMessage.getAttachments().get(1).fileName()).isEqualTo(Optional.of("2.txt"));
+
assertThat(indexableMessage.getAttachments().get(1).fileExtension()).isEqualTo(Optional.of("txt"));
+ }
+
@SuppressWarnings("checkstyle:LocalVariableName")
@Test
void otherAttachmentsShouldBeenIndexedWhenOneOfThemCannotBeParsed() throws
Exception {
diff --git a/mailbox/opensearch/src/test/resources/eml/alternative.json
b/mailbox/opensearch/src/test/resources/eml/alternative.json
index 4c08c45d45..86a63fc739 100644
--- a/mailbox/opensearch/src/test/resources/eml/alternative.json
+++ b/mailbox/opensearch/src/test/resources/eml/alternative.json
@@ -3,8 +3,8 @@
{
"mediaType":"application",
"subtype":"json",
- "fileName":null,
- "fileExtension":null,
+ "fileName":"id_rsa.txt",
+ "fileExtension":"txt",
"contentDisposition":"attachment",
"textContent":null
}
diff --git
a/mailbox/opensearch/src/test/resources/eml/attachments-filename-in-content-type.eml
b/mailbox/opensearch/src/test/resources/eml/attachments-filename-in-content-type.eml
new file mode 100644
index 0000000000..837b4fc121
--- /dev/null
+++
b/mailbox/opensearch/src/test/resources/eml/attachments-filename-in-content-type.eml
@@ -0,0 +1,48 @@
+MIME-Version: 1.0
+Subject: Hello xyz
+From: Bob <[email protected]>
+To: [email protected]
+Message-ID: <[email protected]>
+User-Agent: Twake-Mail/0.15.3 Mozilla/5.0 (Macintosh; Intel Mac OS X
+ 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0
+ Safari/537.36
+Content-Type: multipart/mixed;
+ boundary="-=Part.16.2ce5c5497f23d613.196e75cd044.32ca13e0fd5dd663=-"
+
+---=Part.16.2ce5c5497f23d613.196e75cd044.32ca13e0fd5dd663=-
+Content-Type: multipart/alternative;
+ boundary="-=Part.15.4e5239b38c309d78.196e75cd043.9d6830842151817f=-"
+
+---=Part.15.4e5239b38c309d78.196e75cd043.9d6830842151817f=-
+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
+
+body
+
+
+---=Part.15.4e5239b38c309d78.196e75cd043.9d6830842151817f=-
+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>body<br><br></div>
+---=Part.15.4e5239b38c309d78.196e75cd043.9d6830842151817f=---
+
+---=Part.16.2ce5c5497f23d613.196e75cd044.32ca13e0fd5dd663=-
+Content-Type: text/plain; name="=?US-ASCII?Q?1.txt?="; charset=windows-1252
+Content-Disposition: attachment
+Content-Transfer-Encoding: base64
+
+d2hhdGV2ZXIK
+
+---=Part.16.2ce5c5497f23d613.196e75cd044.32ca13e0fd5dd663=-
+Content-Type: text/plain; name="=?US-ASCII?Q?2.txt?="; charset=windows-1252
+Content-Disposition: attachment
+Content-Transfer-Encoding: base64
+
+d2hhdGV2ZXIyCg==
+
+---=Part.16.2ce5c5497f23d613.196e75cd044.32ca13e0fd5dd663=---
diff --git
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/mime/MimePartParser.java
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/mime/MimePartParser.java
index 827a2270c3..3dbd62c917 100644
---
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/mime/MimePartParser.java
+++
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/mime/MimePartParser.java
@@ -129,8 +129,12 @@ public class MimePartParser {
Optional.ofNullable(descriptor.getSubType())
.map(SubType::of)
.ifPresent(currentlyBuildMimePart::addSubType);
-
currentlyBuildMimePart.addContentDisposition(descriptor.getContentDispositionType())
- .addFileName(descriptor.getContentDispositionFilename());
+
currentlyBuildMimePart.addContentDisposition(descriptor.getContentDispositionType());
+
+ Optional.ofNullable(descriptor.getContentDispositionFilename())
+ .or(() ->
Optional.ofNullable(descriptor.getContentTypeParameters().get("name")))
+ .ifPresent(currentlyBuildMimePart::addFileName);
+
extractCharset(descriptor);
} catch (Exception e) {
LOGGER.warn("Failed to extract mime body part description", e);
diff --git
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/attachment-filename-in-content-type.eml
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/attachment-filename-in-content-type.eml
new file mode 100644
index 0000000000..e48ee18253
--- /dev/null
+++
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/resources/eml/attachment-filename-in-content-type.eml
@@ -0,0 +1,43 @@
+MIME-Version: 1.0
+Subject: test send mail with attachment filename search
+From: Bob <[email protected]>
+To: "[email protected]" <[email protected]>
+Reply-To: [email protected]
+Message-ID: <[email protected]>
+User-Agent: Twake-Mail/0.15.3 Mozilla/5.0 (Macintosh; Intel Mac OS X
+ 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0
+ Safari/537.36
+Content-Type: multipart/mixed;
+ boundary="-=Part.9.facdaad4d7d1e26d.196e6b48470.15f465553d6bace6=-"
+
+---=Part.9.facdaad4d7d1e26d.196e6b48470.15f465553d6bace6=-
+Content-Type: multipart/alternative;
+ boundary="-=Part.8.8fd856f88f8fdedd.196e6b48470.d48dd74354571387=-"
+
+---=Part.8.8fd856f88f8fdedd.196e6b48470.d48dd74354571387=-
+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
+
+body
+
+
+---=Part.8.8fd856f88f8fdedd.196e6b48470.d48dd74354571387=-
+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>body<br><br></div>
+---=Part.8.8fd856f88f8fdedd.196e6b48470.d48dd74354571387=---
+
+---=Part.9.facdaad4d7d1e26d.196e6b48470.15f465553d6bace6=-
+Content-Type: text/plain; name="=?US-ASCII?Q?filename.txt?=";
+ charset=windows-1252
+Content-Disposition: attachment
+Content-Transfer-Encoding: base64
+
+d2hhdGV2ZXIgY29udGVudCA1OTUzNjI0NzY0Cg==
+
+---=Part.9.facdaad4d7d1e26d.196e6b48470.15f465553d6bace6=---
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 ca4c8fec63..eeff40dbdf 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
@@ -6705,6 +6705,50 @@ trait EmailQueryMethodContract {
}
}
+ @Test
+ def
attachmentFilenameShouldBeSearchableWhenMissingInContentDispositionAndExistsInContentType(server:
GuiceJamesServer): Unit = {
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(inbox(BOB))
+ val messageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB), AppendCommand.from(
+
ClassLoaderUtils.getSystemResourceAsSharedStream("eml/attachment-filename-in-content-type.eml")))
+ .getMessageId
+
+ val request =
+ s"""{
+ | "using": [
+ | "urn:ietf:params:jmap:core",
+ | "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId":
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "filter": {
+ | "text": "filename"
+ | }
+ | },
+ | "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"""["${messageId.serialize()}"]""".stripMargin)
+ }
+ }
+
@Test
def emailQueryShouldSupportAndOperator(server: GuiceJamesServer): Unit = {
val message: Message = buildTestMessage
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]