001/**
002 *
003 * Copyright the original author or authors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.util;
018
019import java.util.logging.Level;
020import java.util.logging.Logger;
021
022/**
023 * <p>Encodes and decodes to and from Base64 notation.</p>
024 * This code was obtained from <a href="http://iharder.net/base64">http://iharder.net/base64</a></p>
025 *
026 *
027 * @author Robert Harder
028 * @author rob@iharder.net
029 * @version 2.2.1
030 */
031public class Base64
032{
033    private static final Logger LOGGER = Logger.getLogger(Base64.class.getName());
034
035    /* ********  P U B L I C   F I E L D S  ******** */
036
037    /** No options specified. Value is zero. */
038    public final static int NO_OPTIONS = 0;
039
040    /** Specify encoding. */
041    public final static int ENCODE = 1;
042
043
044    /** Specify decoding. */
045    public final static int DECODE = 0;
046
047
048    /** Specify that data should be gzip-compressed. */
049    public final static int GZIP = 2;
050
051
052    /** Don't break lines when encoding (violates strict Base64 specification) */
053    public final static int DONT_BREAK_LINES = 8;
054
055        /**
056         * Encode using Base64-like encoding that is URL- and Filename-safe as described
057         * in Section 4 of RFC3548:
058         * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
059         * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
060         * or at the very least should not be called Base64 without also specifying that is
061         * was encoded using the URL- and Filename-safe dialect.
062         */
063         public final static int URL_SAFE = 16;
064
065
066         /**
067          * Encode using the special "ordered" dialect of Base64 described here:
068          * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
069          */
070         public final static int ORDERED = 32;
071
072
073/* ********  P R I V A T E   F I E L D S  ******** */
074
075
076    /** Maximum line length (76) of Base64 output. */
077    private final static int MAX_LINE_LENGTH = 76;
078
079
080    /** The equals sign (=) as a byte. */
081    private final static byte EQUALS_SIGN = (byte)'=';
082
083
084    /** The new line character (\n) as a byte. */
085    private final static byte NEW_LINE = (byte)'\n';
086
087
088    /** Preferred encoding. */
089    private final static String PREFERRED_ENCODING = "UTF-8";
090
091
092    // I think I end up not using the BAD_ENCODING indicator.
093    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
094    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
095    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
096
097
098/* ********  S T A N D A R D   B A S E 6 4   A L P H A B E T  ******** */
099
100    /** The 64 valid Base64 values. */
101    //private final static byte[] ALPHABET;
102        /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
103        private final static byte[] _STANDARD_ALPHABET =
104    {
105        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
106        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
107        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
108        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
109        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
110        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
111        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
112        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
113        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
114        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
115    };
116
117
118    /**
119     * Translates a Base64 value to either its 6-bit reconstruction value
120     * or a negative number indicating some other meaning.
121     **/
122    private final static byte[] _STANDARD_DECODABET =
123    {
124        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
125        -5,-5,                                      // Whitespace: Tab and Linefeed
126        -9,-9,                                      // Decimal 11 - 12
127        -5,                                         // Whitespace: Carriage Return
128        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
129        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
130        -5,                                         // Whitespace: Space
131        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
132        62,                                         // Plus sign at decimal 43
133        -9,-9,-9,                                   // Decimal 44 - 46
134        63,                                         // Slash at decimal 47
135        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
136        -9,-9,-9,                                   // Decimal 58 - 60
137        -1,                                         // Equals sign at decimal 61
138        -9,-9,-9,                                      // Decimal 62 - 64
139        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
140        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
141        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
142        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
143        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
144        -9,-9,-9,-9                                 // Decimal 123 - 126
145        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
146        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
147        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
148        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
149        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
150        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
151        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
152        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
153        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
154        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
155    };
156
157
158/* ********  U R L   S A F E   B A S E 6 4   A L P H A B E T  ******** */
159
160        /**
161         * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
162         * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
163         * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
164         */
165    private final static byte[] _URL_SAFE_ALPHABET =
166    {
167      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
168      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
169      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
170      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
171      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
172      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
173      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
174      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
175      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
176      (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
177    };
178
179        /**
180         * Used in decoding URL- and Filename-safe dialects of Base64.
181         */
182    private final static byte[] _URL_SAFE_DECODABET =
183    {
184      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
185      -5,-5,                                      // Whitespace: Tab and Linefeed
186      -9,-9,                                      // Decimal 11 - 12
187      -5,                                         // Whitespace: Carriage Return
188      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
189      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
190      -5,                                         // Whitespace: Space
191      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
192      -9,                                         // Plus sign at decimal 43
193      -9,                                         // Decimal 44
194      62,                                         // Minus sign at decimal 45
195      -9,                                         // Decimal 46
196      -9,                                         // Slash at decimal 47
197      52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
198      -9,-9,-9,                                   // Decimal 58 - 60
199      -1,                                         // Equals sign at decimal 61
200      -9,-9,-9,                                   // Decimal 62 - 64
201      0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
202      14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
203      -9,-9,-9,-9,                                // Decimal 91 - 94
204      63,                                         // Underscore at decimal 95
205      -9,                                         // Decimal 96
206      26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
207      39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
208      -9,-9,-9,-9                                 // Decimal 123 - 126
209      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
210      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
211      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
212      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
213      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
214      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
215      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
216      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
217      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
218      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
219    };
220
221
222
223/* ********  O R D E R E D   B A S E 6 4   A L P H A B E T  ******** */
224
225        /**
226         * I don't get the point of this technique, but it is described here:
227         * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
228         */
229    private final static byte[] _ORDERED_ALPHABET =
230    {
231      (byte)'-',
232      (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
233      (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
234      (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
235      (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
236      (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
237      (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
238      (byte)'_',
239      (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
240      (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
241      (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
242      (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
243    };
244
245        /**
246         * Used in decoding the "ordered" dialect of Base64.
247         */
248    private final static byte[] _ORDERED_DECODABET =
249    {
250      -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
251      -5,-5,                                      // Whitespace: Tab and Linefeed
252      -9,-9,                                      // Decimal 11 - 12
253      -5,                                         // Whitespace: Carriage Return
254      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
255      -9,-9,-9,-9,-9,                             // Decimal 27 - 31
256      -5,                                         // Whitespace: Space
257      -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
258      -9,                                         // Plus sign at decimal 43
259      -9,                                         // Decimal 44
260      0,                                          // Minus sign at decimal 45
261      -9,                                         // Decimal 46
262      -9,                                         // Slash at decimal 47
263      1,2,3,4,5,6,7,8,9,10,                       // Numbers zero through nine
264      -9,-9,-9,                                   // Decimal 58 - 60
265      -1,                                         // Equals sign at decimal 61
266      -9,-9,-9,                                   // Decimal 62 - 64
267      11,12,13,14,15,16,17,18,19,20,21,22,23,     // Letters 'A' through 'M'
268      24,25,26,27,28,29,30,31,32,33,34,35,36,     // Letters 'N' through 'Z'
269      -9,-9,-9,-9,                                // Decimal 91 - 94
270      37,                                         // Underscore at decimal 95
271      -9,                                         // Decimal 96
272      38,39,40,41,42,43,44,45,46,47,48,49,50,     // Letters 'a' through 'm'
273      51,52,53,54,55,56,57,58,59,60,61,62,63,     // Letters 'n' through 'z'
274      -9,-9,-9,-9                                 // Decimal 123 - 126
275      /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
276        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
277        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
278        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
279        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
280        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
281        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
282        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
283        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
284        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
285    };
286
287
288/* ********  D E T E R M I N E   W H I C H   A L H A B E T  ******** */
289
290
291        /**
292         * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
293         * the options specified.
294         * It's possible, though silly, to specify ORDERED and URLSAFE
295         * in which case one of them will be picked, though there is
296         * no guarantee as to which one will be picked.
297         */
298        private final static byte[] getAlphabet( int options )
299        {
300                if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET;
301                else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET;
302                else return _STANDARD_ALPHABET;
303
304        }       // end getAlphabet
305
306
307        /**
308         * Returns one of the _SOMETHING_DECODABET byte arrays depending on
309         * the options specified.
310         * It's possible, though silly, to specify ORDERED and URL_SAFE
311         * in which case one of them will be picked, though there is
312         * no guarantee as to which one will be picked.
313         */
314        private final static byte[] getDecodabet( int options )
315        {
316                if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET;
317                else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET;
318                else return _STANDARD_DECODABET;
319
320        }       // end getAlphabet
321
322
323
324    /** Defeats instantiation. */
325    private Base64(){}
326
327/* ********  E N C O D I N G   M E T H O D S  ******** */
328
329
330    /**
331     * Encodes up to the first three bytes of array <var>threeBytes</var>
332     * and returns a four-byte array in Base64 notation.
333     * The actual number of significant bytes in your array is
334     * given by <var>numSigBytes</var>.
335     * The array <var>threeBytes</var> needs only be as big as
336     * <var>numSigBytes</var>.
337     * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
338     *
339     * @param b4 A reusable byte array to reduce array instantiation
340     * @param threeBytes the array to convert
341     * @param numSigBytes the number of significant bytes in your array
342     * @return four byte array in Base64 notation.
343     * @since 1.5.1
344     */
345    private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options )
346    {
347        encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
348        return b4;
349    }   // end encode3to4
350
351
352    /**
353     * <p>Encodes up to three bytes of the array <var>source</var>
354     * and writes the resulting four Base64 bytes to <var>destination</var>.
355     * The source and destination arrays can be manipulated
356     * anywhere along their length by specifying
357     * <var>srcOffset</var> and <var>destOffset</var>.
358     * This method does not check to make sure your arrays
359     * are large enough to accomodate <var>srcOffset</var> + 3 for
360     * the <var>source</var> array or <var>destOffset</var> + 4 for
361     * the <var>destination</var> array.
362     * The actual number of significant bytes in your array is
363     * given by <var>numSigBytes</var>.</p>
364         * <p>This is the lowest level of the encoding methods with
365         * all possible parameters.</p>
366     *
367     * @param source the array to convert
368     * @param srcOffset the index where conversion begins
369     * @param numSigBytes the number of significant bytes in your array
370     * @param destination the array to hold the conversion
371     * @param destOffset the index where output will be put
372     * @return the <var>destination</var> array
373     * @since 1.3
374     */
375    private static byte[] encode3to4(
376     byte[] source, int srcOffset, int numSigBytes,
377     byte[] destination, int destOffset, int options )
378    {
379                byte[] ALPHABET = getAlphabet( options );
380
381        //           1         2         3
382        // 01234567890123456789012345678901 Bit position
383        // --------000000001111111122222222 Array position from threeBytes
384        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
385        //          >>18  >>12  >> 6  >> 0  Right shift necessary
386        //                0x3f  0x3f  0x3f  Additional AND
387
388        // Create buffer with zero-padding if there are only one or two
389        // significant bytes passed in the array.
390        // We have to shift left 24 in order to flush out the 1's that appear
391        // when Java treats a value as negative that is cast from a byte to an int.
392        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
393                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
394                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
395
396        switch( numSigBytes )
397        {
398            case 3:
399                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
400                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
401                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
402                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
403                return destination;
404
405            case 2:
406                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
407                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
408                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
409                destination[ destOffset + 3 ] = EQUALS_SIGN;
410                return destination;
411
412            case 1:
413                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
414                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
415                destination[ destOffset + 2 ] = EQUALS_SIGN;
416                destination[ destOffset + 3 ] = EQUALS_SIGN;
417                return destination;
418
419            default:
420                return destination;
421        }   // end switch
422    }   // end encode3to4
423
424
425
426    /**
427     * Serializes an object and returns the Base64-encoded
428     * version of that serialized object. If the object
429     * cannot be serialized or there is another error,
430     * the method will return <tt>null</tt>.
431     * The object is not GZip-compressed before being encoded.
432     *
433     * @param serializableObject The object to encode
434     * @return The Base64-encoded object
435     * @since 1.4
436     */
437    public static String encodeObject( java.io.Serializable serializableObject )
438    {
439        return encodeObject( serializableObject, NO_OPTIONS );
440    }   // end encodeObject
441
442
443
444    /**
445     * Serializes an object and returns the Base64-encoded
446     * version of that serialized object. If the object
447     * cannot be serialized or there is another error,
448     * the method will return <tt>null</tt>.
449     * <p>
450     * Valid options:<pre>
451     *   GZIP: gzip-compresses object before encoding it.
452     *   DONT_BREAK_LINES: don't break lines at 76 characters
453     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
454     * </pre>
455     * <p>
456     * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
457     * <p>
458     * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
459     *
460     * @param serializableObject The object to encode
461     * @param options Specified options
462     * @return The Base64-encoded object
463     * @see Base64#GZIP
464     * @see Base64#DONT_BREAK_LINES
465     * @since 2.0
466     */
467    public static String encodeObject( java.io.Serializable serializableObject, int options )
468    {
469        // Streams
470        java.io.ByteArrayOutputStream  baos  = null;
471        java.io.OutputStream           b64os = null;
472        java.io.ObjectOutputStream     oos   = null;
473        java.util.zip.GZIPOutputStream gzos  = null;
474
475        // Isolate options
476        int gzip           = (options & GZIP);
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            LOGGER.log(Level.SEVERE, "Error encoding object", e);
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                LOGGER.log(Level.SEVERE, "Error encoding bytes", e);
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                LOGGER.log(Level.SEVERE, e.getMessage(), e);
782                LOGGER.severe(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
783                LOGGER.severe(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
784                LOGGER.severe(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
785                LOGGER.severe(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
786                return -1;
787            }   // end catch
788        }
789    }   // end decodeToBytes
790
791
792
793
794    /**
795     * Very low-level access to decoding ASCII characters in
796     * the form of a byte array. Does not support automatically
797     * gunzipping or any other "fancy" features.
798     *
799     * @param source The Base64 encoded data
800     * @param off    The offset of where to begin decoding
801     * @param len    The length of characters to decode
802     * @return decoded data
803     * @since 1.3
804     */
805    public static byte[] decode( byte[] source, int off, int len, int options )
806    {
807                byte[] DECODABET = getDecodabet( options );
808
809        int    len34   = len * 3 / 4;
810        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
811        int    outBuffPosn = 0;
812
813        byte[] b4        = new byte[4];
814        int    b4Posn    = 0;
815        int    i         = 0;
816        byte   sbiCrop   = 0;
817        byte   sbiDecode = 0;
818        for( i = off; i < off+len; i++ )
819        {
820            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
821            sbiDecode = DECODABET[ sbiCrop ];
822
823            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
824            {
825                if( sbiDecode >= EQUALS_SIGN_ENC )
826                {
827                    b4[ b4Posn++ ] = sbiCrop;
828                    if( b4Posn > 3 )
829                    {
830                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
831                        b4Posn = 0;
832
833                        // If that was the equals sign, break out of 'for' loop
834                        if( sbiCrop == EQUALS_SIGN )
835                            break;
836                    }   // end if: quartet built
837
838                }   // end if: equals sign or better
839
840            }   // end if: white space, equals sign or better
841            else
842            {
843                LOGGER.warning("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
844                return null;
845            }   // end else:
846        }   // each input character
847
848        byte[] out = new byte[ outBuffPosn ];
849        System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
850        return out;
851    }   // end decode
852
853
854
855
856    /**
857     * Decodes data from Base64 notation, automatically
858     * detecting gzip-compressed data and decompressing it.
859     *
860     * @param s the string to decode
861     * @return the decoded data
862     * @since 1.4
863     */
864    public static byte[] decode( String s )
865        {
866                return decode( s, NO_OPTIONS );
867        }
868
869
870    /**
871     * Decodes data from Base64 notation, automatically
872     * detecting gzip-compressed data and decompressing it.
873     *
874     * @param s the string to decode
875         * @param options encode options such as URL_SAFE
876     * @return the decoded data
877     * @since 1.4
878     */
879    public static byte[] decode( String s, int options )
880    {
881        byte[] bytes;
882        try
883        {
884            bytes = s.getBytes( PREFERRED_ENCODING );
885        }   // end try
886        catch( java.io.UnsupportedEncodingException uee )
887        {
888            bytes = s.getBytes();
889        }   // end catch
890                //</change>
891
892        // Decode
893        bytes = decode( bytes, 0, bytes.length, options );
894
895
896        // Check to see if it's gzip-compressed
897        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
898        if( bytes != null && bytes.length >= 4 )
899        {
900
901            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
902            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
903            {
904                java.io.ByteArrayInputStream  bais = null;
905                java.util.zip.GZIPInputStream gzis = null;
906                java.io.ByteArrayOutputStream baos = null;
907                byte[] buffer = new byte[2048];
908                int    length = 0;
909
910                try
911                {
912                    baos = new java.io.ByteArrayOutputStream();
913                    bais = new java.io.ByteArrayInputStream( bytes );
914                    gzis = new java.util.zip.GZIPInputStream( bais );
915
916                    while( ( length = gzis.read( buffer ) ) >= 0 )
917                    {
918                        baos.write(buffer,0,length);
919                    }   // end while: reading input
920
921                    // No error? Get new bytes.
922                    bytes = baos.toByteArray();
923
924                }   // end try
925                catch( java.io.IOException e )
926                {
927                    // Just return originally-decoded bytes
928                }   // end catch
929                finally
930                {
931                    try{ baos.close(); } catch( Exception e ){}
932                    try{ gzis.close(); } catch( Exception e ){}
933                    try{ bais.close(); } catch( Exception e ){}
934                }   // end finally
935
936            }   // end if: gzipped
937        }   // end if: bytes.length >= 2
938
939        return bytes;
940    }   // end decode
941
942
943
944
945    /**
946     * Attempts to decode Base64 data and deserialize a Java
947     * Object within. Returns <tt>null</tt> if there was an error.
948     *
949     * @param encodedObject The Base64 data to decode
950     * @return The decoded and deserialized object
951     * @since 1.5
952     */
953    public static Object decodeToObject( String encodedObject )
954    {
955        // Decode and gunzip if necessary
956        byte[] objBytes = decode( encodedObject );
957
958        java.io.ByteArrayInputStream  bais = null;
959        java.io.ObjectInputStream     ois  = null;
960        Object obj = null;
961
962        try
963        {
964            bais = new java.io.ByteArrayInputStream( objBytes );
965            ois  = new java.io.ObjectInputStream( bais );
966
967            obj = ois.readObject();
968        }   // end try
969        catch( java.io.IOException e )
970        {
971            LOGGER.log(Level.SEVERE, "Error reading object", e);
972            obj = null;
973        }   // end catch
974        catch( java.lang.ClassNotFoundException e )
975        {
976            LOGGER.log(Level.SEVERE, "Class not found for encoded object", e);
977            obj = null;
978        }   // end catch
979        finally
980        {
981            try{ bais.close(); } catch( Exception e ){}
982            try{ ois.close();  } catch( Exception e ){}
983        }   // end finally
984
985        return obj;
986    }   // end decodeObject
987
988
989
990    /**
991     * Convenience method for encoding data to a file.
992     *
993     * @param dataToEncode byte array of data to encode in base64 form
994     * @param filename Filename for saving encoded data
995     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
996     *
997     * @since 2.1
998     */
999    public static boolean encodeToFile( byte[] dataToEncode, String filename )
1000    {
1001        boolean success = false;
1002        Base64.OutputStream bos = null;
1003        try
1004        {
1005            bos = new Base64.OutputStream(
1006                      new java.io.FileOutputStream( filename ), Base64.ENCODE );
1007            bos.write( dataToEncode );
1008            success = true;
1009        }   // end try
1010        catch( java.io.IOException e )
1011        {
1012
1013            success = false;
1014        }   // end catch: IOException
1015        finally
1016        {
1017            try{ bos.close(); } catch( Exception e ){}
1018        }   // end finally
1019
1020        return success;
1021    }   // end encodeToFile
1022
1023
1024    /**
1025     * Convenience method for decoding data to a file.
1026     *
1027     * @param dataToDecode Base64-encoded data as a string
1028     * @param filename Filename for saving decoded data
1029     * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1030     *
1031     * @since 2.1
1032     */
1033    public static boolean decodeToFile( String dataToDecode, String filename )
1034    {
1035        boolean success = false;
1036        Base64.OutputStream bos = null;
1037        try
1038        {
1039                bos = new Base64.OutputStream(
1040                          new java.io.FileOutputStream( filename ), Base64.DECODE );
1041                bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
1042                success = true;
1043        }   // end try
1044        catch( java.io.IOException e )
1045        {
1046            success = false;
1047        }   // end catch: IOException
1048        finally
1049        {
1050                try{ bos.close(); } catch( Exception e ){}
1051        }   // end finally
1052
1053        return success;
1054    }   // end decodeToFile
1055
1056
1057
1058
1059    /**
1060     * Convenience method for reading a base64-encoded
1061     * file and decoding it.
1062     *
1063     * @param filename Filename for reading encoded data
1064     * @return decoded byte array or null if unsuccessful
1065     *
1066     * @since 2.1
1067     */
1068    public static byte[] decodeFromFile( String filename )
1069    {
1070        byte[] decodedData = null;
1071        Base64.InputStream bis = null;
1072        try
1073        {
1074            // Set up some useful variables
1075            java.io.File file = new java.io.File( filename );
1076            byte[] buffer = null;
1077            int length   = 0;
1078            int numBytes = 0;
1079
1080            // Check for size of file
1081            if( file.length() > Integer.MAX_VALUE )
1082            {
1083                LOGGER.warning("File is too big for this convenience method (" + file.length() + " bytes).");
1084                return null;
1085            }   // end if: file too big for int index
1086            buffer = new byte[ (int)file.length() ];
1087
1088            // Open a stream
1089            bis = new Base64.InputStream(
1090                      new java.io.BufferedInputStream(
1091                      new java.io.FileInputStream( file ) ), Base64.DECODE );
1092
1093            // Read until done
1094            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1095                length += numBytes;
1096
1097            // Save in a variable to return
1098            decodedData = new byte[ length ];
1099            System.arraycopy( buffer, 0, decodedData, 0, length );
1100
1101        }   // end try
1102        catch( java.io.IOException e )
1103        {
1104            LOGGER.log(Level.SEVERE, "Error decoding from file " + filename, e);
1105        }   // end catch: IOException
1106        finally
1107        {
1108            try{ bis.close(); } catch( Exception e) {}
1109        }   // end finally
1110
1111        return decodedData;
1112    }   // end decodeFromFile
1113
1114
1115
1116    /**
1117     * Convenience method for reading a binary file
1118     * and base64-encoding it.
1119     *
1120     * @param filename Filename for reading binary data
1121     * @return base64-encoded string or null if unsuccessful
1122     *
1123     * @since 2.1
1124     */
1125    public static String encodeFromFile( String filename )
1126    {
1127        String encodedData = null;
1128        Base64.InputStream bis = null;
1129        try
1130        {
1131            // Set up some useful variables
1132            java.io.File file = new java.io.File( filename );
1133            byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1)
1134            int length   = 0;
1135            int numBytes = 0;
1136
1137            // Open a stream
1138            bis = new Base64.InputStream(
1139                      new java.io.BufferedInputStream(
1140                      new java.io.FileInputStream( file ) ), Base64.ENCODE );
1141
1142            // Read until done
1143            while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
1144                length += numBytes;
1145
1146            // Save in a variable to return
1147            encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
1148
1149        }   // end try
1150        catch( java.io.IOException e )
1151        {
1152            LOGGER.log(Level.SEVERE, "Error encoding from file " + filename, e);
1153        }   // end catch: IOException
1154        finally
1155        {
1156            try{ bis.close(); } catch( Exception e) {}
1157        }   // end finally
1158
1159        return encodedData;
1160        }   // end encodeFromFile
1161
1162    /**
1163     * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1164     *
1165     * @param infile Input file
1166     * @param outfile Output file
1167     * @since 2.2
1168     */
1169    public static void encodeFileToFile( String infile, String outfile )
1170    {
1171        String encoded = Base64.encodeFromFile( infile );
1172        java.io.OutputStream out = null;
1173        try{
1174            out = new java.io.BufferedOutputStream(
1175                  new java.io.FileOutputStream( outfile ) );
1176            out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
1177        }   // end try
1178        catch( java.io.IOException ex ) {
1179            LOGGER.log(Level.SEVERE, "Error encoding file " + infile, ex);
1180        }   // end catch
1181        finally {
1182            try { out.close(); }
1183            catch( Exception ex ){}
1184        }   // end finally
1185    }   // end encodeFileToFile
1186
1187
1188    /**
1189     * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1190     *
1191     * @param infile Input file
1192     * @param outfile Output file
1193     * @since 2.2
1194     */
1195    public static void decodeFileToFile( String infile, String outfile )
1196    {
1197        byte[] decoded = Base64.decodeFromFile( infile );
1198        java.io.OutputStream out = null;
1199        try{
1200            out = new java.io.BufferedOutputStream(
1201                  new java.io.FileOutputStream( outfile ) );
1202            out.write( decoded );
1203        }   // end try
1204        catch( java.io.IOException ex ) {
1205            LOGGER.log(Level.SEVERE, "Error decoding file " + infile, ex);
1206        }   // end catch
1207        finally {
1208            try { out.close(); }
1209            catch( Exception ex ){}
1210        }   // end finally
1211    }   // end decodeFileToFile
1212
1213
1214    /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
1215
1216
1217
1218    /**
1219     * A {@link Base64.InputStream} will read data from another
1220     * <tt>java.io.InputStream</tt>, given in the constructor,
1221     * and encode/decode to/from Base64 notation on the fly.
1222     *
1223     * @see Base64
1224     * @since 1.3
1225     */
1226    public static class InputStream extends java.io.FilterInputStream
1227    {
1228        private boolean encode;         // Encoding or decoding
1229        private int     position;       // Current position in the buffer
1230        private byte[]  buffer;         // Small buffer holding converted data
1231        private int     bufferLength;   // Length of buffer (3 or 4)
1232        private int     numSigBytes;    // Number of meaningful bytes in the buffer
1233        private int     lineLength;
1234        private boolean breakLines;     // Break lines at less than 80 characters
1235        private int options; // Record options used to create the stream.
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.decodabet = getDecodabet(options);
1283        }   // end constructor
1284
1285        /**
1286         * Reads enough of the input stream to convert
1287         * to/from Base64 and returns the next byte.
1288         *
1289         * @return next byte
1290         * @since 1.3
1291         */
1292        public int read() throws java.io.IOException
1293        {
1294            // Do we need to get data?
1295            if( position < 0 )
1296            {
1297                if( encode )
1298                {
1299                    byte[] b3 = new byte[3];
1300                    int numBinaryBytes = 0;
1301                    for( int i = 0; i < 3; i++ )
1302                    {
1303                        try
1304                        {
1305                            int b = in.read();
1306
1307                            // If end of stream, b is -1.
1308                            if( b >= 0 )
1309                            {
1310                                b3[i] = (byte)b;
1311                                numBinaryBytes++;
1312                            }   // end if: not end of stream
1313
1314                        }   // end try: read
1315                        catch( java.io.IOException e )
1316                        {
1317                            // Only a problem if we got no data at all.
1318                            if( i == 0 )
1319                                throw e;
1320
1321                        }   // end catch
1322                    }   // end for: each needed input byte
1323
1324                    if( numBinaryBytes > 0 )
1325                    {
1326                        encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1327                        position = 0;
1328                        numSigBytes = 4;
1329                    }   // end if: got data
1330                    else
1331                    {
1332                        return -1;
1333                    }   // end else
1334                }   // end if: encoding
1335
1336                // Else decoding
1337                else
1338                {
1339                    byte[] b4 = new byte[4];
1340                    int i = 0;
1341                    for( i = 0; i < 4; i++ )
1342                    {
1343                        // Read four "meaningful" bytes:
1344                        int b = 0;
1345                        do{ b = in.read(); }
1346                        while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1347
1348                        if( b < 0 )
1349                            break; // Reads a -1 if end of stream
1350
1351                        b4[i] = (byte)b;
1352                    }   // end for: each needed input byte
1353
1354                    if( i == 4 )
1355                    {
1356                        numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1357                        position = 0;
1358                    }   // end if: got four characters
1359                    else if( i == 0 ){
1360                        return -1;
1361                    }   // end else if: also padded correctly
1362                    else
1363                    {
1364                        // Must have broken out from above.
1365                        throw new java.io.IOException( "Improperly padded Base64 input." );
1366                    }   // end
1367
1368                }   // end else: decode
1369            }   // end else: get data
1370
1371            // Got data?
1372            if( position >= 0 )
1373            {
1374                // End of relevant data?
1375                if( /*!encode &&*/ position >= numSigBytes )
1376                    return -1;
1377
1378                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1379                {
1380                    lineLength = 0;
1381                    return '\n';
1382                }   // end if
1383                else
1384                {
1385                    lineLength++;   // This isn't important when decoding
1386                                    // but throwing an extra "if" seems
1387                                    // just as wasteful.
1388
1389                    int b = buffer[ position++ ];
1390
1391                    if( position >= bufferLength )
1392                        position = -1;
1393
1394                    return b & 0xFF; // This is how you "cast" a byte that's
1395                                     // intended to be unsigned.
1396                }   // end else
1397            }   // end if: position >= 0
1398
1399            // Else error
1400            else
1401            {
1402                // When JDK1.4 is more accepted, use an assertion here.
1403                throw new java.io.IOException( "Error in Base64 code reading stream." );
1404            }   // end else
1405        }   // end read
1406
1407
1408        /**
1409         * Calls {@link #read()} repeatedly until the end of stream
1410         * is reached or <var>len</var> bytes are read.
1411         * Returns number of bytes read into array or -1 if
1412         * end of stream is encountered.
1413         *
1414         * @param dest array to hold values
1415         * @param off offset for array
1416         * @param len max number of bytes to read into array
1417         * @return bytes read into array or -1 if end of stream is encountered.
1418         * @since 1.3
1419         */
1420        public int read( byte[] dest, int off, int len ) throws java.io.IOException
1421        {
1422            int i;
1423            int b;
1424            for( i = 0; i < len; i++ )
1425            {
1426                b = read();
1427
1428                //if( b < 0 && i == 0 )
1429                //    return -1;
1430
1431                if( b >= 0 )
1432                    dest[off + i] = (byte)b;
1433                else if( i == 0 )
1434                    return -1;
1435                else
1436                    break; // Out of 'for' loop
1437            }   // end for: each byte read
1438            return i;
1439        }   // end read
1440
1441    }   // end inner class InputStream
1442
1443
1444
1445
1446
1447
1448    /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1449
1450
1451
1452    /**
1453     * A {@link Base64.OutputStream} will write data to another
1454     * <tt>java.io.OutputStream</tt>, given in the constructor,
1455     * and encode/decode to/from Base64 notation on the fly.
1456     *
1457     * @see Base64
1458     * @since 1.3
1459     */
1460    public static class OutputStream extends java.io.FilterOutputStream
1461    {
1462        private boolean encode;
1463        private int     position;
1464        private byte[]  buffer;
1465        private int     bufferLength;
1466        private int     lineLength;
1467        private boolean breakLines;
1468        private byte[]  b4; // Scratch used in a few places
1469        private boolean suspendEncoding;
1470        private int options; // Record for later
1471        private byte[] decodabet; // Local copies to avoid extra method calls
1472
1473        /**
1474         * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1475         *
1476         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1477         * @since 1.3
1478         */
1479        public OutputStream( java.io.OutputStream out )
1480        {
1481            this( out, ENCODE );
1482        }   // end constructor
1483
1484
1485        /**
1486         * Constructs a {@link Base64.OutputStream} in
1487         * either ENCODE or DECODE mode.
1488         * <p>
1489         * Valid options:<pre>
1490         *   ENCODE or DECODE: Encode or Decode as data is read.
1491         *   DONT_BREAK_LINES: don't break lines at 76 characters
1492         *     (only meaningful when encoding)
1493         *     <i>Note: Technically, this makes your encoding non-compliant.</i>
1494         * </pre>
1495         * <p>
1496         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1497         *
1498         * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1499         * @param options Specified options.
1500         * @see Base64#ENCODE
1501         * @see Base64#DECODE
1502         * @see Base64#DONT_BREAK_LINES
1503         * @since 1.3
1504         */
1505        public OutputStream( java.io.OutputStream out, int options )
1506        {
1507            super( out );
1508            this.breakLines   = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1509            this.encode       = (options & ENCODE) == ENCODE;
1510            this.bufferLength = encode ? 3 : 4;
1511            this.buffer       = new byte[ bufferLength ];
1512            this.position     = 0;
1513            this.lineLength   = 0;
1514            this.suspendEncoding = false;
1515            this.b4           = new byte[4];
1516            this.options = options;
1517            this.decodabet = getDecodabet(options);
1518        }   // end constructor
1519
1520
1521        /**
1522         * Writes the byte to the output stream after
1523         * converting to/from Base64 notation.
1524         * When encoding, bytes are buffered three
1525         * at a time before the output stream actually
1526         * gets a write() call.
1527         * When decoding, bytes are buffered four
1528         * at a time.
1529         *
1530         * @param theByte the byte to write
1531         * @since 1.3
1532         */
1533        public void write(int theByte) throws java.io.IOException
1534        {
1535            // Encoding suspended?
1536            if( suspendEncoding )
1537            {
1538                super.out.write( theByte );
1539                return;
1540            }   // end if: supsended
1541
1542            // Encode?
1543            if( encode )
1544            {
1545                buffer[ position++ ] = (byte)theByte;
1546                if( position >= bufferLength )  // Enough to encode.
1547                {
1548                    out.write( encode3to4( b4, buffer, bufferLength, options ) );
1549
1550                    lineLength += 4;
1551                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1552                    {
1553                        out.write( NEW_LINE );
1554                        lineLength = 0;
1555                    }   // end if: end of line
1556
1557                    position = 0;
1558                }   // end if: enough to output
1559            }   // end if: encoding
1560
1561            // Else, Decoding
1562            else
1563            {
1564                // Meaningful Base64 character?
1565                if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1566                {
1567                    buffer[ position++ ] = (byte)theByte;
1568                    if( position >= bufferLength )  // Enough to output.
1569                    {
1570                        int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1571                        out.write( b4, 0, len );
1572                        //out.write( Base64.decode4to3( buffer ) );
1573                        position = 0;
1574                    }   // end if: enough to output
1575                }   // end if: meaningful base64 character
1576                else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1577                {
1578                    throw new java.io.IOException( "Invalid character in Base64 data." );
1579                }   // end else: not white space either
1580            }   // end else: decoding
1581        }   // end write
1582
1583
1584
1585        /**
1586         * Calls {@link #write(int)} repeatedly until <var>len</var>
1587         * bytes are written.
1588         *
1589         * @param theBytes array from which to read bytes
1590         * @param off offset for array
1591         * @param len max number of bytes to read into array
1592         * @since 1.3
1593         */
1594        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1595        {
1596            // Encoding suspended?
1597            if( suspendEncoding )
1598            {
1599                super.out.write( theBytes, off, len );
1600                return;
1601            }   // end if: supsended
1602
1603            for( int i = 0; i < len; i++ )
1604            {
1605                write( theBytes[ off + i ] );
1606            }   // end for: each byte written
1607
1608        }   // end write
1609
1610
1611
1612        /**
1613         * Method added by PHIL. [Thanks, PHIL. -Rob]
1614         * This pads the buffer without closing the stream.
1615         */
1616        public void flushBase64() throws java.io.IOException
1617        {
1618            if( position > 0 )
1619            {
1620                if( encode )
1621                {
1622                    out.write( encode3to4( b4, buffer, position, options ) );
1623                    position = 0;
1624                }   // end if: encoding
1625                else
1626                {
1627                    throw new java.io.IOException( "Base64 input not properly padded." );
1628                }   // end else: decoding
1629            }   // end if: buffer partially full
1630
1631        }   // end flush
1632
1633
1634        /**
1635         * Flushes and closes (I think, in the superclass) the stream.
1636         *
1637         * @since 1.3
1638         */
1639        public void close() throws java.io.IOException
1640        {
1641            // 1. Ensure that pending characters are written
1642            flushBase64();
1643
1644            // 2. Actually close the stream
1645            // Base class both flushes and closes.
1646            super.close();
1647
1648            buffer = null;
1649            out    = null;
1650        }   // end close
1651
1652
1653
1654        /**
1655         * Suspends encoding of the stream.
1656         * May be helpful if you need to embed a piece of
1657         * base640-encoded data in a stream.
1658         *
1659         * @since 1.5.1
1660         */
1661        public void suspendEncoding() throws java.io.IOException
1662        {
1663            flushBase64();
1664            this.suspendEncoding = true;
1665        }   // end suspendEncoding
1666
1667
1668        /**
1669         * Resumes encoding of the stream.
1670         * May be helpful if you need to embed a piece of
1671         * base640-encoded data in a stream.
1672         *
1673         * @since 1.5.1
1674         */
1675        public void resumeEncoding()
1676        {
1677            this.suspendEncoding = false;
1678        }   // end resumeEncoding
1679
1680
1681
1682    }   // end inner class OutputStream
1683
1684
1685}   // end class Base64
1686