package com.planet_ink.coffee_mud.core; public class B64Encoder { private B64Encoder(){}; /* Base 64 Encoding stuff */ private static byte[] ALPHABET; public static final int NO_OPTIONS = 0; public static final int ENCODE = 1; public static final int DECODE = 0; public static final int GZIP = 2; public static final int DONT_BREAK_LINES = 8; public static final int MAX_LINE_LENGTH = 76; public static final byte EQUALS_SIGN = (byte)'='; public static final byte NEW_LINE = (byte)'\n'; public static final String PREFERRED_ENCODING = "UTF-8"; public static final byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' }; public static final byte[] DECODABET = { -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 -5,-5, // Whitespace: Tab and Linefeed -9,-9, // Decimal 11 - 12 -5, // Whitespace: Carriage Return -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 -9,-9,-9,-9,-9, // Decimal 27 - 31 -5, // Whitespace: Space -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 62, // Plus sign at decimal 43 -9,-9,-9, // Decimal 44 - 46 63, // Slash at decimal 47 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine -9,-9,-9, // Decimal 58 - 60 -1, // Equals sign at decimal 61 -9,-9,-9, // Decimal 62 - 64 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' -9,-9,-9,-9 // Decimal 123 - 126 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; public static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding public static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding protected static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) { encode3to4( threeBytes, 0, numSigBytes, b4, 0 ); return b4; } static { byte[] __bytes; try { __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING ); } // end try catch (java.io.UnsupportedEncodingException use) { __bytes = _NATIVE_ALPHABET; // Fall back to native encoding } // end catch if(ALPHABET==null) ALPHABET = __bytes; } protected static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset ) { int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); switch( numSigBytes ) { case 3: destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; return destination; case 2: destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; destination[ destOffset + 3 ] = EQUALS_SIGN; return destination; case 1: destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; destination[ destOffset + 2 ] = EQUALS_SIGN; destination[ destOffset + 3 ] = EQUALS_SIGN; return destination; default: return destination; } } public static String B64encodeObject( java.io.Serializable serializableObject ) { return B64encodeObject( serializableObject, NO_OPTIONS ); } public static String B64encodeObject( java.io.Serializable serializableObject, int options ) { java.io.ByteArrayOutputStream baos = null; java.io.OutputStream b64os = null; java.io.ObjectOutputStream oos = null; java.util.zip.GZIPOutputStream gzos = null; int gzip = (options & GZIP); int dontBreakLines = (options & DONT_BREAK_LINES); try { baos = new java.io.ByteArrayOutputStream(); b64os = new B64OutputStream( baos, ENCODE | dontBreakLines ); if( gzip == GZIP ) { gzos = new java.util.zip.GZIPOutputStream( b64os ); oos = new java.io.ObjectOutputStream( gzos ); } else oos = new java.io.ObjectOutputStream( b64os ); oos.writeObject( serializableObject ); } catch( java.io.IOException e ) { e.printStackTrace(); return null; } finally { try{ oos.close(); } catch( Exception e ){} try{ gzos.close(); } catch( Exception e ){} try{ b64os.close(); } catch( Exception e ){} try{ baos.close(); } catch( Exception e ){} } try { return new String( baos.toByteArray(), PREFERRED_ENCODING ); } catch (java.io.UnsupportedEncodingException uue) { return new String( baos.toByteArray() ); } } public static String B64encodeBytes( byte[] source ) { return B64encodeBytes( source, 0, source.length, NO_OPTIONS ); } // end encodeBytes public static String B64encodeBytes( byte[] source, int options ) { return B64encodeBytes( source, 0, source.length, options ); } // end encodeBytes public static String B64encodeBytes( byte[] source, int off, int len ) { return B64encodeBytes( source, off, len, NO_OPTIONS ); } // end encodeBytes public static String B64encodeBytes( byte[] source, int off, int len, int options ) { int dontBreakLines = ( options & DONT_BREAK_LINES ); int gzip = ( options & GZIP ); if( gzip == GZIP ) { java.io.ByteArrayOutputStream baos = null; java.util.zip.GZIPOutputStream gzos = null; B64OutputStream b64os = null; try { baos = new java.io.ByteArrayOutputStream(); b64os = new B64OutputStream( baos, ENCODE | dontBreakLines ); gzos = new java.util.zip.GZIPOutputStream( b64os ); gzos.write( source, off, len ); gzos.close(); } catch( java.io.IOException e ) { e.printStackTrace(); return null; } finally { try{ gzos.close(); } catch( Exception e ){} try{ b64os.close(); } catch( Exception e ){} try{ baos.close(); } catch( Exception e ){} } try { return new String( baos.toByteArray(), PREFERRED_ENCODING ); } catch (java.io.UnsupportedEncodingException uue) { return new String( baos.toByteArray() ); } } boolean breakLines = dontBreakLines == 0; int len43 = len * 4 / 3; byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines int d = 0; int e = 0; int len2 = len - 2; int lineLength = 0; for( ; d < len2; d+=3, e+=4 ) { encode3to4( source, d+off, 3, outBuff, e ); lineLength += 4; if( breakLines && lineLength == MAX_LINE_LENGTH ) { outBuff[e+4] = NEW_LINE; e++; lineLength = 0; } } if( d < len ) { encode3to4( source, d+off, len - d, outBuff, e ); e += 4; } try { return new String( outBuff, 0, e, PREFERRED_ENCODING ); } catch (java.io.UnsupportedEncodingException uue) { return new String( outBuff, 0, e ); } } protected static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) { if( source[ srcOffset + 2] == EQUALS_SIGN ) { int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); destination[ destOffset ] = (byte)( outBuff >>> 16 ); return 1; } else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); destination[ destOffset ] = (byte)( outBuff >>> 16 ); destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); return 2; } else { try{ int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); destination[ destOffset ] = (byte)( outBuff >> 16 ); destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); destination[ destOffset + 2 ] = (byte)( outBuff ); return 3; }catch( Exception e){ Log.errOut("CMEncoder",e); return -1; } } } public static byte[] B64decode( byte[] source, int off, int len ) { int len34 = len * 3 / 4; byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output int outBuffPosn = 0; byte[] b4 = new byte[4]; int b4Posn = 0; int i = 0; byte sbiCrop = 0; byte sbiDecode = 0; for( i = off; i < off+len; i++ ) { sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits sbiDecode = DECODABET[ sbiCrop ]; if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better { if( sbiDecode >= EQUALS_SIGN_ENC ) { b4[ b4Posn++ ] = sbiCrop; if( b4Posn > 3 ) { outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); b4Posn = 0; if( sbiCrop == EQUALS_SIGN ) break; } } } else { System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); return null; } } byte[] out = new byte[ outBuffPosn ]; System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); return out; } public static byte[] B64decode( String s ) { byte[] bytes; try { bytes = s.getBytes( PREFERRED_ENCODING ); } catch( java.io.UnsupportedEncodingException uee ) { bytes = s.getBytes(); } bytes = B64decode( bytes, 0, bytes.length ); if( bytes != null && bytes.length >= 4 ) { int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { java.io.ByteArrayInputStream bais = null; java.util.zip.GZIPInputStream gzis = null; java.io.ByteArrayOutputStream baos = null; byte[] buffer = new byte[2048]; int length = 0; try { baos = new java.io.ByteArrayOutputStream(); bais = new java.io.ByteArrayInputStream( bytes ); gzis = new java.util.zip.GZIPInputStream( bais ); while( ( length = gzis.read( buffer ) ) >= 0 ) { baos.write(buffer,0,length); } bytes = baos.toByteArray(); } catch( java.io.IOException e ) {} finally { try{ baos.close(); } catch( Exception e ){} try{ gzis.close(); } catch( Exception e ){} try{ bais.close(); } catch( Exception e ){} } } } return bytes; } public static Object B64decodeToObject( String encodedObject ) { byte[] objBytes = B64decode( encodedObject ); java.io.ByteArrayInputStream bais = null; java.io.ObjectInputStream ois = null; Object obj = null; try { bais = new java.io.ByteArrayInputStream( objBytes ); ois = new java.io.ObjectInputStream( bais ); obj = ois.readObject(); } catch( java.io.IOException e ) { e.printStackTrace(); obj = null; } catch( java.lang.ClassNotFoundException e ) { e.printStackTrace(); obj = null; } finally { try{ bais.close(); } catch( Exception e ){} try{ ois.close(); } catch( Exception e ){} } return obj; } public static boolean B64encodeToFile( byte[] dataToEncode, String filename ) { boolean success = false; B64OutputStream bos = null; try { bos = new B64OutputStream( new java.io.FileOutputStream( filename ), ENCODE ); bos.write( dataToEncode ); success = true; } catch( java.io.IOException e ) { success = false; } finally { try{ bos.close(); } catch( Exception e ){} } return success; } public static boolean B64decodeToFile( String dataToDecode, String filename ) { boolean success = false; B64OutputStream bos = null; try { bos = new B64OutputStream( new java.io.FileOutputStream( filename ), DECODE ); bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); success = true; } catch( java.io.IOException e ) { success = false; } finally { try{ bos.close(); } catch( Exception e ){} } return success; } public static byte[] B64decodeFromFile( String filename ) { byte[] decodedData = null; B64InputStream bis = null; try { java.io.File file = new java.io.File( filename ); byte[] buffer = null; int length = 0; int numBytes = 0; if( file.length() > Integer.MAX_VALUE ) { System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); return null; } buffer = new byte[ (int)file.length() ]; bis = new B64InputStream( new java.io.BufferedInputStream( new java.io.FileInputStream( file ) ), DECODE ); while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) length += numBytes; decodedData = new byte[ length ]; System.arraycopy( buffer, 0, decodedData, 0, length ); } catch( java.io.IOException e ) { System.err.println( "Error decoding from file " + filename ); } finally { try{ bis.close(); } catch( Exception e) {} } return decodedData; } public static String B64encodeFromFile( String filename ) { String encodedData = null; B64InputStream bis = null; try { java.io.File file = new java.io.File( filename ); byte[] buffer = new byte[ (int)(file.length() * 1.4) ]; int length = 0; int numBytes = 0; bis = new B64InputStream( new java.io.BufferedInputStream( new java.io.FileInputStream( file ) ), ENCODE ); while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) length += numBytes; encodedData = new String( buffer, 0, length, PREFERRED_ENCODING ); } catch( java.io.IOException e ) { System.err.println( "Error encoding from file " + filename ); } finally { try{ bis.close(); } catch( Exception e) {} } return encodedData; } private static class B64InputStream extends java.io.FilterInputStream { private boolean encode; // Encoding or decoding private int position; // Current position in the buffer private byte[] buffer; // Small buffer holding converted data private int bufferLength; // Length of buffer (3 or 4) private int numSigBytes; // Number of meaningful bytes in the buffer private int lineLength; private boolean breakLines; // Break lines at less than 80 characters public B64InputStream( java.io.InputStream in ) { this( in, DECODE ); } public B64InputStream( java.io.InputStream in, int options ) { super( in ); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; this.bufferLength = encode ? 4 : 3; this.buffer = new byte[ bufferLength ]; this.position = -1; this.lineLength = 0; } public int read() throws java.io.IOException { if( position < 0 ) { if( encode ) { byte[] b3 = new byte[3]; int numBinaryBytes = 0; for( int i = 0; i < 3; i++ ) { try { int b = in.read(); if( b >= 0 ) { b3[i] = (byte)b; numBinaryBytes++; } } catch( java.io.IOException e ) { if( i == 0 ) throw e; } } if( numBinaryBytes > 0 ) { encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); position = 0; numSigBytes = 4; } else { return -1; } } else { byte[] b4 = new byte[4]; int i = 0; for( i = 0; i < 4; i++ ) { int b = 0; do{ b = in.read(); } while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); if( b < 0 ) break; // Reads a -1 if end of stream b4[i] = (byte)b; } if( i == 4 ) { numSigBytes = decode4to3( b4, 0, buffer, 0 ); position = 0; } else if( i == 0 ){ return -1; } else { throw new java.io.IOException( "Improperly padded Base64 input." ); } } } if( position >= 0 ) { if( position >= numSigBytes ) return -1; if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { lineLength = 0; return '\n'; } lineLength++; int b = buffer[ position++ ]; if( position >= bufferLength ) position = -1; return b & 0xFF; } throw new java.io.IOException( "Error in Base64 code reading stream." ); } public int read( byte[] dest, int off, int len ) throws java.io.IOException { int i; int b; for( i = 0; i < len; i++ ) { b = read(); if( b >= 0 ) dest[off + i] = (byte)b; else if( i == 0 ) return -1; else break; } return i; } } private static class B64OutputStream extends java.io.FilterOutputStream { private boolean encode; private int position; private byte[] buffer; private int bufferLength; private int lineLength; private boolean breakLines; private byte[] b4; // Scratch used in a few places private boolean suspendEncoding; public B64OutputStream( java.io.OutputStream out ) { this( out, ENCODE ); } public B64OutputStream( java.io.OutputStream out, int options ) { super( out ); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; this.bufferLength = encode ? 3 : 4; this.buffer = new byte[ bufferLength ]; this.position = 0; this.lineLength = 0; this.suspendEncoding = false; this.b4 = new byte[4]; } public void write(int theByte) throws java.io.IOException { if( suspendEncoding ) { super.out.write( theByte ); return; } if( encode ) { buffer[ position++ ] = (byte)theByte; if( position >= bufferLength ) { out.write( encode3to4( b4, buffer, bufferLength ) ); lineLength += 4; if( breakLines && lineLength >= MAX_LINE_LENGTH ) { out.write( NEW_LINE ); lineLength = 0; } position = 0; } } else { if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { buffer[ position++ ] = (byte)theByte; if( position >= bufferLength ) { int len = decode4to3( buffer, 0, b4, 0 ); out.write( b4, 0, len ); position = 0; } } else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { throw new java.io.IOException( "Invalid character in Base64 data." ); } } } public void write( byte[] theBytes, int off, int len ) throws java.io.IOException { if( suspendEncoding ) { super.out.write( theBytes, off, len ); return; } for( int i = 0; i < len; i++ ) { write( theBytes[ off + i ] ); } } public void flushBase64() throws java.io.IOException { if( position > 0 ) { if( encode ) { out.write( encode3to4( b4, buffer, position ) ); position = 0; } else { throw new java.io.IOException( "Base64 input not properly padded." ); } } } public void close() throws java.io.IOException { flushBase64(); super.close(); buffer = null; out = null; } public void suspendEncoding() throws java.io.IOException { flushBase64(); this.suspendEncoding = true; } public void resumeEncoding() { this.suspendEncoding = false; } } }