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 7c58485dc4 [FIX] Parsing for long content disposition filename (#2555)
7c58485dc4 is described below
commit 7c58485dc429484151100ad0d6e636a503c300f5
Author: Benoit TELLIER <[email protected]>
AuthorDate: Tue Dec 10 21:09:08 2024 +0100
[FIX] Parsing for long content disposition filename (#2555)
---
.../transport/matchers/AttachmentFileNameIs.java | 96 +++++++++++++++++++---
.../matchers/AttachmentFileNameIsTest.java | 86 +++++++++++++++++++
2 files changed, 169 insertions(+), 13 deletions(-)
diff --git
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java
index 7423a2135e..115b9fad84 100755
---
a/mailet/standard/src/main/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java
+++
b/mailet/standard/src/main/java/org/apache/james/transport/matchers/AttachmentFileNameIs.java
@@ -21,7 +21,10 @@ package org.apache.james.transport.matchers;
import java.io.IOException;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -31,12 +34,21 @@ import jakarta.mail.Part;
import org.apache.james.core.MailAddress;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.codec.DecoderUtil;
+import org.apache.james.mime4j.dom.field.ContentDispositionField;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
+import org.apache.james.mime4j.field.ContentDispositionFieldLenientImpl;
+import org.apache.james.mime4j.field.ContentTypeFieldLenientImpl;
+import org.apache.james.mime4j.stream.RawFieldParser;
+import org.apache.james.mime4j.util.ContentUtil;
+import org.apache.james.mime4j.util.MimeParameterMapping;
+import org.apache.james.mime4j.util.MimeUtil;
import org.apache.james.transport.matchers.utils.MimeWalk;
import org.apache.mailet.Mail;
import org.apache.mailet.base.GenericMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.github.fge.lambdas.Throwing;
import com.google.common.annotations.VisibleForTesting;
/**
@@ -93,22 +105,80 @@ public class AttachmentFileNameIs extends GenericMatcher {
.matchMail(mail);
}
- private boolean partMatch(Part part) throws MessagingException,
IOException {
- String fileName = part.getFileName();
- if (fileName != null) {
- fileName = cleanFileName(fileName);
- // check the file name
- if (matchFound(fileName)) {
- if (configuration.isDebug()) {
- LOGGER.debug("matched {}", fileName);
+ private boolean partMatch(Part part) throws MessagingException {
+ Optional<ContentDispositionField> contentDispositionField =
Optional.ofNullable(part.getHeader("Content-Disposition"))
+ .map(headers -> headers[0])
+ .map(value -> "Content-Disposition: " + value)
+ .map(ContentUtil::encode)
+ .map(Throwing.function(RawFieldParser.DEFAULT::parseField))
+ .map(raw -> ContentDispositionFieldLenientImpl.PARSER.parse(raw,
DecodeMonitor.SILENT));
+ Optional<ContentTypeField> contentTypeField =
Optional.ofNullable(part.getHeader("Content-Type"))
+ .map(headers -> headers[0])
+ .map(value -> "Content-Type: " + value)
+ .map(ContentUtil::encode)
+ .map(Throwing.function(RawFieldParser.DEFAULT::parseField))
+ .map(raw -> ContentTypeFieldLenientImpl.PARSER.parse(raw,
DecodeMonitor.SILENT));
+
+ return extractFilename(contentTypeField, contentDispositionField)
+ .map(AttachmentFileNameIs::cleanFileName)
+ .map(Throwing.function(fileName -> {
+ if (matchFound(fileName)) {
+ if (configuration.isDebug()) {
+ LOGGER.debug("matched {}", fileName);
+ }
+ return true;
}
- return true;
- }
- if (configuration.unzipIsRequested() &&
fileName.endsWith(ZIP_SUFFIX) && matchFoundInZip(part)) {
- return true;
+ if (configuration.unzipIsRequested() &&
fileName.endsWith(ZIP_SUFFIX) && matchFoundInZip(part)) {
+ return true;
+ }
+ return false;
+ }))
+ .orElse(false);
+ }
+
+ private Optional<String> extractFilename(Optional<ContentTypeField>
contentTypeField, Optional<ContentDispositionField> contentDispositionField) {
+ Comparator<Map.Entry<String, String>> comparingName = (e1, e2) ->
extractParameterName(e1.getKey()).compareTo(extractParameterName(e2.getKey()));
+ Comparator<Map.Entry<String, String>> comparingPartNumbers = (e1, e2)
-> Integer.compare(extractPartNumber(e1.getKey()),
+ extractPartNumber(e2.getKey()));
+
+ return contentTypeField
+ .flatMap(field -> {
+ MimeParameterMapping mimeParameterMapping = new
MimeParameterMapping();
+ field.getParameters().entrySet().stream()
+ .sorted(comparingName.thenComparing(comparingPartNumbers))
+ .forEach(e ->
mimeParameterMapping.addParameter(e.getKey(), e.getValue()));
+ return Optional.ofNullable(mimeParameterMapping.get("name"));
+ })
+ .or(() ->
contentDispositionField.map(ContentDispositionField::getFilename))
+ .map(MimeUtil::unscrambleHeaderValue);
+ }
+
+ String extractParameterName(String name) {
+ int separatorPosition = name.indexOf('*');
+ if (separatorPosition > 0) {
+ return name.substring(0, separatorPosition);
+ }
+ return name;
+ }
+
+ String removeTrailingSeparator(String name) {
+ int separatorPosition = name.indexOf('*');
+ if (separatorPosition > 0) {
+ return name.substring(0, separatorPosition);
+ }
+ return name;
+ }
+
+ int extractPartNumber(String name) {
+ int separatorPosition = name.indexOf('*');
+ if (separatorPosition > 0) {
+ try {
+ return
Integer.parseInt(removeTrailingSeparator(name.substring(separatorPosition +
1)));
+ } catch (NumberFormatException e) {
+ return 0;
}
}
- return false;
+ return 0;
}
/**
diff --git
a/mailet/standard/src/test/java/org/apache/james/transport/matchers/AttachmentFileNameIsTest.java
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/AttachmentFileNameIsTest.java
index d0657c1dcf..3b50eeb7ac 100644
---
a/mailet/standard/src/test/java/org/apache/james/transport/matchers/AttachmentFileNameIsTest.java
+++
b/mailet/standard/src/test/java/org/apache/james/transport/matchers/AttachmentFileNameIsTest.java
@@ -22,8 +22,13 @@ package org.apache.james.transport.matchers;
import static org.apache.mailet.base.MailAddressFixture.ANY_AT_JAMES;
import static org.assertj.core.api.Assertions.assertThat;
+import java.nio.charset.StandardCharsets;
+
+import jakarta.mail.internet.MimeMessage;
+
import org.apache.james.core.builder.MimeMessageBuilder;
import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.MimeMessageUtil;
import org.apache.mailet.Mail;
import org.apache.mailet.base.test.FakeMail;
import org.apache.mailet.base.test.FakeMatcherConfig;
@@ -151,6 +156,31 @@ class AttachmentFileNameIsTest {
.containsOnly(ANY_AT_JAMES);
}
+ @Test
+ void shouldMatchWhenLong() throws Exception {
+ Mail mail = FakeMail.builder()
+ .name("mail")
+ .recipient(ANY_AT_JAMES)
+ .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+ .setText("abc", "text/plain")
+ .addHeader("Content-Disposition", "attachment;\n" +
+ "
filename*0=\"looooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\";\n"
+
+ "
filename*1=\"oooooooooooooooooooooooooooooooooooooong_fiiiiiiiiiiiiiiiiii\";\n"
+
+ "
filename*2=\"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiileeeeeeeeeeeeeeeeeee\";\n"
+
+ "
filename*3=\"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.txt\""))
+ .build();
+
+ AttachmentFileNameIs testee = new AttachmentFileNameIs();
+
+ testee.init(FakeMatcherConfig.builder()
+ .matcherName("AttachmentFileNameIs")
+ .condition("*.txt")
+ .build());
+
+ assertThat(testee.match(mail))
+ .containsOnly(ANY_AT_JAMES);
+ }
+
@Test
void shouldSupportWildcardPrefix() throws Exception {
Mail mail = FakeMail.builder()
@@ -400,6 +430,62 @@ class AttachmentFileNameIsTest {
.containsOnly(ANY_AT_JAMES);
}
+ @Test
+ void shouldSupportMultilineFilenameWithTrailingStar() throws Exception {
+ /*Content-Type: text/plain;
+ name*0=fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;
+ name*1=iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile;
+ name*2=.txt; charset=us-ascii
+ */
+
+ MimeMessage mimeMessage =
MimeMessageUtil.mimeMessageFromBytes(("Content-Type: text/plain;\r\n" +
+ "
name*0*=fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;\r\n" +
+ "
name*1*=iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile;\r\n" +
+ " name*2*=.txt;
charset=us-ascii\r\n\r\n").getBytes(StandardCharsets.US_ASCII));
+ Mail mail = FakeMail.builder()
+ .name("mail")
+ .recipient(ANY_AT_JAMES)
+ .mimeMessage(mimeMessage)
+ .build();
+
+ AttachmentFileNameIs testee = new AttachmentFileNameIs();
+
+ testee.init(FakeMatcherConfig.builder()
+ .matcherName("AttachmentFileNameIs")
+
.condition("fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile.txt")
+ .build());
+
+ assertThat(testee.match(mail))
+ .containsOnly(ANY_AT_JAMES);
+ }
+
+ @Test
+ void shouldSupportMultilineFilename2() throws Exception {
+ /*Content-Type: text/plain;
+ name*0=fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;
+ name*1=iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile;
+ name*2=.txt; charset=us-ascii
+ */
+
+ Mail mail = FakeMail.builder()
+ .name("mail")
+ .recipient(ANY_AT_JAMES)
+ .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+ .setText("abc", "text/plain;\r\n name=\"" +
"fiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiile".repeat(7)
+ ".txt\"")
+ .build())
+ .build();
+
+ AttachmentFileNameIs testee = new AttachmentFileNameIs();
+
+ testee.init(FakeMatcherConfig.builder()
+ .matcherName("AttachmentFileNameIs")
+ .condition("*.txt")
+ .build());
+
+ assertThat(testee.match(mail))
+ .containsOnly(ANY_AT_JAMES);
+ }
+
@Test
void shouldSupportTrimming() throws Exception {
Mail mail = FakeMail.builder()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]