alefhidalgo

BASE64DecoderStream

Jun 20th, 2011
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 14.03 KB | None | 0 0
  1.    
  2. /*
  3.  * The contents of this file are subject to the terms
  4.  * of the Common Development and Distribution License
  5.  * (the "License").  You may not use this file except
  6.  * in compliance with the License.
  7.  *
  8.  * You can obtain a copy of the license at
  9.  * glassfish/bootstrap/legal/CDDLv1.0.txt or
  10.  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
  11.  * See the License for the specific language governing
  12.  * permissions and limitations under the License.
  13.  *
  14.  * When distributing Covered Code, include this CDDL
  15.  * HEADER in each file and include the License file at
  16.  * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable,
  17.  * add the following below this CDDL HEADER, with the
  18.  * fields enclosed by brackets "[]" replaced with your
  19.  * own identifying information: Portions Copyright [yyyy]
  20.  * [name of copyright owner]
  21.  */
  22.  
  23. /*
  24.  * @(#)BASE64DecoderStream.java 1.15 06/09/25
  25.  *
  26.  * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
  27.  */
  28.  
  29.  
  30. import java.io.*;
  31.  
  32. /**
  33.  * This class implements a BASE64 Decoder. It is implemented as
  34.  * a FilterInputStream, so one can just wrap this class around
  35.  * any input stream and read bytes from this filter. The decoding
  36.  * is done as the bytes are read out.
  37.  *
  38.  * @author John Mani
  39.  * @author Bill Shannon
  40.  */
  41.  
  42. public class BASE64DecoderStream extends FilterInputStream {
  43.     // buffer of decoded bytes for single byte reads
  44.     private byte[] buffer = new byte[3];
  45.     private int bufsize = 0;  // size of the cache
  46.     private int index = 0;  // index into the cache
  47.  
  48.     // buffer for almost 8K of typical 76 chars + CRLF lines,
  49.     // used by getByte method.  this buffer contains encoded bytes.
  50.     private byte[] input_buffer = new byte[78*105];
  51.     private int input_pos = 0;
  52.     private int input_len = 0;;
  53.  
  54.     private boolean ignoreErrors = false;
  55.  
  56.     /**
  57.      * Create a BASE64 decoder that decodes the specified input stream.
  58.      * The System property <code>mail.mime.base64.ignoreerrors</code>
  59.      * controls whether errors in the encoded data cause an exception
  60.      * or are ignored.  The default is false (errors cause exception).
  61.      *
  62.      * @param in  the input stream
  63.      */
  64.     public BASE64DecoderStream(InputStream in) {
  65.   super(in);
  66.   try {
  67.       String s = System.getProperty("mail.mime.base64.ignoreerrors");
  68.       // default to false
  69.       ignoreErrors = s != null && !s.equalsIgnoreCase("false");
  70.   } catch (SecurityException sex) {
  71.       // ignore it
  72.   }
  73.     }
  74.  
  75.     /**
  76.      * Create a BASE64 decoder that decodes the specified input stream.
  77.      *
  78.      * @param in  the input stream
  79.      * @param ignoreErrors  ignore errors in encoded data?
  80.      */
  81.     public BASE64DecoderStream(InputStream in, boolean ignoreErrors) {
  82.   super(in);
  83.   this.ignoreErrors = ignoreErrors;
  84.     }
  85.  
  86.     /**
  87.      * Read the next decoded byte from this input stream. The byte
  88.      * is returned as an <code>int</code> in the range <code>0</code>
  89.      * to <code>255</code>. If no byte is available because the end of
  90.      * the stream has been reached, the value <code>-1</code> is returned.
  91.      * This method blocks until input data is available, the end of the
  92.      * stream is detected, or an exception is thrown.
  93.      *
  94.      * @return     next byte of data, or <code>-1</code> if the end of the
  95.      *             stream is reached.
  96.      * @exception  IOException  if an I/O error occurs.
  97.      * @see        java.io.FilterInputStream#in
  98.      */
  99.     public int read() throws IOException {
  100.   if (index >= bufsize) {
  101.       bufsize = decode(buffer, 0, buffer.length);
  102.       if (bufsize <= 0) // buffer is empty
  103.     return -1;
  104.       index = 0; // reset index into buffer
  105.   }
  106.   return buffer[index++] & 0xff; // Zero off the MSB
  107.     }
  108.  
  109.     /**
  110.      * Reads up to <code>len</code> decoded bytes of data from this input stream
  111.      * into an array of bytes. This method blocks until some input is
  112.      * available.
  113.      * <p>
  114.      *
  115.      * @param      buf   the buffer into which the data is read.
  116.      * @param      off   the start offset of the data.
  117.      * @param      len   the maximum number of bytes read.
  118.      * @return     the total number of bytes read into the buffer, or
  119.      *             <code>-1</code> if there is no more data because the end of
  120.      *             the stream has been reached.
  121.      * @exception  IOException  if an I/O error occurs.
  122.      */
  123.     public int read(byte[] buf, int off, int len) throws IOException {
  124.   // empty out single byte read buffer
  125.   int off0 = off;
  126.   while (index < bufsize && len > 0) {
  127.       buf[off++] = buffer[index++];
  128.       len--;
  129.   }
  130.   if (index >= bufsize)
  131.       bufsize = index = 0;
  132.  
  133.   int bsize = (len / 3) * 3;  // round down to multiple of 3 bytes
  134.   if (bsize > 0) {
  135.       int size = decode(buf, off, bsize);
  136.       off += size;
  137.       len -= size;
  138.  
  139.       if (size != bsize) {  // hit EOF?
  140.     if (off == off0)  // haven't returned any data
  141.         return -1;
  142.     else      // returned some data before hitting EOF
  143.         return off - off0;
  144.       }
  145.   }
  146.  
  147.   // finish up with a partial read if necessary
  148.   for (; len > 0; len--) {
  149.       int c = read();
  150.       if (c == -1)  // EOF
  151.     break;
  152.       buf[off++] = (byte)c;
  153.   }
  154.  
  155.   if (off == off0)  // haven't returned any data
  156.       return -1;
  157.   else      // returned some data before hitting EOF
  158.       return off - off0;
  159.     }
  160.  
  161.     /**
  162.      * Tests if this input stream supports marks. Currently this class
  163.      * does not support marks
  164.      */
  165.     public boolean markSupported() {
  166.   return false; // Maybe later ..
  167.     }
  168.  
  169.     /**
  170.      * Returns the number of bytes that can be read from this input
  171.      * stream without blocking. However, this figure is only
  172.      * a close approximation in case the original encoded stream
  173.      * contains embedded CRLFs; since the CRLFs are discarded, not decoded
  174.      */
  175.     public int available() throws IOException {
  176.    // This is only an estimate, since in.available()
  177.    // might include CRLFs too ..
  178.    return ((in.available() * 3)/4 + (bufsize-index));
  179.     }
  180.  
  181.     /**
  182.      * This character array provides the character to value map
  183.      * based on RFC1521.
  184.      */  
  185.     private final static char pem_array[] = {
  186.   'A','B','C','D','E','F','G','H', // 0
  187.   'I','J','K','L','M','N','O','P', // 1
  188.   'Q','R','S','T','U','V','W','X', // 2
  189.   'Y','Z','a','b','c','d','e','f', // 3
  190.   'g','h','i','j','k','l','m','n', // 4
  191.   'o','p','q','r','s','t','u','v', // 5
  192.   'w','x','y','z','0','1','2','3', // 6
  193.   '4','5','6','7','8','9','+','/'  // 7
  194.     };
  195.  
  196.     private final static byte pem_convert_array[] = new byte[256];
  197.  
  198.     static {
  199.   for (int i = 0; i < 255; i++)
  200.       pem_convert_array[i] = -1;
  201.   for (int i = 0; i < pem_array.length; i++)
  202.       pem_convert_array[pem_array[i]] = (byte)i;
  203.     }
  204.  
  205.     /**
  206.      * The decoder algorithm.  Most of the complexity here is dealing
  207.      * with error cases.  Returns the number of bytes decoded, which
  208.      * may be zero.  Decoding is done by filling an int with 4 6-bit
  209.      * values by shifting them in from the bottom and then extracting
  210.      * 3 8-bit bytes from the int by shifting them out from the bottom.
  211.      *
  212.      * @param outbuf  the buffer into which to put the decoded bytes
  213.      * @param pos position in the buffer to start filling
  214.      * @param len the number of bytes to fill
  215.      * @return    the number of bytes filled, always a multiple
  216.      *      of three, and may be zero
  217.      * @exception IOException if the data is incorrectly formatted
  218.      */
  219.     private int decode(byte[] outbuf, int pos, int len) throws IOException {
  220.   int pos0 = pos;
  221.   while (len >= 3) {
  222.       /*
  223.        * We need 4 valid base64 characters before we start decoding.
  224.        * We skip anything that's not a valid base64 character (usually
  225.        * just CRLF).
  226.        */
  227.       int got = 0;
  228.       int val = 0;
  229.       while (got < 4) {
  230.     int i = getByte();
  231.     if (i == -1 || i == -2) {
  232.         boolean atEOF;
  233.         if (i == -1) {
  234.       if (got == 0)
  235.           return pos - pos0;
  236.       if (!ignoreErrors)
  237.           throw new IOException("Error in encoded stream: " +
  238.         "needed 4 valid base64 characters " +
  239.         "but only got " + got + " before EOF" +
  240.         recentChars());
  241.       atEOF = true; // don't read any more
  242.         } else {  // i == -2
  243.       // found a padding character, we're at EOF
  244.       // XXX - should do something to make EOF "sticky"
  245.       if (got < 2 && !ignoreErrors)
  246.           throw new IOException("Error in encoded stream: " +
  247.         "needed at least 2 valid base64 characters," +
  248.         " but only got " + got +
  249.         " before padding character (=)" +
  250.         recentChars());
  251.  
  252.       // didn't get any characters before padding character?
  253.       if (got == 0)
  254.           return pos - pos0;
  255.       atEOF = false;  // need to keep reading
  256.         }
  257.  
  258.         // pad partial result with zeroes
  259.  
  260.         // how many bytes will we produce on output?
  261.         // (got always < 4, so size always < 3)
  262.         int size = got - 1;
  263.         if (size == 0)
  264.       size = 1;
  265.  
  266.         // handle the one padding character we've seen
  267.         got++;
  268.         val <<= 6;
  269.  
  270.         while (got < 4) {
  271.       if (!atEOF) {
  272.           // consume the rest of the padding characters,
  273.           // filling with zeroes
  274.           i = getByte();
  275.           if (i == -1) {
  276.         if (!ignoreErrors)
  277.             throw new IOException(
  278.           "Error in encoded stream: " +
  279.           "hit EOF while looking for " +
  280.           "padding characters (=)" +
  281.           recentChars());
  282.           } else if (i != -2) {
  283.         if (!ignoreErrors)
  284.             throw new IOException(
  285.           "Error in encoded stream: " +
  286.           "found valid base64 character after " +
  287.           "a padding character (=)" +
  288.           recentChars());
  289.           }
  290.       }
  291.       val <<= 6;
  292.       got++;
  293.         }
  294.  
  295.         // now pull out however many valid bytes we got
  296.         val >>= 8;    // always skip first one
  297.         if (size == 2)
  298.       outbuf[pos + 1] = (byte)(val & 0xff);
  299.         val >>= 8;
  300.         outbuf[pos] = (byte)(val & 0xff);
  301.         // len -= size; // not needed, return below
  302.         pos += size;
  303.         return pos - pos0;
  304.     } else {
  305.         // got a valid byte
  306.         val <<= 6;
  307.         got++;
  308.         val |= i;
  309.     }
  310.       }
  311.  
  312.       // read 4 valid characters, now extract 3 bytes
  313.       outbuf[pos + 2] = (byte)(val & 0xff);
  314.       val >>= 8;
  315.       outbuf[pos + 1] = (byte)(val & 0xff);
  316.       val >>= 8;
  317.       outbuf[pos] = (byte)(val & 0xff);
  318.       len -= 3;
  319.       pos += 3;
  320.   }
  321.   return pos - pos0;
  322.     }
  323.  
  324.     /**
  325.      * Read the next valid byte from the input stream.
  326.      * Buffer lots of data from underlying stream in input_buffer,
  327.      * for efficiency.
  328.      *
  329.      * @return  the next byte, -1 on EOF, or -2 if next byte is '='
  330.      *    (padding at end of encoded data)
  331.      */
  332.     private int getByte() throws IOException {
  333.   int c;
  334.   do {
  335.       if (input_pos >= input_len) {
  336.     try {
  337.         input_len = in.read(input_buffer);
  338.     } catch (EOFException ex) {
  339.         return -1;
  340.     }
  341.     if (input_len <= 0)
  342.         return -1;
  343.     input_pos = 0;
  344.       }
  345.       // get the next byte in the buffer
  346.       c = input_buffer[input_pos++] & 0xff;
  347.       // is it a padding byte?
  348.       if (c == '=')
  349.     return -2;
  350.       // no, convert it
  351.       c = pem_convert_array[c];
  352.       // loop until we get a legitimate byte
  353.   } while (c == -1);
  354.   return c;
  355.     }
  356.  
  357.     /**
  358.      * Return the most recent characters, for use in an error message.
  359.      */
  360.     private String recentChars() {
  361.   // reach into the input buffer and extract up to 10
  362.   // recent characters, to help in debugging.
  363.   String errstr = "";
  364.   int nc = input_pos > 10 ? 10 : input_pos;
  365.   if (nc > 0) {
  366.       errstr += ", the " + nc +
  367.           " most recent characters were: \"";
  368.       for (int k = input_pos - nc; k < input_pos; k++) {
  369.     char c = (char)(input_buffer[k] & 0xff);
  370.     switch (c) {
  371.     case '\r':  errstr += "\\r"; break;
  372.     case '\n':  errstr += "\\n"; break;
  373.     case '\t':  errstr += "\\t"; break;
  374.     default:
  375.         if (c >= ' ' && c < 0177)
  376.       errstr += c;
  377.         else
  378.       errstr += ("\\" + (int)c);
  379.     }
  380.       }
  381.       errstr += "\"";
  382.   }
  383.   return errstr;
  384.     }
  385.  
  386.     /**
  387.      * Base64 decode a byte array.  No line breaks are allowed.
  388.      * This method is suitable for short strings, such as those
  389.      * in the IMAP AUTHENTICATE protocol, but not to decode the
  390.      * entire content of a MIME part.
  391.      *
  392.      * NOTE: inbuf may only contain valid base64 characters.
  393.      *       Whitespace is not ignored.
  394.      */
  395.     public static byte[] decode(byte[] inbuf) {
  396.   int size = (inbuf.length / 4) * 3;
  397.   if (size == 0)
  398.       return inbuf;
  399.  
  400.   if (inbuf[inbuf.length - 1] == '=') {
  401.       size--;
  402.       if (inbuf[inbuf.length - 2] == '=')
  403.     size--;
  404.   }
  405.   byte[] outbuf = new byte[size];
  406.  
  407.   int inpos = 0, outpos = 0;
  408.   size = inbuf.length;
  409.   while (size > 0) {
  410.       int val;
  411.       int osize = 3;
  412.       val = pem_convert_array[inbuf[inpos++] & 0xff];
  413.       val <<= 6;
  414.       val |= pem_convert_array[inbuf[inpos++] & 0xff];
  415.       val <<= 6;
  416.       if (inbuf[inpos] != '=') // End of this BASE64 encoding
  417.     val |= pem_convert_array[inbuf[inpos++] & 0xff];
  418.       else
  419.     osize--;
  420.       val <<= 6;
  421.       if (inbuf[inpos] != '=') // End of this BASE64 encoding
  422.     val |= pem_convert_array[inbuf[inpos++] & 0xff];
  423.       else
  424.     osize--;
  425.       if (osize > 2)
  426.     outbuf[outpos + 2] = (byte)(val & 0xff);
  427.       val >>= 8;
  428.       if (osize > 1)
  429.     outbuf[outpos + 1] = (byte)(val & 0xff);
  430.       val >>= 8;
  431.       outbuf[outpos] = (byte)(val & 0xff);
  432.       outpos += osize;
  433.       size -= 4;
  434.   }
  435.   return outbuf;
  436.     }
  437.  
  438.     /*** begin TEST program ***
  439.     public static void main(String argv[]) throws Exception {
  440.       FileInputStream infile = new FileInputStream(argv[0]);
  441.   BASE64DecoderStream decoder = new BASE64DecoderStream(infile);
  442.   int c;
  443.  
  444.   while ((c = decoder.read()) != -1)
  445.       System.out.print((char)c);
  446.   System.out.flush();
  447.     }
  448.     *** end TEST program ***/
  449. }
Add Comment
Please, Sign In to add comment