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