2323import java .time .format .DateTimeParseException ;
2424import java .util .ArrayList ;
2525import java .util .Base64 ;
26+ import java .util .BitSet ;
2627import java .util .List ;
2728import java .util .regex .Matcher ;
2829import 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 ) {
0 commit comments