001: /*
002: * @(#)BASE64Decoder.java 1.25 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027: package sun.misc;
028:
029: import java.io.OutputStream;
030: import java.io.InputStream;
031: import java.io.PrintStream;
032:
033: /**
034: * This class implements a BASE64 Character decoder as specified in RFC1521.
035: *
036: * This RFC is part of the MIME specification which is published by the
037: * Internet Engineering Task Force (IETF). Unlike some other encoding
038: * schemes there is nothing in this encoding that tells the decoder
039: * where a buffer starts or stops, so to use it you will need to isolate
040: * your encoded data into a single chunk and then feed them this decoder.
041: * The simplest way to do that is to read all of the encoded data into a
042: * string and then use:
043: * <pre>
044: * byte mydata[];
045: * BASE64Decoder base64 = new BASE64Decoder();
046: *
047: * mydata = base64.decodeBuffer(bufferString);
048: * </pre>
049: * This will decode the String in <i>bufferString</i> and give you an array
050: * of bytes in the array <i>myData</i>.
051: *
052: * On errors, this class throws a CEFormatException with the following detail
053: * strings:
054: * <pre>
055: * "BASE64Decoder: Not enough bytes for an atom."
056: * </pre>
057: *
058: * @version 1.19, 02/02/00
059: * @author Chuck McManis
060: * @see CharacterEncoder
061: * @see BASE64Decoder
062: */
063:
064: public class BASE64Decoder extends CharacterDecoder {
065:
066: /** This class has 4 bytes per atom */
067: protected int bytesPerAtom() {
068: return (4);
069: }
070:
071: /** Any multiple of 4 will do, 72 might be common */
072: protected int bytesPerLine() {
073: return (72);
074: }
075:
076: /**
077: * This character array provides the character to value map
078: * based on RFC1521.
079: */
080: private final static char pem_array[] = {
081: // 0 1 2 3 4 5 6 7
082: 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
083: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
084: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
085: 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
086: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
087: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
088: 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
089: '4', '5', '6', '7', '8', '9', '+', '/' // 7
090: };
091:
092: private final static byte pem_convert_array[] = new byte[256];
093:
094: static {
095: for (int i = 0; i < 255; i++) {
096: pem_convert_array[i] = -1;
097: }
098: for (int i = 0; i < pem_array.length; i++) {
099: pem_convert_array[pem_array[i]] = (byte) i;
100: }
101: }
102:
103: byte decode_buffer[] = new byte[4];
104:
105: /**
106: * Decode one BASE64 atom into 1, 2, or 3 bytes of data.
107: */
108: protected void decodeAtom(InputStream inStream,
109: OutputStream outStream, int rem) throws java.io.IOException {
110: int i;
111: byte a = -1, b = -1, c = -1, d = -1;
112:
113: if (rem < 2) {
114: throw new CEFormatException(
115: "BASE64Decoder: Not enough bytes for an atom.");
116: }
117: do {
118: i = inStream.read();
119: if (i == -1) {
120: throw new CEStreamExhausted();
121: }
122: } while (i == '\n' || i == '\r');
123: decode_buffer[0] = (byte) i;
124:
125: i = readFully(inStream, decode_buffer, 1, rem - 1);
126: if (i == -1) {
127: throw new CEStreamExhausted();
128: }
129:
130: if (rem > 3 && decode_buffer[3] == '=') {
131: rem = 3;
132: }
133: if (rem > 2 && decode_buffer[2] == '=') {
134: rem = 2;
135: }
136: switch (rem) {
137: case 4:
138: d = pem_convert_array[decode_buffer[3] & 0xff];
139: // NOBREAK
140: case 3:
141: c = pem_convert_array[decode_buffer[2] & 0xff];
142: // NOBREAK
143: case 2:
144: b = pem_convert_array[decode_buffer[1] & 0xff];
145: a = pem_convert_array[decode_buffer[0] & 0xff];
146: break;
147: }
148:
149: switch (rem) {
150: case 2:
151: outStream
152: .write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)));
153: break;
154: case 3:
155: outStream
156: .write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)));
157: outStream
158: .write((byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)));
159: break;
160: case 4:
161: outStream
162: .write((byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)));
163: outStream
164: .write((byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)));
165: outStream.write((byte) (((c << 6) & 0xc0) | (d & 0x3f)));
166: break;
167: }
168: return;
169: }
170: }
|