Source code: org/mrbook/mrpostman/another/Base64.java
1 /*
2 * -*- mode: java; c-basic-indent: 4; indent-tabs-mode: nil -*-
3 * :indentSize=4:noTabs=true:tabSize=4:indentOnTab=true:indentOnEnter=true:mode=java:
4 * ex: set tabstop=4 expandtab:
5 *
6 * MrPostman - webmail <-> email gateway
7 * Copyright (C) 2002-2003 MrPostman Development Group
8 * Projectpage: http://mrbook.org/mrpostman/
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 * In particular, this implies that users are responsible for
21 * using MrPostman after reading the terms and conditions given
22 * by their web-mail provider.
23 *
24 * You should have received a copy of the GNU General Public License
25 * Named LICENSE in the base directory of this distribution,
26 * if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30 package org.mrbook.mrpostman.another;
31
32 import java.util.logging.Level;
33
34 /**
35 * Encodes and decodes to and from Base64 notation.
36 *
37 * <p>
38 * Change Log:
39 * </p>
40 * <ul>
41 * <li>v1.4 - Added helper methods to read/write files.</li>
42 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
43 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
44 * where last buffer being read, if not completely full, was not returned.</li>
45 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
46 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
47 * </ul>
48 *
49 * <p>
50 * I am placing this code in the Public Domain. Do with it as you will.
51 * This software comes with no guarantees or warranties but with
52 * plenty of well-wishing instead!
53 * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
54 * periodically to check for updates or to contribute improvements.
55 * </p>
56 *
57 * @author Robert Harder
58 * @author rob@iharder.net
59 * @version 1.4
60 */
61 import java.util.logging.Logger;
62
63
64 public class Base64 {
65 public static final String CVSID = "$Id: Base64.java,v 1.7 2003/02/09 23:38:13 lbruand Exp $";
66 private static Logger logger = Logger.getLogger("org.mrbook.mrpostman.another.Base64");
67
68 /** Specify encoding (value is <tt>true</tt>). */
69 public static final boolean ENCODE = true;
70
71 /** Specify decoding (value is <tt>false</tt>). */
72 public static final boolean DECODE = false;
73
74 /** Maximum line length (76) of Base64 output. */
75 private static final int MAX_LINE_LENGTH = 76;
76
77 /** The equals sign (=) as a byte. */
78 private static final byte EQUALS_SIGN = (byte) '=';
79
80 /** The new line character (\n) as a byte. */
81 private static final byte NEW_LINE = (byte) '\n';
82
83 /** The 64 valid Base64 values. */
84 private static final byte[] ALPHABET = {
85 (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I',
86 (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
87 (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
88 (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
89 (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
90 (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
91 (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+',
92 (byte) '/'
93 };
94
95 /**
96 * Translates a Base64 value to either its 6-bit reconstruction value
97 * or a negative number indicating some other meaning.
98 **/
99 private static final byte[] DECODABET = {
100 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
101 -5, -5, // Whitespace: Tab and Linefeed
102 -9, -9, // Decimal 11 - 12
103 -5, // Whitespace: Carriage Return
104 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
105 -9, -9, -9, -9, -9, // Decimal 27 - 31
106 -5, // Whitespace: Space
107 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
108 62, // Plus sign at decimal 43
109 -9, -9, -9, // Decimal 44 - 46
110 63, // Slash at decimal 47
111 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
112 -9, -9, -9, // Decimal 58 - 60
113 -1, // Equals sign at decimal 61
114 -9, -9, -9, // Decimal 62 - 64
115 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
116 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
117 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
118 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
119 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
120 -9, -9, -9, -9 // Decimal 123 - 126
121 };
122 private static final byte BAD_ENCODING = -9; // Indicates error in encoding
123 private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
124 private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
125
126 /** Defeats instantiation. */
127 private Base64() {
128 }
129
130 /**
131 * Testing. Feel free--in fact I encourage you--to throw out
132 * this entire "main" method when you actually deploy this code.
133 */
134 public static void main(String[] args) {
135 try {
136 // Test encoding/decoding byte arrays
137 {
138 byte[] bytes1 = {(byte) 2, (byte) 2, (byte) 3, (byte) 0, (byte) 9}; // My zip code
139 byte[] bytes2 = {(byte) 99, (byte) 2, (byte) 2, (byte) 3, (byte) 0, (byte) 9};
140 System.out.println("Bytes 2,2,3,0,9 as Base64: " + encodeBytes(bytes1));
141 System.out.println("Bytes 2,2,3,0,9 w/ offset: " + encodeBytes(bytes2, 1, bytes2.length - 1));
142 byte[] dbytes = decode(encodeBytes(bytes1));
143 System.out.print(encodeBytes(bytes1) + " decoded: ");
144
145 for (int i = 0; i < dbytes.length; i++) {
146 System.out.print(dbytes[i] + ((i < (dbytes.length - 1)) ? "," : "\n"));
147 }
148 }
149
150 // end testing byte arrays
151 // Test Input Stream
152 {
153 // Read GIF stored in base64 form.
154 java.io.FileInputStream fis = null; //new java.io.FileInputStream( "test.gif.b64" );
155 Base64.InputStream b64is = null; //new Base64.InputStream( fis, DECODE );
156
157 byte[] bytes = new byte[0];
158 int b = -1;
159 /*
160 while( (b = b64is.read()) >= 0 ){
161 byte[] temp = new byte[ bytes.length + 1 ];
162 System.arraycopy( bytes,0, temp,0,bytes.length );
163 temp[bytes.length] = (byte)b;
164 bytes = temp;
165 } // end while: terribly inefficient way to read data
166 b64is.close();
167 */
168 bytes = Base64.readFile("test.gif.b64", false);
169 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon(bytes);
170 javax.swing.JLabel jlabel = new javax.swing.JLabel("Read from test.gif.b64", iicon, 0);
171 javax.swing.JFrame jframe = new javax.swing.JFrame();
172 jframe.getContentPane().add(jlabel);
173 jframe.pack();
174 jframe.show();
175
176 // Write raw bytes to file
177 java.io.FileOutputStream fos = new java.io.FileOutputStream("test.gif_out");
178 fos.write(bytes);
179 fos.close();
180
181 // Read raw bytes and encode
182 fis = new java.io.FileInputStream("test.gif_out");
183 b64is = new Base64.InputStream(fis, ENCODE);
184 byte[] ebytes = new byte[0];
185 b = -1;
186
187 while ((b = b64is.read()) >= 0) {
188 byte[] temp = new byte[ebytes.length + 1];
189 System.arraycopy(ebytes, 0, temp, 0, ebytes.length);
190 temp[ebytes.length] = (byte) b;
191 ebytes = temp;
192 }
193
194 // end while: terribly inefficient way to read data
195 b64is.close();
196 String s = new String(ebytes);
197 javax.swing.JTextArea jta = new javax.swing.JTextArea(s);
198 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane(jta);
199 jframe = new javax.swing.JFrame();
200 jframe.setTitle("Read from test.gif_out");
201 jframe.getContentPane().add(jsp);
202 jframe.pack();
203 jframe.show();
204
205 // Write encoded bytes to file
206 fos = new java.io.FileOutputStream("test.gif.b64_out");
207 fos.write(ebytes);
208
209 // Read GIF stored in base64 form.
210 fis = new java.io.FileInputStream("test.gif.b64_out");
211 b64is = new Base64.InputStream(fis, DECODE);
212 byte[] edbytes = new byte[0];
213 b = -1;
214
215 while ((b = b64is.read()) >= 0) {
216 byte[] temp = new byte[edbytes.length + 1];
217 System.arraycopy(edbytes, 0, temp, 0, edbytes.length);
218 temp[edbytes.length] = (byte) b;
219 edbytes = temp;
220 }
221
222 // end while: terribly inefficient way to read data
223 b64is.close();
224 iicon = new javax.swing.ImageIcon(edbytes);
225 jlabel = new javax.swing.JLabel("Read from test.gif.b64_out", iicon, 0);
226 jframe = new javax.swing.JFrame();
227 jframe.getContentPane().add(jlabel);
228 jframe.pack();
229 jframe.show();
230 }
231
232 // end: Test Input Stream
233 // Test Output Stream
234 {
235 // Read raw bytes
236 java.io.FileInputStream fis = new java.io.FileInputStream("test.gif_out");
237 byte[] rbytes = new byte[0];
238 int b = -1;
239
240 while ((b = fis.read()) >= 0) {
241 byte[] temp = new byte[rbytes.length + 1];
242 System.arraycopy(rbytes, 0, temp, 0, rbytes.length);
243 temp[rbytes.length] = (byte) b;
244 rbytes = temp;
245 }
246
247 // end while: terribly inefficient way to read data
248 fis.close();
249
250 // Write raw bytes to encoded file
251 java.io.FileOutputStream fos = null; //new java.io.FileOutputStream("test.gif.b64_out2");
252 Base64.OutputStream b64os = null; //new Base64.OutputStream( fos, ENCODE );
253
254 Base64.writeFile(rbytes, "test.gif.b64", ENCODE);
255
256 // Read raw bytes that are actually encoded (but we'll ignore that)
257 fis = new java.io.FileInputStream("test.gif.b64_out2");
258 byte[] rebytes = new byte[0];
259 b = -1;
260
261 while ((b = fis.read()) >= 0) {
262 byte[] temp = new byte[rebytes.length + 1];
263 System.arraycopy(rebytes, 0, temp, 0, rebytes.length);
264 temp[rebytes.length] = (byte) b;
265 rebytes = temp;
266 }
267
268 // end while: terribly inefficient way to read data
269 fis.close();
270 String s = new String(rebytes);
271 javax.swing.JTextArea jta = new javax.swing.JTextArea(s);
272 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane(jta);
273 javax.swing.JFrame jframe = new javax.swing.JFrame();
274 jframe.setTitle("Read from test.gif.b64_out2");
275 jframe.getContentPane().add(jsp);
276 jframe.pack();
277 jframe.show();
278
279 // Write encoded bytes to decoded raw file
280 fos = new java.io.FileOutputStream("test.gif_out2");
281 b64os = new Base64.OutputStream(fos, DECODE);
282 b64os.write(rebytes);
283 b64os.close();
284 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon("test.gif_out2");
285 javax.swing.JLabel jlabel = new javax.swing.JLabel("Read from test.gif_out2", iicon, 0);
286 jframe = new javax.swing.JFrame();
287 jframe.getContentPane().add(jlabel);
288 jframe.pack();
289 jframe.show();
290 }
291
292 // end: Test Output Stream
293 } catch (Exception e) {
294 logger.log(Level.SEVERE, "should not happen", e);
295 }
296 }
297
298 // end main
299
300 /* ******** E N C O D I N G M E T H O D S ******** */
301
302 /**
303 * Encodes the first three bytes of array <var>threeBytes</var>
304 * and returns a four-byte array in Base64 notation.
305 *
306 * @param threeBytes the array to convert
307 * @return four byte array in Base64 notation.
308 * @since 1.3
309 */
310 private static byte[] encode3to4(byte[] threeBytes) {
311 return encode3to4(threeBytes, 3);
312 }
313
314 // end encodeToBytes
315
316 /**
317 * Encodes up to the first three bytes of array <var>threeBytes</var>
318 * and returns a four-byte array in Base64 notation.
319 * The actual number of significant bytes in your array is
320 * given by <var>numSigBytes</var>.
321 * The array <var>threeBytes</var> needs only be as big as
322 * <var>numSigBytes</var>.
323 *
324 * @param threeBytes the array to convert
325 * @param numSigBytes the number of significant bytes in your array
326 * @return four byte array in Base64 notation.
327 * @since 1.3
328 */
329 private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
330 byte[] dest = new byte[4];
331 encode3to4(threeBytes, 0, numSigBytes, dest, 0);
332 return dest;
333 }
334
335 /**
336 * Encodes up to three bytes of the array <var>source</var>
337 * and writes the resulting four Base64 bytes to <var>destination</var>.
338 * The source and destination arrays can be manipulated
339 * anywhere along their length by specifying
340 * <var>srcOffset</var> and <var>destOffset</var>.
341 * This method does not check to make sure your arrays
342 * are large enough to accomodate <var>srcOffset</var> + 3 for
343 * the <var>source</var> array or <var>destOffset</var> + 4 for
344 * the <var>destination</var> array.
345 * The actual number of significant bytes in your array is
346 * given by <var>numSigBytes</var>.
347 *
348 * @param source the array to convert
349 * @param srcOffset the index where conversion begins
350 * @param numSigBytes the number of significant bytes in your array
351 * @param destination the array to hold the conversion
352 * @param destOffset the index where output will be put
353 * @return the <var>destination</var> array
354 * @since 1.3
355 */
356 private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) {
357 // 1 2 3
358 // 01234567890123456789012345678901 Bit position
359 // --------000000001111111122222222 Array position from threeBytes
360 // --------| || || || | Six bit groups to index ALPHABET
361 // >>18 >>12 >> 6 >> 0 Right shift necessary
362 // 0x3f 0x3f 0x3f Additional AND
363 // Create buffer with zero-padding if there are only one or two
364 // significant bytes passed in the array.
365 // We have to shift left 24 in order to flush out the 1's that appear
366 // when Java treats a value as negative that is cast from a byte to an int.
367 int inBuff = ((numSigBytes > 0) ? ((source[srcOffset] << 24) >>> 8) : 0)
368 | ((numSigBytes > 1) ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
369 | ((numSigBytes > 2) ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
370
371 switch (numSigBytes) {
372 case 3:
373 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
374 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
375 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
376 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
377 return destination;
378 case 2:
379 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
380 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
381 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
382 destination[destOffset + 3] = EQUALS_SIGN;
383 return destination;
384 case 1:
385 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
386 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
387 destination[destOffset + 2] = EQUALS_SIGN;
388 destination[destOffset + 3] = EQUALS_SIGN;
389 return destination;
390 default:
391 return destination;
392 } // end switch
393 }
394
395 // end encode3to4
396
397 /**
398 * Serializes an object and returns the Base64-encoded
399 * version of that serialized object. If the object
400 * cannot be serialized or there is another error,
401 * the method will return <tt>null</tt>.
402 *
403 * @param serializableObject The object to encode
404 * @return The Base64-encoded object
405 * @since 1.4
406 */
407 public static String encodeObject(java.io.Serializable serializableObject) {
408 return encodeObject(serializableObject, true);
409 }
410
411 // end encodeObject
412
413 /**
414 * Serializes an object and returns the Base64-encoded
415 * version of that serialized object. If the object
416 * cannot be serialized or there is another error,
417 * the method will return <tt>null</tt>.
418 *
419 * @param serializableObject The object to encode
420 * @param breakLines Break lines at 80 characters or less.
421 * @return The Base64-encoded object
422 * @since 1.4
423 */
424 public static String encodeObject(java.io.Serializable serializableObject, boolean breakLines) {
425 java.io.ByteArrayOutputStream baos = null;
426 java.io.OutputStream b64os = null;
427 java.io.ObjectOutputStream oos = null;
428
429 try {
430 baos = new java.io.ByteArrayOutputStream();
431 b64os = new Base64.OutputStream(baos, Base64.ENCODE, breakLines);
432 oos = new java.io.ObjectOutputStream(b64os);
433
434 oos.writeObject(serializableObject);
435 } catch (java.io.IOException e) {
436 logger.log(Level.SEVERE, "should not happen", e);
437 return null;
438 } finally {
439 try {
440 oos.close();
441 } catch (Exception e) {
442 logger.log(Level.SEVERE, "should not happen", e);
443 }
444
445 try {
446 b64os.close();
447 } catch (Exception e) {
448 logger.log(Level.SEVERE, "should not happen", e);
449 }
450
451 try {
452 baos.close();
453 } catch (Exception e) {
454 logger.log(Level.SEVERE, "should not happen", e);
455 }
456 }
457
458 // end finally
459 return new String(baos.toByteArray());
460 }
461
462 // end encode
463
464 /**
465 * Encodes a byte array into Base64 notation.
466 * Equivalen to calling
467 * <code>encodeBytes( source, 0, source.length )</code>
468 *
469 * @param source The data to convert
470 * @since 1.4
471 */
472 public static String encodeBytes(byte[] source) {
473 return encodeBytes(source, true);
474 }
475
476 // end encodeBytes
477
478 /**
479 * Encodes a byte array into Base64 notation.
480 * Equivalen to calling
481 * <code>encodeBytes( source, 0, source.length )</code>
482 *
483 * @param source The data to convert
484 * @param breakLines Break lines at 80 characters or less.
485 * @since 1.4
486 */
487 public static String encodeBytes(byte[] source, boolean breakLines) {
488 return encodeBytes(source, 0, source.length, breakLines);
489 }
490
491 // end encodeBytes
492
493 /**
494 * Encodes a byte array into Base64 notation.
495 *
496 * @param source The data to convert
497 * @param off Offset in array where conversion should begin
498 * @param len Length of data to convert
499 * @since 1.4
500 */
501 public static String encodeBytes(byte[] source, int off, int len) {
502 return encodeBytes(source, off, len, true);
503 }
504
505 // end encodeBytes
506
507 /**
508 * Encodes a byte array into Base64 notation.
509 *
510 * @param source The data to convert
511 * @param off Offset in array where conversion should begin
512 * @param len Length of data to convert
513 * @param breakLines Break lines at 80 characters or less.
514 * @since 1.4
515 */
516 public static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
517 int len43 = (len * 4) / 3;
518 byte[] outBuff = new byte[(len43) // Main 4:3
519 + (((len % 3) > 0) ? 4 : 0) // Account for padding
520 + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
521 int d = 0;
522 int e = 0;
523 int len2 = len - 2;
524 int lineLength = 0;
525
526 for (; d < len2; d += 3, e += 4) {
527 encode3to4(source, d + off, 3, outBuff, e);
528
529 lineLength += 4;
530
531 if (breakLines && (lineLength == MAX_LINE_LENGTH)) {
532 outBuff[e + 4] = NEW_LINE;
533 e++;
534 lineLength = 0;
535 }
536
537 // end if: end of line
538 }
539
540 // en dfor: each piece of array
541 if (d < len) {
542 encode3to4(source, d + off, len - d, outBuff, e);
543 e += 4;
544 }
545
546 // end if: some padding needed
547 return new String(outBuff, 0, e);
548 }
549
550 // end encodeBytes
551
552 /**
553 * Encodes a string in Base64 notation with line breaks
554 * after every 75 Base64 characters.
555 * Of course you probably only need to encode a string
556 * if there are non-ASCII characters in it such as many
557 * non-English languages.
558 *
559 * @param s the string to encode
560 * @return the encoded string
561 * @since 1.3
562 */
563 public static String encodeString(String s) {
564 return encodeString(s, true);
565 }
566
567 // end encodeString
568
569 /**
570 * Encodes a string in Base64 notation with line breaks
571 * after every 75 Base64 characters.
572 * Of course you probably only need to encode a string
573 * if there are non-ASCII characters in it such as many
574 * non-English languages.
575 *
576 * @param s the string to encode
577 * @param breakLines Break lines at 80 characters or less.
578 * @return the encoded string
579 * @since 1.3
580 */
581 public static String encodeString(String s, boolean breakLines) {
582 return encodeBytes(s.getBytes(), breakLines);
583 }
584
585 // end encodeString
586
587 /**
588 * Reads a file and either encodes or decodes it.
589 *
590 * @param file The file to read
591 * @param encode Whether or not to encode file as it is read.
592 * @return The encoded/decoded file or <tt>null</tt> if there was an error.
593 * @since 1.4
594 */
595 public static byte[] readFile(String file, boolean encode) {
596 return readFile(new java.io.File(file), encode);
597 }
598
599 // end readFile
600
601 /**
602 * Reads a file and either encodes or decodes it.
603 *
604 * @param file The file to read
605 * @param encode Whether or not to encode file as it is read.
606 * @return The encoded/decoded file or <tt>null</tt> if there was an error.
607 * @since 1.4
608 */
609 public static byte[] readFile(java.io.File file, boolean encode) {
610 byte[] data = new byte[100];
611 byte[] returnValue = null;
612 int nextIndex = 0;
613 int b = -1;
614 Base64.InputStream bis = null;
615
616 try {
617 bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), encode);
618
619 while ((b = bis.read()) >= 0) {
620 // Resize array?
621 if (nextIndex >= data.length) {
622 byte[] temp = new byte[data.length << 1];
623 System.arraycopy(data, 0, temp, 0, data.length);
624 data = temp;
625 }
626
627 // end if: resize array
628 data[nextIndex++] = (byte) b;
629 }
630
631 // end while: each byte
632 returnValue = new byte[nextIndex];
633 System.arraycopy(data, 0, returnValue, 0, nextIndex);
634 } catch (java.io.IOException e) {
635 returnValue = null;
636 } finally {
637 try {
638 bis.close();
639 } catch (Exception e) {
640 logger.log(Level.SEVERE, "should not happen", e);
641 }
642 }
643
644 // end finally
645 return returnValue;
646 }
647
648 // end readFile
649
650 /**
651 * Writes a byte array to a file either encoding
652 * it or decoding it as specified.
653 *
654 * @param data The array to write to a file.
655 * @param file The file to write to.
656 * @param encode Whether or not to encode the data.
657 * @return Whether or not the write was a success.
658 * @since 1.4
659 */
660 public static boolean writeFile(byte[] data, String file, boolean encode) {
661 return writeFile(data, 0, data.length, new java.io.File(file), encode);
662 }
663
664 // end writeFile
665
666 /**
667 * Writes a byte array to a file either encoding
668 * it or decoding it as specified.
669 *
670 * @param data The array to write to a file.
671 * @param file The file to write to.
672 * @param encode Whether or not to encode the data.
673 * @return Whether or not the write was a success.
674 * @since 1.4
675 */
676 public static boolean writeFile(byte[] data, java.io.File file, boolean encode) {
677 return writeFile(data, 0, data.length, file, encode);
678 }
679
680 // end writeFile
681
682 /**
683 * Writes a byte array to a file either encoding
684 * it or decoding it as specified.
685 *
686 * @param data The array to write to a file.
687 * @param offset The offset where the "real" data begins.
688 * @param length The amount of data to write.
689 * @param file The file to write to.
690 * @param encode Whether or not to encode the data.
691 * @return Whether or not the write was a success.
692 * @since 1.4
693 */
694 public static boolean writeFile(byte[] data, int offset, int length, java.io.File file, boolean encode) {
695 Base64.OutputStream bos = null;
696 boolean success = false;
697
698 try {
699 bos = new Base64.OutputStream(new java.io.BufferedOutputStream(new java.io.FileOutputStream(file)), encode);
700
701 bos.write(data, offset, length);
702 success = true;
703 } catch (java.io.IOException e) {
704 success = false;
705 } finally {
706 try {
707 bos.close();
708 } catch (Exception e) {
709 logger.log(Level.SEVERE, "should not happen", e);
710 }
711 }
712
713 // end finally
714 return success;
715 }
716
717 // end writeFile
718
719 /**
720 * Simple helper method that Base64-encodes a file
721 * and returns the encoded string or <tt>null</tt>
722 * if there was an error.
723 *
724 * @param rawfile The file to read
725 * @return The encoded file or <tt>null</tt> if there was an error.
726 * @since 1.4
727 */
728 public static String encodeFromFile(String rawfile) {
729 byte[] ebytes = readFile(rawfile, ENCODE);
730
731 return (ebytes == null) ? null : new String(ebytes);
732 }
733
734 // end encodeFromFile
735
736 /**
737 * Simple helper method that Base64-decodes a file
738 * and returns the decoded data or <tt>null</tt>
739 * if there was an error.
740 *
741 * @param encfile The file to read
742 * @return The decoded file or <tt>null</tt> if there was an error.
743 * @since 1.4
744 */
745 public static byte[] decodeFromFile(String encfile) {
746 return readFile(encfile, DECODE);
747 }
748
749 // end encodeFromFile
750
751 /**
752 * Simple helper method that Base64-encodes data to a file.
753 *
754 * @param rawdata The data to write.
755 * @param file The file to read.
756 * @return Whether or not the write was a success.
757 * @since 1.4
758 */
759 public static boolean encodeToFile(byte[] rawdata, String file) {
760 return writeFile(rawdata, file, ENCODE);
761 }
762
763 // end encodeFromFile
764
765 /**
766 * Simple helper method that Base64-decodes data to a file.
767 *
768 * @param encdata The data to write.
769 * @param file The file to read.
770 * @return Whether or not the write was a success.
771 * @since 1.4
772 */
773 public static boolean decodeToFile(byte[] encdata, String file) {
774 return writeFile(encdata, file, DECODE);
775 }
776
777 // end encodeFromFile
778
779 /* ******** D E C O D I N G M E T H O D S ******** */
780
781 /**
782 * Decodes the first four bytes of array <var>fourBytes</var>
783 * and returns an array up to three bytes long with the
784 * decoded values.
785 *
786 * @param fourBytes the array with Base64 content
787 * @return array with decoded values
788 * @since 1.3
789 */
790 private static byte[] decode4to3(byte[] fourBytes) {
791 byte[] outBuff1 = new byte[3];
792 int count = decode4to3(fourBytes, 0, outBuff1, 0);
793 byte[] outBuff2 = new byte[count];
794
795 for (int i = 0; i < count; i++) {
796 outBuff2[i] = outBuff1[i];
797 }
798
799 return outBuff2;
800 }
801
802 /**
803 * Decodes four bytes from array <var>source</var>
804 * and writes the resulting bytes (up to three of them)
805 * to <var>destination</var>.
806 * The source and destination arrays can be manipulated
807 * anywhere along their length by specifying
808 * <var>srcOffset</var> and <var>destOffset</var>.
809 * This method does not check to make sure your arrays
810 * are large enough to accomodate <var>srcOffset</var> + 4 for
811 * the <var>source</var> array or <var>destOffset</var> + 3 for
812 * the <var>destination</var> array.
813 * This method returns the actual number of bytes that
814 * were converted from the Base64 encoding.
815 *
816 *
817 * @param source the array to convert
818 * @param srcOffset the index where conversion begins
819 * @param destination the array to hold the conversion
820 * @param destOffset the index where output will be put
821 * @return the number of decoded bytes converted
822 * @since 1.3
823 */
824 private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
825 // Example: Dk==
826 if (source[srcOffset + 2] == EQUALS_SIGN) {
827 // Two ways to do the same thing. Don't know which way I like best.
828 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
829 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
830 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
831 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
832
833 destination[destOffset] = (byte) (outBuff >>> 16);
834 return 1;
835 } else if (source[srcOffset + 3] == EQUALS_SIGN) { // Example: DkL=
836 // Two ways to do the same thing. Don't know which way I like best.
837 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
838 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
839 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
840 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
841 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
842
843 destination[destOffset] = (byte) (outBuff >>> 16);
844 destination[destOffset + 1] = (byte) (outBuff >>> 8);
845 return 2;
846 } else { // Example: DkLE
847 try {
848 // Two ways to do the same thing. Don't know which way I like best.
849 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
850 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
851 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
852 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
853 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
854 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
855 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | (DECODABET[source[srcOffset + 3]] & 0xFF);
856
857 destination[destOffset] = (byte) (outBuff >> 16);
858 destination[destOffset + 1] = (byte) (outBuff >> 8);
859 destination[destOffset + 2] = (byte) (outBuff);
860
861 return 3;
862 } catch (Exception e) {
863 logger.log(Level.SEVERE, "should not happen", e);
864 logger.fine("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
865 logger.fine("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
866 logger.fine("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
867 logger.fine("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
868 return -1;
869 }
870
871 //e nd catch
872 }
873 }
874
875 // end decodeToBytes
876
877 /**
878 * Decodes data from Base64 notation.
879 *
880 * @param s the string to decode
881 * @return the decoded data
882 * @since 1.4
883 */
884 public static byte[] decode(String s) {
885 byte[] bytes = s.getBytes();
886 return decode(bytes, 0, bytes.length);
887 }
888
889 // end decode
890
891 /**
892 * Decodes data from Base64 notation and
893 * returns it as a string.
894 * Equivlaent to calling
895 * <code>new String( decode( s ) )</code>
896 *
897 * @param s the strind to decode
898 * @return The data as a string
899 * @since 1.4
900 */
901 public static String decodeToString(String s) {
902 return new String(decode(s));
903 }
904
905 // end decodeToString
906
907 /**
908 * Attempts to decode Base64 data and deserialize a Java
909 * Object within. Returns <tt>null if there was an error.
910 *
911 * @param encodedObject The Base64 data to decode
912 * @return The decoded and deserialized object
913 * @since 1.4
914 */
915 public static Object decodeToObject(String encodedObject) {
916 byte[] objBytes = decode(encodedObject);
917
918 java.io.ByteArrayInputStream bais = null;
919 java.io.ObjectInputStream ois = null;
920
921 try {
922 bais = new java.io.ByteArrayInputStream(objBytes);
923 ois = new java.io.ObjectInputStream(bais);
924
925 return ois.readObject();
926 } catch (java.io.IOException e) {
927 logger.log(Level.SEVERE, "should not happen", e);
928 return null;
929 } catch (java.lang.ClassNotFoundException e) {
930 logger.log(Level.SEVERE, "should not happen", e);
931 return null;
932 } finally {
933 try {
934 bais.close();
935 } catch (Exception e) {
936 logger.log(Level.SEVERE, "should not happen", e);
937 }
938
939 try {
940 ois.close();
941 } catch (Exception e) {
942 logger.log(Level.SEVERE, "should not happen", e);
943 }
944 }
945
946 // end finally
947 }
948
949 // end decodeObject
950
951 /**
952 * Decodes Base64 content in byte array format and returns
953 * the decoded byte array.
954 *
955 * @param source The Base64 encoded data
956 * @param off The offset of where to begin decoding
957 * @param len The length of characters to decode
958 * @return decoded data
959 * @since 1.3
960 */
961 public static byte[] decode(byte[] source, int off, int len) {
962 int len34 = (len * 3) / 4;
963 byte[] outBuff = new byte[len34]; // Upper limit on size of output
964 int outBuffPosn = 0;
965
966 byte[] b4 = new byte[4];
967 int b4Posn = 0;
968 int i = 0;
969 byte sbiCrop = 0;
970 byte sbiDecode = 0;
971
972 for (i = 0; i < len; i++) {
973 sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
974 sbiDecode = DECODABET[sbiCrop];
975
976 if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
977 {
978 if (sbiDecode >= EQUALS_SIGN_ENC) {
979 b4[b4Posn++] = sbiCrop;
980
981 if (b4Posn > 3) {
982 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
983 b4Posn = 0;
984
985 // If that was the equals sign, break out of 'for' loop
986 if (sbiCrop == EQUALS_SIGN) {
987 break;
988 }
989 }
990
991 // end if: quartet built
992 }
993
994 // end if: equals sign or better
995 } else { // end if: white space, equals sign or better
996 logger.warning("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
997 return null;
998 }
999
1000 // end else:
1001 }
1002
1003 // each input character
1004 byte[] out = new byte[outBuffPosn];
1005 System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
1006 return out;
1007 }
1008
1009 // end decode
1010 public static class InputStream extends java.io.FilterInputStream {
1011 private boolean encode; // Encoding or decoding
1012 private int position; // Current position in the buffer
1013 private byte[] buffer; // Small buffer holding converted data
1014 private int bufferLength; // Length of buffer (3 or 4)
1015 private int numSigBytes; // Number of meaningful bytes in the buffer
1016 private int lineLength;
1017 private boolean breakLines; // Break lines at less than 80 characters
1018
1019 /**
1020 * Constructs a {@link Base64#InputStream} in DECODE mode.
1021 *
1022 * @param in the {@link java.io.InputStream} from which to read data.
1023 * @since 1.3
1024 */
1025 public InputStream(java.io.InputStream in) {
1026 this(in, Base64.DECODE);
1027 }
1028
1029 // end constructor
1030
1031 /**
1032 * Constructs a {@link Base64#InputStream} in
1033 * either ENCODE or DECODE mode.
1034 *
1035 * @param in the {@link java.io.InputStream} from which to read data.
1036 * @param encode Conversion direction
1037 * @see Base64#ENCODE
1038 * @see Base64#DECODE
1039 * @since 1.3
1040 */
1041 public InputStream(java.io.InputStream in, boolean encode) {
1042 this(in, encode, true);
1043 }
1044
1045 // end constructor
1046
1047 /**
1048 * Constructs a {@link Base64#InputStream} in
1049 * either ENCODE or DECODE mode.
1050 *
1051 * @param in the {@link java.io.InputStream} from which to read data.
1052 * @param encode Conversion direction
1053 * @param breakLines Break lines at less than 80 characters.
1054 * @see Base64#ENCODE
1055 * @see Base64#DECODE
1056 * @since 1.3
1057 */
1058 public InputStream(java.io.InputStream in, boolean encode, boolean breakLines) {
1059 super(in);
1060 this.breakLines = breakLines;
1061 this.encode = encode;
1062 this.bufferLength = encode ? 4 : 3;
1063 this.buffer = new byte[bufferLength];
1064 this.position = -1;
1065 this.lineLength = 0;
1066 }
1067
1068 // end constructor
1069
1070 /**
1071 * Reads enough of the input stream to convert
1072 * to/from Base64 and returns the next byte.
1073 *
1074 * @return next byte
1075 * @since 1.3
1076 */
1077 public int read() throws java.io.IOException {
1078 // Do we need to get data?
1079 if (position < 0) {
1080 if (encode) {
1081 byte[] b3 = new byte[3];
1082 int numBinaryBytes = 0;
1083
1084 for (int i = 0; i < 3; i++) {
1085 try {
1086 int b = in.read();
1087
1088 // If end of stream, b is -1.
1089 if (b >= 0) {
1090 b3[i] = (byte) b;
1091 numBinaryBytes++;
1092 }
1093
1094 // end if: not end of stream
1095 } catch (java.io.IOException e) {
1096 // Only a problem if we got no data at all.
1097 if (i == 0) {
1098 throw e;
1099 }
1100 }
1101
1102 // end catch
1103 }
1104
1105 // end for: each needed input byte
1106 if (numBinaryBytes > 0) {
1107 encode3to4(b3, 0, numBinaryBytes, buffer, 0);
1108 position = 0;
1109 numSigBytes = 4;
1110 } else { // Got data
1111 return -1;
1112 }
1113
1114 // end else
1115 } else {
1116 byte[] b4 = new byte[4];
1117 int i = 0;
1118
1119 for (i = 0; i < 4; i++) {
1120 // Read four "meaningful" bytes:
1121 int b = 0;
1122
1123 do {
1124 b = in.read();
1125 } while ((b >= 0) && (DECODABET[b & 0x7f] <= WHITE_SPACE_ENC));
1126
1127 if (b < 0) {
1128 break; // Reads a -1 if end of stream
1129 }
1130
1131 b4[i] = (byte) b;
1132 }
1133
1134 // end for: each needed input byte
1135 if (i == 4) {
1136 numSigBytes = decode4to3(b4, 0, buffer, 0);
1137 position = 0;
1138 } else if (i == 0) {
1139 return -1;
1140 } else {
1141 // Must have broken out from above.
1142 throw new java.io.IOException("Improperly padded Base64 input.");
1143 }
1144
1145 // end
1146 }
1147
1148 // end else: decode
1149 }
1150
1151 // end else: get data
1152 // Got data?
1153 if (position >= 0) {
1154 // End of relevant data?
1155 if (position >= numSigBytes) { /*!encode &&*/
1156 return -1;
1157 }
1158
1159 if (encode && breakLines && (lineLength >= MAX_LINE_LENGTH)) {
1160 lineLength = 0;
1161 return '\n';
1162 } else {
1163 lineLength++; // This isn't important when decoding
1164 // but throwing an extra "if" seems
1165 // just as wasteful.
1166
1167 int b = buffer[position++];
1168
1169 if (position >= bufferLength) {
1170 position = -1;
1171 }
1172
1173 return b & 0xFF; // This is how you "cast" a byte that's
1174 // intended to be unsigned.
1175 }
1176
1177 // end else
1178 } else {
1179 // When JDK1.4 is more accepted, use an assertion here.
1180 throw new java.io.IOException("Error in Base64 code reading stream.");
1181 }
1182
1183 // end else
1184 }
1185
1186 // end read
1187
1188 /**
1189 * Calls {@link #read} repeatedly until the end of stream
1190 * is reached or <var>len</var> bytes are read.
1191 * Returns number of bytes read into array or -1 if
1192 * end of stream is encountered.
1193 *
1194 * @param dest array to hold values
1195 * @param off offset for array
1196 * @param len max number of bytes to read into array
1197 * @return bytes read into array or -1 if end of stream is encountered.
1198 * @since 1.3
1199 */
1200 public int read(byte[] dest, int off, int len)
1201 throws java.io.IOException {
1202 int i;
1203 int b;
1204
1205 for (i = 0; i < len; i++) {
1206 b = read();
1207
1208 //if( b < 0 && i == 0 )
1209 // return -1;
1210 if (b >= 0) {
1211 dest[off + i] = (byte) b;
1212 } else if (i == 0) {
1213 return -1;
1214 } else {
1215 break; // Out of 'for' loop
1216 }
1217 }
1218
1219 // end for: each byte read
1220 return i;
1221 }
1222 // end read
1223 }
1224
1225 // end inner class InputStream
1226 public static class OutputStream extends java.io.FilterOutputStream {
1227 private boolean encode;
1228 private int position;
1229 private byte[] buffer;
1230 private int bufferLength;
1231 private int lineLength;
1232 private boolean breakLines;
1233
1234 /**
1235 * Constructs a {@link Base64#OutputStream} in ENCODE mode.
1236 *
1237 * @param out the {@link java.io.OutputStream} to which data will be written.
1238 * @since 1.3
1239 */
1240 public OutputStream(java.io.OutputStream out) {
1241 this(out, Base64.ENCODE);
1242 }
1243
1244 // end constructor
1245
1246 /**
1247 * Constructs a {@link Base64#OutputStream} in
1248 * either ENCODE or DECODE mode.
1249 *
1250 * @param out the {@link java.io.OutputStream} to which data will be written.
1251 * @param encode Conversion direction
1252 * @see Base64#ENCODE
1253 * @see Base64#DECODE
1254 * @since 1.3
1255 */
1256 public OutputStream(java.io.OutputStream out, boolean encode) {
1257 this(out, encode, true);
1258 }
1259
1260 // end constructor
1261
1262 /**
1263 * Constructs a {@link Base64#OutputStream} in
1264 * either ENCODE or DECODE mode.
1265 *
1266 * @param out the {@link java.io.OutputStream} to which data will be written.
1267 * @param encode Conversion direction
1268 * @param breakLines Break lines to be less than 80 characters.
1269 * @see Base64#ENCODE
1270 * @see Base64#DECODE
1271 * @since 1.3
1272 */
1273 public OutputStream(java.io.OutputStream out, boolean encode, boolean breakLines) {
1274 super(out);
1275 this.breakLines = breakLines;
1276 this.encode = encode;
1277 this.bufferLength = encode ? 3 : 4;
1278 this.buffer = new byte[bufferLength];
1279 this.position = 0;
1280 this.lineLength = 0;
1281 }
1282
1283 // end constructor
1284
1285 /**
1286 * Writes the byte to the output stream after
1287 * converting to/from Base64 notation.
1288 * When encoding, bytes are buffered three
1289 * at a time before the output stream actually
1290 * gets a write() call.
1291 * When decoding, bytes are buffered four
1292 * at a time.
1293 *
1294 * @param theByte the byte to write
1295 * @since 1.3
1296 */
1297 public void write(int theByte) throws java.io.IOException {
1298 if (encode) {
1299 buffer[position++] = (byte) theByte;
1300
1301 if (position >= bufferLength) { // Enough to encode.
1302 out.write(Base64.encode3to4(buffer, bufferLength));
1303
1304 lineLength += 4;
1305
1306 if (breakLines && (lineLength >= MAX_LINE_LENGTH)) {
1307 out.write(NEW_LINE);
1308 lineLength = 0;
1309 }
1310
1311 // end if: end of line
1312 position = 0;
1313 }
1314
1315 // end if: enough to output
1316 } else {
1317 // Meaningful Base64 character?
1318 if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
1319 buffer[position++] = (byte) theByte;
1320
1321 if (position >= bufferLength) { // Enough to output.
1322
1323 out.write(Base64.decode4to3(buffer));
1324 position = 0;
1325 }
1326
1327 // end if: enough to output
1328 } else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
1329 throw new java.io.IOException("Invalid character in Base64 data.");
1330 }
1331
1332 // end else: not white space either
1333 }
1334
1335 // end else: decoding
1336 }
1337
1338 // end write
1339
1340 /**
1341 * Calls {@link #write} repeatedly until <var>len</var>
1342 * bytes are written.
1343 *
1344 * @param theBytes array from which to read bytes
1345 * @param off offset for array
1346 * @param len max number of bytes to read into array
1347 * @since 1.3
1348 */
1349 public void write(byte[] theBytes, int off, int len)
1350 throws java.io.IOException {
1351 for (int i = 0; i < len; i++) {
1352 write(theBytes[off + i]);
1353 }
1354
1355 // end for: each byte written
1356 }
1357
1358 // end write
1359
1360 /**
1361 * Appropriately pads Base64 notation when encoding
1362 * or throws an exception if Base64 input is not
1363 * properly padded when decoding.
1364 *
1365 * @since 1.3
1366 */
1367 public void flush() throws java.io.IOException {
1368 super.flush();
1369
1370 if (position > 0) {
1371 if (encode) {
1372 out.write(Base64.encode3to4(buffer, position));
1373 position = 0;
1374