Skip to content

Commit efafccd

Browse files
committed
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
1 parent 195b622 commit efafccd

File tree

2 files changed

+35
-15
lines changed

2 files changed

+35
-15
lines changed

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public final class ContentDisposition {
5353
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?B\\?([+/0-9a-zA-Z]+=*)\\?=");
5454

5555
private final static Pattern QUOTED_PRINTABLE_ENCODED_PATTERN =
56-
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?Q\\?(\\p{Print}+)\\?=");
56+
Pattern.compile("=\\?([0-9a-zA-Z-_]+)\\?Q\\?([!->@-~]+)\\?="); // Printable ASCII other than "?" or SPACE
5757

5858
private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
5959
"Invalid header field parameter format (as defined in RFC 5987)";
@@ -375,22 +375,29 @@ else if (attribute.equals("filename") && (filename == null)) {
375375
if (value.startsWith("=?") ) {
376376
Matcher matcher = BASE64_ENCODED_PATTERN.matcher(value);
377377
if (matcher.find()) {
378-
charset = Charset.forName(matcher.group(1));
379-
String encodedValue = matcher.group(2);
380-
StringBuilder sb = new StringBuilder(new String(Base64.getDecoder().decode(encodedValue), charset));
381-
while (matcher.find()){
378+
Base64.Decoder decoder = Base64.getDecoder();
379+
StringBuilder builder = new StringBuilder();
380+
do {
382381
charset = Charset.forName(matcher.group(1));
383-
encodedValue = matcher.group(2);
384-
sb.append(new String(Base64.getDecoder().decode(encodedValue), charset));
382+
byte[] decoded = decoder.decode(matcher.group(2));
383+
builder.append(new String(decoded, charset));
385384
}
386-
filename = sb.toString();
385+
while (matcher.find());
386+
387+
filename = builder.toString();
387388
}
388389
else {
389390
matcher = QUOTED_PRINTABLE_ENCODED_PATTERN.matcher(value);
390391
if (matcher.find()) {
391-
charset = Charset.forName(matcher.group(1));
392-
String encodedValue = matcher.group(2);
393-
filename = decodeQuotedPrintableFilename(encodedValue, charset);
392+
StringBuilder builder = new StringBuilder();
393+
do {
394+
charset = Charset.forName(matcher.group(1));
395+
String decoded = decodeQuotedPrintableFilename(matcher.group(2), charset);
396+
builder.append(decoded);
397+
}
398+
while (matcher.find());
399+
400+
filename = builder.toString();
394401
}
395402
else {
396403
filename = value;

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ void parseBase64EncodedFilename() {
8787
}
8888

8989
@Test
90-
void parseBase64EncodedFilenameHasMoreSegments() {
91-
/* https://datatracker.ietf.org/doc/html/rfc2047#section-2
92-
* An 'encoded-word' may not be more than 75 characters long */
93-
String input = "attachment; filename=\"=?utf-8?B?U3ByaW5n5qGG5p625Li65Z+65LqOSmF2YeeahOeOsOS7o+S8geS4muW6lA==?= =?utf-8?B?55So56iL5bqP5o+Q5L6b5LqG5YWo6Z2i55qE57yW56iL5ZKM6YWN572u5qih?= =?utf-8?B?5Z6LLnR4dA==?=\"";
90+
void parseBase64EncodedFilenameMultipleSegments() {
91+
String input =
92+
"attachment; filename=\"=?utf-8?B?U3ByaW5n5qGG5p625Li65Z+65LqOSmF2YeeahOeOsOS7o+S8geS4muW6lA==?= " +
93+
"=?utf-8?B?55So56iL5bqP5o+Q5L6b5LqG5YWo6Z2i55qE57yW56iL5ZKM6YWN572u5qih?= " +
94+
"=?utf-8?B?5Z6LLnR4dA==?=\"";
9495
assertThat(parse(input).getFilename()).isEqualTo("Spring框架为基于Java的现代企业应用程序提供了全面的编程和配置模型.txt");
9596
}
9697

@@ -106,6 +107,18 @@ void parseQuotedPrintableFilename() {
106107
assertThat(parse(input).getFilename()).isEqualTo("日本語.csv");
107108
}
108109

110+
@Test
111+
void parseQuotedPrintableFilenameMultipleSegments() {
112+
String input =
113+
"attachment; filename=\"=?utf-8?Q?Spring=E6=A1=86=E6=9E=B6=E4=B8=BA=E5=9F=BA=E4=BA=8E?=" +
114+
"=?utf-8?Q?Java=E7=9A=84=E7=8E=B0=E4=BB=A3=E4=BC=81=E4=B8=9A=E5=BA=94?=" +
115+
"=?utf-8?Q?=E7=94=A8=E7=A8=8B=E5=BA=8F=E6=8F=90=E4=BE=9B=E4=BA=86=E5=85=A8?=" +
116+
"=?utf-8?Q?=E9=9D=A2=E7=9A=84=E7=BC=96=E7=A8=8B=E5=92=8C=E9=85=8D=E7=BD=AE?=" +
117+
"=?utf-8?Q?=E6=A8=A1=E5=9E=8B.txt?=\"";
118+
assertThat(parse(input).getFilename()).isEqualTo("Spring框架为基于Java的现代企业应用程序提供了全面的编程和配置模型.txt");
119+
120+
}
121+
109122
@Test
110123
void parseQuotedPrintableShiftJISFilename() {
111124
String input = "attachment; filename=\"=?SHIFT_JIS?Q?=93=FA=96{=8C=EA.csv?=\"";

0 commit comments

Comments
 (0)