001: /*
002: * MCS Media Computer Software Copyright (c) 2005 by MCS
003: * -------------------------------------- Created on 16.01.2004 by w.klaas
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
006: * use this file except in compliance with the License. You may obtain a copy of
007: * the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017: package de.mcs.utils.codecs;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.FileInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.io.UnsupportedEncodingException;
026:
027: /**
028: * Decode a BASE64 encoded input stream to some output stream. This class
029: * implements BASE64 decoding, as specified in the <a
030: * href="http://ds.internic.net/rfc/rfc1521.txt">MIME specification </a>. see
031: * org.w3c.tools.codec.Base64Encoder This class is a modified version based on
032: * code obtained from the w3 consortium website, which is subject to their
033: * generic copyright notice:
034: * <dl>
035: * <dd><a href="http://www.w3.org/Consortium/Legal/">Copyright </a>©
036: * [$date-of-software] <a HREF="http://www.w3.org/">World Wide Web Consortium
037: * </a>, ( <a HREF="http://www.lcs.mit.edu/">Massachusetts Institute of
038: * Technology </a>, <a HREF="http://www.inria.fr/">Institut National de
039: * Recherche en Informatique et en Automatique </a>, <a
040: * HREF="http://www.keio.ac.jp/">Keio University </a>). All Rights Reserved.
041: * This program is distributed under the <a
042: * HREF="http://www.w3.org/Consortium/Legal/copyright-software-19980720.html">W3C's
043: * Software Intellectual Property License </a>. This program is distributed in
044: * the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
045: * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
046: * W3C License <a
047: * href="http://www.w3.org/Consortium/Legal/">http://www.w3.org/Consortium/Legal/
048: * </a> for more details.</dd>
049: * </dl>
050: *
051: * @version $Revision: 1.1 $
052: * @deprecated please use the base64 class.
053: */
054: public class Base64Decoder {
055: /** Größe des puffers. */
056: private static final int BUFFER_SIZE = 1024;
057:
058: /** encoding. */
059: private String encoding = null;
060:
061: /** Field in. */
062: private InputStream in = null;
063:
064: /** Field out. */
065: private OutputStream out = null;
066:
067: /** Field stringp. */
068: private boolean stringp = false;
069:
070: // private void printHex(int x) {
071: // int h = (x & 0xf0) >> 4;
072: // int l = (x & 0x0f);
073: // System.out.print(
074: // (new Character((char) ((h > 9) ? 'A' + h - 10 : '0' + h))).toString()
075: // + (new Character((char) ((l > 9) ? 'A' + l - 10 : '0' + l))).toString());
076: // }
077:
078: // private void printHex(byte buf[], int off, int len) {
079: // while (off < len) {
080: // printHex(buf[off++]);
081: // System.out.print(" ");
082: // }
083: // System.out.println("");
084: // }
085:
086: // private void printHex(String s) {
087: // byte bytes[];
088: // try {
089: // bytes = s.getBytes("ISO-8859-1");
090: // }
091: // catch (UnsupportedEncodingException ex) {
092: // throw new RuntimeException(
093: // this.getClass().getName() + "[printHex] Unable to convert" + "properly
094: // char to bytes");
095: // }
096: // printHex(bytes, 0, bytes.length);
097: // }
098:
099: /**
100: * decode mime string to byte array.
101: *
102: * @param string
103: * the string to decode
104: * @return byte[]
105: * @throws Exception
106: * if something goes wrong
107: */
108: public static byte[] decode(final String string) throws Exception {
109: ByteArrayInputStream bin = new ByteArrayInputStream(string
110: .getBytes());
111: ByteArrayOutputStream bout = new ByteArrayOutputStream();
112: Base64Decoder myDecode = new Base64Decoder(bin, bout);
113: myDecode.process();
114: return bout.toByteArray();
115: }
116:
117: /**
118: * @param buf
119: * der Puffer
120: * @param off
121: * der Offset
122: * @return int das Zeichen
123: */
124: private int get1(final byte[] buf, final int off) {
125: return ((buf[off] & 0x3f) << 2) | ((buf[off + 1] & 0x30) >>> 4);
126: }
127:
128: /**
129: * @param buf
130: * der Puffer
131: * @param off
132: * der Offset
133: * @return int das Zeichen
134: */
135: private int get2(final byte[] buf, final int off) {
136: return ((buf[off + 1] & 0x0f) << 4)
137: | ((buf[off + 2] & 0x3c) >>> 2);
138: }
139:
140: /**
141: * @param buf
142: * der Puffer
143: * @param off
144: * der Offset
145: * @return int das Zeichen
146: */
147: private int get3(final byte[] buf, final int off) {
148: return ((buf[off + 2] & 0x03) << 6) | (buf[off + 3] & 0x3f);
149: }
150:
151: /**
152: * check something.
153: *
154: * @param ch
155: * input character
156: * @return int
157: */
158: private int check(final int ch) {
159: if ((ch >= 'A') && (ch <= 'Z')) {
160: return ch - 'A';
161: } else if ((ch >= 'a') && (ch <= 'z')) {
162: return ch - 'a' + 26;
163: } else if ((ch >= '0') && (ch <= '9')) {
164: return ch - '0' + 52;
165: } else {
166: switch (ch) {
167: case '=':
168: return 65;
169: case '+':
170: return 62;
171: case '/':
172: return 63;
173: default:
174: return -1;
175: }
176: }
177: }
178:
179: /**
180: * Do the actual decoding. Process the input stream by decoding it and
181: * emiting the resulting bytes into the output stream. exception IOException
182: * If the input or output stream accesses failed.
183: *
184: * @exception Exception
185: * If the input stream is not compliant with the BASE64
186: * specification.
187: */
188:
189: public final void process() throws Exception {
190: byte[] buffer = new byte[BUFFER_SIZE];
191: byte[] chunk = new byte[4];
192: int got = -1;
193: int ready = 0;
194:
195: fill: while ((got = in.read(buffer)) > 0) {
196: int skiped = 0;
197: while (skiped < got) {
198: // Check for un-understood characters:
199: while (ready < 4) {
200: if (skiped >= got) {
201: continue fill;
202: }
203: int ch = check(buffer[skiped++]);
204: if (ch >= 0) {
205: chunk[ready++] = (byte) ch;
206: }
207: }
208: if (chunk[2] == 65) {
209: out.write(get1(chunk, 0));
210: return;
211: } else if (chunk[3] == 65) {
212: out.write(get1(chunk, 0));
213: out.write(get2(chunk, 0));
214: return;
215: } else {
216: out.write(get1(chunk, 0));
217: out.write(get2(chunk, 0));
218: out.write(get3(chunk, 0));
219: }
220: ready = 0;
221: }
222: }
223: if (ready != 0) {
224: throw new Exception("Invalid length.");
225: }
226: out.flush();
227: }
228:
229: /**
230: * Do the decoding, and return a String. This methods should be called when
231: * the decoder is used in <em>String</em> mode. It decodes the input
232: * string to an output string that is returned.
233: *
234: * @return String exception RuntimeException If the object wasn't
235: * constructed to decode a String.
236: */
237:
238: public final String processString() {
239: if (!stringp) {
240: throw new RuntimeException(this .getClass().getName()
241: + "[processString]" + "invalid call (not a String)");
242: }
243: try {
244: process();
245: } catch (IOException e) {
246: e.printStackTrace();
247: } catch (Exception ex) {
248: ex.printStackTrace();
249: }
250: String s;
251: try {
252: s = ((ByteArrayOutputStream) out).toString(encoding);
253: } catch (Exception ex) {
254: throw new RuntimeException(this .getClass().getName()
255: + "[processString] Unable to convert"
256: + "properly char to bytes");
257: }
258: return s;
259: }
260:
261: /**
262: * Constructor for Base64Decoder.
263: *
264: * @param input
265: * String
266: */
267: public Base64Decoder(final String input) {
268: this (input, null);
269: }
270:
271: /**
272: * Create a decoder to decode a String.
273: *
274: * @param input
275: * The string to be decoded.
276: * @param sEncoding
277: * String
278: */
279:
280: public Base64Decoder(final String input, final String sEncoding) {
281: byte[] bytes;
282: String myEncoding = sEncoding;
283: if (myEncoding == null) {
284: myEncoding = "ISO-8859-1";
285: }
286:
287: try {
288: bytes = input.getBytes(encoding);
289: } catch (UnsupportedEncodingException ex) {
290: throw new RuntimeException(this .getClass().getName()
291: + "[Constructor] Unable to convert"
292: + "properly char to bytes");
293: }
294: this .stringp = true;
295: this .in = new ByteArrayInputStream(bytes);
296: this .encoding = myEncoding;
297:
298: this .out = new ByteArrayOutputStream();
299: }
300:
301: /**
302: * Create a decoder to decode a stream.
303: *
304: * @param isIn
305: * The input stream (to be decoded).
306: * @param isOut
307: * The output stream, to write decoded data to.
308: */
309:
310: public Base64Decoder(final InputStream isIn,
311: final OutputStream isOut) {
312: this .in = isIn;
313: this .out = isOut;
314: this .stringp = false;
315: }
316:
317: /**
318: * Test the decoder. Run it with one argument: the string to be decoded, it
319: * will print out the decoded value.
320: *
321: * @param args
322: * String[]
323: */
324:
325: public static void main(final String[] args) {
326: if (args.length == 1) {
327: try {
328: Base64Decoder b = new Base64Decoder(args[0]);
329: System.out.println("[" + b.processString() + "]");
330: } catch (RuntimeException e) {
331: System.out.println("Invalid Base64 format !");
332: System.exit(1);
333: }
334: } else if ((args.length == 2) && (args[0].equals("-f"))) {
335: try {
336: FileInputStream in = new FileInputStream(args[1]);
337: Base64Decoder b = new Base64Decoder(in, System.out);
338: b.process();
339: } catch (Exception ex) {
340: System.out.println("error: " + ex.getMessage());
341: System.exit(1);
342: }
343: } else {
344: System.out.println("Base64Decoder [strong] [-f file]");
345: }
346: System.exit(0);
347: }
348: }
|