001: /*
002: * Base64Decoder.java
003: *
004: * Copyright (C) 2000-2002 Peter Graves
005: * $Id: Base64Decoder.java,v 1.1.1.1 2002/09/24 16:09:56 piso Exp $
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
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j.mail;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.IOException;
026: import java.io.OutputStream;
027: import org.armedbear.j.Debug;
028: import org.armedbear.j.FastStringReader;
029: import org.armedbear.j.Log;
030: import org.armedbear.j.Utilities;
031:
032: public final class Base64Decoder {
033: private final static int BAD = 127;
034: private final static int END = -1;
035:
036: public static boolean decode(String input, OutputStream outputStream)
037: throws IOException {
038: FastStringReader reader = new FastStringReader(input);
039: String extra = null;
040: String s;
041: while ((s = reader.readLine()) != null) {
042: s = s.trim();
043: if (extra != null) {
044: s = extra.concat(s);
045: extra = null;
046: }
047: int length = s.length();
048: if ((length % 4) != 0) {
049: length = (length / 4) * 4;
050: extra = s.substring(length);
051: s = s.substring(0, length);
052: }
053: for (int i = 0; i < length; i += 4) {
054: if (i + 4 < length) {
055: // Full 4-character block.
056: if (!decode(s, i, outputStream))
057: return false;
058: } else {
059: // Last block (may be short).
060: if (!decodeFinal(s, i, outputStream))
061: return false;
062: }
063: }
064: }
065: if (extra != null)
066: return decodeFinal(extra, 0, outputStream);
067: return true;
068: }
069:
070: public static byte[] decode(String s) {
071: try {
072: ByteArrayOutputStream out = new ByteArrayOutputStream(80);
073: int length = s.length();
074: for (int i = 0; i < length; i += 4) {
075: if (i + 4 < length) {
076: // Full 4-character block.
077: if (!decode(s, i, out))
078: return null;
079: } else {
080: // Last block (may be short).
081: if (!decodeFinal(s, i, out))
082: return null;
083: }
084: }
085: return out.toByteArray();
086: } catch (IOException e) {
087: Log.error(e);
088: return null;
089: }
090: }
091:
092: // Decodes a full 4-character block.
093: private static boolean decode(String input, int offset,
094: OutputStream out) throws IOException {
095: // Check for illegal characters.
096: byte byte1 = values[input.charAt(offset)];
097: if (byte1 == BAD) {
098: error(input, offset);
099: return false;
100: }
101: byte byte2 = values[input.charAt(offset + 1)];
102: if (byte2 == BAD) {
103: error(input, offset + 1);
104: return false;
105: }
106: byte byte3 = values[input.charAt(offset + 2)];
107: if (byte3 == BAD) {
108: error(input, offset + 2);
109: return false;
110: }
111: byte byte4 = values[input.charAt(offset + 3)];
112: if (byte4 == BAD) {
113: error(input, offset + 3);
114: return false;
115: }
116: int n = (byte1 << 18) + (byte2 << 12) + (byte3 << 6) + byte4;
117: out.write((byte) (0xff & (n >> 16)));
118: out.write((byte) (0xff & (n >> 8)));
119: out.write((byte) (0xff & n));
120: return true;
121: }
122:
123: // At the end, the input string may have fewer than four characters left.
124: // According to RFC2045: "Because it is used only for padding at the end
125: // of the data, the occurrence of any '=' characters may be taken as
126: // evidence that the end of the data has been reached (without truncation
127: // in transit)."
128: private static boolean decodeFinal(String input, int offset,
129: OutputStream out) throws IOException {
130: byte b0 = 0;
131: byte b1 = 0;
132: byte b2 = 0;
133: byte b3 = 0;
134: int numPaddingChars = 0;
135: Debug.assertTrue(offset < input.length());
136: b0 = values[input.charAt(offset)];
137: if (b0 == END)
138: return true; // OK, end of data.
139: if (b0 == BAD) {
140: error(input, offset);
141: return false;
142: }
143: if (offset + 1 >= input.length()) {
144: Log
145: .error("Base64Decoder.decodeFinal premature end of input");
146: error(input, offset + 1);
147: return false;
148: }
149: b1 = values[input.charAt(offset + 1)];
150: if (b1 == END || b1 == BAD) {
151: // END is not legal here, since then we'd only have 6 bits of
152: // output.
153: error(input, offset + 1);
154: return false;
155: }
156: if (offset + 2 < input.length())
157: b2 = values[input.charAt(offset + 2)];
158: else
159: b2 = END;
160: if (b2 == BAD) {
161: error(input, offset + 2);
162: return false;
163: }
164: if (b2 == END) {
165: b2 = 0;
166: numPaddingChars = 2;
167: } else {
168: // No END yet.
169: if (offset + 3 < input.length())
170: b3 = values[input.charAt(offset + 3)];
171: else
172: b3 = END;
173: if (b3 == BAD) {
174: error(input, offset + 3);
175: return false;
176: }
177: if (b3 == END) {
178: b3 = 0;
179: numPaddingChars = 1;
180: }
181: }
182: int n = (b0 << 18) | (b1 << 12) | (b2 << 6) | b3;
183: out.write((byte) (0xff & (n >> 16)));
184: if (numPaddingChars < 2) {
185: out.write((byte) (0xff & (n >> 8)));
186: if (numPaddingChars == 0)
187: out.write((byte) (0xff & n));
188: }
189: return true;
190: }
191:
192: private static void error(String input, int offset) {
193: Log.error("Base64Decoder error at offset " + offset);
194: Log.error("Base64Decoder input = |" + input + "|");
195: Log.error("Base64Decoder " + Utilities.spaces(offset)
196: + '^');
197: }
198:
199: private static final byte values[] = {
200: BAD,
201: BAD,
202: BAD,
203: BAD,
204: BAD,
205: BAD,
206: BAD,
207: BAD, // 0x00-0x07
208: BAD,
209: BAD,
210: BAD,
211: BAD,
212: BAD,
213: BAD,
214: BAD,
215: BAD, // 0x09-0xff
216: BAD,
217: BAD,
218: BAD,
219: BAD,
220: BAD,
221: BAD,
222: BAD,
223: BAD, // 0x10-0x17
224: BAD,
225: BAD,
226: BAD,
227: BAD,
228: BAD,
229: BAD,
230: BAD,
231: BAD, // 0x18-0x1f
232: BAD,
233: BAD,
234: BAD,
235: BAD,
236: BAD,
237: BAD,
238: BAD,
239: BAD, // 0x20-0x27 !"#$%&'
240: BAD,
241: BAD,
242: BAD,
243: 62,
244: BAD,
245: BAD,
246: BAD,
247: 63, // 0x28-0x2f ()*+,-./
248: 52,
249: 53,
250: 54,
251: 55,
252: 56,
253: 57,
254: 58,
255: 59, // 0x30-0x37 01234567
256: 60,
257: 61,
258: BAD,
259: BAD,
260: BAD,
261: END,
262: BAD,
263: BAD, // 0x38-0x40 89:;<=>?
264: BAD,
265: 0,
266: 1,
267: 2,
268: 3,
269: 4,
270: 5,
271: 6, // 0x41-0x47 @ABCDEFG
272: 7,
273: 8,
274: 9,
275: 10,
276: 11,
277: 12,
278: 13,
279: 14, // 0x48-0x4f HIJKLMNO
280: 15,
281: 16,
282: 17,
283: 18,
284: 19,
285: 20,
286: 21,
287: 22, // 0x50-0x57 PQRSTUVW
288: 23,
289: 24,
290: 25,
291: BAD,
292: BAD,
293: BAD,
294: BAD,
295: BAD, // 0x58-0x5f XYZ[\]^_
296: BAD,
297: 26,
298: 27,
299: 28,
300: 29,
301: 30,
302: 31,
303: 32, // 0x60-0x67 `abcdefg
304: 33,
305: 34,
306: 35,
307: 36,
308: 37,
309: 38,
310: 39,
311: 40, // 0x68-0x6f hijklmno
312: 41,
313: 42,
314: 43,
315: 44,
316: 45,
317: 46,
318: 47,
319: 48, // 0x70-0x77 pqrstuvw
320: 49,
321: 50,
322: 51,
323: BAD,
324: BAD,
325: BAD,
326: BAD,
327: BAD, // 0x78-0x7f xyz{|}~
328: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
329: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
330: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
331: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
332: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
333: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
334: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
335: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
336: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
337: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
338: BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD };
339: }
|