/*
* Copyright © World Wide Web Consortium, (Massachusetts Institute of Technology,
* Institut National de Recherche en Informatique et en Automatique, Keio University).
* All Rights Reserved. http://www.w3.org/Consortium/Legal/
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* Decode a BASE64 encoded input stream to some output stream.
* This class implements BASE64 decoding, as specified in the
* <a href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification</a>.
* @see org.w3c.tools.codec.Base64Encoder
*/
public class Base64Decoder
{
private static final int BUFFER_SIZE = 1024;
InputStream in = null;
OutputStream out = null;
boolean stringp = false;
private void printHex(int x)
{
int h = (x & 0xf0) >> 4;
int l = (x & 0x0f);
System.out.print(
(new Character((char) ((h > 9) ? 'A' + h - 10 : '0' + h)))
.toString()
+ (new Character((char) ((l > 9) ? 'A' + l - 10 : '0' + l)))
.toString());
}
private void printHex(byte buf[], int off, int len)
{
while (off < len)
{
printHex(buf[off++]);
System.out.print(" ");
}
System.out.println("");
}
private void printHex(String s)
{
byte bytes[];
try
{
bytes = s.getBytes("ISO-8859-1");
}
catch (UnsupportedEncodingException ex)
{
throw new RuntimeException(
this.getClass().getName()
+ "[printHex] Unable to convert"
+ "properly char to bytes");
}
printHex(bytes, 0, bytes.length);
}
private final int get1(byte buf[], int off)
{
return ((buf[off] & 0x3f) << 2) | ((buf[off + 1] & 0x30) >>> 4);
}
private final int get2(byte buf[], int off)
{
return ((buf[off + 1] & 0x0f) << 4) | ((buf[off + 2] & 0x3c) >>> 2);
}
private final int get3(byte buf[], int off)
{
return ((buf[off + 2] & 0x03) << 6) | (buf[off + 3] & 0x3f);
}
private final int check(int ch)
{
if ((ch >= 'A') && (ch <= 'Z'))
{
return ch - 'A';
}
else if ((ch >= 'a') && (ch <= 'z'))
{
return ch - 'a' + 26;
}
else if ((ch >= '0') && (ch <= '9'))
{
return ch - '0' + 52;
}
else
{
switch (ch)
{
case '=' :
return 65;
case '+' :
return 62;
case '/' :
return 63;
default :
return -1;
}
}
}
/**
* Do the actual decoding.
* Process the input stream by decoding it and emiting the resulting bytes
* into the output stream.
* @exception IOException If the input or output stream accesses failed.
* @exception Base64FormatException If the input stream is not compliant
* with the BASE64 specification.
*/
public void process() throws IOException, Exception
{
byte buffer[] = new byte[BUFFER_SIZE];
byte chunk[] = new byte[4];
int got = -1;
int ready = 0;
fill : while ((got = in.read(buffer)) > 0)
{
int skiped = 0;
while (skiped < got)
{
// Check for un-understood characters:
while (ready < 4)
{
if (skiped >= got)
continue fill;
int ch = check(buffer[skiped++]);
if (ch >= 0)
chunk[ready++] = (byte) ch;
}
if (chunk[2] == 65)
{
out.write(get1(chunk, 0));
return;
}
else if (chunk[3] == 65)
{
out.write(get1(chunk, 0));
out.write(get2(chunk, 0));
return;
}
else
{
out.write(get1(chunk, 0));
out.write(get2(chunk, 0));
out.write(get3(chunk, 0));
}
ready = 0;
}
}
if (ready != 0)
throw new Exception("Invalid length.");
out.flush();
}
/**
* Do the decoding, and return a String.
* This methods should be called when the decoder is used in
* <em>String</em> mode. It decodes the input string to an output string
* that is returned.
* @exception RuntimeException If the object wasn't constructed to
* decode a String.
* @exception Base64FormatException If the input string is not compliant
* with the BASE64 specification.
*/
public String processString() throws Exception
{
if (!stringp)
throw new RuntimeException(
this.getClass().getName()
+ "[processString]"
+ "invalid call (not a String)");
try
{
process();
}
catch (IOException e)
{
}
String s;
try
{
s = ((ByteArrayOutputStream) out).toString("ISO-8859-1");
}
catch (UnsupportedEncodingException ex)
{
throw new RuntimeException(
this.getClass().getName()
+ "[processString] Unable to convert"
+ "properly char to bytes");
}
return s;
}
/**
* Create a decoder to decode a String.
* @param input The string to be decoded.
*/
public Base64Decoder(String input)
{
byte bytes[];
try
{
bytes = input.getBytes("ISO-8859-1");
}
catch (UnsupportedEncodingException ex)
{
throw new RuntimeException(
this.getClass().getName()
+ "[Constructor] Unable to convert"
+ "properly char to bytes");
}
this.stringp = true;
this.in = new ByteArrayInputStream(bytes);
this.out = new ByteArrayOutputStream();
}
/**
* Create a decoder to decode a stream.
* @param in The input stream (to be decoded).
* @param out The output stream, to write decoded data to.
*/
public Base64Decoder(InputStream in, OutputStream out)
{
this.in = in;
this.out = out;
this.stringp = false;
}
/**
* Test the decoder.
* Run it with one argument: the string to be decoded, it will print out
* the decoded value.
*/
public static void main(String args[])
{
if (args.length == 1)
{
try
{
Base64Decoder b = new Base64Decoder(args[0]);
System.out.println("[" + b.processString() + "]");
}
catch (Exception e)
{
System.out.println("Invalid Base64 format !");
System.exit(1);
}
}
else if ((args.length == 2) && (args[0].equals("-f")))
{
try
{
FileInputStream in = new FileInputStream(args[1]);
Base64Decoder b = new Base64Decoder(in, System.out);
b.process();
}
catch (Exception ex)
{
System.out.println("error: " + ex.getMessage());
System.exit(1);
}
}
else
{
System.out.println("Base64Decoder [strong] [-f file]");
}
System.exit(0);
}
}
|