1/**
2 * $RCSfile$
3 * $Revision$
4 * $Date$
5 *
6 */
7package org.jivesoftware.smack.util;
8
9/**
10 * <p>Encodes and decodes to and from Base64 notation.</p>
11 * This code was obtained from <a href="http://iharder.net/base64">http://iharder.net/base64</a></p>
12 *
13 *
14 * @author Robert Harder
15 * @author rob@iharder.net
16 * @version 2.2.1
17 */
18public class Base64
19{
20
21/* ********  P U B L I C   F I E L D S  ******** */
22
23
24    /** No options specified. Value is zero. */
25    public final static int NO_OPTIONS = 0;
26
27    /** Specify encoding. */
28    public final static int ENCODE = 1;
29
30
31    /** Specify decoding. */
32    public final static int DECODE = 0;
33
34
35    /** Specify that data should be gzip-compressed. */
36    public final static int GZIP = 2;
37
38
39    /** Don't break lines when encoding (violates strict Base64 specification) */
40    public final static int DONT_BREAK_LINES = 8;
41
42	/**
43	 * Encode using Base64-like encoding that is URL- and Filename-safe as described
44	 * in Section 4 of RFC3548:
45	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
46	 * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
47	 * or at the very least should not be called Base64 without also specifying that is
48	 * was encoded using the URL- and Filename-safe dialect.
49	 */
50	 public final static int URL_SAFE = 16;
51
52
53	 /**
54	  * Encode using the special "ordered" dialect of Base64 described here:
55	  * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
56	  */
57	 public final static int ORDERED = 32;
58
59
60/* ********  P R I V A T E   F I E L D S  ******** */
61
62
63    /** Maximum line length (76) of Base64 output. */
64    private final static int MAX_LINE_LENGTH = 76;
65
66
67    /** The equals sign (=) as a byte. */
68    private final static byte EQUALS_SIGN = (byte)'=';
69
70
71    /** The new line character (\n) as a byte. */
72    private final static byte NEW_LINE = (byte)'\n';
73
74
75    /** Preferred encoding. */
76    private final static String PREFERRED_ENCODING = "UTF-8";
77
78
79    // I think I end up not using the BAD_ENCODING indicator.
80    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
81    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
82    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
83
84
85/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */
86
87    /** The 64 valid Base64 values. */
88    //private final static byte[] ALPHABET;
89	/* Host platform me be something funny like EBCDIC, so we hardcode these values. */
90	private final static byte[] _STANDARD_ALPHABET =
91    {
92        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
93        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
94        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
95        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
96        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
97        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
98        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
99        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
100        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
101        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
102    };
103
104
105    /**
106     * Translates a Base64 value to either its 6-bit reconstruction value
107     * or a negative number indicating some other meaning.
108     **/
109    private final static byte[] _STANDARD_DECODABET =
110    {
111        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
112        -5,-5,                                      // Whitespace: Tab and Linefeed
113        -9,-9,                                      // Decimal 11 - 12
114        -5,                                         // Whitespace: Carriage Return
115        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
116        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
117        -5,                                         // Whitespace: Space
118        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
119        62,                                         // Plus sign at decimal 43
120        -9,-9,-9,                                   // Decimal 44 - 46
121        63,                                         // Slash at decimal 47
122        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
123        -9,-9,-9,                                   // Decimal 58 - 60
124        -1,                                         // Equals sign at decimal 61
125        -9,-9,-9,                                      // Decimal 62 - 64
126        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
127        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
128        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
129        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
130        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
131        -9,-9,-9,-9                                 // Decimal 123 - 126
132        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
133        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
134        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
135        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
136        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
137        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
138        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
139        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
140        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
141        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
142    };
143
144
145/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
146
147	/**
148	 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
149	 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
150	 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
151	 */
152    private final static byte[] _URL_SAFE_ALPHABET =
153    {
154      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
155      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
156      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
157      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
158      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
159      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
160      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
161      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
162      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
163      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
164    };
165
166	/**
167	 * Used in decoding URL- and Filename-safe dialects of Base64.
168	 */
169    private final static byte[] _URL_SAFE_DECODABET =
170    {
171      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
172      -5,-5,                                      // Whitespace: Tab and Linefeed
173      -9,-9,                                      // Decimal 11 - 12
174      -5,                                         // Whitespace: Carriage Return
175      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
176      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
177      -5,                                         // Whitespace: Space
178      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
179      -9,                                         // Plus sign at decimal 43
180      -9,                                         // Decimal 44
181      62,                                         // Minus sign at decimal 45
182      -9,                                         // Decimal 46
183      -9,                                         // Slash at decimal 47
184      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
185      -9,-9,-9,                                   // Decimal 58 - 60
186      -1,                                         // Equals sign at decimal 61
187      -9,-9,-9,                                   // Decimal 62 - 64
188      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
189      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
190      -9,-9,-9,-9,                                // Decimal 91 - 94
191      63,                                         // Underscore at decimal 95
192      -9,                                         // Decimal 96
193      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
194      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
195      -9,-9,-9,-9                                 // Decimal 123 - 126
196      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
197      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
198      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
199      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
200      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
201      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
202      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
203      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
204      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
205      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
206    };
207
208
209
210/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
211
212	/**
213	 * I don't get the point of this technique, but it is described here:
214	 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
215	 */
216    private final static byte[] _ORDERED_ALPHABET =
217    {
218      (byte)'-',
219      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
220      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
221      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
222      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
223      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
224      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
225      (byte)'_',
226      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
227      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
228      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
229      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
230    };
231
232	/**
233	 * Used in decoding the "ordered" dialect of Base64.
234	 */
235    private final static byte[] _ORDERED_DECODABET =
236    {
237      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
238      -5,-5,                                      // Whitespace: Tab and Linefeed
239      -9,-9,                                      // Decimal 11 - 12
240      -5,                                         // Whitespace: Carriage Return
241      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
242      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
243      -5,                                         // Whitespace: Space
244      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
245      -9,                                         // Plus sign at decimal 43
246      -9,                                         // Decimal 44
247      0,                                          // Minus sign at decimal 45
248      -9,                                         // Decimal 46
249      -9,                                         // Slash at decimal 47
250      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
251      -9,-9,-9,                                   // Decimal 58 - 60
252      -1,                                         // Equals sign at decimal 61
253      -9,-9,-9,                                   // Decimal 62 - 64
254      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
255      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
256      -9,-9,-9,-9,                                // Decimal 91 - 94
257      37,                                         // Underscore at decimal 95
258      -9,                                         // Decimal 96
259      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
260      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
261      -9,-9,-9,-9                                 // Decimal 123 - 126
262      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
263        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
264        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
265        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
266        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
267        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
268        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
269        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
270        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
271        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
272    };
273
274
275/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
276
277
278	/**
279	 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
280	 * the options specified.
281	 * It's possible, though silly, to specify ORDERED and URLSAFE
282	 * in which case one of them will be picked, though there is
283	 * no guarantee as to which one will be picked.
284	 */
285	private final static byte[] getAlphabet( int options )
286	{
287		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
288		else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
289		else return _STANDARD_ALPHABET;
290
291	}	// end getAlphabet
292
293
294	/**
295	 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
296	 * the options specified.
297	 * It's possible, though silly, to specify ORDERED and URL_SAFE
298	 * in which case one of them will be picked, though there is
299	 * no guarantee as to which one will be picked.
300	 */
301	private final static byte[] getDecodabet( int options )
302	{
303		if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
304		else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
305		else return _STANDARD_DECODABET;
306
307	}	// end getAlphabet
308
309
310
311    /** Defeats instantiation. */
312    private Base64(){}
313
314    /**
315     * Prints command line usage.
316     *
317     * @param msg A message to include with usage info.
318     */
319    private final static void usage( String msg )
320    {
321        System.err.println( msg );
322        System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" );
323    }   // end usage
324
325
326/* ********  E N C O D I N G   M E T H O D S  ******** */
327
328
329    /**
330     * Encodes up to the first three bytes of array <var>threeBytes</var>
331     * and returns a four-byte array in Base64 notation.
332     * The actual number of significant bytes in your array is
333     * given by <var>numSigBytes</var>.
334     * The array <var>threeBytes</var> needs only be as big as
335     * <var>numSigBytes</var>.
336     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
337     *
338     * @param b4 A reusable byte array to reduce array instantiation
339     * @param threeBytes the array to convert
340     * @param numSigBytes the number of significant bytes in your array
341     * @return four byte array in Base64 notation.
342     * @since 1.5.1
343     */
344    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
345    {
346        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
347        return b4;
348    }   // end encode3to4
349
350
351    /**
352     * <p>Encodes up to three bytes of the array <var>source</var>
353     * and writes the resulting four Base64 bytes to <var>destination</var>.
354     * The source and destination arrays can be manipulated
355     * anywhere along their length by specifying
356     * <var>srcOffset</var> and <var>destOffset</var>.
357     * This method does not check to make sure your arrays
358     * are large enough to accomodate <var>srcOffset</var> + 3 for
359     * the <var>source</var> array or <var>destOffset</var> + 4 for
360     * the <var>destination</var> array.
361     * The actual number of significant bytes in your array is
362     * given by <var>numSigBytes</var>.</p>
363	 * <p>This is the lowest level of the encoding methods with
364	 * all possible parameters.</p>
365     *
366     * @param source the array to convert
367     * @param srcOffset the index where conversion begins
368     * @param numSigBytes the number of significant bytes in your array
369     * @param destination the array to hold the conversion
370     * @param destOffset the index where output will be put
371     * @return the <var>destination</var> array
372     * @since 1.3
373     */
374    private static byte[] encode3to4(
375     byte[] source, int srcOffset, int numSigBytes,
376     byte[] destination, int destOffset, int options )
377    {
378		byte[] ALPHABET = getAlphabet( options );
379
380        //           1         2         3
381        // 01234567890123456789012345678901 Bit position
382        // --------000000001111111122222222 Array position from threeBytes
383        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
384        //          >>18  >>12  >> 6  >> 0  Right shift necessary
385        //                0x3f  0x3f  0x3f  Additional AND
386
387        // Create buffer with zero-padding if there are only one or two
388        // significant bytes passed in the array.
389        // We have to shift left 24 in order to flush out the 1's that appear
390        // when Java treats a value as negative that is cast from a byte to an int.
391        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
392                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
393                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
394
395        switch( numSigBytes )
396        {
397            case 3:
398                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
399                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
400                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
401                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
402                return destination;
403
404            case 2:
405                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
406                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
407                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
408                destination[ destOffset + 3 ] = EQUALS_SIGN;
409                return destination;
410
411            case 1:
412                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
413                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
414                destination[ destOffset + 2 ] = EQUALS_SIGN;
415                destination[ destOffset + 3 ] = EQUALS_SIGN;
416                return destination;
417
418            default:
419                return destination;
420        }   // end switch
421    }   // end encode3to4
422
423
424
425    /**
426     * Serializes an object and returns the Base64-encoded
427     * version of that serialized object. If the object
428     * cannot be serialized or there is another error,
429     * the method will return <tt>null</tt>.
430     * The object is not GZip-compressed before being encoded.
431     *
432     * @param serializableObject The object to encode
433     * @return The Base64-encoded object
434     * @since 1.4
435     */
436    public static String encodeObject( java.io.Serializable serializableObject )
437    {
438        return encodeObject( serializableObject, NO_OPTIONS );
439    }   // end encodeObject
440
441
442
443    /**
444     * Serializes an object and returns the Base64-encoded
445     * version of that serialized object. If the object
446     * cannot be serialized or there is another error,
447     * the method will return <tt>null</tt>.
448     * <p>
449     * Valid options:<pre>
450     *   GZIP: gzip-compresses object before encoding it.
451     *   DONT_BREAK_LINES: don't break lines at 76 characters
452     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
453     * </pre>
454     * <p>
455     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
456     * <p>
457     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
458     *
459     * @param serializableObject The object to encode
460     * @param options Specified options
461     * @return The Base64-encoded object
462     * @see Base64#GZIP
463     * @see Base64#DONT_BREAK_LINES
464     * @since 2.0
465     */
466    public static String encodeObject( java.io.Serializable serializableObject, int options )
467    {
468        // Streams
469        java.io.ByteArrayOutputStream  baos  = null;
470        java.io.OutputStream           b64os = null;
471        java.io.ObjectOutputStream     oos   = null;
472        java.util.zip.GZIPOutputStream gzos  = null;
473
474        // Isolate options
475        int gzip           = (options & GZIP);
476        int dontBreakLines = (options & DONT_BREAK_LINES);
477
478        try
479        {
480            // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
481            baos  = new java.io.ByteArrayOutputStream();
482            b64os = new Base64.OutputStream( baos, ENCODE | options );
483
484            // GZip?
485            if( gzip == GZIP )
486            {
487                gzos = new java.util.zip.GZIPOutputStream( b64os );
488                oos  = new java.io.ObjectOutputStream( gzos );
489            }   // end if: gzip
490            else
491                oos   = new java.io.ObjectOutputStream( b64os );
492
493            oos.writeObject( serializableObject );
494        }   // end try
495        catch( java.io.IOException e )
496        {
497            e.printStackTrace();
498            return null;
499        }   // end catch
500        finally
501        {
502            try{ oos.close();   } catch( Exception e ){}
503            try{ gzos.close();  } catch( Exception e ){}
504            try{ b64os.close(); } catch( Exception e ){}
505            try{ baos.close();  } catch( Exception e ){}
506        }   // end finally
507
508        // Return value according to relevant encoding.
509        try
510        {
511            return new String( baos.toByteArray(), PREFERRED_ENCODING );
512        }   // end try
513        catch (java.io.UnsupportedEncodingException uue)
514        {
515            return new String( baos.toByteArray() );
516        }   // end catch
517
518    }   // end encode
519
520
521
522    /**
523     * Encodes a byte array into Base64 notation.
524     * Does not GZip-compress data.
525     *
526     * @param source The data to convert
527     * @since 1.4
528     */
529    public static String encodeBytes( byte[] source )
530    {
531        return encodeBytes( source, 0, source.length, NO_OPTIONS );
532    }   // end encodeBytes
533
534
535
536    /**
537     * Encodes a byte array into Base64 notation.
538     * <p>
539     * Valid options:<pre>
540     *   GZIP: gzip-compresses object before encoding it.
541     *   DONT_BREAK_LINES: don't break lines at 76 characters
542     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
543     * </pre>
544     * <p>
545     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
546     * <p>
547     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
548     *
549     *
550     * @param source The data to convert
551     * @param options Specified options
552     * @see Base64#GZIP
553     * @see Base64#DONT_BREAK_LINES
554     * @since 2.0
555     */
556    public static String encodeBytes( byte[] source, int options )
557    {
558        return encodeBytes( source, 0, source.length, options );
559    }   // end encodeBytes
560
561
562    /**
563     * Encodes a byte array into Base64 notation.
564     * Does not GZip-compress data.
565     *
566     * @param source The data to convert
567     * @param off Offset in array where conversion should begin
568     * @param len Length of data to convert
569     * @since 1.4
570     */
571    public static String encodeBytes( byte[] source, int off, int len )
572    {
573        return encodeBytes( source, off, len, NO_OPTIONS );
574    }   // end encodeBytes
575
576
577
578    /**
579     * Encodes a byte array into Base64 notation.
580     * <p>
581     * Valid options:<pre>
582     *   GZIP: gzip-compresses object before encoding it.
583     *   DONT_BREAK_LINES: don't break lines at 76 characters
584     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
585     * </pre>
586     * <p>
587     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
588     * <p>
589     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
590     *
591     *
592     * @param source The data to convert
593     * @param off Offset in array where conversion should begin
594     * @param len Length of data to convert
595     * @param options Specified options; alphabet type is pulled from this (standard, url-safe, ordered)
596     * @see Base64#GZIP
597     * @see Base64#DONT_BREAK_LINES
598     * @since 2.0
599     */
600    public static String encodeBytes( byte[] source, int off, int len, int options )
601    {
602        // Isolate options
603        int dontBreakLines = ( options & DONT_BREAK_LINES );
604        int gzip           = ( options & GZIP   );
605
606        // Compress?
607        if( gzip == GZIP )
608        {
609            java.io.ByteArrayOutputStream  baos  = null;
610            java.util.zip.GZIPOutputStream gzos  = null;
611            Base64.OutputStream            b64os = null;
612
613
614            try
615            {
616                // GZip -> Base64 -> ByteArray
617                baos = new java.io.ByteArrayOutputStream();
618                b64os = new Base64.OutputStream( baos, ENCODE | options );
619                gzos  = new java.util.zip.GZIPOutputStream( b64os );
620
621                gzos.write( source, off, len );
622                gzos.close();
623            }   // end try
624            catch( java.io.IOException e )
625            {
626                e.printStackTrace();
627                return null;
628            }   // end catch
629            finally
630            {
631                try{ gzos.close();  } catch( Exception e ){}
632                try{ b64os.close(); } catch( Exception e ){}
633                try{ baos.close();  } catch( Exception e ){}
634            }   // end finally
635
636            // Return value according to relevant encoding.
637            try
638            {
639                return new String( baos.toByteArray(), PREFERRED_ENCODING );
640            }   // end try
641            catch (java.io.UnsupportedEncodingException uue)
642            {
643                return new String( baos.toByteArray() );
644            }   // end catch
645        }   // end if: compress
646
647        // Else, don't compress. Better not to use streams at all then.
648        else
649        {
650            // Convert option to boolean in way that code likes it.
651            boolean breakLines = dontBreakLines == 0;
652
653            int    len43   = len * 4 / 3;
654            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
655                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
656                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
657            int d = 0;
658            int e = 0;
659            int len2 = len - 2;
660            int lineLength = 0;
661            for( ; d < len2; d+=3, e+=4 )
662            {
663                encode3to4( source, d+off, 3, outBuff, e, options );
664
665                lineLength += 4;
666                if( breakLines && lineLength == MAX_LINE_LENGTH )
667                {
668                    outBuff[e+4] = NEW_LINE;
669                    e++;
670                    lineLength = 0;
671                }   // end if: end of line
672            }   // en dfor: each piece of array
673
674            if( d < len )
675            {
676                encode3to4( source, d+off, len - d, outBuff, e, options );
677                e += 4;
678            }   // end if: some padding needed
679
680
681            // Return value according to relevant encoding.
682            try
683            {
684                return new String( outBuff, 0, e, PREFERRED_ENCODING );
685            }   // end try
686            catch (java.io.UnsupportedEncodingException uue)
687            {
688                return new String( outBuff, 0, e );
689            }   // end catch
690
691        }   // end else: don't compress
692
693    }   // end encodeBytes
694
695
696
697
698
699/* ********  D E C O D I N G   M E T H O D S  ******** */
700
701
702    /**
703     * Decodes four bytes from array <var>source</var>
704     * and writes the resulting bytes (up to three of them)
705     * to <var>destination</var>.
706     * The source and destination arrays can be manipulated
707     * anywhere along their length by specifying
708     * <var>srcOffset</var> and <var>destOffset</var>.
709     * This method does not check to make sure your arrays
710     * are large enough to accomodate <var>srcOffset</var> + 4 for
711     * the <var>source</var> array or <var>destOffset</var> + 3 for
712     * the <var>destination</var> array.
713     * This method returns the actual number of bytes that
714     * were converted from the Base64 encoding.
715	 * <p>This is the lowest level of the decoding methods with
716	 * all possible parameters.</p>
717     *
718     *
719     * @param source the array to convert
720     * @param srcOffset the index where conversion begins
721     * @param destination the array to hold the conversion
722     * @param destOffset the index where output will be put
723	 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
724     * @return the number of decoded bytes converted
725     * @since 1.3
726     */
727    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options )
728    {
729		byte[] DECODABET = getDecodabet( options );
730
731        // Example: Dk==
732        if( source[ srcOffset + 2] == EQUALS_SIGN )
733        {
734            // Two ways to do the same thing. Don't know which way I like best.
735            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
736            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
737            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
738                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
739
740            destination[ destOffset ] = (byte)( outBuff >>> 16 );
741            return 1;
742        }
743
744        // Example: DkL=
745        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
746        {
747            // Two ways to do the same thing. Don't know which way I like best.
748            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
749            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
750            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
751            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
752                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
753                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
754
755            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
756            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
757            return 2;
758        }
759
760        // Example: DkLE
761        else
762        {
763            try{
764            // Two ways to do the same thing. Don't know which way I like best.
765            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
766            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
767            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
768            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
769            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
770                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
771                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
772                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
773
774
775            destination[ destOffset     ] = (byte)( outBuff >> 16 );
776            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
777            destination[ destOffset + 2 ] = (byte)( outBuff       );
778
779            return 3;
780            }catch( Exception e){
781                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
782                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
783                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
784                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
785                return -1;
786            }   // end catch
787        }
788    }   // end decodeToBytes
789
790
791
792
793    /**
794     * Very low-level access to decoding ASCII characters in
795     * the form of a byte array. Does not support automatically
796     * gunzipping or any other "fancy" features.
797     *
798     * @param source The Base64 encoded data
799     * @param off    The offset of where to begin decoding
800     * @param len    The length of characters to decode
801     * @return decoded data
802     * @since 1.3
803     */
804    public static byte[] decode( byte[] source, int off, int len, int options )
805    {
806		byte[] DECODABET = getDecodabet( options );
807
808        int    len34   = len * 3 / 4;
809        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
810        int    outBuffPosn = 0;
811
812        byte[] b4        = new byte[4];
813        int    b4Posn    = 0;
814        int    i         = 0;
815        byte   sbiCrop   = 0;
816        byte   sbiDecode = 0;
817        for( i = off; i < off+len; i++ )
818        {
819            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
820            sbiDecode = DECODABET[ sbiCrop ];
821
822            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
823            {
824                if( sbiDecode >= EQUALS_SIGN_ENC )
825                {
826                    b4[ b4Posn++ ] = sbiCrop;
827                    if( b4Posn > 3 )
828                    {
829                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
830                        b4Posn = 0;
831
832                        // If that was the equals sign, break out of 'for' loop
833                        if( sbiCrop == EQUALS_SIGN )
834                            break;
835                    }   // end if: quartet built
836
837                }   // end if: equals sign or better
838
839            }   // end if: white space, equals sign or better
840            else
841            {
842                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
843                return null;
844            }   // end else:
845        }   // each input character
846
847        byte[] out = new byte[ outBuffPosn ];
848        System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
849        return out;
850    }   // end decode
851
852
853
854
855    /**
856     * Decodes data from Base64 notation, automatically
857     * detecting gzip-compressed data and decompressing it.
858     *
859     * @param s the string to decode
860     * @return the decoded data
861     * @since 1.4
862     */
863    public static byte[] decode( String s )
864	{
865		return decode( s, NO_OPTIONS );
866	}
867
868
869    /**
870     * Decodes data from Base64 notation, automatically
871     * detecting gzip-compressed data and decompressing it.
872     *
873     * @param s the string to decode
874	 * @param options encode options such as URL_SAFE
875     * @return the decoded data
876     * @since 1.4
877     */
878    public static byte[] decode( String s, int options )
879    {
880        byte[] bytes;
881        try
882        {
883            bytes = s.getBytes( PREFERRED_ENCODING );
884        }   // end try
885        catch( java.io.UnsupportedEncodingException uee )
886        {
887            bytes = s.getBytes();
888        }   // end catch
889		//</change>
890
891        // Decode
892        bytes = decode( bytes, 0, bytes.length, options );
893
894
895        // Check to see if it's gzip-compressed
896        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
897        if( bytes != null && bytes.length >= 4 )
898        {
899
900            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
901            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
902            {
903                java.io.ByteArrayInputStream  bais = null;
904                java.util.zip.GZIPInputStream gzis = null;
905                java.io.ByteArrayOutputStream baos = null;
906                byte[] buffer = new byte[2048];
907                int    length = 0;
908
909                try
910                {
911                    baos = new java.io.ByteArrayOutputStream();
912                    bais = new java.io.ByteArrayInputStream( bytes );
913                    gzis = new java.util.zip.GZIPInputStream( bais );
914
915                    while( ( length = gzis.read( buffer ) ) >= 0 )
916                    {
917                        baos.write(buffer,0,length);
918                    }   // end while: reading input
919
920                    // No error? Get new bytes.
921                    bytes = baos.toByteArray();
922
923                }   // end try
924                catch( java.io.IOException e )
925                {
926                    // Just return originally-decoded bytes
927                }   // end catch
928                finally
929                {
930                    try{ baos.close(); } catch( Exception e ){}
931                    try{ gzis.close(); } catch( Exception e ){}
932                    try{ bais.close(); } catch( Exception e ){}
933                }   // end finally
934
935            }   // end if: gzipped
936        }   // end if: bytes.length >= 2
937
938        return bytes;
939    }   // end decode
940
941
942
943
944    /**
945     * Attempts to decode Base64 data and deserialize a Java
946     * Object within. Returns <tt>null</tt> if there was an error.
947     *
948     * @param encodedObject The Base64 data to decode
949     * @return The decoded and deserialized object
950     * @since 1.5
951     */
952    public static Object decodeToObject( String encodedObject )
953    {
954        // Decode and gunzip if necessary
955        byte[] objBytes = decode( encodedObject );
956
957        java.io.ByteArrayInputStream  bais = null;
958        java.io.ObjectInputStream     ois  = null;
959        Object obj = null;
960
961        try
962        {
963            bais = new java.io.ByteArrayInputStream( objBytes );
964            ois  = new java.io.ObjectInputStream( bais );
965
966            obj = ois.readObject();
967        }   // end try
968        catch( java.io.IOException e )
969        {
970            e.printStackTrace();
971            obj = null;
972        }   // end catch
973        catch( java.lang.ClassNotFoundException e )
974        {
975            e.printStackTrace();
976            obj = null;
977        }   // end catch
978        finally
979        {
980            try{ bais.close(); } catch( Exception e ){}
981            try{ ois.close();  } catch( Exception e ){}
982        }   // end finally
983
984        return obj;
985    }   // end decodeObject
986
987
988
989    /**
990     * Convenience method for encoding data to a file.
991     *
992     * @param dataToEncode byte array of data to encode in base64 form
993     * @param filename Filename for saving encoded data
994     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
995     *
996     * @since 2.1
997     */
998    public static boolean encodeToFile( byte[] dataToEncode, String filename )
999    {
1000        boolean success = false;
1001        Base64.OutputStream bos = null;
1002        try
1003        {
1004            bos = new Base64.OutputStream(
1005                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
1006            bos.write( dataToEncode );
1007            success = true;
1008        }   // end try
1009        catch( java.io.IOException e )
1010        {
1011
1012            success = false;
1013        }   // end catch: IOException
1014        finally
1015        {
1016            try{ bos.close(); } catch( Exception e ){}
1017        }   // end finally
1018
1019        return success;
1020    }   // end encodeToFile
1021
1022
1023    /**
1024     * Convenience method for decoding data to a file.
1025     *
1026     * @param dataToDecode Base64-encoded data as a string
1027     * @param filename Filename for saving decoded data
1028     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1029     *
1030     * @since 2.1
1031     */
1032    public static boolean decodeToFile( String dataToDecode, String filename )
1033    {
1034        boolean success = false;
1035        Base64.OutputStream bos = null;
1036        try
1037        {
1038                bos = new Base64.OutputStream(
1039                          new java.io.FileOutputStream( filename ), Base64.DECODE );
1040                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
1041                success = true;
1042        }   // end try
1043        catch( java.io.IOException e )
1044        {
1045            success = false;
1046        }   // end catch: IOException
1047        finally
1048        {
1049                try{ bos.close(); } catch( Exception e ){}
1050        }   // end finally
1051
1052        return success;
1053    }   // end decodeToFile
1054
1055
1056
1057
1058    /**
1059     * Convenience method for reading a base64-encoded
1060     * file and decoding it.
1061     *
1062     * @param filename Filename for reading encoded data
1063     * @return decoded byte array or null if unsuccessful
1064     *
1065     * @since 2.1
1066     */
1067    public static byte[] decodeFromFile( String filename )
1068    {
1069        byte[] decodedData = null;
1070        Base64.InputStream bis = null;
1071        try
1072        {
1073            // Set up some useful variables
1074            java.io.File file = new java.io.File( filename );
1075            byte[] buffer = null;
1076            int length   = 0;
1077            int numBytes = 0;
1078
1079            // Check for size of file
1080            if( file.length() > Integer.MAX_VALUE )
1081            {
1082                System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
1083                return null;
1084            }   // end if: file too big for int index
1085            buffer = new byte[ (int)file.length() ];
1086
1087            // Open a stream
1088            bis = new Base64.InputStream(
1089                      new java.io.BufferedInputStream(
1090                      new java.io.FileInputStream( file ) ), Base64.DECODE );
1091
1092            // Read until done
1093            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1094                length += numBytes;
1095
1096            // Save in a variable to return
1097            decodedData = new byte[ length ];
1098            System.arraycopy( buffer, 0, decodedData, 0, length );
1099
1100        }   // end try
1101        catch( java.io.IOException e )
1102        {
1103            System.err.println( "Error decoding from file " + filename );
1104        }   // end catch: IOException
1105        finally
1106        {
1107            try{ bis.close(); } catch( Exception e) {}
1108        }   // end finally
1109
1110        return decodedData;
1111    }   // end decodeFromFile
1112
1113
1114
1115    /**
1116     * Convenience method for reading a binary file
1117     * and base64-encoding it.
1118     *
1119     * @param filename Filename for reading binary data
1120     * @return base64-encoded string or null if unsuccessful
1121     *
1122     * @since 2.1
1123     */
1124    public static String encodeFromFile( String filename )
1125    {
1126        String encodedData = null;
1127        Base64.InputStream bis = null;
1128        try
1129        {
1130            // Set up some useful variables
1131            java.io.File file = new java.io.File( filename );
1132            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
1133            int length   = 0;
1134            int numBytes = 0;
1135
1136            // Open a stream
1137            bis = new Base64.InputStream(
1138                      new java.io.BufferedInputStream(
1139                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
1140
1141            // Read until done
1142            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1143                length += numBytes;
1144
1145            // Save in a variable to return
1146            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
1147
1148        }   // end try
1149        catch( java.io.IOException e )
1150        {
1151            System.err.println( "Error encoding from file " + filename );
1152        }   // end catch: IOException
1153        finally
1154        {
1155            try{ bis.close(); } catch( Exception e) {}
1156        }   // end finally
1157
1158        return encodedData;
1159        }   // end encodeFromFile
1160
1161    /**
1162     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1163     *
1164     * @param infile Input file
1165     * @param outfile Output file
1166     * @since 2.2
1167     */
1168    public static void encodeFileToFile( String infile, String outfile )
1169    {
1170        String encoded = Base64.encodeFromFile( infile );
1171        java.io.OutputStream out = null;
1172        try{
1173            out = new java.io.BufferedOutputStream(
1174                  new java.io.FileOutputStream( outfile ) );
1175            out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
1176        }   // end try
1177        catch( java.io.IOException ex ) {
1178            ex.printStackTrace();
1179        }   // end catch
1180        finally {
1181            try { out.close(); }
1182            catch( Exception ex ){}
1183        }   // end finally
1184    }   // end encodeFileToFile
1185
1186
1187    /**
1188     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1189     *
1190     * @param infile Input file
1191     * @param outfile Output file
1192     * @since 2.2
1193     */
1194    public static void decodeFileToFile( String infile, String outfile )
1195    {
1196        byte[] decoded = Base64.decodeFromFile( infile );
1197        java.io.OutputStream out = null;
1198        try{
1199            out = new java.io.BufferedOutputStream(
1200                  new java.io.FileOutputStream( outfile ) );
1201            out.write( decoded );
1202        }   // end try
1203        catch( java.io.IOException ex ) {
1204            ex.printStackTrace();
1205        }   // end catch
1206        finally {
1207            try { out.close(); }
1208            catch( Exception ex ){}
1209        }   // end finally
1210    }   // end decodeFileToFile
1211
1212
1213    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
1214
1215
1216
1217    /**
1218     * A {@link Base64.InputStream} will read data from another
1219     * <tt>java.io.InputStream</tt>, given in the constructor,
1220     * and encode/decode to/from Base64 notation on the fly.
1221     *
1222     * @see Base64
1223     * @since 1.3
1224     */
1225    public static class InputStream extends java.io.FilterInputStream
1226    {
1227        private boolean encode;         // Encoding or decoding
1228        private int     position;       // Current position in the buffer
1229        private byte[]  buffer;         // Small buffer holding converted data
1230        private int     bufferLength;   // Length of buffer (3 or 4)
1231        private int     numSigBytes;    // Number of meaningful bytes in the buffer
1232        private int     lineLength;
1233        private boolean breakLines;     // Break lines at less than 80 characters
1234		private int     options;        // Record options used to create the stream.
1235		private byte[]  alphabet;	    // Local copies to avoid extra method calls
1236		private byte[]  decodabet;		// Local copies to avoid extra method calls
1237
1238
1239        /**
1240         * Constructs a {@link Base64.InputStream} in DECODE mode.
1241         *
1242         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1243         * @since 1.3
1244         */
1245        public InputStream( java.io.InputStream in )
1246        {
1247            this( in, DECODE );
1248        }   // end constructor
1249
1250
1251        /**
1252         * Constructs a {@link Base64.InputStream} in
1253         * either ENCODE or DECODE mode.
1254         * <p>
1255         * Valid options:<pre>
1256         *   ENCODE or DECODE: Encode or Decode as data is read.
1257         *   DONT_BREAK_LINES: don't break lines at 76 characters
1258         *     (only meaningful when encoding)
1259         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1260         * </pre>
1261         * <p>
1262         * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1263         *
1264         *
1265         * @param in the <tt>java.io.InputStream</tt> from which to read data.
1266         * @param options Specified options
1267         * @see Base64#ENCODE
1268         * @see Base64#DECODE
1269         * @see Base64#DONT_BREAK_LINES
1270         * @since 2.0
1271         */
1272        public InputStream( java.io.InputStream in, int options )
1273        {
1274            super( in );
1275            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1276            this.encode       = (options & ENCODE) == ENCODE;
1277            this.bufferLength = encode ? 4 : 3;
1278            this.buffer       = new byte[ bufferLength ];
1279            this.position     = -1;
1280            this.lineLength   = 0;
1281			this.options      = options; // Record for later, mostly to determine which alphabet to use
1282			this.alphabet     = getAlphabet(options);
1283			this.decodabet    = getDecodabet(options);
1284        }   // end constructor
1285
1286        /**
1287         * Reads enough of the input stream to convert
1288         * to/from Base64 and returns the next byte.
1289         *
1290         * @return next byte
1291         * @since 1.3
1292         */
1293        public int read() throws java.io.IOException
1294        {
1295            // Do we need to get data?
1296            if( position < 0 )
1297            {
1298                if( encode )
1299                {
1300                    byte[] b3 = new byte[3];
1301                    int numBinaryBytes = 0;
1302                    for( int i = 0; i < 3; i++ )
1303                    {
1304                        try
1305                        {
1306                            int b = in.read();
1307
1308                            // If end of stream, b is -1.
1309                            if( b >= 0 )
1310                            {
1311                                b3[i] = (byte)b;
1312                                numBinaryBytes++;
1313                            }   // end if: not end of stream
1314
1315                        }   // end try: read
1316                        catch( java.io.IOException e )
1317                        {
1318                            // Only a problem if we got no data at all.
1319                            if( i == 0 )
1320                                throw e;
1321
1322                        }   // end catch
1323                    }   // end for: each needed input byte
1324
1325                    if( numBinaryBytes > 0 )
1326                    {
1327                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1328                        position = 0;
1329                        numSigBytes = 4;
1330                    }   // end if: got data
1331                    else
1332                    {
1333                        return -1;
1334                    }   // end else
1335                }   // end if: encoding
1336
1337                // Else decoding
1338                else
1339                {
1340                    byte[] b4 = new byte[4];
1341                    int i = 0;
1342                    for( i = 0; i < 4; i++ )
1343                    {
1344                        // Read four "meaningful" bytes:
1345                        int b = 0;
1346                        do{ b = in.read(); }
1347                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1348
1349                        if( b < 0 )
1350                            break; // Reads a -1 if end of stream
1351
1352                        b4[i] = (byte)b;
1353                    }   // end for: each needed input byte
1354
1355                    if( i == 4 )
1356                    {
1357                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1358                        position = 0;
1359                    }   // end if: got four characters
1360                    else if( i == 0 ){
1361                        return -1;
1362                    }   // end else if: also padded correctly
1363                    else
1364                    {
1365                        // Must have broken out from above.
1366                        throw new java.io.IOException( "Improperly padded Base64 input." );
1367                    }   // end
1368
1369                }   // end else: decode
1370            }   // end else: get data
1371
1372            // Got data?
1373            if( position >= 0 )
1374            {
1375                // End of relevant data?
1376                if( /*!encode &&*/ position >= numSigBytes )
1377                    return -1;
1378
1379                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1380                {
1381                    lineLength = 0;
1382                    return '\n';
1383                }   // end if
1384                else
1385                {
1386                    lineLength++;   // This isn't important when decoding
1387                                    // but throwing an extra "if" seems
1388                                    // just as wasteful.
1389
1390                    int b = buffer[ position++ ];
1391
1392                    if( position >= bufferLength )
1393                        position = -1;
1394
1395                    return b & 0xFF; // This is how you "cast" a byte that's
1396                                     // intended to be unsigned.
1397                }   // end else
1398            }   // end if: position >= 0
1399
1400            // Else error
1401            else
1402            {
1403                // When JDK1.4 is more accepted, use an assertion here.
1404                throw new java.io.IOException( "Error in Base64 code reading stream." );
1405            }   // end else
1406        }   // end read
1407
1408
1409        /**
1410         * Calls {@link #read()} repeatedly until the end of stream
1411         * is reached or <var>len</var> bytes are read.
1412         * Returns number of bytes read into array or -1 if
1413         * end of stream is encountered.
1414         *
1415         * @param dest array to hold values
1416         * @param off offset for array
1417         * @param len max number of bytes to read into array
1418         * @return bytes read into array or -1 if end of stream is encountered.
1419         * @since 1.3
1420         */
1421        public int read( byte[] dest, int off, int len ) throws java.io.IOException
1422        {
1423            int i;
1424            int b;
1425            for( i = 0; i < len; i++ )
1426            {
1427                b = read();
1428
1429                //if( b < 0 && i == 0 )
1430                //    return -1;
1431
1432                if( b >= 0 )
1433                    dest[off + i] = (byte)b;
1434                else if( i == 0 )
1435                    return -1;
1436                else
1437                    break; // Out of 'for' loop
1438            }   // end for: each byte read
1439            return i;
1440        }   // end read
1441
1442    }   // end inner class InputStream
1443
1444
1445
1446
1447
1448
1449    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1450
1451
1452
1453    /**
1454     * A {@link Base64.OutputStream} will write data to another
1455     * <tt>java.io.OutputStream</tt>, given in the constructor,
1456     * and encode/decode to/from Base64 notation on the fly.
1457     *
1458     * @see Base64
1459     * @since 1.3
1460     */
1461    public static class OutputStream extends java.io.FilterOutputStream
1462    {
1463        private boolean encode;
1464        private int     position;
1465        private byte[]  buffer;
1466        private int     bufferLength;
1467        private int     lineLength;
1468        private boolean breakLines;
1469        private byte[]  b4; // Scratch used in a few places
1470        private boolean suspendEncoding;
1471		private int options; // Record for later
1472		private byte[]  alphabet;	    // Local copies to avoid extra method calls
1473		private byte[]  decodabet;		// Local copies to avoid extra method calls
1474
1475        /**
1476         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1477         *
1478         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1479         * @since 1.3
1480         */
1481        public OutputStream( java.io.OutputStream out )
1482        {
1483            this( out, ENCODE );
1484        }   // end constructor
1485
1486
1487        /**
1488         * Constructs a {@link Base64.OutputStream} in
1489         * either ENCODE or DECODE mode.
1490         * <p>
1491         * Valid options:<pre>
1492         *   ENCODE or DECODE: Encode or Decode as data is read.
1493         *   DONT_BREAK_LINES: don't break lines at 76 characters
1494         *     (only meaningful when encoding)
1495         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1496         * </pre>
1497         * <p>
1498         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1499         *
1500         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1501         * @param options Specified options.
1502         * @see Base64#ENCODE
1503         * @see Base64#DECODE
1504         * @see Base64#DONT_BREAK_LINES
1505         * @since 1.3
1506         */
1507        public OutputStream( java.io.OutputStream out, int options )
1508        {
1509            super( out );
1510            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1511            this.encode       = (options & ENCODE) == ENCODE;
1512            this.bufferLength = encode ? 3 : 4;
1513            this.buffer       = new byte[ bufferLength ];
1514            this.position     = 0;
1515            this.lineLength   = 0;
1516            this.suspendEncoding = false;
1517            this.b4           = new byte[4];
1518			this.options      = options;
1519			this.alphabet     = getAlphabet(options);
1520			this.decodabet    = getDecodabet(options);
1521        }   // end constructor
1522
1523
1524        /**
1525         * Writes the byte to the output stream after
1526         * converting to/from Base64 notation.
1527         * When encoding, bytes are buffered three
1528         * at a time before the output stream actually
1529         * gets a write() call.
1530         * When decoding, bytes are buffered four
1531         * at a time.
1532         *
1533         * @param theByte the byte to write
1534         * @since 1.3
1535         */
1536        public void write(int theByte) throws java.io.IOException
1537        {
1538            // Encoding suspended?
1539            if( suspendEncoding )
1540            {
1541                super.out.write( theByte );
1542                return;
1543            }   // end if: supsended
1544
1545            // Encode?
1546            if( encode )
1547            {
1548                buffer[ position++ ] = (byte)theByte;
1549                if( position >= bufferLength )  // Enough to encode.
1550                {
1551                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
1552
1553                    lineLength += 4;
1554                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1555                    {
1556                        out.write( NEW_LINE );
1557                        lineLength = 0;
1558                    }   // end if: end of line
1559
1560                    position = 0;
1561                }   // end if: enough to output
1562            }   // end if: encoding
1563
1564            // Else, Decoding
1565            else
1566            {
1567                // Meaningful Base64 character?
1568                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1569                {
1570                    buffer[ position++ ] = (byte)theByte;
1571                    if( position >= bufferLength )  // Enough to output.
1572                    {
1573                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1574                        out.write( b4, 0, len );
1575                        //out.write( Base64.decode4to3( buffer ) );
1576                        position = 0;
1577                    }   // end if: enough to output
1578                }   // end if: meaningful base64 character
1579                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1580                {
1581                    throw new java.io.IOException( "Invalid character in Base64 data." );
1582                }   // end else: not white space either
1583            }   // end else: decoding
1584        }   // end write
1585
1586
1587
1588        /**
1589         * Calls {@link #write(int)} repeatedly until <var>len</var>
1590         * bytes are written.
1591         *
1592         * @param theBytes array from which to read bytes
1593         * @param off offset for array
1594         * @param len max number of bytes to read into array
1595         * @since 1.3
1596         */
1597        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1598        {
1599            // Encoding suspended?
1600            if( suspendEncoding )
1601            {
1602                super.out.write( theBytes, off, len );
1603                return;
1604            }   // end if: supsended
1605
1606            for( int i = 0; i < len; i++ )
1607            {
1608                write( theBytes[ off + i ] );
1609            }   // end for: each byte written
1610
1611        }   // end write
1612
1613
1614
1615        /**
1616         * Method added by PHIL. [Thanks, PHIL. -Rob]
1617         * This pads the buffer without closing the stream.
1618         */
1619        public void flushBase64() throws java.io.IOException
1620        {
1621            if( position > 0 )
1622            {
1623                if( encode )
1624                {
1625                    out.write( encode3to4( b4, buffer, position, options ) );
1626                    position = 0;
1627                }   // end if: encoding
1628                else
1629                {
1630                    throw new java.io.IOException( "Base64 input not properly padded." );
1631                }   // end else: decoding
1632            }   // end if: buffer partially full
1633
1634        }   // end flush
1635
1636
1637        /**
1638         * Flushes and closes (I think, in the superclass) the stream.
1639         *
1640         * @since 1.3
1641         */
1642        public void close() throws java.io.IOException
1643        {
1644            // 1. Ensure that pending characters are written
1645            flushBase64();
1646
1647            // 2. Actually close the stream
1648            // Base class both flushes and closes.
1649            super.close();
1650
1651            buffer = null;
1652            out    = null;
1653        }   // end close
1654
1655
1656
1657        /**
1658         * Suspends encoding of the stream.
1659         * May be helpful if you need to embed a piece of
1660         * base640-encoded data in a stream.
1661         *
1662         * @since 1.5.1
1663         */
1664        public void suspendEncoding() throws java.io.IOException
1665        {
1666            flushBase64();
1667            this.suspendEncoding = true;
1668        }   // end suspendEncoding
1669
1670
1671        /**
1672         * Resumes encoding of the stream.
1673         * May be helpful if you need to embed a piece of
1674         * base640-encoded data in a stream.
1675         *
1676         * @since 1.5.1
1677         */
1678        public void resumeEncoding()
1679        {
1680            this.suspendEncoding = false;
1681        }   // end resumeEncoding
1682
1683
1684
1685    }   // end inner class OutputStream
1686
1687
1688}   // end class Base64
1689
1690