/*
* Copyright (C) 2000 - 2008 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.nary.net;
import java.io.ByteArrayOutputStream;
import java.util.BitSet;
/**
* This class collects various encoders and decoders.
*/
public class Base64 {
private static BitSet BoundChar;
private static BitSet EBCDICUnsafeChar;
private static byte[] Base64EncMap, Base64DecMap;
private static char[] UUEncMap;
private static byte[] UUDecMap;
// Class Initializer
static {
// rfc-2046 & rfc-2045: (bcharsnospace & token)
// used for multipart codings
BoundChar = new BitSet( 256 );
for (int ch = '0'; ch <= '9'; ch++)
BoundChar.set( ch );
for (int ch = 'A'; ch <= 'Z'; ch++)
BoundChar.set( ch );
for (int ch = 'a'; ch <= 'z'; ch++)
BoundChar.set( ch );
BoundChar.set( '+' );
BoundChar.set( '_' );
BoundChar.set( '-' );
BoundChar.set( '.' );
// EBCDIC unsafe characters to be quoted in quoted-printable
// See first NOTE in section 6.7 of rfc-2045
EBCDICUnsafeChar = new BitSet( 256 );
EBCDICUnsafeChar.set( '!' );
EBCDICUnsafeChar.set( '"' );
EBCDICUnsafeChar.set( '#' );
EBCDICUnsafeChar.set( '$' );
EBCDICUnsafeChar.set( '@' );
EBCDICUnsafeChar.set( '[' );
EBCDICUnsafeChar.set( '\\' );
EBCDICUnsafeChar.set( ']' );
EBCDICUnsafeChar.set( '^' );
EBCDICUnsafeChar.set( '`' );
EBCDICUnsafeChar.set( '{' );
EBCDICUnsafeChar.set( '|' );
EBCDICUnsafeChar.set( '}' );
EBCDICUnsafeChar.set( '~' );
// rfc-2045: Base64 Alphabet
byte[] map = { (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) '/' };
Base64EncMap = map;
Base64DecMap = new byte[128];
for (int idx = 0; idx < Base64EncMap.length; idx++)
Base64DecMap[Base64EncMap[idx]] = (byte) idx;
// browsers convert '+' to ' ' when storing Base64-encoded strings in
// cookies;
// therefore, treat ' ' as '+' when decoding
Base64DecMap[' '] = Base64DecMap['+'];
// uuencode'ing maps
UUEncMap = new char[64];
for (int idx = 0; idx < UUEncMap.length; idx++)
UUEncMap[idx] = (char) ( idx + 0x20 );
UUDecMap = new byte[128];
for (int idx = 0; idx < UUEncMap.length; idx++)
UUDecMap[UUEncMap[idx]] = (byte) idx;
}
// Constructors
/**
* This class isn't meant to be instantiated.
*/
private Base64() {
}
// Methods
/**
* This method encodes the given string using the base64-encoding specified in
* RFC-2045 (Section 6.8). It's used for example in the "Basic" authorization
* scheme.
*
* @param str
* the string
* @return the base64-encoded <var>str</var>
*/
@SuppressWarnings("deprecation")
public final static String base64Encode( String str ) {
if ( str == null )
return null;
byte data[] = new byte[str.length()];
str.getBytes( 0, str.length(), data, 0 );
return new String( base64Encode( data ), 0 );
}
/**
* This method encodes the given byte[] using the base64-encoding specified in
* RFC-2045 (Section 6.8).
*
* @param data
* the data
* @return the base64-encoded <var>data</var>
*/
public final static byte[] base64Encode( byte[] data ) {
if ( data == null )
return null;
int sidx, didx;
byte dest[] = new byte[( ( data.length + 2 ) / 3 ) * 4];
// 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
for (sidx = 0, didx = 0; sidx < data.length - 2; sidx += 3) {
dest[didx++] = Base64EncMap[( data[sidx] >>> 2 ) & 077];
dest[didx++] = Base64EncMap[( data[sidx + 1] >>> 4 ) & 017
| ( data[sidx] << 4 ) & 077];
dest[didx++] = Base64EncMap[( data[sidx + 2] >>> 6 ) & 003
| ( data[sidx + 1] << 2 ) & 077];
dest[didx++] = Base64EncMap[data[sidx + 2] & 077];
}
if ( sidx < data.length ) {
dest[didx++] = Base64EncMap[( data[sidx] >>> 2 ) & 077];
if ( sidx < data.length - 1 ) {
dest[didx++] = Base64EncMap[( data[sidx + 1] >>> 4 ) & 017
| ( data[sidx] << 4 ) & 077];
dest[didx++] = Base64EncMap[( data[sidx + 1] << 2 ) & 077];
} else
dest[didx++] = Base64EncMap[( data[sidx] << 4 ) & 077];
}
// add padding
for (; didx < dest.length; didx++)
dest[didx] = (byte) '=';
return dest;
}
/**
* This method decodes the given string using the base64-encoding specified in
* RFC-2045 (Section 6.8).
*
* @param str
* the base64-encoded string.
* @return the decoded <var>str</var>.
*/
@SuppressWarnings("deprecation")
public final static String base64Decode( String str ) {
if ( str == null )
return null;
byte data[] = new byte[str.length()];
str.getBytes( 0, str.length(), data, 0 );
return new String( base64Decode( data ), 0 );
}
/**
* This method decodes the given byte[] using the base64-encoding specified in
* RFC-2045 (Section 6.8).
*
* @param data
* the base64-encoded data.
* @return the decoded <var>data</var>.
*/
public final static byte[] base64Decode( byte[] data ) {
if ( data == null )
return null;
if ( data.length == 0 )
return data;
int tail = data.length;
while (data[tail - 1] == '=')
tail--;
byte dest[] = new byte[tail - data.length / 4];
// ascii printable to 0-63 conversion
for (int idx = 0; idx < data.length; idx++)
data[idx] = Base64DecMap[data[idx]];
// 4-byte to 3-byte conversion
int sidx, didx;
for (sidx = 0, didx = 0; didx < dest.length - 2; sidx += 4, didx += 3) {
dest[didx] = (byte) ( ( ( data[sidx] << 2 ) & 255 ) | ( ( data[sidx + 1] >>> 4 ) & 003 ) );
dest[didx + 1] = (byte) ( ( ( data[sidx + 1] << 4 ) & 255 ) | ( ( data[sidx + 2] >>> 2 ) & 017 ) );
dest[didx + 2] = (byte) ( ( ( data[sidx + 2] << 6 ) & 255 ) | ( data[sidx + 3] & 077 ) );
}
if ( didx < dest.length )
dest[didx] = (byte) ( ( ( data[sidx] << 2 ) & 255 ) | ( ( data[sidx + 1] >>> 4 ) & 003 ) );
if ( ++didx < dest.length )
dest[didx] = (byte) ( ( ( data[sidx + 1] << 4 ) & 255 ) | ( ( data[sidx + 2] >>> 2 ) & 017 ) );
return dest;
}
/**
* This method encodes the given byte[] using the unix uuencode encding. The
* output is split into lines starting with the encoded number of encoded
* octets in the line and ending with a newline. No line is longer than 45
* octets (60 characters), not including length and newline.
*
* <P>
* <em>Note:</em> just the raw data is encoded; no 'begin' and 'end' lines
* are added as is done by the unix <code>uuencode</code> utility.
*
* @param data
* the data
* @return the uuencoded <var>data</var>
*/
public final static char[] uuencode( byte[] data ) {
if ( data == null )
return null;
if ( data.length == 0 )
return new char[0];
int line_len = 45; // line length, in octets
int sidx, didx;
char nl[] = new char[]{ '\n' }, dest[] = new char[( data.length + 2 )
/ 3
* 4
+ ( ( data.length + line_len - 1 ) / line_len )
* ( nl.length + 1 )];
// split into lines, adding line-length and line terminator
for (sidx = 0, didx = 0; sidx + line_len < data.length;) {
// line length
dest[didx++] = UUEncMap[line_len];
// 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
for (int end = sidx + line_len; sidx < end; sidx += 3) {
dest[didx++] = UUEncMap[( data[sidx] >>> 2 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 1] >>> 4 ) & 017
| ( data[sidx] << 4 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 2] >>> 6 ) & 003
| ( data[sidx + 1] << 2 ) & 077];
dest[didx++] = UUEncMap[data[sidx + 2] & 077];
}
// line terminator
for (int idx = 0; idx < nl.length; idx++)
dest[didx++] = nl[idx];
}
// last line
// line length
dest[didx++] = UUEncMap[data.length - sidx];
// 3-byte to 4-byte conversion + 0-63 to ascii printable conversion
for (; sidx + 2 < data.length; sidx += 3) {
dest[didx++] = UUEncMap[( data[sidx] >>> 2 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 1] >>> 4 ) & 017
| ( data[sidx] << 4 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 2] >>> 6 ) & 003
| ( data[sidx + 1] << 2 ) & 077];
dest[didx++] = UUEncMap[data[sidx + 2] & 077];
}
if ( sidx < data.length - 1 ) {
dest[didx++] = UUEncMap[( data[sidx] >>> 2 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 1] >>> 4 ) & 017
| ( data[sidx] << 4 ) & 077];
dest[didx++] = UUEncMap[( data[sidx + 1] << 2 ) & 077];
dest[didx++] = UUEncMap[0];
} else if ( sidx < data.length ) {
dest[didx++] = UUEncMap[( data[sidx] >>> 2 ) & 077];
dest[didx++] = UUEncMap[( data[sidx] << 4 ) & 077];
dest[didx++] = UUEncMap[0];
dest[didx++] = UUEncMap[0];
}
// line terminator
for (int idx = 0; idx < nl.length; idx++)
dest[didx++] = nl[idx];
// sanity check
if ( didx != dest.length )
throw new Error( "Calculated " + dest.length + " chars but wrote " + didx
+ " chars!" );
return dest;
}
/**
* This method decodes the given uuencoded char[].
*
* <P>
* <em>Note:</em> just the actual data is decoded; any 'begin' and 'end'
* lines such as those generated by the unix <code>uuencode</code> utility
* must not be included.
*
* @param data
* the uuencode-encoded data.
* @return the decoded <var>data</var>.
*/
public final static byte[] uudecode( char[] data ) {
if ( data == null )
return null;
int sidx, didx;
ByteArrayOutputStream bos = new ByteArrayOutputStream( data.length );
for (sidx = 0, didx = 0; sidx < data.length;) {
// get line length (in number of encoded octets)
int len = UUDecMap[data[sidx++]];
// ascii printable to 0-63 and 4-byte to 3-byte conversion
int end = didx + len;
for (; didx < end - 2; sidx += 4) {
byte A = UUDecMap[data[sidx]], B = UUDecMap[data[sidx + 1]], C = UUDecMap[data[sidx + 2]], D = UUDecMap[data[sidx + 3]];
bos.write( (byte) ( ( ( A << 2 ) & 255 ) | ( ( B >>> 4 ) & 003 ) ) );
bos.write( (byte) ( ( ( B << 4 ) & 255 ) | ( ( C >>> 2 ) & 017 ) ) );
bos.write( (byte) ( ( ( C << 6 ) & 255 ) | ( D & 077 ) ) );
didx+=3;
}
if ( didx < end ) {
byte A = UUDecMap[data[sidx]], B = UUDecMap[data[sidx + 1]];
bos.write( (byte) ( ( ( A << 2 ) & 255 ) | ( ( B >>> 4 ) & 003 ) ) );
didx++;
}
if ( didx < end ) {
byte B = UUDecMap[data[sidx + 1]], C = UUDecMap[data[sidx + 2]];
bos.write( (byte) ( ( ( B << 4 ) & 255 ) | ( ( C >>> 2 ) & 017 ) ) );
didx++;
}
// skip padding
while (sidx < data.length && data[sidx] != '\n' && data[sidx] != '\r')
sidx++;
// skip end of line
while (sidx < data.length && ( data[sidx] == '\n' || data[sidx] == '\r' ))
sidx++;
}
return bos.toByteArray();
}
}