Skip to content

Commit 7492c0e

Browse files
committed
Merge branch '6.0.x'
2 parents 65a430b + 685548b commit 7492c0e

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.time.format.DateTimeParseException;
2424
import java.util.ArrayList;
2525
import java.util.Base64;
26+
import java.util.BitSet;
2627
import java.util.List;
2728
import java.util.regex.Matcher;
2829
import java.util.regex.Pattern;
@@ -58,6 +59,19 @@ public final class ContentDisposition {
5859
private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
5960
"Invalid header field parameter format (as defined in RFC 5987)";
6061

62+
private static final BitSet PRINTABLE = new BitSet(256);
63+
64+
65+
static {
66+
// RFC 2045, Section 6.7, and RFC 2047, Section 4.2
67+
for (int i=33; i<= 126; i++) {
68+
PRINTABLE.set(i);
69+
}
70+
PRINTABLE.set(61, false); // =
71+
PRINTABLE.set(63, false); // ?
72+
PRINTABLE.set(95, false); // _
73+
}
74+
6175

6276
@Nullable
6377
private final String type;
@@ -545,7 +559,7 @@ private static String decodeQuotedPrintableFilename(String filename, Charset cha
545559
int index = 0;
546560
while (index < value.length) {
547561
byte b = value[index];
548-
if (b == '_') {
562+
if (b == '_') { // RFC 2047, section 4.2, rule (2)
549563
baos.write(' ');
550564
index++;
551565
}
@@ -583,7 +597,10 @@ private static String encodeQuotedPrintableFilename(String filename, Charset cha
583597
sb.append(charset.name());
584598
sb.append("?Q?");
585599
for (byte b : source) {
586-
if (isPrintable(b)) {
600+
if (b == 32) { // RFC 2047, section 4.2, rule (2)
601+
sb.append('_');
602+
}
603+
else if (isPrintable(b)) {
587604
sb.append((char) b);
588605
}
589606
else {
@@ -599,7 +616,11 @@ private static String encodeQuotedPrintableFilename(String filename, Charset cha
599616
}
600617

601618
private static boolean isPrintable(byte c) {
602-
return (c >= '!' && c <= '<') || (c >= '>' && c <= '~');
619+
int b = c;
620+
if (b < 0) {
621+
b = 256 + b;
622+
}
623+
return PRINTABLE.get(b);
603624
}
604625

605626
private static String encodeQuotedPairs(String filename) {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,26 @@ void parseFormatted() {
325325
assertThat(parsed.toString()).isEqualTo(cd.toString());
326326
}
327327

328+
@Test // gh-30252
329+
void parseFormattedWithQuestionMark() {
330+
String filename = "filename with ?问号.txt";
331+
ContentDisposition cd = ContentDisposition.attachment()
332+
.filename(filename, StandardCharsets.UTF_8)
333+
.build();
334+
String result = cd.toString();
335+
assertThat(result).isEqualTo("attachment; " +
336+
"filename=\"=?UTF-8?Q?filename_with_=3F=E9=97=AE=E5=8F=B7.txt?=\"; " +
337+
"filename*=UTF-8''filename%20with%20%3F%E9%97%AE%E5%8F%B7.txt");
338+
339+
String[] parts = result.split("; ");
340+
341+
String quotedPrintableFilename = parts[0] + "; " + parts[1];
342+
assertThat(ContentDisposition.parse(quotedPrintableFilename).getFilename())
343+
.isEqualTo(filename);
344+
345+
String rfc5987Filename = parts[0] + "; " + parts[2];
346+
assertThat(ContentDisposition.parse(rfc5987Filename).getFilename())
347+
.isEqualTo(filename);
348+
}
349+
328350
}

0 commit comments

Comments
 (0)