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