clean base64 stream

This commit is contained in:
Jörg Prante 2022-10-22 15:17:57 +02:00
parent 3a1adbbcfe
commit 721fa0e17e
4 changed files with 33 additions and 49 deletions

View file

@ -13,7 +13,7 @@ import java.io.InputStream;
* any input stream and read bytes from this filter. The decoding * any input stream and read bytes from this filter. The decoding
* is done as the bytes are read out. * is done as the bytes are read out.
*/ */
final class BASE64DecoderStream extends FilterInputStream { final class Base64DecoderStream extends FilterInputStream {
/** /**
* This character array provides the character to value map * This character array provides the character to value map
* based on RFC1521. * based on RFC1521.
@ -41,12 +41,17 @@ final class BASE64DecoderStream extends FilterInputStream {
// buffer of decoded bytes for single byte reads // buffer of decoded bytes for single byte reads
private final byte[] buffer = new byte[3]; private final byte[] buffer = new byte[3];
// buffer for almost 8K of typical 76 chars + CRLF lines, // buffer for almost 8K of typical 76 chars + CRLF lines,
// used by getByte method. this buffer contains encoded bytes. // used by getByte method. this buffer contains encoded bytes.
private final byte[] input_buffer = new byte[78 * 105]; private final byte[] input_buffer = new byte[78 * 105];
private int bufsize = 0; // size of the cache private int bufsize = 0; // size of the cache
private int index = 0; // index into the cache private int index = 0; // index into the cache
private int input_pos = 0; private int input_pos = 0;
private int input_len = 0; private int input_len = 0;
/** /**
@ -57,7 +62,7 @@ final class BASE64DecoderStream extends FilterInputStream {
* *
* @param in the input stream * @param in the input stream
*/ */
public BASE64DecoderStream(InputStream in) { public Base64DecoderStream(InputStream in) {
super(in); super(in);
} }
@ -75,7 +80,6 @@ final class BASE64DecoderStream extends FilterInputStream {
if (size == 0) { if (size == 0) {
return inbuf; return inbuf;
} }
if (inbuf[inbuf.length - 1] == '=') { if (inbuf[inbuf.length - 1] == '=') {
size--; size--;
if (inbuf[inbuf.length - 2] == '=') { if (inbuf[inbuf.length - 2] == '=') {
@ -83,7 +87,6 @@ final class BASE64DecoderStream extends FilterInputStream {
} }
} }
byte[] outbuf = new byte[size]; byte[] outbuf = new byte[size];
int inpos = 0, outpos = 0; int inpos = 0, outpos = 0;
size = inbuf.length; size = inbuf.length;
while (size > 0) { while (size > 0) {
@ -173,7 +176,6 @@ final class BASE64DecoderStream extends FilterInputStream {
if (index >= bufsize) { if (index >= bufsize) {
bufsize = index = 0; bufsize = index = 0;
} }
int bsize = (len / 3) * 3; // round down to multiple of 3 bytes int bsize = (len / 3) * 3; // round down to multiple of 3 bytes
if (bsize > 0) { if (bsize > 0) {
int size = 0; int size = 0;
@ -184,7 +186,6 @@ final class BASE64DecoderStream extends FilterInputStream {
} }
off += size; off += size;
len -= size; len -= size;
if (size != bsize) { // hit EOF? if (size != bsize) { // hit EOF?
if (off == off0) { if (off == off0) {
return -1; return -1;
@ -193,7 +194,6 @@ final class BASE64DecoderStream extends FilterInputStream {
} }
} }
} }
// finish up with a partial read if necessary // finish up with a partial read if necessary
for (; len > 0; len--) { for (; len > 0; len--) {
int c = read(); int c = read();
@ -202,7 +202,6 @@ final class BASE64DecoderStream extends FilterInputStream {
} }
buf[off++] = (byte) c; buf[off++] = (byte) c;
} }
if (off == off0) { if (off == off0) {
return -1; return -1;
} else { } else {
@ -292,50 +291,35 @@ final class BASE64DecoderStream extends FilterInputStream {
" before padding character (=)" + " before padding character (=)" +
recentChars()); recentChars());
} }
// didn't get any characters before padding character?
if (got == 0) {
return pos - pos0;
}
atEOF = false; // need to keep reading atEOF = false; // need to keep reading
} }
// pad partial result with zeroes // pad partial result with zeroes
// how many bytes will we produce on output? // how many bytes will we produce on output?
// (got always < 4, so size always < 3) // (got always < 4, so size always < 3)
int size = got - 1; int size = got - 1;
if (size == 0) {
size = 1;
}
// handle the one padding character we've seen // handle the one padding character we've seen
got++; got++;
val <<= 6; val <<= 6;
while (got < 4) { while (got < 4) {
if (!atEOF) { // consume the rest of the padding characters,
// consume the rest of the padding characters, // filling with zeroes
// filling with zeroes i = getByte();
i = getByte(); if (i == -1) {
if (i == -1) { throw new MimeException(
throw new MimeException( "BASE64Decoder: Error in encoded " +
"BASE64Decoder: Error in encoded " + "stream: hit EOF while looking for " +
"stream: hit EOF while looking for " + "padding characters (=)" +
"padding characters (=)" + recentChars());
recentChars()); } else if (i != -2) {
} else if (i != -2) { throw new MimeException(
throw new MimeException( "BASE64Decoder: Error in encoded " +
"BASE64Decoder: Error in encoded " + "stream: found valid base64 " +
"stream: found valid base64 " + "character after a padding character " +
"character after a padding character " + "(=)" + recentChars());
"(=)" + recentChars());
}
} }
val <<= 6; val <<= 6;
got++; got++;
} }
// now pull out however many valid bytes we got // now pull out however many valid bytes we got
val >>= 8; // always skip first one val >>= 8; // always skip first one
if (size == 2) { if (size == 2) {
@ -353,7 +337,6 @@ final class BASE64DecoderStream extends FilterInputStream {
val |= i; val |= i;
} }
} }
// read 4 valid characters, now extract 3 bytes // read 4 valid characters, now extract 3 bytes
outbuf[pos + 2] = (byte) (val & 0xff); outbuf[pos + 2] = (byte) (val & 0xff);
val >>= 8; val >>= 8;
@ -424,7 +407,7 @@ final class BASE64DecoderStream extends FilterInputStream {
errstr.append("\\t"); errstr.append("\\t");
break; break;
default: default:
if (c >= ' ' && c < 0177) { if (c >= ' ' && c < 127) {
errstr.append(c); errstr.append(c);
} else { } else {
errstr.append("\\").append((int) c); errstr.append("\\").append((int) c);

View file

@ -11,7 +11,7 @@ public class Base64InputStream extends FilterInputStream {
private static final byte[] EMPTY = new byte[0]; private static final byte[] EMPTY = new byte[0];
private static final int BUFFER_SIZE = 2048; private static final int BUFFER_SIZE = 8192;
private final Base64.Coder coder; private final Base64.Coder coder;
@ -97,7 +97,7 @@ public class Base64InputStream extends FilterInputStream {
return 0; return 0;
} }
long bytes = Math.min(n, outputEnd-outputStart); long bytes = Math.min(n, outputEnd-outputStart);
outputStart += bytes; outputStart = outputStart + (int)bytes;
return bytes; return bytes;
} }
@ -123,7 +123,7 @@ public class Base64InputStream extends FilterInputStream {
} }
int bytes = Math.min(len, outputEnd-outputStart); int bytes = Math.min(len, outputEnd-outputStart);
System.arraycopy(coder.output, outputStart, b, off, bytes); System.arraycopy(coder.output, outputStart, b, off, bytes);
outputStart += bytes; outputStart = outputStart + bytes;
return bytes; return bytes;
} }

View file

@ -11,7 +11,7 @@ public class MimeStream {
public static InputStream decode(InputStream inputStream, String encoding) throws MimeException { public static InputStream decode(InputStream inputStream, String encoding) throws MimeException {
if (encoding.equalsIgnoreCase("base64")) if (encoding.equalsIgnoreCase("base64"))
return new BASE64DecoderStream(inputStream); return new Base64DecoderStream(inputStream);
else if (encoding.equalsIgnoreCase("quoted-printable")) else if (encoding.equalsIgnoreCase("quoted-printable"))
return new QuotedPrintableInputStream(inputStream); return new QuotedPrintableInputStream(inputStream);
else if (encoding.equalsIgnoreCase("binary") || else if (encoding.equalsIgnoreCase("binary") ||

View file

@ -17,10 +17,11 @@ public class MimeMultipartTest {
@Test @Test
public void multiPartTest() throws MimeException, IOException { public void multiPartTest() throws MimeException, IOException {
InputStream inputStream = getClass().getResourceAsStream("/org/xbib/net/mime/test/msg.txt"); try (InputStream inputStream = getClass().getResourceAsStream("/org/xbib/net/mime/test/msg.txt")) {
Objects.requireNonNull(inputStream); Objects.requireNonNull(inputStream);
MimeMultipartParser parser = new MimeMultipartParser("multipart/mixed; boundary=\"----=_Part_4_910054940.1065629194743\"; charset=\"ISO-8859-1\""); MimeMultipartParser parser = new MimeMultipartParser("multipart/mixed; boundary=\"----=_Part_4_910054940.1065629194743\"; charset=\"ISO-8859-1\"");
parser.parse(ByteBuffer.wrap(inputStream.readAllBytes()), parser.parse(ByteBuffer.wrap(inputStream.readAllBytes()),
e -> logger.log(Level.INFO, e.getHeaders().toString() + " length = " + e.getLength() + " content = " + e.getCharset().decode(e.getBody()))); e -> logger.log(Level.INFO, e.getHeaders().toString() + " length = " + e.getLength() + " content = " + e.getCharset().decode(e.getBody())));
}
} }
} }