0,0 → 1,315 |
package ak.backpath; |
|
/** |
* Special version for packpath (ak) - |
* symbols +/= replaced with .*! to produce correct URL-parts; |
* most methods are deleted</li> |
* |
* Original author Robert Harder (rob@iharder.net) |
*/ |
class Base64 |
{ |
/** The equals sign (=) as a byte. */ |
private final static byte EQUALS_SIGN = (byte)'!'; |
|
/** The new line character (\n) as a byte. */ |
private final static byte NEW_LINE = (byte)'\n'; |
|
/** Preferred encoding. */ |
private final static String PREFERRED_ENCODING = "UTF-8"; |
|
/** The 64 valid Base64 values. */ |
private final static byte[] ALPHABET; |
private final static 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)'*' |
}; |
|
/** Determine which ALPHABET to use. */ |
static |
{ |
byte[] __bytes; |
try |
{ |
__bytes = new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.*") |
.getBytes( PREFERRED_ENCODING ); |
} |
catch (java.io.UnsupportedEncodingException use) |
{ |
__bytes = _NATIVE_ALPHABET; // Fall back to native encoding |
} |
ALPHABET = __bytes; |
} |
|
/** |
* Translates a Base64 value to either its 6-bit reconstruction value |
* or a negative number indicating some other meaning. |
**/ |
private final static 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 |
-1, // ! at decimal 33 |
-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 34 - 41 |
63, // * at decimal 42 |
-9, // Plus sign at decimal 43 |
-9,-9,62, // Decimal 44 - 46 |
-9, // Slash at decimal 47 |
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine |
-9,-9,-9, // Decimal 58 - 60 |
-9, // 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 |
}; |
|
private final static byte BAD_ENCODING = -9; // Indicates error in encoding |
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding |
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding |
|
private Base64(){} |
|
private static byte[] encode3to4(byte[] destination, byte[] source, int numSigBytes) |
{ |
// 1 2 3 |
// 01234567890123456789012345678901 Bit position |
// --------000000001111111122222222 Array position from threeBytes |
// --------| || || || | Six bit groups to index ALPHABET |
// >>18 >>12 >> 6 >> 0 Right shift necessary |
// 0x3f 0x3f 0x3f Additional AND |
|
// Create buffer with zero-padding if there are only one or two |
// significant bytes passed in the array. |
// We have to shift left 24 in order to flush out the 1's that appear |
// when Java treats a value as negative that is cast from a byte to an int. |
int inBuff = ( numSigBytes > 0 ? ((source[ 0 ] << 24) >>> 8) : 0 ) |
| ( numSigBytes > 1 ? ((source[ 1 ] << 24) >>> 16) : 0 ) |
| ( numSigBytes > 2 ? ((source[ 2 ] << 24) >>> 24) : 0 ); |
|
switch( numSigBytes ) |
{ |
case 3: |
destination[ 0 ] = ALPHABET[ (inBuff >>> 18) ]; |
destination[ 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; |
destination[ 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; |
destination[ 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; |
return destination; |
|
case 2: |
destination[ 0 ] = ALPHABET[ (inBuff >>> 18) ]; |
destination[ 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; |
destination[ 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; |
destination[ 3 ] = EQUALS_SIGN; |
return destination; |
|
case 1: |
destination[ 0 ] = ALPHABET[ (inBuff >>> 18) ]; |
destination[ 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; |
destination[ 2 ] = EQUALS_SIGN; |
destination[ 3 ] = EQUALS_SIGN; |
return destination; |
|
default: |
return destination; |
} |
} |
|
private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) |
{ |
// Example: Dk== |
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; |
} |
|
// Example: DkL= |
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; |
} |
|
// Example: DkLE |
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){ |
return -1; |
} |
} |
} |
|
static class InputStream extends java.io.FilterInputStream |
{ |
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 |
|
public InputStream( java.io.InputStream in) |
{ |
super( in ); |
this.bufferLength = 3; |
this.buffer = new byte[ bufferLength ]; |
this.position = -1; |
} |
|
public int read() throws java.io.IOException |
{ |
// Do we need to get data? |
if( position < 0 ) |
{ |
byte[] b4 = new byte[4]; |
int i = 0; |
for( i = 0; i < 4; i++ ) |
{ |
// Read four "meaningful" bytes: |
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." ); |
} |
} |
|
// Got data? |
if( position >= 0 ) |
{ |
if( position >= numSigBytes ) |
return -1; |
|
int b = buffer[ position++ ]; |
|
if( position >= bufferLength ) |
position = -1; |
|
return b & 0xFF; |
} |
else |
{ |
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; |
} |
|
} |
|
static class OutputStream extends java.io.FilterOutputStream |
{ |
private int position; |
private byte[] buffer; |
private int bufferLength; |
private byte[] b4; |
|
public OutputStream( java.io.OutputStream out) |
{ |
super( out ); |
this.bufferLength = 3; |
this.buffer = new byte[ bufferLength ]; |
this.position = 0; |
this.b4 = new byte[4]; |
} |
|
public void write(int theByte) throws java.io.IOException |
{ |
buffer[ position++ ] = (byte)theByte; |
if( position >= bufferLength ) // Enough to encode. |
{ |
out.write( encode3to4( b4, buffer, bufferLength ) ); |
position = 0; |
} |
} |
|
public void write( byte[] theBytes, int off, int len ) throws java.io.IOException |
{ |
for( int i = 0; i < len; i++ ) |
{ |
write( theBytes[ off + i ] ); |
} |
} |
|
public void close() throws java.io.IOException |
{ |
if( position > 0 ) |
{ |
out.write( encode3to4( b4, buffer, position ) ); |
position = 0; |
} |
|
super.close(); |
buffer = null; |
out = null; |
} |
} |
} |