KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > drftpd > slave > async > Base64


1 /**
2  * Encodes and decodes to and from Base64 notation.
3  *
4  * <p>
5  * Change Log:
6  * </p>
7  * <ul>
8  * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
9  * with other encodings (like EBCDIC).</li>
10  * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
11  * encoded data was a single byte.</li>
12  * <li>v2.0 - I got rid of methods that used booleans to set options.
13  * Now everything is more consolidated and cleaner. The code now detects
14  * when data that's being decoded is gzip-compressed and will decompress it
15  * automatically. Generally things are cleaner. You'll probably have to
16  * change some method calls that you were making to support the new
17  * options format (<tt>int</tt>s that you "OR" together).</li>
18  * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
19  * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
20  * Added the ability to "suspend" encoding in the Output Stream so
21  * you can turn on and off the encoding if you need to embed base64
22  * data in an otherwise "normal" stream (like an XML file).</li>
23  * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
24  * This helps when using GZIP streams.
25  * Added the ability to GZip-compress objects before encoding them.</li>
26  * <li>v1.4 - Added helper methods to read/write files.</li>
27  * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
28  * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
29  * where last buffer being read, if not completely full, was not returned.</li>
30  * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
31  * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
32  * </ul>
33  *
34  * <p>
35  * I am placing this code in the Public Domain. Do with it as you will.
36  * This software comes with no guarantees or warranties but with
37  * plenty of well-wishing instead!
38  * Please visit <a HREF="http://iharder.net/xmlizable">http://iharder.net/base64</a>
39  * periodically to check for updates or to contribute improvements.
40  * </p>
41  *
42  * @author Robert Harder
43  * @author rob@iharder.net
44  * @version 2.0
45  */

46 package org.drftpd.slave.async;
47
48 public class Base64 {
49     
50     /* ******** P U B L I C F I E L D S ******** */
51     
52     
53     /** No options specified. Value is zero. */
54     public final static int NO_OPTIONS = 0;
55     
56     /** Specify encoding. */
57     public final static int ENCODE = 1;
58     
59     
60     /** Specify decoding. */
61     public final static int DECODE = 0;
62     
63     
64     /** Specify that data should be gzip-compressed. */
65     public final static int GZIP = 2;
66     
67     
68     /** Don't break lines when encoding (violates strict Base64 specification) */
69     public final static int DONT_BREAK_LINES = 8;
70     
71     
72     /* ******** P R I V A T E F I E L D S ******** */
73     
74     
75     /** Maximum line length (76) of Base64 output. */
76     private final static int MAX_LINE_LENGTH = 76;
77     
78     
79     /** The equals sign (=) as a byte. */
80     private final static byte EQUALS_SIGN = (byte)'=';
81     
82     
83     /** The new line character (\n) as a byte. */
84     private final static byte NEW_LINE = (byte)'\n';
85     
86     
87     /** Preferred encoding. */
88     private final static String JavaDoc PREFERRED_ENCODING = "UTF-8";
89     
90     
91     /** The 64 valid Base64 values. */
92     private final static byte[] ALPHABET;
93     private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */ {
94         (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
95         (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
96         (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
97         (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
98         (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
99         (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
100         (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
101         (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
102         (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
103         (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
104     };
105     
106     /** Determine which ALPHABET to use. */
107     static {
108         byte[] __bytes;
109         try {
110             __bytes = new String JavaDoc("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").getBytes( PREFERRED_ENCODING );
111         } // end try
112
catch (java.io.UnsupportedEncodingException JavaDoc use) {
113             __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
114
} // end catch
115
ALPHABET = __bytes;
116     } // end static
117

118     
119     /**
120      * Translates a Base64 value to either its 6-bit reconstruction value
121      * or a negative number indicating some other meaning.
122      **/

123     private final static byte[] DECODABET = {
124         -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
125
-5,-5, // Whitespace: Tab and Linefeed
126
-9,-9, // Decimal 11 - 12
127
-5, // Whitespace: Carriage Return
128
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
129
-9,-9,-9,-9,-9, // Decimal 27 - 31
130
-5, // Whitespace: Space
131
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
132
62, // Plus sign at decimal 43
133
-9,-9,-9, // Decimal 44 - 46
134
63, // Slash at decimal 47
135
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
136
-9,-9,-9, // Decimal 58 - 60
137
-1, // Equals sign at decimal 61
138
-9,-9,-9, // Decimal 62 - 64
139
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
140
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
141
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96
142
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
143
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
144
-9,-9,-9,-9 // Decimal 123 - 126
145
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
146         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
147         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
148         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
149         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
150         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
151         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
152         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
153         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
154         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */

155     };
156     
157     private final static byte BAD_ENCODING = -9; // Indicates error in encoding
158
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
159
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
160

161     
162     /** Defeats instantiation. */
163     private Base64(){}
164     
165     
166     
167     /* ******** E N C O D I N G M E T H O D S ******** */
168     
169     
170     /**
171      * Encodes the first three bytes of array <var>threeBytes</var>
172      * and returns a four-byte array in Base64 notation.
173      *
174      * @param threeBytes the array to convert
175      * @return four byte array in Base64 notation.
176      * @since 1.3
177      */

178     private static byte[] encode3to4( byte[] threeBytes ) {
179         return encode3to4( threeBytes, 3 );
180     } // end encodeToBytes
181

182     
183     
184     /**
185      * Encodes up to the first three bytes of array <var>threeBytes</var>
186      * and returns a four-byte array in Base64 notation.
187      * The actual number of significant bytes in your array is
188      * given by <var>numSigBytes</var>.
189      * The array <var>threeBytes</var> needs only be as big as
190      * <var>numSigBytes</var>.
191      *
192      * @param threeBytes the array to convert
193      * @param numSigBytes the number of significant bytes in your array
194      * @return four byte array in Base64 notation.
195      * @since 1.3
196      */

197     private static byte[] encode3to4( byte[] threeBytes, int numSigBytes ) {
198         byte[] dest = new byte[4];
199         encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
200         return dest;
201     }
202     
203     /**
204      * Encodes up to the first three bytes of array <var>threeBytes</var>
205      * and returns a four-byte array in Base64 notation.
206      * The actual number of significant bytes in your array is
207      * given by <var>numSigBytes</var>.
208      * The array <var>threeBytes</var> needs only be as big as
209      * <var>numSigBytes</var>.
210      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
211      *
212      * @param b4 A reusable byte array to reduce array instantiation
213      * @param threeBytes the array to convert
214      * @param numSigBytes the number of significant bytes in your array
215      * @return four byte array in Base64 notation.
216      * @since 1.5.1
217      */

218     private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes ) {
219         encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
220         return b4;
221     } // end encode3to4
222

223     
224     /**
225      * Encodes up to three bytes of the array <var>source</var>
226      * and writes the resulting four Base64 bytes to <var>destination</var>.
227      * The source and destination arrays can be manipulated
228      * anywhere along their length by specifying
229      * <var>srcOffset</var> and <var>destOffset</var>.
230      * This method does not check to make sure your arrays
231      * are large enough to accomodate <var>srcOffset</var> + 3 for
232      * the <var>source</var> array or <var>destOffset</var> + 4 for
233      * the <var>destination</var> array.
234      * The actual number of significant bytes in your array is
235      * given by <var>numSigBytes</var>.
236      *
237      * @param source the array to convert
238      * @param srcOffset the index where conversion begins
239      * @param numSigBytes the number of significant bytes in your array
240      * @param destination the array to hold the conversion
241      * @param destOffset the index where output will be put
242      * @return the <var>destination</var> array
243      * @since 1.3
244      */

245     private static byte[] encode3to4(
246     byte[] source, int srcOffset, int numSigBytes,
247     byte[] destination, int destOffset ) {
248         // 1 2 3
249
// 01234567890123456789012345678901 Bit position
250
// --------000000001111111122222222 Array position from threeBytes
251
// --------| || || || | Six bit groups to index ALPHABET
252
// >>18 >>12 >> 6 >> 0 Right shift necessary
253
// 0x3f 0x3f 0x3f Additional AND
254

255         // Create buffer with zero-padding if there are only one or two
256
// significant bytes passed in the array.
257
// We have to shift left 24 in order to flush out the 1's that appear
258
// when Java treats a value as negative that is cast from a byte to an int.
259
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
260         | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
261         | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
262         
263         switch( numSigBytes ) {
264             case 3:
265                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
266                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
267                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
268                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
269                 return destination;
270                 
271             case 2:
272                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
273                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
274                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
275                 destination[ destOffset + 3 ] = EQUALS_SIGN;
276                 return destination;
277                 
278             case 1:
279                 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
280                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
281                 destination[ destOffset + 2 ] = EQUALS_SIGN;
282                 destination[ destOffset + 3 ] = EQUALS_SIGN;
283                 return destination;
284                 
285             default:
286                 return destination;
287         } // end switch
288
} // end encode3to4
289

290     
291     
292     /**
293      * Serializes an object and returns the Base64-encoded
294      * version of that serialized object. If the object
295      * cannot be serialized or there is another error,
296      * the method will return <tt>null</tt>.
297      * The object is not GZip-compressed before being encoded.
298      *
299      * @param serializableObject The object to encode
300      * @return The Base64-encoded object
301      * @since 1.4
302      */

303     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject ) {
304         return encodeObject( serializableObject, NO_OPTIONS );
305     } // end encodeObject
306

307     
308     
309     /**
310      * Serializes an object and returns the Base64-encoded
311      * version of that serialized object. If the object
312      * cannot be serialized or there is another error,
313      * the method will return <tt>null</tt>.
314      * <p>
315      * Valid options:<pre>
316      * GZIP: gzip-compresses object before encoding it.
317      * DONT_BREAK_LINES: don't break lines at 76 characters
318      * <i>Note: Technically, this makes your encoding non-compliant.</i>
319      * </pre>
320      * <p>
321      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
322      * <p>
323      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
324      *
325      * @param serializableObject The object to encode
326      * @options Specified options
327      * @return The Base64-encoded object
328      * @see Base64#GZIP
329      * @see Base64#DONT_BREAK_LINES
330      * @since 2.0
331      */

332     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject, int options ) {
333         // Streams
334
java.io.ByteArrayOutputStream JavaDoc baos = null;
335         java.io.OutputStream JavaDoc b64os = null;
336         java.io.ObjectOutputStream JavaDoc oos = null;
337         java.util.zip.GZIPOutputStream JavaDoc gzos = null;
338         
339         // Isolate options
340
int gzip = (options & GZIP);
341         int dontBreakLines = (options & DONT_BREAK_LINES);
342         
343         try {
344             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
345
baos = new java.io.ByteArrayOutputStream JavaDoc();
346             b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
347             
348             // GZip?
349
if( gzip == GZIP ) {
350                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
351                 oos = new java.io.ObjectOutputStream JavaDoc( gzos );
352             } // end if: gzip
353
else
354                 oos = new java.io.ObjectOutputStream JavaDoc( b64os );
355             
356             oos.writeObject( serializableObject );
357         } // end try
358
catch( java.io.IOException JavaDoc e ) {
359             e.printStackTrace();
360             return null;
361         } // end catch
362
finally {
363             try{ oos.close(); } catch( Exception JavaDoc e ){}
364             try{ gzos.close(); } catch( Exception JavaDoc e ){}
365             try{ b64os.close(); } catch( Exception JavaDoc e ){}
366             try{ baos.close(); } catch( Exception JavaDoc e ){}
367         } // end finally
368

369         // Return value according to relevant encoding.
370
try {
371             return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
372         } // end try
373
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
374             return new String JavaDoc( baos.toByteArray() );
375         } // end catch
376

377     } // end encode
378

379     
380     
381     /**
382      * Encodes a byte array into Base64 notation.
383      * Does not GZip-compress data.
384      *
385      * @param source The data to convert
386      * @since 1.4
387      */

388     public static String JavaDoc encodeBytes( byte[] source ) {
389         return encodeBytes( source, 0, source.length, NO_OPTIONS );
390     } // end encodeBytes
391

392     
393     
394     /**
395      * Encodes a byte array into Base64 notation.
396      * <p>
397      * Valid options:<pre>
398      * GZIP: gzip-compresses object before encoding it.
399      * DONT_BREAK_LINES: don't break lines at 76 characters
400      * <i>Note: Technically, this makes your encoding non-compliant.</i>
401      * </pre>
402      * <p>
403      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
404      * <p>
405      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
406      *
407      *
408      * @param source The data to convert
409      * @param options Specified options
410      * @see Base64#GZIP
411      * @see Base64#DONT_BREAK_LINES
412      * @since 2.0
413      */

414     public static String JavaDoc encodeBytes( byte[] source, int options ) {
415         return encodeBytes( source, 0, source.length, options );
416     } // end encodeBytes
417

418     
419     /**
420      * Encodes a byte array into Base64 notation.
421      * Does not GZip-compress data.
422      *
423      * @param source The data to convert
424      * @param off Offset in array where conversion should begin
425      * @param len Length of data to convert
426      * @since 1.4
427      */

428     public static String JavaDoc encodeBytes( byte[] source, int off, int len ) {
429         return encodeBytes( source, off, len, NO_OPTIONS );
430     } // end encodeBytes
431

432     
433     
434     /**
435      * Encodes a byte array into Base64 notation.
436      * <p>
437      * Valid options:<pre>
438      * GZIP: gzip-compresses object before encoding it.
439      * DONT_BREAK_LINES: don't break lines at 76 characters
440      * <i>Note: Technically, this makes your encoding non-compliant.</i>
441      * </pre>
442      * <p>
443      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
444      * <p>
445      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
446      *
447      *
448      * @param source The data to convert
449      * @param off Offset in array where conversion should begin
450      * @param len Length of data to convert
451      * @param breakLines Break lines at 80 characters or less.
452      * @param options Specified options
453      * @see Base64#GZIP
454      * @see Base64#DONT_BREAK_LINES
455      * @since 2.0
456      */

457     public static String JavaDoc encodeBytes( byte[] source, int off, int len, int options ) {
458         // Isolate options
459
int dontBreakLines = ( options & DONT_BREAK_LINES );
460         int gzip = ( options & GZIP );
461         
462         // Compress?
463
if( gzip == GZIP ) {
464             java.io.ByteArrayOutputStream JavaDoc baos = null;
465             java.util.zip.GZIPOutputStream JavaDoc gzos = null;
466             Base64.OutputStream b64os = null;
467             
468             
469             try {
470                 // GZip -> Base64 -> ByteArray
471
baos = new java.io.ByteArrayOutputStream JavaDoc();
472                 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
473                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
474                 
475                 gzos.write( source, off, len );
476                 gzos.close();
477             } // end try
478
catch( java.io.IOException JavaDoc e ) {
479                 e.printStackTrace();
480                 return null;
481             } // end catch
482
finally {
483                 try{ gzos.close(); } catch( Exception JavaDoc e ){}
484                 try{ b64os.close(); } catch( Exception JavaDoc e ){}
485                 try{ baos.close(); } catch( Exception JavaDoc e ){}
486             } // end finally
487

488             // Return value according to relevant encoding.
489
try {
490                 return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
491             } // end try
492
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
493                 return new String JavaDoc( baos.toByteArray() );
494             } // end catch
495
} // end if: compress
496

497         // Else, don't compress. Better not to use streams at all then.
498
else {
499             // Convert option to boolean in way that code likes it.
500
boolean breakLines = dontBreakLines == 0;
501             
502             int len43 = len * 4 / 3;
503             byte[] outBuff = new byte[ ( len43 ) // Main 4:3
504
+ ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
505
+ (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
506
int d = 0;
507             int e = 0;
508             int len2 = len - 2;
509             int lineLength = 0;
510             for( ; d < len2; d+=3, e+=4 ) {
511                 encode3to4( source, d+off, 3, outBuff, e );
512                 
513                 lineLength += 4;
514                 if( breakLines && lineLength == MAX_LINE_LENGTH ) {
515                     outBuff[e+4] = NEW_LINE;
516                     e++;
517                     lineLength = 0;
518                 } // end if: end of line
519
} // en dfor: each piece of array
520

521             if( d < len ) {
522                 encode3to4( source, d+off, len - d, outBuff, e );
523                 e += 4;
524             } // end if: some padding needed
525

526             
527             // Return value according to relevant encoding.
528
try {
529                 return new String JavaDoc( outBuff, 0, e, PREFERRED_ENCODING );
530             } // end try
531
catch (java.io.UnsupportedEncodingException JavaDoc uue) {
532                 return new String JavaDoc( outBuff, 0, e );
533             } // end catch
534

535         } // end else: don't compress
536

537     } // end encodeBytes
538

539     
540     
541     
542     
543     /* ******** D E C O D I N G M E T H O D S ******** */
544     
545     
546     /**
547      * Decodes the first four bytes of array <var>fourBytes</var>
548      * and returns an array up to three bytes long with the
549      * decoded values.
550      *
551      * @param fourBytes the array with Base64 content
552      * @return array with decoded values
553      * @since 1.3
554      */

555     private static byte[] decode4to3( byte[] fourBytes ) {
556         byte[] outBuff1 = new byte[3];
557         int count = decode4to3( fourBytes, 0, outBuff1, 0 );
558         byte[] outBuff2 = new byte[ count ];
559         
560         for( int i = 0; i < count; i++ )
561             outBuff2[i] = outBuff1[i];
562         
563         return outBuff2;
564     }
565     
566     
567     
568     
569     /**
570      * Decodes four bytes from array <var>source</var>
571      * and writes the resulting bytes (up to three of them)
572      * to <var>destination</var>.
573      * The source and destination arrays can be manipulated
574      * anywhere along their length by specifying
575      * <var>srcOffset</var> and <var>destOffset</var>.
576      * This method does not check to make sure your arrays
577      * are large enough to accomodate <var>srcOffset</var> + 4 for
578      * the <var>source</var> array or <var>destOffset</var> + 3 for
579      * the <var>destination</var> array.
580      * This method returns the actual number of bytes that
581      * were converted from the Base64 encoding.
582      *
583      *
584      * @param source the array to convert
585      * @param srcOffset the index where conversion begins
586      * @param destination the array to hold the conversion
587      * @param destOffset the index where output will be put
588      * @return the number of decoded bytes converted
589      * @since 1.3
590      */

591     private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) {
592         // Example: Dk==
593
if( source[ srcOffset + 2] == EQUALS_SIGN ) {
594             // Two ways to do the same thing. Don't know which way I like best.
595
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
596
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
597
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
598             | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
599             
600             destination[ destOffset ] = (byte)( outBuff >>> 16 );
601             return 1;
602         }
603         
604         // Example: DkL=
605
else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) {
606             // Two ways to do the same thing. Don't know which way I like best.
607
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
608
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
609
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
610
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
611             | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
612             | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
613             
614             destination[ destOffset ] = (byte)( outBuff >>> 16 );
615             destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
616             return 2;
617         }
618         
619         // Example: DkLE
620
else {
621             try{
622                 // Two ways to do the same thing. Don't know which way I like best.
623
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
624
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
625
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
626
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
627
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
628                 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
629                 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
630                 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
631                 
632                 
633                 destination[ destOffset ] = (byte)( outBuff >> 16 );
634                 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
635                 destination[ destOffset + 2 ] = (byte)( outBuff );
636                 
637                 return 3;
638             }catch( Exception JavaDoc e){
639                 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
640                 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
641                 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
642                 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
643                 return -1;
644             } //e nd catch
645
}
646     } // end decodeToBytes
647

648     
649     
650     
651     /**
652      * Very low-level access to decoding ASCII characters in
653      * the form of a byte array. Does not support automatically
654      * gunzipping or any other "fancy" features.
655      *
656      * @param source The Base64 encoded data
657      * @param off The offset of where to begin decoding
658      * @param len The length of characters to decode
659      * @return decoded data
660      * @since 1.3
661      */

662     public static byte[] decode( byte[] source, int off, int len ) {
663         int len34 = len * 3 / 4;
664         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
665
int outBuffPosn = 0;
666         
667         byte[] b4 = new byte[4];
668         int b4Posn = 0;
669         int i = 0;
670         byte sbiCrop = 0;
671         byte sbiDecode = 0;
672         for( i = off; i < off+len; i++ ) {
673             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
674
sbiDecode = DECODABET[ sbiCrop ];
675             
676             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
677
{
678                 if( sbiDecode >= EQUALS_SIGN_ENC ) {
679                     b4[ b4Posn++ ] = sbiCrop;
680                     if( b4Posn > 3 ) {
681                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
682                         b4Posn = 0;
683                         
684                         // If that was the equals sign, break out of 'for' loop
685
if( sbiCrop == EQUALS_SIGN )
686                             break;
687                     } // end if: quartet built
688

689                 } // end if: equals sign or better
690

691             } // end if: white space, equals sign or better
692
else {
693                 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
694                 return null;
695             } // end else:
696
} // each input character
697

698         byte[] out = new byte[ outBuffPosn ];
699         System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
700         return out;
701     } // end decode
702

703     
704     
705     
706     /**
707      * Decodes data from Base64 notation, automatically
708      * detecting gzip-compressed data and decompressing it.
709      *
710      * @param s the string to decode
711      * @return the decoded data
712      * @since 1.4
713      */

714     public static byte[] decode( String JavaDoc s ) {
715         byte[] bytes;
716         try {
717             bytes = s.getBytes( PREFERRED_ENCODING );
718         } // end try
719
catch( java.io.UnsupportedEncodingException JavaDoc uee ) {
720             bytes = s.getBytes();
721         } // end catch
722
//</change>
723

724         // Decode
725
bytes = decode( bytes, 0, bytes.length );
726         
727         
728         // Check to see if it's gzip-compressed
729
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
730
if( bytes.length >= 2 ) {
731             
732             int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
733             if(
734             bytes != null && // In case decoding returned null
735
bytes.length >= 4 && // Don't want to get ArrayIndexOutOfBounds exception
736
java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) {
737                 java.io.ByteArrayInputStream JavaDoc bais = null;
738                 java.util.zip.GZIPInputStream JavaDoc gzis = null;
739                 java.io.ByteArrayOutputStream JavaDoc baos = null;
740                 byte[] buffer = new byte[2048];
741                 int length = 0;
742                 
743                 try {
744                     baos = new java.io.ByteArrayOutputStream JavaDoc();
745                     bais = new java.io.ByteArrayInputStream JavaDoc( bytes );
746                     gzis = new java.util.zip.GZIPInputStream JavaDoc( bais );
747                     
748                     while( ( length = gzis.read( buffer ) ) >= 0 ) {
749                         baos.write(buffer,0,length);
750                     } // end while: reading input
751

752                     // No error? Get new bytes.
753
bytes = baos.toByteArray();
754                     
755                 } // end try
756
catch( java.io.IOException JavaDoc e ) {
757                     // Just return originally-decoded bytes
758
} // end catch
759
finally {
760                     try{ baos.close(); } catch( Exception JavaDoc e ){}
761                     try{ gzis.close(); } catch( Exception JavaDoc e ){}
762                     try{ bais.close(); } catch( Exception JavaDoc e ){}
763                 } // end finally
764

765             } // end if: gzipped
766
} // end if: bytes.length >= 2
767

768         return bytes;
769     } // end decode
770

771     
772     
773     
774     /**
775      * Attempts to decode Base64 data and deserialize a Java
776      * Object within. Returns <tt>null</tt> if there was an error.
777      *
778      * @param encodedObject The Base64 data to decode
779      * @return The decoded and deserialized object
780      * @since 1.5
781      */

782     public static Object JavaDoc decodeToObject( String JavaDoc encodedObject ) {
783         // Decode and gunzip if necessary
784
byte[] objBytes = decode( encodedObject );
785         
786         java.io.ByteArrayInputStream JavaDoc bais = null;
787         java.io.ObjectInputStream JavaDoc ois = null;
788         Object JavaDoc obj = null;
789         
790         try {
791             bais = new java.io.ByteArrayInputStream JavaDoc( objBytes );
792             ois = new java.io.ObjectInputStream JavaDoc( bais );
793             
794             obj = ois.readObject();
795         } // end try
796
catch( java.io.IOException JavaDoc e ) {
797             e.printStackTrace();
798             obj = null;
799         } // end catch
800
catch( java.lang.ClassNotFoundException JavaDoc e ) {
801             e.printStackTrace();
802             obj = null;
803         } // end catch
804
finally {
805             try{ bais.close(); } catch( Exception JavaDoc e ){}
806             try{ ois.close(); } catch( Exception JavaDoc e ){}
807         } // end finally
808

809         return obj;
810     } // end decodeObject
811

812     
813     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
814     
815     
816     
817     /**
818      * A {@link Base64#InputStream} will read data from another
819      * {@link java.io.InputStream}, given in the constructor,
820      * and encode/decode to/from Base64 notation on the fly.
821      *
822      * @see Base64
823      * @see java.io.FilterInputStream
824      * @since 1.3
825      */

826     public static class InputStream extends java.io.FilterInputStream JavaDoc {
827         private int options; // Options specified
828
private boolean encode; // Encoding or decoding
829
private int position; // Current position in the buffer
830
private byte[] buffer; // Small buffer holding converted data
831
private int bufferLength; // Length of buffer (3 or 4)
832
private int numSigBytes; // Number of meaningful bytes in the buffer
833
private int lineLength;
834         private boolean breakLines; // Break lines at less than 80 characters
835

836         
837         /**
838          * Constructs a {@link Base64#InputStream} in DECODE mode.
839          *
840          * @param in the {@link java.io.InputStream} from which to read data.
841          * @since 1.3
842          */

843         public InputStream( java.io.InputStream JavaDoc in ) {
844             this( in, DECODE );
845         } // end constructor
846

847         
848         /**
849          * Constructs a {@link Base64#InputStream} in
850          * either ENCODE or DECODE mode.
851          * <p>
852          * Valid options:<pre>
853          * ENCODE or DECODE: Encode or Decode as data is read.
854          * DONT_BREAK_LINES: don't break lines at 76 characters
855          * (only meaningful when encoding)
856          * <i>Note: Technically, this makes your encoding non-compliant.</i>
857          * </pre>
858          * <p>
859          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
860          *
861          *
862          * @param in the {@link java.io.InputStream} from which to read data.
863          * @param options Specified options
864          * @see Base64#ENCODE
865          * @see Base64#DECODE
866          * @see Base64#DONT_BREAK_LINES
867          * @since 2.0
868          */

869         public InputStream( java.io.InputStream JavaDoc in, int options ) {
870             super( in );
871             this.options = options;
872             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
873             this.encode = (options & ENCODE) == ENCODE;
874             this.bufferLength = encode ? 4 : 3;
875             this.buffer = new byte[ bufferLength ];
876             this.position = -1;
877             this.lineLength = 0;
878         } // end constructor
879

880         /**
881          * Reads enough of the input stream to convert
882          * to/from Base64 and returns the next byte.
883          *
884          * @return next byte
885          * @since 1.3
886          */

887         public int read() throws java.io.IOException JavaDoc {
888             // Do we need to get data?
889
if( position < 0 ) {
890                 if( encode ) {
891                     byte[] b3 = new byte[3];
892                     int numBinaryBytes = 0;
893                     for( int i = 0; i < 3; i++ ) {
894                         try {
895                             int b = in.read();
896                             
897                             // If end of stream, b is -1.
898
if( b >= 0 ) {
899                                 b3[i] = (byte)b;
900                                 numBinaryBytes++;
901                             } // end if: not end of stream
902

903                         } // end try: read
904
catch( java.io.IOException JavaDoc e ) {
905                             // Only a problem if we got no data at all.
906
if( i == 0 )
907                                 throw e;
908                             
909                         } // end catch
910
} // end for: each needed input byte
911

912                     if( numBinaryBytes > 0 ) {
913                         encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
914                         position = 0;
915                         numSigBytes = 4;
916                     } // end if: got data
917
else {
918                         return -1;
919                     } // end else
920
} // end if: encoding
921

922                 // Else decoding
923
else {
924                     byte[] b4 = new byte[4];
925                     int i = 0;
926                     for( i = 0; i < 4; i++ ) {
927                         // Read four "meaningful" bytes:
928
int b = 0;
929                         do{ b = in.read(); }
930                         while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
931                         
932                         if( b < 0 )
933                             break; // Reads a -1 if end of stream
934

935                         b4[i] = (byte)b;
936                     } // end for: each needed input byte
937

938                     if( i == 4 ) {
939                         numSigBytes = decode4to3( b4, 0, buffer, 0 );
940                         position = 0;
941                     } // end if: got four characters
942
else if( i == 0 ){
943                         return -1;
944                     } // end else if: also padded correctly
945
else {
946                         // Must have broken out from above.
947
throw new java.io.IOException JavaDoc( "Improperly padded Base64 input." );
948                     } // end
949

950                 } // end else: decode
951
} // end else: get data
952

953             // Got data?
954
if( position >= 0 ) {
955                 // End of relevant data?
956
if( /*!encode &&*/ position >= numSigBytes )
957                     return -1;
958                 
959                 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
960                     lineLength = 0;
961                     return '\n';
962                 } // end if
963
else {
964                     lineLength++; // This isn't important when decoding
965
// but throwing an extra "if" seems
966
// just as wasteful.
967

968                     int b = buffer[ position++ ];
969                     
970                     if( position >= bufferLength )
971                         position = -1;
972                     
973                     return b & 0xFF; // This is how you "cast" a byte that's
974
// intended to be unsigned.
975
} // end else
976
} // end if: position >= 0
977

978             // Else error
979
else {
980                 // When JDK1.4 is more accepted, use an assertion here.
981
throw new java.io.IOException JavaDoc( "Error in Base64 code reading stream." );
982             } // end else
983
} // end read
984

985         
986         /**
987          * Calls {@link #read} repeatedly until the end of stream
988          * is reached or <var>len</var> bytes are read.
989          * Returns number of bytes read into array or -1 if
990          * end of stream is encountered.
991          *
992          * @param dest array to hold values
993          * @param off offset for array
994          * @param len max number of bytes to read into array
995          * @return bytes read into array or -1 if end of stream is encountered.
996          * @since 1.3
997          */

998         public int read( byte[] dest, int off, int len ) throws java.io.IOException JavaDoc {
999             int i;
1000            int b;
1001            for( i = 0; i < len; i++ ) {
1002                b = read();
1003                
1004                //if( b < 0 && i == 0 )
1005
// return -1;
1006

1007                if( b >= 0 )
1008                    dest[off + i] = (byte)b;
1009                else if( i == 0 )
1010                    return -1;
1011                else
1012                    break; // Out of 'for' loop
1013
} // end for: each byte read
1014
return i;
1015        } // end read
1016

1017    } // end inner class InputStream
1018

1019    
1020    
1021    
1022    
1023    
1024    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1025    
1026    
1027    
1028    /**
1029     * A {@link Base64#OutputStream} will write data to another
1030     * {@link java.io.OutputStream}, given in the constructor,
1031     * and encode/decode to/from Base64 notation on the fly.
1032     *
1033     * @see Base64
1034     * @see java.io.FilterOutputStream
1035     * @since 1.3
1036     */

1037    public static class OutputStream extends java.io.FilterOutputStream JavaDoc {
1038        private int options;
1039        private boolean encode;
1040        private int position;
1041        private byte[] buffer;
1042        private int bufferLength;
1043        private int lineLength;
1044        private boolean breakLines;
1045        private byte[] b4; // Scratch used in a few places
1046
private boolean suspendEncoding;
1047        
1048        /**
1049         * Constructs a {@link Base64#OutputStream} in ENCODE mode.
1050         *
1051         * @param out the {@link java.io.OutputStream} to which data will be written.
1052         * @since 1.3
1053         */

1054        public OutputStream( java.io.OutputStream JavaDoc out ) {
1055            this( out, ENCODE );
1056        } // end constructor
1057

1058        
1059        /**
1060         * Constructs a {@link Base64#OutputStream} in
1061         * either ENCODE or DECODE mode.
1062         * <p>
1063         * Valid options:<pre>
1064         * ENCODE or DECODE: Encode or Decode as data is read.
1065         * DONT_BREAK_LINES: don't break lines at 76 characters
1066         * (only meaningful when encoding)
1067         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1068         * </pre>
1069         * <p>
1070         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1071         *
1072         * @param out the {@link java.io.OutputStream} to which data will be written.
1073         * @param options Specified options.
1074         * @see Base64#ENCODE
1075         * @see Base64#DECODE
1076         * @see Base64#DONT_BREAK_LINES
1077         * @since 1.3
1078         */

1079        public OutputStream( java.io.OutputStream JavaDoc out, int options ) {
1080            super( out );
1081            this.options = options;
1082            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1083            this.encode = (options & ENCODE) == ENCODE;
1084            this.bufferLength = encode ? 3 : 4;
1085            this.buffer = new byte[ bufferLength ];
1086            this.position = 0;
1087            this.lineLength = 0;
1088            this.suspendEncoding = false;
1089            this.b4 = new byte[4];
1090        } // end constructor
1091

1092        
1093        /**
1094         * Writes the byte to the output stream after
1095         * converting to/from Base64 notation.
1096         * When encoding, bytes are buffered three
1097         * at a time before the output stream actually
1098         * gets a write() call.
1099         * When decoding, bytes are buffered four
1100         * at a time.
1101         *
1102         * @param theByte the byte to write
1103         * @since 1.3
1104         */

1105        public void write(int theByte) throws java.io.IOException JavaDoc {
1106            // Encoding suspended?
1107
if( suspendEncoding ) {
1108                super.out.write( theByte );
1109                return;
1110            } // end if: supsended
1111

1112            // Encode?
1113
if( encode ) {
1114                buffer[ position++ ] = (byte)theByte;
1115                if( position >= bufferLength ) // Enough to encode.
1116
{
1117                    out.write( encode3to4( b4, buffer, bufferLength ) );
1118                    
1119                    lineLength += 4;
1120                    if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
1121                        out.write( NEW_LINE );
1122                        lineLength = 0;
1123                    } // end if: end of line
1124

1125                    position = 0;
1126                } // end if: enough to output
1127
} // end if: encoding
1128

1129            // Else, Decoding
1130
else {
1131                // Meaningful Base64 character?
1132
if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) {
1133                    buffer[ position++ ] = (byte)theByte;
1134                    if( position >= bufferLength ) // Enough to output.
1135
{
1136                        int len = Base64.decode4to3( buffer, 0, b4, 0 );
1137                        out.write( b4, 0, len );
1138                        //out.write( Base64.decode4to3( buffer ) );
1139
position = 0;
1140                    } // end if: enough to output
1141
} // end if: meaningful base64 character
1142
else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
1143                    throw new java.io.IOException JavaDoc( "Invalid character in Base64 data." );
1144                } // end else: not white space either
1145
} // end else: decoding
1146
} // end write
1147

1148        
1149        
1150        /**
1151         * Calls {@link #write} repeatedly until <var>len</var>
1152         * bytes are written.
1153         *
1154         * @param theBytes array from which to read bytes
1155         * @param off offset for array
1156         * @param len max number of bytes to read into array
1157         * @since 1.3
1158         */

1159        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException JavaDoc {
1160            // Encoding suspended?
1161
if( suspendEncoding ) {
1162                super.out.write( theBytes, off, len );
1163                return;
1164            } // end if: supsended
1165

1166            for( int i = 0; i < len; i++ ) {
1167                write( theBytes[ off + i ] );
1168            } // end for: each byte written
1169

1170        } // end write
1171

1172        
1173        
1174        /**
1175         * Method added by PHIL. [Thanks, PHIL. -Rob]
1176         * This pads the buffer without closing the stream.
1177         */

1178        public void flushBase64() throws java.io.IOException JavaDoc {
1179            if( position > 0 ) {
1180                if( encode ) {
1181                    out.write( encode3to4( b4, buffer, position ) );
1182                    position = 0;
1183                } // end if: encoding
1184
else {
1185                    throw new java.io.IOException JavaDoc( "Base64 input not properly padded." );
1186                } // end else: decoding
1187
} // end if: buffer partially full
1188

1189        } // end flush
1190

1191        
1192        /**
1193         * Flushes and closes (I think, in the superclass) the stream.
1194         *
1195         * @since 1.3
1196         */

1197        public void close() throws java.io.IOException JavaDoc {
1198            // 1. Ensure that pending characters are written
1199
flushBase64();
1200            
1201            // 2. Actually close the stream
1202
// Base class both flushes and closes.
1203
super.close();
1204            
1205            buffer = null;
1206            out = null;
1207        } // end close
1208

1209        
1210        
1211        /**
1212         * Suspends encoding of the stream.
1213         * May be helpful if you need to embed a piece of
1214         * base640-encoded data in a stream.
1215         *
1216         * @since 1.5.1
1217         */

1218        public void suspendEncoding() throws java.io.IOException JavaDoc {
1219            flushBase64();
1220            this.suspendEncoding = true;
1221        } // end suspendEncoding
1222

1223        
1224        /**
1225         * Resumes encoding of the stream.
1226         * May be helpful if you need to embed a piece of
1227         * base640-encoded data in a stream.
1228         *
1229         * @since 1.5.1
1230         */

1231        public void resumeEncoding() {
1232            this.suspendEncoding = false;
1233        } // end resumeEncoding
1234

1235        
1236        
1237    } // end inner class OutputStream
1238

1239    
1240} // end class Base64
1241
Popular Tags