/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.libraries.xmlns.parser; /** * Provides encoding of raw bytes to base64-encoded characters, and decoding of base64 characters to raw bytes. date: 06 * August 1998 modified: 14 February 2000 modified: 22 September 2000 * * @author Kevin Kelley (kelley@ruralnet.net) * @version 1.3 */ public class Base64 { /** * Private constructor prevents object creation. */ private Base64() { } /** * returns an array of base64-encoded characters to represent the passed data array. * * @param data the array of bytes to encode * @return base64-coded character array. */ public static char[] encode( final byte[] data ) { final char[] out = new char[ ( ( data.length + 2 ) / 3 ) * 4 ]; // // 3 bytes encode to 4 chars. Output is always an even // multiple of 4 characters. // for ( int i = 0, index = 0; i < data.length; i += 3, index += 4 ) { int val = ( 0xFF & data[ i ] ); val <<= 8; boolean trip = false; if ( ( i + 1 ) < data.length ) { val |= ( 0xFF & data[ i + 1 ] ); trip = true; } val <<= 8; boolean quad = false; if ( ( i + 2 ) < data.length ) { val |= ( 0xFF & data[ i + 2 ] ); quad = true; } out[ index + 3 ] = Base64.ALPHABET[ ( quad ? ( val & 0x3F ) : 64 ) ]; val >>= 6; out[ index + 2 ] = Base64.ALPHABET[ ( trip ? ( val & 0x3F ) : 64 ) ]; val >>= 6; out[ index + 1 ] = Base64.ALPHABET[ val & 0x3F ]; val >>= 6; out[ index ] = Base64.ALPHABET[ val & 0x3F ]; } return out; } /** * Decodes a BASE-64 encoded stream to recover the original data. White space before and after will be trimmed away, * but no other manipulation of the input will be performed. * <p/> * As of version 1.2 this method will properly handle input containing junk characters (newlines and the like) rather * than throwing an error. It does this by pre-parsing the input and generating from that a count of VALID input * characters. * * @param data the character data. * @return The decoded data. */ public static byte[] decode( final char[] data ) { // as our input could contain non-BASE64 data (newlines, // whitespace of any sort, whatever) we must first adjust // our count of USABLE data so that... // (a) we don't misallocate the output array, and // (b) think that we miscalculated our data length // just because of extraneous throw-away junk int tempLen = data.length; for ( int ix = 0; ix < data.length; ix++ ) { if ( ( data[ ix ] > 255 ) || Base64.CODES[ data[ ix ] ] < 0 ) { --tempLen; // ignore non-valid chars and padding } } // calculate required length: // -- 3 bytes for every 4 valid base64 chars // -- plus 2 bytes if there are 3 extra base64 chars, // or plus 1 byte if there are 2 extra. int len = ( tempLen / 4 ) * 3; if ( ( tempLen % 4 ) == 3 ) { len += 2; } if ( ( tempLen % 4 ) == 2 ) { len += 1; } final byte[] out = new byte[ len ]; int shift = 0; // # of excess bits stored in accum int accum = 0; // excess bits int index = 0; // we now go through the entire array (NOT using the 'tempLen' value) for ( int ix = 0; ix < data.length; ix++ ) { final int value = ( data[ ix ] > 255 ) ? -1 : Base64.CODES[ data[ ix ] ]; if ( value >= 0 ) { // skip over non-code accum <<= 6; // bits shift up by 6 each time thru shift += 6; // loop, with new bits being put in accum |= value; // at the bottom. if ( shift >= 8 ) { // whenever there are 8 or more shifted in, shift -= 8; // write them out (from the top, leaving any out[ index ] = // excess at the bottom for next iteration. (byte) ( ( accum >> shift ) & 0xff ); index += 1; } } // we will also have skipped processing a padding null byte ('=') here; // these are used ONLY for padding to an even length and do not legally // occur as encoded data. for this reason we can ignore the fact that // no index++ operation occurs in that special case: the out[] array is // initialized to all-zero bytes to start with and that works to our // advantage in this combination. } // if there is STILL something wrong we just have to throw up now! if ( index != out.length ) { throw new Error( "Miscalculated data length (wrote " + index + " instead of " + out.length + ')' ); } return out; } // // code characters for values 0..63 // private static final char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray(); // // lookup table for converting base64 characters to value in range 0..63 // private static final byte[] CODES = new byte[ 256 ]; static { for ( int i = 0; i < 256; i++ ) { Base64.CODES[ i ] = -1; } for ( int i = 'A'; i <= 'Z'; i++ ) { Base64.CODES[ i ] = (byte) ( i - 'A' ); } for ( int i = 'a'; i <= 'z'; i++ ) { Base64.CODES[ i ] = (byte) ( 26 + i - 'a' ); } for ( int i = '0'; i <= '9'; i++ ) { Base64.CODES[ i ] = (byte) ( 52 + i - '0' ); } Base64.CODES[ '+' ] = 62; Base64.CODES[ '/' ] = 63; } // // // /////////////////////////////////////////////////// // // remainder (main method and helper functions) is // // for testing purposes only, feel free to clip it. // /////////////////////////////////////////////////// // // /** // * Entry point. // * // * @param args the command line arguments. // */ // public static void main(final String[] args) { // boolean decode = false; // // if (args.length == 0) { // System.out.println("usage: java Base64 [-d[ecode]] filename"); // System.exit(0); // } // for (int i = 0; i < args.length; i++) { // if ("-decode".equalsIgnoreCase(args[i])) { // decode = true; // } // else if ("-d".equalsIgnoreCase(args[i])) { // decode = true; // } // } // // final String filename = args[args.length - 1]; // final File file = new File(filename); // if (!file.exists()) { // System.out.println("Error: file '" + filename + "' doesn't exist!"); // System.exit(0); // } // // if (decode) { // final char[] encoded = org.pentaho.reporting.libraries.xmls.parser.Base64.readChars(file); // final byte[] decoded = org.pentaho.reporting.libraries.xmls.parser.Base64.decode(encoded); // org.pentaho.reporting.libraries.xmls.parser.Base64.writeBytes(file, decoded); // } // else { // final byte[] decoded = org.pentaho.reporting.libraries.xmls.parser.Base64.readBytes(file); // final char[] encoded = org.pentaho.reporting.libraries.xmls.parser.Base64.encode(decoded); // org.pentaho.reporting.libraries.xmls.parser.Base64.writeChars(file, encoded); // } // } // // private static byte[] readBytes(final File file) { // final ByteArrayOutputStream baos = new ByteArrayOutputStream(); // try { // final InputStream fis = new FileInputStream(file); // final InputStream is = new BufferedInputStream(fis); // // int count; // final byte[] buf = new byte[16384]; // while ((count = is.read(buf)) != -1) { // if (count > 0) { // baos.write(buf, 0, count); // } // } // is.close(); // } // catch (Exception e) { // e.printStackTrace(); // } // // return baos.toByteArray(); // } // // private static char[] readChars(final File file) { // final CharArrayWriter caw = new CharArrayWriter(); // try { // final Reader fr = new FileReader(file); // final Reader in = new BufferedReader(fr); // int count; // final char[] buf = new char[16384]; // while ((count = in.read(buf)) != -1) { // if (count > 0) { // caw.write(buf, 0, count); // } // } // in.close(); // } // catch (Exception e) { // e.printStackTrace(); // } // // return caw.toCharArray(); // } // // private static void writeBytes(final File file, final byte[] data) { // try { // final OutputStream fos = new FileOutputStream(file); // final OutputStream os = new BufferedOutputStream(fos); // os.write(data); // os.close(); // } // catch (Exception e) { // e.printStackTrace(); // } // } // // private static void writeChars(final File file, final char[] data) { // try { // final Writer fos = new FileWriter(file); // final Writer os = new BufferedWriter(fos); // os.write(data); // os.close(); // } // catch (Exception e) { // e.printStackTrace(); // } // } // /////////////////////////////////////////////////// // // end of test code. // /////////////////////////////////////////////////// // }