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