Browse Source

Polish contribution & Support multiple quoted printable segments in Content-Disposition

This commit polishes the contribution for support of multiple
base64 segments, and adds supports for multiple quoted printable
segments in Content-Disposition.

Closes gh-28236
pull/28403/head
Arjen Poutsma 4 years ago
parent
commit
efafccde2b
  1. 29
      spring-web/src/main/java/org/springframework/http/ContentDisposition.java
  2. 21
      spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java

29
spring-web/src/main/java/org/springframework/http/ContentDisposition.java

@ -53,7 +53,7 @@ public final class ContentDisposition {
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?B\\?([+/0-9a-zA-Z]+=*)\\?="); Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?B\\?([+/0-9a-zA-Z]+=*)\\?=");
private final static Pattern QUOTED_PRINTABLE_ENCODED_PATTERN = private final static Pattern QUOTED_PRINTABLE_ENCODED_PATTERN =
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?Q\\?(\\p{Print}+)\\?="); Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?Q\\?([!->@-~]+)\\?="); // Printable ASCII other than "?" or SPACE
private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT = private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
"Invalid header field parameter format (as defined in RFC 5987)"; "Invalid header field parameter format (as defined in RFC 5987)";
@ -375,22 +375,29 @@ public final class ContentDisposition {
if (value.startsWith("=?") ) { if (value.startsWith("=?") ) {
Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value); Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value);
if (matcher.find()) { if (matcher.find()) {
charset = Charset.forName(matcher.group(1)); Base64.Decoder decoder = Base64.getDecoder();
String encodedValue = matcher.group(2); StringBuilder builder = new StringBuilder();
StringBuilder sb = new StringBuilder(new String(Base64.getDecoder().decode(encodedValue), charset)); do {
while (matcher.find()){
charset = Charset.forName(matcher.group(1)); charset = Charset.forName(matcher.group(1));
encodedValue = matcher.group(2); byte[] decoded = decoder.decode(matcher.group(2));
sb.append(new String(Base64.getDecoder().decode(encodedValue), charset)); builder.append(new String(decoded, charset));
} }
filename = sb.toString(); while (matcher.find());
filename = builder.toString();
} }
else { else {
matcher = QUOTED_PRINTABLE_ENCODED_PATTERN.matcher(value); matcher = QUOTED_PRINTABLE_ENCODED_PATTERN.matcher(value);
if (matcher.find()) { if (matcher.find()) {
charset = Charset.forName(matcher.group(1)); StringBuilder builder = new StringBuilder();
String encodedValue = matcher.group(2); do {
filename = decodeQuotedPrintableFilename(encodedValue, charset); charset = Charset.forName(matcher.group(1));
String decoded = decodeQuotedPrintableFilename(matcher.group(2), charset);
builder.append(decoded);
}
while (matcher.find());
filename = builder.toString();
} }
else { else {
filename = value; filename = value;

21
spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java

@ -87,10 +87,11 @@ class ContentDispositionTests {
} }
@Test @Test
void parseBase64EncodedFilenameHasMoreSegments() { void parseBase64EncodedFilenameMultipleSegments() {
/* https://datatracker.ietf.org/doc/html/rfc2047#section-2 String input =
* An 'encoded-word' may not be more than 75 characters long */ "attachment; filename=\"=?utf-8?B?U3ByaW5n5qGG5p625Li65Z+65LqOSmF2YeeahOeOsOS7o+S8geS4muW6lA==?= " +
String input = "attachment; filename=\"=?utf-8?B?U3ByaW5n5qGG5p625Li65Z+65LqOSmF2YeeahOeOsOS7o+S8geS4muW6lA==?= =?utf-8?B?55So56iL5bqP5o+Q5L6b5LqG5YWo6Z2i55qE57yW56iL5ZKM6YWN572u5qih?= =?utf-8?B?5Z6LLnR4dA==?=\""; "=?utf-8?B?55So56iL5bqP5o+Q5L6b5LqG5YWo6Z2i55qE57yW56iL5ZKM6YWN572u5qih?= " +
"=?utf-8?B?5Z6LLnR4dA==?=\"";
assertThat(parse(input).getFilename()).isEqualTo("Spring框架为基于Java的现代企业应用程序提供了全面的编程和配置模型.txt"); assertThat(parse(input).getFilename()).isEqualTo("Spring框架为基于Java的现代企业应用程序提供了全面的编程和配置模型.txt");
} }
@ -106,6 +107,18 @@ class ContentDispositionTests {
assertThat(parse(input).getFilename()).isEqualTo("日本語.csv"); assertThat(parse(input).getFilename()).isEqualTo("日本語.csv");
} }
@Test
void parseQuotedPrintableFilenameMultipleSegments() {
String input =
"attachment; filename=\"=?utf-8?Q?Spring=E6=A1=86=E6=9E=B6=E4=B8=BA=E5=9F=BA=E4=BA=8E?=" +
"=?utf-8?Q?Java=E7=9A=84=E7=8E=B0=E4=BB=A3=E4=BC=81=E4=B8=9A=E5=BA=94?=" +
"=?utf-8?Q?=E7=94=A8=E7=A8=8B=E5=BA=8F=E6=8F=90=E4=BE=9B=E4=BA=86=E5=85=A8?=" +
"=?utf-8?Q?=E9=9D=A2=E7=9A=84=E7=BC=96=E7=A8=8B=E5=92=8C=E9=85=8D=E7=BD=AE?=" +
"=?utf-8?Q?=E6=A8=A1=E5=9E=8B.txt?=\"";
assertThat(parse(input).getFilename()).isEqualTo("Spring框架为基于Java的现代企业应用程序提供了全面的编程和配置模型.txt");
}
@Test @Test
void parseQuotedPrintableShiftJISFilename() { void parseQuotedPrintableShiftJISFilename() {
String input = "attachment; filename=\"=?SHIFT_JIS?Q?=93=FA=96{=8C=EA.csv?=\""; String input = "attachment; filename=\"=?SHIFT_JIS?Q?=93=FA=96{=8C=EA.csv?=\"";

Loading…
Cancel
Save