View Javadoc

1   /**
2    * Encodes and decodes to and from Base64 notation.
3    *
4    * <p>
5    * Change Log:
6    * </p>
7    * <ul>
8    *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
9    *   some convenience methods for reading and writing to and from files.</li>
10   *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
11   *   with other encodings (like EBCDIC).</li>
12   *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
13   *   encoded data was a single byte.</li>
14   *  <li>v2.0 - I got rid of methods that used booleans to set options. 
15   *   Now everything is more consolidated and cleaner. The code now detects
16   *   when data that's being decoded is gzip-compressed and will decompress it
17   *   automatically. Generally things are cleaner. You'll probably have to
18   *   change some method calls that you were making to support the new
19   *   options format (<tt>int</tt>s that you "OR" together).</li>
20   *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
21   *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
22   *   Added the ability to "suspend" encoding in the Output Stream so        
23   *   you can turn on and off the encoding if you need to embed base64       
24   *   data in an otherwise "normal" stream (like an XML file).</li>  
25   *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
26   *      This helps when using GZIP streams.
27   *      Added the ability to GZip-compress objects before encoding them.</li>
28   *  <li>v1.4 - Added helper methods to read/write files.</li>
29   *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
30   *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
31   *      where last buffer being read, if not completely full, was not returned.</li>
32   *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
33   *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
34   * </ul>
35   *
36   * <p>
37   * I am placing this code in the Public Domain. Do with it as you will.
38   * This software comes with no guarantees or warranties but with
39   * plenty of well-wishing instead!
40   * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
41   * periodically to check for updates or to contribute improvements.
42   * </p>
43   *
44   * @author Robert Harder
45   * @author rob@iharder.net
46   * @version 2.1
47   */
48  package org.rcfaces.core.internal.util;
49  
50  import org.apache.commons.logging.Log;
51  import org.apache.commons.logging.LogFactory;
52  
53  public class Base64 {
54      private static final String REVISION = "$Revision: 1.3 $";
55  
56      private static final Log LOG = LogFactory.getLog(Base64.class);
57  
58      /* ******** P U B L I C F I E L D S ******** */
59  
60      /** No options specified. Value is zero. */
61      public final static int NO_OPTIONS = 0;
62  
63      /** Specify encoding. */
64      public final static int ENCODE = 1;
65  
66      /** Specify decoding. */
67      public final static int DECODE = 0;
68  
69      /** Don't break lines when encoding (violates strict Base64 specification) */
70      public final static int DONT_BREAK_LINES = 8;
71  
72      /* ******** P R I V A T E F I E L D S ******** */
73  
74      /** Maximum line length (76) of Base64 output. */
75      private final static int MAX_LINE_LENGTH = 76;
76  
77      /** The equals sign (=) as a byte. */
78      private final static byte EQUALS_SIGN = (byte) '=';
79  
80      /** The new line character (\n) as a byte. */
81      private final static byte NEW_LINE = (byte) '\n';
82  
83      /** Preferred encoding. */
84      private final static String PREFERRED_ENCODING = "UTF-8";
85  
86      /** The 64 valid Base64 values. */
87      private final static byte[] ALPHABET;
88  
89      private final static byte[] _NATIVE_ALPHABET = /*
90                                                       * May be something funny
91                                                       * like EBCDIC
92                                                       */
93      { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
94              (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
95              (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
96              (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
97              (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
98              (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
99              (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
100             (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
101             (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
102             (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
103             (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
104             (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
105             (byte) '9', (byte) '+', (byte) '/' };
106 
107     /** Determine which ALPHABET to use. */
108     static {
109         byte[] __bytes;
110         try {
111             __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
112                     .getBytes(PREFERRED_ENCODING);
113         } // end try
114         catch (java.io.UnsupportedEncodingException use) {
115             __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
116         } // end catch
117         ALPHABET = __bytes;
118     } // end static
119 
120     /**
121      * Translates a Base64 value to either its 6-bit reconstruction value or a
122      * negative number indicating some other meaning.
123      */
124     private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9,
125             -9, // Decimal 0 - 8
126             -5, -5, // Whitespace: Tab and Linefeed
127             -9, -9, // Decimal 11 - 12
128             -5, // Whitespace: Carriage Return
129             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
130             // 26
131             -9, -9, -9, -9, -9, // Decimal 27 - 31
132             -5, // Whitespace: Space
133             -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
134             62, // Plus sign at decimal 43
135             -9, -9, -9, // Decimal 44 - 46
136             63, // Slash at decimal 47
137             52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
138             -9, -9, -9, // Decimal 58 - 60
139             -1, // Equals sign at decimal 61
140             -9, -9, -9, // Decimal 62 - 64
141             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
142             // through 'N'
143             14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
144             // through 'Z'
145             -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
146             26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
147             // through 'm'
148             39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
149             // through 'z'
150             -9, -9, -9, -9 // Decimal 123 - 126
151     /*
152      * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
153      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
154      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
155      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
156      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
157      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
158      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
159      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
160      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
161      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
162      */
163     };
164 
165     // I think I end up not using the BAD_ENCODING indicator.
166     // private final static byte BAD_ENCODING = -9; // Indicates error in
167     // encoding
168     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
169 
170     // encoding
171 
172     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
173 
174     // encoding
175 
176     /** Defeats instantiation. */
177     private Base64() {
178     }
179 
180     /* ******** E N C O D I N G M E T H O D S ******** */
181 
182     /**
183      * Encodes up to the first three bytes of array <var>threeBytes</var> and
184      * returns a four-byte array in Base64 notation. The actual number of
185      * significant bytes in your array is given by <var>numSigBytes</var>. The
186      * array <var>threeBytes</var> needs only be as big as <var>numSigBytes</var>.
187      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
188      * 
189      * @param b4
190      *            A reusable byte array to reduce array instantiation
191      * @param threeBytes
192      *            the array to convert
193      * @param numSigBytes
194      *            the number of significant bytes in your array
195      * @return four byte array in Base64 notation.
196      * @since 1.5.1
197      */
198     private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
199             int numSigBytes) {
200         encode3to4(threeBytes, 0, numSigBytes, b4, 0);
201         return b4;
202     } // end encode3to4
203 
204     /**
205      * Encodes up to three bytes of the array <var>source</var> and writes the
206      * resulting four Base64 bytes to <var>destination</var>. The source and
207      * destination arrays can be manipulated anywhere along their length by
208      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
209      * does not check to make sure your arrays are large enough to accomodate
210      * <var>srcOffset</var> + 3 for the <var>source</var> array or
211      * <var>destOffset</var> + 4 for the <var>destination</var> array. The
212      * actual number of significant bytes in your array is given by
213      * <var>numSigBytes</var>.
214      * 
215      * @param source
216      *            the array to convert
217      * @param srcOffset
218      *            the index where conversion begins
219      * @param numSigBytes
220      *            the number of significant bytes in your array
221      * @param destination
222      *            the array to hold the conversion
223      * @param destOffset
224      *            the index where output will be put
225      * @return the <var>destination</var> array
226      * @since 1.3
227      */
228     private static byte[] encode3to4(byte[] source, int srcOffset,
229             int numSigBytes, byte[] destination, int destOffset) {
230         // 1 2 3
231         // 01234567890123456789012345678901 Bit position
232         // --------000000001111111122222222 Array position from threeBytes
233         // --------| || || || | Six bit groups to index ALPHABET
234         // >>18 >>12 >> 6 >> 0 Right shift necessary
235         // 0x3f 0x3f 0x3f Additional AND
236 
237         // Create buffer with zero-padding if there are only one or two
238         // significant bytes passed in the array.
239         // We have to shift left 24 in order to flush out the 1's that appear
240         // when Java treats a value as negative that is cast from a byte to an
241         // int.
242         int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
243                 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
244                 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
245 
246         switch (numSigBytes) {
247         case 3:
248             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
249             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
250             destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
251             destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
252             return destination;
253 
254         case 2:
255             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
256             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
257             destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
258             destination[destOffset + 3] = EQUALS_SIGN;
259             return destination;
260 
261         case 1:
262             destination[destOffset] = ALPHABET[(inBuff >>> 18)];
263             destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
264             destination[destOffset + 2] = EQUALS_SIGN;
265             destination[destOffset + 3] = EQUALS_SIGN;
266             return destination;
267 
268         default:
269             return destination;
270         } // end switch
271     } // end encode3to4
272 
273     /**
274      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
275      * 
276      * @param source
277      *            The data to convert
278      * @since 1.4
279      */
280     public static String encodeBytes(byte[] source) {
281         return encodeBytes(source, 0, source.length, NO_OPTIONS);
282     } // end encodeBytes
283 
284     /**
285      * Encodes a byte array into Base64 notation.
286      * <p>
287      * Valid options:
288      * 
289      * <pre>
290      *                                                 GZIP: gzip-compresses object before encoding it.
291      *                                                 DONT_BREAK_LINES: don't break lines at 76 characters
292      *                                                   &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
293      * </pre>
294      * 
295      * <p>
296      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
297      * <p>
298      * Example:
299      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
300      * 
301      * 
302      * @param source
303      *            The data to convert
304      * @param options
305      *            Specified options
306      * @see Base64#GZIP
307      * @see Base64#DONT_BREAK_LINES
308      * @since 2.0
309      */
310     public static String encodeBytes(byte[] source, int options) {
311         return encodeBytes(source, 0, source.length, options);
312     } // end encodeBytes
313 
314     /**
315      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
316      * 
317      * @param source
318      *            The data to convert
319      * @param off
320      *            Offset in array where conversion should begin
321      * @param len
322      *            Length of data to convert
323      * @since 1.4
324      */
325     public static String encodeBytes(byte[] source, int off, int len) {
326         return encodeBytes(source, off, len, NO_OPTIONS);
327     } // end encodeBytes
328 
329     /**
330      * Encodes a byte array into Base64 notation.
331      * <p>
332      * Valid options:
333      * 
334      * <pre>
335      *                                                 GZIP: gzip-compresses object before encoding it.
336      *                                                 DONT_BREAK_LINES: don't break lines at 76 characters
337      *                                                   &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
338      * </pre>
339      * 
340      * <p>
341      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
342      * <p>
343      * Example:
344      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
345      * 
346      * 
347      * @param source
348      *            The data to convert
349      * @param off
350      *            Offset in array where conversion should begin
351      * @param len
352      *            Length of data to convert
353      * @param options
354      *            Specified options
355      * @see Base64#DONT_BREAK_LINES
356      * @since 2.0
357      */
358     public static String encodeBytes(byte[] source, int off, int len,
359             int options) {
360         // Isolate options
361         int dontBreakLines = (options & DONT_BREAK_LINES);
362 
363         // Convert option to boolean in way that code likes it.
364         boolean breakLines = dontBreakLines == 0;
365 
366         int len43 = len * 4 / 3;
367         byte[] outBuff = new byte[(len43) // Main 4:3
368                 + ((len % 3) > 0 ? 4 : 0) // Account for padding
369                 + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New
370         // lines
371         int d = 0;
372         int e = 0;
373         int len2 = len - 2;
374         int lineLength = 0;
375         for (; d < len2; d += 3, e += 4) {
376             encode3to4(source, d + off, 3, outBuff, e);
377 
378             lineLength += 4;
379             if (breakLines && lineLength == MAX_LINE_LENGTH) {
380                 outBuff[e + 4] = NEW_LINE;
381                 e++;
382                 lineLength = 0;
383             } // end if: end of line
384         } // en dfor: each piece of array
385 
386         if (d < len) {
387             encode3to4(source, d + off, len - d, outBuff, e);
388             e += 4;
389         } // end if: some padding needed
390 
391         // Return value according to relevant encoding.
392         try {
393             return new String(outBuff, 0, e, PREFERRED_ENCODING);
394         } // end try
395         catch (java.io.UnsupportedEncodingException uue) {
396             return new String(outBuff, 0, e);
397         } // end catch
398 
399         // end else: don't compress
400 
401     } // end encodeBytes
402 
403     /* ******** D E C O D I N G M E T H O D S ******** */
404 
405     /**
406      * Decodes four bytes from array <var>source</var> and writes the resulting
407      * bytes (up to three of them) to <var>destination</var>. The source and
408      * destination arrays can be manipulated anywhere along their length by
409      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
410      * does not check to make sure your arrays are large enough to accomodate
411      * <var>srcOffset</var> + 4 for the <var>source</var> array or
412      * <var>destOffset</var> + 3 for the <var>destination</var> array. This
413      * method returns the actual number of bytes that were converted from the
414      * Base64 encoding.
415      * 
416      * 
417      * @param source
418      *            the array to convert
419      * @param srcOffset
420      *            the index where conversion begins
421      * @param destination
422      *            the array to hold the conversion
423      * @param destOffset
424      *            the index where output will be put
425      * @return the number of decoded bytes converted
426      * @since 1.3
427      */
428     private static int decode4to3(byte[] source, int srcOffset,
429             byte[] destination, int destOffset) {
430         // Example: Dk==
431         if (source[srcOffset + 2] == EQUALS_SIGN) {
432             // Two ways to do the same thing. Don't know which way I like best.
433             // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
434             // )
435             // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
436             int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
437                     | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
438 
439             destination[destOffset] = (byte) (outBuff >>> 16);
440             return 1;
441         }
442 
443         // Example: DkL=
444         else if (source[srcOffset + 3] == EQUALS_SIGN) {
445             // Two ways to do the same thing. Don't know which way I like best.
446             // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
447             // )
448             // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
449             // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
450             int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
451                     | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
452                     | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
453 
454             destination[destOffset] = (byte) (outBuff >>> 16);
455             destination[destOffset + 1] = (byte) (outBuff >>> 8);
456             return 2;
457         }
458 
459         // Example: DkLE
460         else {
461             try {
462                 // Two ways to do the same thing. Don't know which way I like
463                 // best.
464                 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
465                 // >>> 6 )
466                 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
467                 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
468                 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
469                 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
470                         | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
471                         | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
472                         | ((DECODABET[source[srcOffset + 3]] & 0xFF));
473 
474                 destination[destOffset] = (byte) (outBuff >> 16);
475                 destination[destOffset + 1] = (byte) (outBuff >> 8);
476                 destination[destOffset + 2] = (byte) (outBuff);
477 
478                 return 3;
479 
480             } catch (Exception e) {
481                 LOG.error(e);
482                 LOG.error("" + source[srcOffset] + ": "
483                         + (DECODABET[source[srcOffset]]));
484                 LOG.error("" + source[srcOffset + 1] + ": "
485                         + (DECODABET[source[srcOffset + 1]]));
486                 LOG.error("" + source[srcOffset + 2] + ": "
487                         + (DECODABET[source[srcOffset + 2]]));
488                 LOG.error("" + source[srcOffset + 3] + ": "
489                         + (DECODABET[source[srcOffset + 3]]));
490                 return -1;
491             } // e nd catch
492         }
493     } // end decodeToBytes
494 
495     /**
496      * Very low-level access to decoding ASCII characters in the form of a byte
497      * array. Does not support automatically gunzipping or any other "fancy"
498      * features.
499      * 
500      * @param source
501      *            The Base64 encoded data
502      * @param off
503      *            The offset of where to begin decoding
504      * @param len
505      *            The length of characters to decode
506      * @return decoded data
507      * @since 1.3
508      */
509     public static byte[] decode(byte[] source, int off, int len) {
510         int len34 = len * 3 / 4;
511         byte[] outBuff = new byte[len34]; // Upper limit on size of output
512         int outBuffPosn = 0;
513 
514         byte[] b4 = new byte[4];
515         int b4Posn = 0;
516         int i = 0;
517         byte sbiCrop = 0;
518         byte sbiDecode = 0;
519         for (i = off; i < off + len; i++) {
520             sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
521             sbiDecode = DECODABET[sbiCrop];
522 
523             if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or
524             // better
525             {
526                 if (sbiDecode >= EQUALS_SIGN_ENC) {
527                     b4[b4Posn++] = sbiCrop;
528                     if (b4Posn > 3) {
529                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
530                         b4Posn = 0;
531 
532                         // If that was the equals sign, break out of 'for' loop
533                         if (sbiCrop == EQUALS_SIGN)
534                             break;
535                     } // end if: quartet built
536 
537                 } // end if: equals sign or better
538 
539             } // end if: white space, equals sign or better
540             else {
541                 LOG.error("Bad Base64 input character at " + i + ": "
542                         + source[i] + "(decimal)");
543                 return null;
544             } // end else:
545         } // each input character
546 
547         byte[] out = new byte[outBuffPosn];
548         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
549         return out;
550     } // end decode
551 
552     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
553 
554     /**
555      * A {@link Base64.Base64InputStream} will read data from another
556      * <tt>java.io.InputStream</tt>, given in the constructor, and
557      * encode/decode to/from Base64 notation on the fly.
558      * 
559      * @see Base64
560      * @since 1.3
561      */
562     public static class Base64InputStream extends java.io.FilterInputStream {
563         private boolean encode; // Encoding or decoding
564 
565         private int position; // Current position in the buffer
566 
567         private byte[] buffer; // Small buffer holding converted data
568 
569         private int bufferLength; // Length of buffer (3 or 4)
570 
571         private int numSigBytes; // Number of meaningful bytes in the buffer
572 
573         private int lineLength;
574 
575         private boolean breakLines; // Break lines at less than 80 characters
576 
577         /**
578          * Constructs a {@link Base64.Base64InputStream} in DECODE mode.
579          * 
580          * @param in
581          *            the <tt>java.io.InputStream</tt> from which to read
582          *            data.
583          * @since 1.3
584          */
585         public Base64InputStream(java.io.InputStream in) {
586             this(in, DECODE);
587         } // end constructor
588 
589         /**
590          * Constructs a {@link Base64.Base64InputStream} in either ENCODE or
591          * DECODE mode.
592          * <p>
593          * Valid options:
594          * 
595          * <pre>
596          *                                                 ENCODE or DECODE: Encode or Decode as data is read.
597          *                                                 DONT_BREAK_LINES: don't break lines at 76 characters
598          *                                                   (only meaningful when encoding)
599          *                                                   &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
600          * </pre>
601          * 
602          * <p>
603          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
604          * 
605          * 
606          * @param in
607          *            the <tt>java.io.InputStream</tt> from which to read
608          *            data.
609          * @param options
610          *            Specified options
611          * @see Base64#ENCODE
612          * @see Base64#DECODE
613          * @see Base64#DONT_BREAK_LINES
614          * @since 2.0
615          */
616         public Base64InputStream(java.io.InputStream in, int options) {
617             super(in);
618             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
619             this.encode = (options & ENCODE) == ENCODE;
620             this.bufferLength = encode ? 4 : 3;
621             this.buffer = new byte[bufferLength];
622             this.position = -1;
623             this.lineLength = 0;
624         }
625 
626         /**
627          * Reads enough of the input stream to convert to/from Base64 and
628          * returns the next byte.
629          * 
630          * @return next byte
631          * @since 1.3
632          */
633         public int read() throws java.io.IOException {
634             // Do we need to get data?
635             if (position < 0) {
636                 if (encode) {
637                     byte[] b3 = new byte[3];
638                     int numBinaryBytes = 0;
639                     for (int i = 0; i < 3; i++) {
640                         try {
641                             int b = in.read();
642 
643                             // If end of stream, b is -1.
644                             if (b >= 0) {
645                                 b3[i] = (byte) b;
646                                 numBinaryBytes++;
647                             } // end if: not end of stream
648 
649                         } // end try: read
650                         catch (java.io.IOException e) {
651                             // Only a problem if we got no data at all.
652                             if (i == 0)
653                                 throw e;
654 
655                         } // end catch
656                     } // end for: each needed input byte
657 
658                     if (numBinaryBytes > 0) {
659                         encode3to4(b3, 0, numBinaryBytes, buffer, 0);
660                         position = 0;
661                         numSigBytes = 4;
662                     } // end if: got data
663                     else {
664                         return -1;
665                     } // end else
666                 } // end if: encoding
667 
668                 // Else decoding
669                 else {
670                     byte[] b4 = new byte[4];
671                     int i = 0;
672                     for (i = 0; i < 4; i++) {
673                         // Read four "meaningful" bytes:
674                         int b = 0;
675                         do {
676                             b = in.read();
677                         } while (b >= 0
678                                 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
679 
680                         if (b < 0)
681                             break; // Reads a -1 if end of stream
682 
683                         b4[i] = (byte) b;
684                     } // end for: each needed input byte
685 
686                     if (i == 4) {
687                         numSigBytes = decode4to3(b4, 0, buffer, 0);
688                         position = 0;
689                     } // end if: got four characters
690                     else if (i == 0) {
691                         return -1;
692                     } // end else if: also padded correctly
693                     else {
694                         // Must have broken out from above.
695                         throw new java.io.IOException(
696                                 "Improperly padded Base64 input.");
697                     } // end
698 
699                 } // end else: decode
700             } // end else: get data
701 
702             // Got data?
703             if (position >= 0) {
704                 // End of relevant data?
705                 if ( /* !encode && */position >= numSigBytes)
706                     return -1;
707 
708                 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
709                     lineLength = 0;
710                     return '\n';
711                 } // end if
712 
713                 lineLength++; // This isn't important when decoding
714                 // but throwing an extra "if" seems
715                 // just as wasteful.
716 
717                 int b = buffer[position++];
718 
719                 if (position >= bufferLength)
720                     position = -1;
721 
722                 return b & 0xFF; // This is how you "cast" a byte that's
723                 // intended to be unsigned.
724                 // end else
725             } // end if: position >= 0
726 
727             // Else error
728 
729             // When JDK1.4 is more accepted, use an assertion here.
730             throw new java.io.IOException(
731                     "Error in Base64 code reading stream.");
732             // end else
733         } // end read
734 
735         /**
736          * Calls {@link #read()} repeatedly until the end of stream is reached
737          * or <var>len</var> bytes are read. Returns number of bytes read into
738          * array or -1 if end of stream is encountered.
739          * 
740          * @param dest
741          *            array to hold values
742          * @param off
743          *            offset for array
744          * @param len
745          *            max number of bytes to read into array
746          * @return bytes read into array or -1 if end of stream is encountered.
747          * @since 1.3
748          */
749         public int read(byte[] dest, int off, int len)
750                 throws java.io.IOException {
751             int i;
752             for (i = 0; i < len; i++) {
753                 int b = read();
754 
755                 // if( b < 0 && i == 0 )
756                 // return -1;
757 
758                 if (b >= 0) {
759                     dest[off + i] = (byte) b;
760                     continue;
761                 }
762 
763                 if (i == 0) {
764                     return -1;
765                 }
766 
767                 break;
768             }
769             return i;
770         }
771     }
772 
773     /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
774 
775     /**
776      * A {@link Base64.Base64OutputStream} will write data to another
777      * <tt>java.io.OutputStream</tt>, given in the constructor, and
778      * encode/decode to/from Base64 notation on the fly.
779      * 
780      * @see Base64
781      * @since 1.3
782      */
783     public static class Base64OutputStream extends java.io.FilterOutputStream {
784         private boolean encode;
785 
786         private int position;
787 
788         private byte[] buffer;
789 
790         private int bufferLength;
791 
792         private int lineLength;
793 
794         private boolean breakLines;
795 
796         private byte[] b4; // Scratch used in a few places
797 
798         private boolean suspendEncoding;
799 
800         /**
801          * Constructs a {@link Base64.Base64OutputStream} in ENCODE mode.
802          * 
803          * @param out
804          *            the <tt>java.io.OutputStream</tt> to which data will be
805          *            written.
806          * @since 1.3
807          */
808         public Base64OutputStream(java.io.OutputStream out) {
809             this(out, ENCODE);
810         } // end constructor
811 
812         /**
813          * Constructs a {@link Base64.Base64OutputStream} in either ENCODE or
814          * DECODE mode.
815          * <p>
816          * Valid options:
817          * 
818          * <pre>
819          *                                                 ENCODE or DECODE: Encode or Decode as data is read.
820          *                                                 DONT_BREAK_LINES: don't break lines at 76 characters
821          *                                                   (only meaningful when encoding)
822          *                                                   &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
823          * </pre>
824          * 
825          * <p>
826          * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
827          * 
828          * @param out
829          *            the <tt>java.io.OutputStream</tt> to which data will be
830          *            written.
831          * @param options
832          *            Specified options.
833          * @see Base64#ENCODE
834          * @see Base64#DECODE
835          * @see Base64#DONT_BREAK_LINES
836          * @since 1.3
837          */
838         public Base64OutputStream(java.io.OutputStream out, int options) {
839             super(out);
840             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
841             this.encode = (options & ENCODE) == ENCODE;
842             this.bufferLength = encode ? 3 : 4;
843             this.buffer = new byte[bufferLength];
844             this.position = 0;
845             this.lineLength = 0;
846             this.suspendEncoding = false;
847             this.b4 = new byte[4];
848         } // end constructor
849 
850         /**
851          * Writes the byte to the output stream after converting to/from Base64
852          * notation. When encoding, bytes are buffered three at a time before
853          * the output stream actually gets a write() call. When decoding, bytes
854          * are buffered four at a time.
855          * 
856          * @param theByte
857          *            the byte to write
858          * @since 1.3
859          */
860         public void write(int theByte) throws java.io.IOException {
861             // Encoding suspended?
862             if (suspendEncoding) {
863                 super.out.write(theByte);
864                 return;
865             } // end if: supsended
866 
867             // Encode?
868             if (encode) {
869                 buffer[position++] = (byte) theByte;
870                 if (position >= bufferLength) // Enough to encode.
871                 {
872                     out.write(encode3to4(b4, buffer, bufferLength));
873 
874                     lineLength += 4;
875                     if (breakLines && lineLength >= MAX_LINE_LENGTH) {
876                         out.write(NEW_LINE);
877                         lineLength = 0;
878                     } // end if: end of line
879 
880                     position = 0;
881                 } // end if: enough to output
882             } // end if: encoding
883 
884             // Else, Decoding
885             else {
886                 // Meaningful Base64 character?
887                 if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
888                     buffer[position++] = (byte) theByte;
889                     if (position >= bufferLength) // Enough to output.
890                     {
891                         int len = Base64.decode4to3(buffer, 0, b4, 0);
892                         out.write(b4, 0, len);
893                         // out.write( Base64.decode4to3( buffer ) );
894                         position = 0;
895                     } // end if: enough to output
896                 } // end if: meaningful base64 character
897                 else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
898                     throw new java.io.IOException(
899                             "Invalid character in Base64 data.");
900                 } // end else: not white space either
901             } // end else: decoding
902         } // end write
903 
904         /**
905          * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
906          * written.
907          * 
908          * @param theBytes
909          *            array from which to read bytes
910          * @param off
911          *            offset for array
912          * @param len
913          *            max number of bytes to read into array
914          * @since 1.3
915          */
916         public void write(byte[] theBytes, int off, int len)
917                 throws java.io.IOException {
918             // Encoding suspended?
919             if (suspendEncoding) {
920                 super.out.write(theBytes, off, len);
921                 return;
922             } // end if: supsended
923 
924             for (int i = 0; i < len; i++) {
925                 write(theBytes[off + i]);
926             } // end for: each byte written
927 
928         }
929 
930         /**
931          * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
932          * without closing the stream.
933          */
934         public void flushBase64() throws java.io.IOException {
935             if (position < 1) {
936                 return;
937             }
938 
939             if (encode == false) {
940                 throw new java.io.IOException(
941                         "Base64 input not properly padded.");
942             }
943 
944             out.write(encode3to4(b4, buffer, position));
945             position = 0;
946         }
947 
948         /**
949          * Flushes and closes (I think, in the superclass) the stream.
950          * 
951          * @since 1.3
952          */
953         public void close() throws java.io.IOException {
954             // 1. Ensure that pending characters are written
955             flushBase64();
956 
957             // 2. Actually close the stream
958             // Base class both flushes and closes.
959             super.close();
960 
961             buffer = null;
962             out = null;
963         }
964 
965         /**
966          * Suspends encoding of the stream. May be helpful if you need to embed
967          * a piece of base640-encoded data in a stream.
968          * 
969          * @since 1.5.1
970          */
971         public void suspendEncoding() throws java.io.IOException {
972             flushBase64();
973             this.suspendEncoding = true;
974         }
975 
976         /**
977          * Resumes encoding of the stream. May be helpful if you need to embed a
978          * piece of base640-encoded data in a stream.
979          * 
980          * @since 1.5.1
981          */
982         public void resumeEncoding() {
983             this.suspendEncoding = false;
984         }
985     }
986 }