package org.bouncycastle.est; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.bouncycastle.util.encoders.Base64; class CTEBase64InputStream extends InputStream { protected final InputStream src; protected final byte[] rawBuf = new byte[1024]; protected final byte[] data = new byte[768]; protected final OutputStream dataOutputStream; protected final Long max; protected int rp; protected int wp; protected boolean end; protected long read; public CTEBase64InputStream(InputStream src, Long limit) { this.src = src; this.dataOutputStream = new OutputStream() { public void write(int b) throws IOException { data[wp++] = (byte)b; } }; this.max = limit; } // Pulls a line from the source, decodes it and returns the decoded length. // Or returns -1 if there is nothing more to read and nothing was read in this pass. protected int pullFromSrc() throws IOException { if (this.read >= this.max) { return -1; } int j = 0; int c = 0; do { j = src.read(); /* * RFC2045 All line breaks or other characters not * found in Table 1 must be ignored by decoding software. * https://tools.ietf.org/html/rfc2045#section-6.8 * This uses the line brakes to to stop reading data and to decode a chunk. */ if (j >= 33 || (j == '\r' || j == '\n')) { if (c >= rawBuf.length) { throw new IOException("Content Transfer Encoding, base64 line length > 1024"); } rawBuf[c++] = (byte)j; read += 1; } else if (j >= 0) { read += 1; } } while (j > -1 && c < rawBuf.length && j != 10 && this.read < this.max); if (c > 0) { try { Base64.decode(rawBuf, 0, c, dataOutputStream); } catch (Exception ex) { throw new IOException("Decode Base64 Content-Transfer-Encoding: " + ex); } } else { if (j == -1) { return -1; } } return wp; } public int read() throws IOException { // When we have read up to the write pointer (wp) pull some more. if (rp == wp) { rp = 0; wp = 0; int i = pullFromSrc(); if (i == -1) { return i; } } return data[rp++] & 0xFF; } public void close() throws IOException { src.close(); } }