001: // Base64Decoder.java
002: // $Id: Base64Decoder.java,v 1.6 2000/08/16 21:37:48 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.tools.codec;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.ByteArrayOutputStream;
010: import java.io.FileInputStream;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.OutputStream;
014: import java.io.PrintStream;
015: import java.io.UnsupportedEncodingException;
016:
017: /**
018: * Decode a BASE64 encoded input stream to some output stream.
019: * This class implements BASE64 decoding, as specified in the
020: * <a href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification</a>.
021: * @see org.w3c.tools.codec.Base64Encoder
022: */
023:
024: public class Base64Decoder {
025: private static final int BUFFER_SIZE = 1024;
026:
027: InputStream in = null;
028: OutputStream out = null;
029: boolean stringp = false;
030:
031: private void printHex(int x) {
032: int h = (x & 0xf0) >> 4;
033: int l = (x & 0x0f);
034: System.out.print((new Character((char) ((h > 9) ? 'A' + h - 10
035: : '0' + h))).toString()
036: + (new Character((char) ((l > 9) ? 'A' + l - 10
037: : '0' + l))).toString());
038: }
039:
040: private void printHex(byte buf[], int off, int len) {
041: while (off < len) {
042: printHex(buf[off++]);
043: System.out.print(" ");
044: }
045: System.out.println("");
046: }
047:
048: private void printHex(String s) {
049: byte bytes[];
050: try {
051: bytes = s.getBytes("ISO-8859-1");
052: } catch (UnsupportedEncodingException ex) {
053: throw new RuntimeException(this .getClass().getName()
054: + "[printHex] Unable to convert"
055: + "properly char to bytes");
056: }
057: printHex(bytes, 0, bytes.length);
058: }
059:
060: private final int get1(byte buf[], int off) {
061: return ((buf[off] & 0x3f) << 2) | ((buf[off + 1] & 0x30) >>> 4);
062: }
063:
064: private final int get2(byte buf[], int off) {
065: return ((buf[off + 1] & 0x0f) << 4)
066: | ((buf[off + 2] & 0x3c) >>> 2);
067: }
068:
069: private final int get3(byte buf[], int off) {
070: return ((buf[off + 2] & 0x03) << 6) | (buf[off + 3] & 0x3f);
071: }
072:
073: private final int check(int ch) {
074: if ((ch >= 'A') && (ch <= 'Z')) {
075: return ch - 'A';
076: } else if ((ch >= 'a') && (ch <= 'z')) {
077: return ch - 'a' + 26;
078: } else if ((ch >= '0') && (ch <= '9')) {
079: return ch - '0' + 52;
080: } else {
081: switch (ch) {
082: case '=':
083: return 65;
084: case '+':
085: return 62;
086: case '/':
087: return 63;
088: default:
089: return -1;
090: }
091: }
092: }
093:
094: /**
095: * Do the actual decoding.
096: * Process the input stream by decoding it and emiting the resulting bytes
097: * into the output stream.
098: * @exception IOException If the input or output stream accesses failed.
099: * @exception Base64FormatException If the input stream is not compliant
100: * with the BASE64 specification.
101: */
102:
103: public void process() throws IOException, Base64FormatException {
104: byte buffer[] = new byte[BUFFER_SIZE];
105: byte chunk[] = new byte[4];
106: int got = -1;
107: int ready = 0;
108:
109: fill: while ((got = in.read(buffer)) > 0) {
110: int skiped = 0;
111: while (skiped < got) {
112: // Check for un-understood characters:
113: while (ready < 4) {
114: if (skiped >= got)
115: continue fill;
116: int ch = check(buffer[skiped++]);
117: if (ch >= 0)
118: chunk[ready++] = (byte) ch;
119: }
120: if (chunk[2] == 65) {
121: out.write(get1(chunk, 0));
122: return;
123: } else if (chunk[3] == 65) {
124: out.write(get1(chunk, 0));
125: out.write(get2(chunk, 0));
126: return;
127: } else {
128: out.write(get1(chunk, 0));
129: out.write(get2(chunk, 0));
130: out.write(get3(chunk, 0));
131: }
132: ready = 0;
133: }
134: }
135: if (ready != 0)
136: throw new Base64FormatException("Invalid length.");
137: out.flush();
138: }
139:
140: /**
141: * Do the decoding, and return a String.
142: * This methods should be called when the decoder is used in
143: * <em>String</em> mode. It decodes the input string to an output string
144: * that is returned.
145: * @exception RuntimeException If the object wasn't constructed to
146: * decode a String.
147: * @exception Base64FormatException If the input string is not compliant
148: * with the BASE64 specification.
149: */
150:
151: public String processString() throws Base64FormatException {
152: if (!stringp)
153: throw new RuntimeException(this .getClass().getName()
154: + "[processString]" + "invalid call (not a String)");
155: try {
156: process();
157: } catch (IOException e) {
158: }
159: String s;
160: try {
161: s = ((ByteArrayOutputStream) out).toString("ISO-8859-1");
162: } catch (UnsupportedEncodingException ex) {
163: throw new RuntimeException(this .getClass().getName()
164: + "[processString] Unable to convert"
165: + "properly char to bytes");
166: }
167: return s;
168: }
169:
170: /**
171: * Create a decoder to decode a String.
172: * @param input The string to be decoded.
173: */
174:
175: public Base64Decoder(String input) {
176: byte bytes[];
177: try {
178: bytes = input.getBytes("ISO-8859-1");
179: } catch (UnsupportedEncodingException ex) {
180: throw new RuntimeException(this .getClass().getName()
181: + "[Constructor] Unable to convert"
182: + "properly char to bytes");
183: }
184: this .stringp = true;
185: this .in = new ByteArrayInputStream(bytes);
186: this .out = new ByteArrayOutputStream();
187: }
188:
189: /**
190: * Create a decoder to decode a stream.
191: * @param in The input stream (to be decoded).
192: * @param out The output stream, to write decoded data to.
193: */
194:
195: public Base64Decoder(InputStream in, OutputStream out) {
196: this .in = in;
197: this .out = out;
198: this .stringp = false;
199: }
200:
201: /**
202: * Test the decoder.
203: * Run it with one argument: the string to be decoded, it will print out
204: * the decoded value.
205: */
206:
207: public static void main(String args[]) {
208: if (args.length == 1) {
209: try {
210: Base64Decoder b = new Base64Decoder(args[0]);
211: System.out.println("[" + b.processString() + "]");
212: } catch (Base64FormatException e) {
213: System.out.println("Invalid Base64 format !");
214: System.exit(1);
215: }
216: } else if ((args.length == 2) && (args[0].equals("-f"))) {
217: try {
218: FileInputStream in = new FileInputStream(args[1]);
219: Base64Decoder b = new Base64Decoder(in, System.out);
220: b.process();
221: } catch (Exception ex) {
222: System.out.println("error: " + ex.getMessage());
223: System.exit(1);
224: }
225: } else {
226: System.out.println("Base64Decoder [strong] [-f file]");
227: }
228: System.exit(0);
229: }
230: }
|