Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.BitSet;
import java.util.HexFormat;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -59,23 +58,9 @@ public final class ContentDisposition {
private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
"Invalid header field parameter format (as defined in RFC 5987)";

private static final BitSet PRINTABLE = new BitSet(256);

private static final HexFormat HEX_FORMAT = HexFormat.of().withUpperCase();


static {
// RFC 2045, Section 6.7, and RFC 2047, Section 4.2
for (int i=33; i<= 126; i++) {
PRINTABLE.set(i);
}
PRINTABLE.set(34, false); // "
PRINTABLE.set(61, false); // =
PRINTABLE.set(63, false); // ?
PRINTABLE.set(95, false); // _
}


private final @Nullable String type;

private final @Nullable String name;
Expand Down Expand Up @@ -195,7 +180,7 @@ public String toString() {
}
else {
sb.append("; filename=\"");
sb.append(encodeQuotedPrintableFilename(this.filename, this.charset)).append('\"');
sb.append(toIso88591(encodeQuotedPairs(this.filename))).append('\"');
sb.append("; filename*=");
sb.append(encodeRfc5987Filename(this.filename, this.charset));
}
Expand Down Expand Up @@ -446,44 +431,8 @@ else if (b == '=' && index < value.length - 2) {
return StreamUtils.copyToString(baos, charset);
}

/**
* Encode the given header field param as described in RFC 2047.
* @param filename the filename
* @param charset the charset for the filename
* @return the encoded header field param
* @see <a href="https://tools.ietf.org/html/rfc2047">RFC 2047</a>
*/
private static String encodeQuotedPrintableFilename(String filename, Charset charset) {
Assert.notNull(filename, "'filename' must not be null");
Assert.notNull(charset, "'charset' must not be null");

byte[] source = filename.getBytes(charset);
StringBuilder sb = new StringBuilder(source.length << 1);
sb.append("=?");
sb.append(charset.name());
sb.append("?Q?");
for (byte b : source) {
if (b == 32) { // RFC 2047, section 4.2, rule (2)
sb.append('_');
}
else if (isPrintable(b)) {
sb.append((char) b);
}
else {
sb.append('=');
HEX_FORMAT.toHexDigits(sb, b);
}
}
sb.append("?=");
return sb.toString();
}

private static boolean isPrintable(byte c) {
int b = c;
if (b < 0) {
b = 256 + b;
}
return PRINTABLE.get(b);
private static String toIso88591(String input) {
return new String(input.getBytes(StandardCharsets.ISO_8859_1));
}

private static String encodeQuotedPairs(String filename) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void formatWithEncodedFilename() {
.filename("中文.txt", StandardCharsets.UTF_8)
.build().toString())
.isEqualTo("form-data; name=\"name\"; " +
"filename=\"=?UTF-8?Q?=E4=B8=AD=E6=96=87.txt?=\"; " +
"filename=\"??.txt\"; " +
"filename*=UTF-8''%E4%B8%AD%E6%96%87.txt");
}

Expand Down Expand Up @@ -272,7 +272,7 @@ void formatWithFilenameWithQuotes() {
void formatWithUtf8FilenameWithQuotes() {
String filename = "\"中文.txt";
assertThat(ContentDisposition.formData().filename(filename, StandardCharsets.UTF_8).build().toString())
.isEqualTo("form-data; filename=\"=?UTF-8?Q?=22=E4=B8=AD=E6=96=87.txt?=\"; filename*=UTF-8''%22%E4%B8%AD%E6%96%87.txt");
.isEqualTo("form-data; filename=\"\\\"??.txt\"; filename*=UTF-8''%22%E4%B8%AD%E6%96%87.txt");
}

@Test
Expand Down Expand Up @@ -303,14 +303,14 @@ void parseFormattedWithQuestionMark() {
.build();
String result = cd.toString();
assertThat(result).isEqualTo("attachment; " +
"filename=\"=?UTF-8?Q?filename_with_=3F=E9=97=AE=E5=8F=B7.txt?=\"; " +
"filename=\"filename with ???.txt\"; " +
"filename*=UTF-8''filename%20with%20%3F%E9%97%AE%E5%8F%B7.txt");

String[] parts = result.split("; ");

String quotedPrintableFilename = parts[0] + "; " + parts[1];
assertThat(ContentDisposition.parse(quotedPrintableFilename).getFilename())
.isEqualTo(filename);
.isEqualTo("filename with ???.txt");

String rfc5987Filename = parts[0] + "; " + parts[2];
assertThat(ContentDisposition.parse(rfc5987Filename).getFilename())
Expand Down