KickJava   Java API By Example, From Geeks To Geeks.

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

123     
124     /**
125      * Translates a Base64 value to either its 6-bit reconstruction value
126      * or a negative number indicating some other meaning.
127      **/

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

161     };
162     
163     private final static byte BAD_ENCODING = -9; // Indicates error in encoding
164
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
165
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
166

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

184     private static byte[] encode3to4( byte[] threeBytes )
185     {
186         return encode3to4( threeBytes, 3 );
187     } // end encodeToBytes
188

189     
190     
191     /**
192      * Encodes up to the first three bytes of array <var>threeBytes</var>
193      * and returns a four-byte array in Base64 notation.
194      * The actual number of significant bytes in your array is
195      * given by <var>numSigBytes</var>.
196      * The array <var>threeBytes</var> needs only be as big as
197      * <var>numSigBytes</var>.
198      *
199      * @param threeBytes the array to convert
200      * @param numSigBytes the number of significant bytes in your array
201      * @return four byte array in Base64 notation.
202      * @since 1.3
203      */

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

226     private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
227     {
228         encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
229         return b4;
230     } // end encode3to4
231

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

254     private static byte[] encode3to4(
255      byte[] source, int srcOffset, int numSigBytes,
256      byte[] destination, int destOffset )
257     {
258         // 1 2 3
259
// 01234567890123456789012345678901 Bit position
260
// --------000000001111111122222222 Array position from threeBytes
261
// --------| || || || | Six bit groups to index ALPHABET
262
// >>18 >>12 >> 6 >> 0 Right shift necessary
263
// 0x3f 0x3f 0x3f Additional AND
264

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

301     
302     
303     /**
304      * Serializes an object and returns the Base64-encoded
305      * version of that serialized object. If the object
306      * cannot be serialized or there is another error,
307      * the method will return <tt>null</tt>.
308      * The object is not GZip-compressed before being encoded.
309      *
310      * @param serializableObject The object to encode
311      * @return The Base64-encoded object
312      * @since 1.4
313      */

314     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject )
315     {
316         return encodeObject( serializableObject, NO_OPTIONS );
317     } // end encodeObject
318

319
320
321     /**
322      * Serializes an object and returns the Base64-encoded
323      * version of that serialized object. If the object
324      * cannot be serialized or there is another error,
325      * the method will return <tt>null</tt>.
326      * <p>
327      * Valid options:<pre>
328      * GZIP: gzip-compresses object before encoding it.
329      * DONT_BREAK_LINES: don't break lines at 76 characters
330      * <i>Note: Technically, this makes your encoding non-compliant.</i>
331      * </pre>
332      * <p>
333      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
334      * <p>
335      * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
336      *
337      * @param serializableObject The object to encode
338      * @options Specified options
339      * @return The Base64-encoded object
340      * @see Base64#GZIP
341      * @see Base64#DONT_BREAK_LINES
342      * @since 2.0
343      */

344     public static String JavaDoc encodeObject( java.io.Serializable JavaDoc serializableObject, int options )
345     {
346         // Streams
347
java.io.ByteArrayOutputStream JavaDoc baos = null;
348         java.io.OutputStream JavaDoc b64os = null;
349         java.io.ObjectOutputStream JavaDoc oos = null;
350         java.util.zip.GZIPOutputStream JavaDoc gzos = null;
351         
352         // Isolate options
353
int gzip = (options & GZIP);
354         int dontBreakLines = (options & DONT_BREAK_LINES);
355         
356         try
357         {
358             // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
359
baos = new java.io.ByteArrayOutputStream JavaDoc();
360             b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
361     
362             // GZip?
363
if( gzip == GZIP )
364             {
365                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
366                 oos = new java.io.ObjectOutputStream JavaDoc( gzos );
367             } // end if: gzip
368
else
369                 oos = new java.io.ObjectOutputStream JavaDoc( b64os );
370             
371             oos.writeObject( serializableObject );
372         } // end try
373
catch( java.io.IOException JavaDoc e )
374         {
375             e.printStackTrace();
376             return null;
377         } // end catch
378
finally
379         {
380             try{ oos.close(); } catch( Exception JavaDoc e ){}
381             try{ gzos.close(); } catch( Exception JavaDoc e ){}
382             try{ b64os.close(); } catch( Exception JavaDoc e ){}
383             try{ baos.close(); } catch( Exception JavaDoc e ){}
384         } // end finally
385

386         // Return value according to relevant encoding.
387
try
388         {
389             return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
390         } // end try
391
catch (java.io.UnsupportedEncodingException JavaDoc uue)
392         {
393             return new String JavaDoc( baos.toByteArray() );
394         } // end catch
395

396     } // end encode
397

398     
399
400     /**
401      * Encodes a byte array into Base64 notation.
402      * Does not GZip-compress data.
403      *
404      * @param source The data to convert
405      * @since 1.4
406      */

407     public static String JavaDoc encodeBytes( byte[] source )
408     {
409         return encodeBytes( source, 0, source.length, NO_OPTIONS );
410     } // end encodeBytes
411

412
413
414     /**
415      * Encodes a byte array into Base64 notation.
416      * <p>
417      * Valid options:<pre>
418      * GZIP: gzip-compresses object before encoding it.
419      * DONT_BREAK_LINES: don't break lines at 76 characters
420      * <i>Note: Technically, this makes your encoding non-compliant.</i>
421      * </pre>
422      * <p>
423      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
424      * <p>
425      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
426      *
427      *
428      * @param source The data to convert
429      * @param options Specified options
430      * @see Base64#GZIP
431      * @see Base64#DONT_BREAK_LINES
432      * @since 2.0
433      */

434     public static String JavaDoc encodeBytes( byte[] source, int options )
435     {
436         return encodeBytes( source, 0, source.length, options );
437     } // end encodeBytes
438

439     
440     /**
441      * Encodes a byte array into Base64 notation.
442      * Does not GZip-compress data.
443      *
444      * @param source The data to convert
445      * @param off Offset in array where conversion should begin
446      * @param len Length of data to convert
447      * @since 1.4
448      */

449     public static String JavaDoc encodeBytes( byte[] source, int off, int len )
450     {
451         return encodeBytes( source, off, len, NO_OPTIONS );
452     } // end encodeBytes
453

454     
455
456     /**
457      * Encodes a byte array into Base64 notation.
458      * <p>
459      * Valid options:<pre>
460      * GZIP: gzip-compresses object before encoding it.
461      * DONT_BREAK_LINES: don't break lines at 76 characters
462      * <i>Note: Technically, this makes your encoding non-compliant.</i>
463      * </pre>
464      * <p>
465      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
466      * <p>
467      * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
468      *
469      *
470      * @param source The data to convert
471      * @param off Offset in array where conversion should begin
472      * @param len Length of data to convert
473      * @param breakLines Break lines at 80 characters or less.
474      * @param options Specified options
475      * @see Base64#GZIP
476      * @see Base64#DONT_BREAK_LINES
477      * @since 2.0
478      */

479     public static String JavaDoc encodeBytes( byte[] source, int off, int len, int options )
480     {
481         // Isolate options
482
int dontBreakLines = ( options & DONT_BREAK_LINES );
483         int gzip = ( options & GZIP );
484         
485         // Compress?
486
if( gzip == GZIP )
487         {
488             java.io.ByteArrayOutputStream JavaDoc baos = null;
489             java.util.zip.GZIPOutputStream JavaDoc gzos = null;
490             Base64.OutputStream b64os = null;
491             
492     
493             try
494             {
495                 // GZip -> Base64 -> ByteArray
496
baos = new java.io.ByteArrayOutputStream JavaDoc();
497                 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
498                 gzos = new java.util.zip.GZIPOutputStream JavaDoc( b64os );
499             
500                 gzos.write( source, off, len );
501                 gzos.close();
502             } // end try
503
catch( java.io.IOException JavaDoc e )
504             {
505                 e.printStackTrace();
506                 return null;
507             } // end catch
508
finally
509             {
510                 try{ gzos.close(); } catch( Exception JavaDoc e ){}
511                 try{ b64os.close(); } catch( Exception JavaDoc e ){}
512                 try{ baos.close(); } catch( Exception JavaDoc e ){}
513             } // end finally
514

515             // Return value according to relevant encoding.
516
try
517             {
518                 return new String JavaDoc( baos.toByteArray(), PREFERRED_ENCODING );
519             } // end try
520
catch (java.io.UnsupportedEncodingException JavaDoc uue)
521             {
522                 return new String JavaDoc( baos.toByteArray() );
523             } // end catch
524
} // end if: compress
525

526         // Else, don't compress. Better not to use streams at all then.
527
else
528         {
529             // Convert option to boolean in way that code likes it.
530
boolean breakLines = dontBreakLines == 0;
531             
532             int len43 = len * 4 / 3;
533             byte[] outBuff = new byte[ ( len43 ) // Main 4:3
534
+ ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
535
+ (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
536
int d = 0;
537             int e = 0;
538             int len2 = len - 2;
539             int lineLength = 0;
540             for( ; d < len2; d+=3, e+=4 )
541             {
542                 encode3to4( source, d+off, 3, outBuff, e );
543
544                 lineLength += 4;
545                 if( breakLines && lineLength == MAX_LINE_LENGTH )
546                 {
547                     outBuff[e+4] = NEW_LINE;
548                     e++;
549                     lineLength = 0;
550                 } // end if: end of line
551
} // en dfor: each piece of array
552

553             if( d < len )
554             {
555                 encode3to4( source, d+off, len - d, outBuff, e );
556                 e += 4;
557             } // end if: some padding needed
558

559             
560             // Return value according to relevant encoding.
561
try
562             {
563                 return new String JavaDoc( outBuff, 0, e, PREFERRED_ENCODING );
564             } // end try
565
catch (java.io.UnsupportedEncodingException JavaDoc uue)
566             {
567                 return new String JavaDoc( outBuff, 0, e );
568             } // end catch
569

570         } // end else: don't compress
571

572     } // end encodeBytes
573

574
575     
576     
577     
578 /* ******** D E C O D I N G M E T H O D S ******** */
579     
580     
581     /**
582      * Decodes the first four bytes of array <var>fourBytes</var>
583      * and returns an array up to three bytes long with the
584      * decoded values.
585      *
586      * @param fourBytes the array with Base64 content
587      * @return array with decoded values
588      * @since 1.3
589      */

590     private static byte[] decode4to3( byte[] fourBytes )
591     {
592         byte[] outBuff1 = new byte[3];
593         int count = decode4to3( fourBytes, 0, outBuff1, 0 );
594         byte[] outBuff2 = new byte[ count ];
595         
596         for( int i = 0; i < count; i++ )
597             outBuff2[i] = outBuff1[i];
598         
599         return outBuff2;
600     }
601     
602     
603     
604     
605     /**
606      * Decodes four bytes from array <var>source</var>
607      * and writes the resulting bytes (up to three of them)
608      * to <var>destination</var>.
609      * The source and destination arrays can be manipulated
610      * anywhere along their length by specifying
611      * <var>srcOffset</var> and <var>destOffset</var>.
612      * This method does not check to make sure your arrays
613      * are large enough to accomodate <var>srcOffset</var> + 4 for
614      * the <var>source</var> array or <var>destOffset</var> + 3 for
615      * the <var>destination</var> array.
616      * This method returns the actual number of bytes that
617      * were converted from the Base64 encoding.
618      *
619      *
620      * @param source the array to convert
621      * @param srcOffset the index where conversion begins
622      * @param destination the array to hold the conversion
623      * @param destOffset the index where output will be put
624      * @return the number of decoded bytes converted
625      * @since 1.3
626      */

627     private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
628     {
629         // Example: Dk==
630
if( source[ srcOffset + 2] == EQUALS_SIGN )
631         {
632             // Two ways to do the same thing. Don't know which way I like best.
633
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
634
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
635
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
636                           | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
637             
638             destination[ destOffset ] = (byte)( outBuff >>> 16 );
639             return 1;
640         }
641         
642         // Example: DkL=
643
else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
644         {
645             // Two ways to do the same thing. Don't know which way I like best.
646
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
647
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
648
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
649
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
650                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
651                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
652             
653             destination[ destOffset ] = (byte)( outBuff >>> 16 );
654             destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
655             return 2;
656         }
657         
658         // Example: DkLE
659
else
660         {
661             try{
662             // Two ways to do the same thing. Don't know which way I like best.
663
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
664
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
665
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
666
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
667
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
668                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
669                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
670                           | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
671
672             
673             destination[ destOffset ] = (byte)( outBuff >> 16 );
674             destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
675             destination[ destOffset + 2 ] = (byte)( outBuff );
676
677             return 3;
678             }catch( Exception JavaDoc e){
679                 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
680                 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
681                 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
682                 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
683                 return -1;
684             } //e nd catch
685
}
686     } // end decodeToBytes
687

688     
689     
690     
691     /**
692      * Very low-level access to decoding ASCII characters in
693      * the form of a byte array. Does not support automatically
694      * gunzipping or any other "fancy" features.
695      *
696      * @param source The Base64 encoded data
697      * @param off The offset of where to begin decoding
698      * @param len The length of characters to decode
699      * @return decoded data
700      * @since 1.3
701      */

702     public static byte[] decode( byte[] source, int off, int len )
703     {
704         int len34 = len * 3 / 4;
705         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
706
int outBuffPosn = 0;
707         
708         byte[] b4 = new byte[4];
709         int b4Posn = 0;
710         int i = 0;
711         byte sbiCrop = 0;
712         byte sbiDecode = 0;
713         for( i = off; i < off+len; i++ )
714         {
715             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
716
sbiDecode = DECODABET[ sbiCrop ];
717             
718             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
719
{
720                 if( sbiDecode >= EQUALS_SIGN_ENC )
721                 {
722                     b4[ b4Posn++ ] = sbiCrop;
723                     if( b4Posn > 3 )
724                     {
725                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
726                         b4Posn = 0;
727                         
728                         // If that was the equals sign, break out of 'for' loop
729
if( sbiCrop == EQUALS_SIGN )
730                             break;
731                     } // end if: quartet built
732

733                 } // end if: equals sign or better
734

735             } // end if: white space, equals sign or better
736
else
737             {
738                 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
739                 return null;
740             } // end else:
741
} // each input character
742

743         byte[] out = new byte[ outBuffPosn ];
744         System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
745         return out;
746     } // end decode
747

748     
749     
750     
751     /**
752      * Decodes data from Base64 notation, automatically
753      * detecting gzip-compressed data and decompressing it.
754      *
755      * @param s the string to decode
756      * @return the decoded data
757      * @since 1.4
758      */

759     public static byte[] decode( String JavaDoc s )
760     {
761         byte[] bytes;
762         try
763         {
764             bytes = s.getBytes( PREFERRED_ENCODING );
765         } // end try
766
catch( java.io.UnsupportedEncodingException JavaDoc uee )
767         {
768             bytes = s.getBytes();
769         } // end catch
770
//</change>
771

772         // Decode
773
bytes = decode( bytes, 0, bytes.length );
774         
775         
776         // Check to see if it's gzip-compressed
777
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
778
if( bytes.length >= 2 )
779         {
780             
781             int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
782             if(
783             bytes != null && // In case decoding returned null
784
bytes.length >= 4 && // Don't want to get ArrayIndexOutOfBounds exception
785
java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
786             {
787                 java.io.ByteArrayInputStream JavaDoc bais = null;
788                 java.util.zip.GZIPInputStream JavaDoc gzis = null;
789                 java.io.ByteArrayOutputStream JavaDoc baos = null;
790                 byte[] buffer = new byte[2048];
791                 int length = 0;
792
793                 try
794                 {
795                     baos = new java.io.ByteArrayOutputStream JavaDoc();
796                     bais = new java.io.ByteArrayInputStream JavaDoc( bytes );
797                     gzis = new java.util.zip.GZIPInputStream JavaDoc( bais );
798
799                     while( ( length = gzis.read( buffer ) ) >= 0 )
800                     {
801                         baos.write(buffer,0,length);
802                     } // end while: reading input
803

804                     // No error? Get new bytes.
805
bytes = baos.toByteArray();
806
807                 } // end try
808
catch( java.io.IOException JavaDoc e )
809                 {
810                     // Just return originally-decoded bytes
811
} // end catch
812
finally
813                 {
814                     try{ baos.close(); } catch( Exception JavaDoc e ){}
815                     try{ gzis.close(); } catch( Exception JavaDoc e ){}
816                     try{ bais.close(); } catch( Exception JavaDoc e ){}
817                 } // end finally
818

819             } // end if: gzipped
820
} // end if: bytes.length >= 2
821

822         return bytes;
823     } // end decode
824

825
826     
827
828     /**
829      * Attempts to decode Base64 data and deserialize a Java
830      * Object within. Returns <tt>null</tt> if there was an error.
831      *
832      * @param encodedObject The Base64 data to decode
833      * @return The decoded and deserialized object
834      * @since 1.5
835      */

836     public static Object JavaDoc decodeToObject( String JavaDoc encodedObject )
837     {
838         // Decode and gunzip if necessary
839
byte[] objBytes = decode( encodedObject );
840         
841         java.io.ByteArrayInputStream JavaDoc bais = null;
842         java.io.ObjectInputStream JavaDoc ois = null;
843         Object JavaDoc obj = null;
844         
845         try
846         {
847             bais = new java.io.ByteArrayInputStream JavaDoc( objBytes );
848             ois = new java.io.ObjectInputStream JavaDoc( bais );
849         
850             obj = ois.readObject();
851         } // end try
852
catch( java.io.IOException JavaDoc e )
853         {
854             e.printStackTrace();
855             obj = null;
856         } // end catch
857
catch( java.lang.ClassNotFoundException JavaDoc e )
858         {
859             e.printStackTrace();
860             obj = null;
861         } // end catch
862
finally
863         {
864             try{ bais.close(); } catch( Exception JavaDoc e ){}
865             try{ ois.close(); } catch( Exception JavaDoc e ){}
866         } // end finally
867

868         return obj;
869     } // end decodeObject
870

871     
872     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
873     
874     
875     
876     /**
877      * A {@link Base64#InputStream} will read data from another
878      * {@link java.io.InputStream}, given in the constructor,
879      * and encode/decode to/from Base64 notation on the fly.
880      *
881      * @see Base64
882      * @see java.io.FilterInputStream
883      * @since 1.3
884      */

885     public static class InputStream extends java.io.FilterInputStream JavaDoc
886     {
887         private int options; // Options specified
888
private boolean encode; // Encoding or decoding
889
private int position; // Current position in the buffer
890
private byte[] buffer; // Small buffer holding converted data
891
private int bufferLength; // Length of buffer (3 or 4)
892
private int numSigBytes; // Number of meaningful bytes in the buffer
893
private int lineLength;
894         private boolean breakLines; // Break lines at less than 80 characters
895

896         
897         /**
898          * Constructs a {@link Base64#InputStream} in DECODE mode.
899          *
900          * @param in the {@link java.io.InputStream} from which to read data.
901          * @since 1.3
902          */

903         public InputStream( java.io.InputStream JavaDoc in )
904         {
905             this( in, DECODE );
906         } // end constructor
907

908         
909         /**
910          * Constructs a {@link Base64#InputStream} in
911          * either ENCODE or DECODE mode.
912          * <p>
913          * Valid options:<pre>
914          * ENCODE or DECODE: Encode or Decode as data is read.
915          * DONT_BREAK_LINES: don't break lines at 76 characters
916          * (only meaningful when encoding)
917          * <i>Note: Technically, this makes your encoding non-compliant.</i>
918          * </pre>
919          * <p>
920          * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
921          *
922          *
923          * @param in the {@link java.io.InputStream} from which to read data.
924          * @param options Specified options
925          * @see Base64#ENCODE
926          * @see Base64#DECODE
927          * @see Base64#DONT_BREAK_LINES
928          * @since 2.0
929          */

930         public InputStream( java.io.InputStream JavaDoc in, int options )
931         {
932             super( in );
933             this.options = options;
934             this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
935             this.encode = (options & ENCODE) == ENCODE;
936             this.bufferLength = encode ? 4 : 3;
937             this.buffer = new byte[ bufferLength ];
938             this.position = -1;
939             this.lineLength = 0;
940         } // end constructor
941

942         /**
943          * Reads enough of the input stream to convert
944          * to/from Base64 and returns the next byte.
945          *
946          * @return next byte
947          * @since 1.3
948          */

949         public int read() throws java.io.IOException JavaDoc
950         {
951             // Do we need to get data?
952
if( position < 0 )
953             {
954                 if( encode )
955                 {
956                     byte[] b3 = new byte[3];
957                     int numBinaryBytes = 0;
958                     for( int i = 0; i < 3; i++ )
959                     {
960                         try
961                         {
962                             int b = in.read();
963                             
964                             // If end of stream, b is -1.
965
if( b >= 0 )
966                             {
967                                 b3[i] = (byte)b;
968                                 numBinaryBytes++;
969                             } // end if: not end of stream
970

971                         } // end try: read
972
catch( java.io.IOException JavaDoc e )
973                         {
974                             // Only a problem if we got no data at all.
975
if( i == 0 )
976                                 throw e;
977                             
978                         } // end catch
979
} // end for: each needed input byte
980

981                     if( numBinaryBytes > 0 )
982                     {
983                         encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
984                         position = 0;
985                         numSigBytes = 4;
986                     } // end if: got data
987
else
988                     {
989                         return -1;
990                     } // end else
991
} // end if: encoding
992

993                 // Else decoding
994
else
995                 {
996                     byte[] b4 = new byte[4];
997                     int i = 0;
998                     for( i = 0; i < 4; i++ )
999                     {
1000                        // Read four "meaningful" bytes:
1001
int b = 0;
1002                        do{ b = in.read(); }
1003                        while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1004                        
1005                        if( b < 0 )
1006                            break; // Reads a -1 if end of stream
1007

1008                        b4[i] = (byte)b;
1009                    } // end for: each needed input byte
1010

1011                    if( i == 4 )
1012                    {
1013                        numSigBytes = decode4to3( b4, 0, buffer, 0 );
1014                        position = 0;
1015                    } // end if: got four characters
1016
else if( i == 0 ){
1017                        return -1;
1018                    } // end else if: also padded correctly
1019
else
1020                    {
1021                        // Must have broken out from above.
1022
throw new java.io.IOException JavaDoc( "Improperly padded Base64 input." );
1023                    } // end
1024

1025                } // end else: decode
1026
} // end else: get data
1027

1028            // Got data?
1029
if( position >= 0 )
1030            {
1031                // End of relevant data?
1032
if( /*!encode &&*/ position >= numSigBytes )
1033                    return -1;
1034                
1035                if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1036                {
1037                    lineLength = 0;
1038                    return '\n';
1039                } // end if
1040
else
1041                {
1042                    lineLength++; // This isn't important when decoding
1043
// but throwing an extra "if" seems
1044
// just as wasteful.
1045

1046                    int b = buffer[ position++ ];
1047
1048                    if( position >= bufferLength )
1049                        position = -1;
1050
1051                    return b & 0xFF; // This is how you "cast" a byte that's
1052
// intended to be unsigned.
1053
} // end else
1054
} // end if: position >= 0
1055

1056            // Else error
1057
else
1058            {
1059                // When JDK1.4 is more accepted, use an assertion here.
1060
throw new java.io.IOException JavaDoc( "Error in Base64 code reading stream." );
1061            } // end else
1062
} // end read
1063

1064        
1065        /**
1066         * Calls {@link #read} repeatedly until the end of stream
1067         * is reached or <var>len</var> bytes are read.
1068         * Returns number of bytes read into array or -1 if
1069         * end of stream is encountered.
1070         *
1071         * @param dest array to hold values
1072         * @param off offset for array
1073         * @param len max number of bytes to read into array
1074         * @return bytes read into array or -1 if end of stream is encountered.
1075         * @since 1.3
1076         */

1077        public int read( byte[] dest, int off, int len ) throws java.io.IOException JavaDoc
1078        {
1079            int i;
1080            int b;
1081            for( i = 0; i < len; i++ )
1082            {
1083                b = read();
1084                
1085                //if( b < 0 && i == 0 )
1086
// return -1;
1087

1088                if( b >= 0 )
1089                    dest[off + i] = (byte)b;
1090                else if( i == 0 )
1091                    return -1;
1092                else
1093                    break; // Out of 'for' loop
1094
} // end for: each byte read
1095
return i;
1096        } // end read
1097

1098    } // end inner class InputStream
1099

1100    
1101    
1102    
1103    
1104    
1105    /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1106    
1107    
1108    
1109    /**
1110     * A {@link Base64#OutputStream} will write data to another
1111     * {@link java.io.OutputStream}, given in the constructor,
1112     * and encode/decode to/from Base64 notation on the fly.
1113     *
1114     * @see Base64
1115     * @see java.io.FilterOutputStream
1116     * @since 1.3
1117     */

1118    public static class OutputStream extends java.io.FilterOutputStream JavaDoc
1119    {
1120        private int options;
1121        private boolean encode;
1122        private int position;
1123        private byte[] buffer;
1124        private int bufferLength;
1125        private int lineLength;
1126        private boolean breakLines;
1127        private byte[] b4; // Scratch used in a few places
1128
private boolean suspendEncoding;
1129        
1130        /**
1131         * Constructs a {@link Base64#OutputStream} in ENCODE mode.
1132         *
1133         * @param out the {@link java.io.OutputStream} to which data will be written.
1134         * @since 1.3
1135         */

1136        public OutputStream( java.io.OutputStream JavaDoc out )
1137        {
1138            this( out, ENCODE );
1139        } // end constructor
1140

1141        
1142        /**
1143         * Constructs a {@link Base64#OutputStream} in
1144         * either ENCODE or DECODE mode.
1145         * <p>
1146         * Valid options:<pre>
1147         * ENCODE or DECODE: Encode or Decode as data is read.
1148         * DONT_BREAK_LINES: don't break lines at 76 characters
1149         * (only meaningful when encoding)
1150         * <i>Note: Technically, this makes your encoding non-compliant.</i>
1151         * </pre>
1152         * <p>
1153         * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1154         *
1155         * @param out the {@link java.io.OutputStream} to which data will be written.
1156         * @param options Specified options.
1157         * @see Base64#ENCODE
1158         * @see Base64#DECODE
1159         * @see Base64#DONT_BREAK_LINES
1160         * @since 1.3
1161         */

1162        public OutputStream( java.io.OutputStream JavaDoc out, int options )
1163        {
1164            super( out );
1165            this.options = options;
1166            this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1167            this.encode = (options & ENCODE) == ENCODE;
1168            this.bufferLength = encode ? 3 : 4;
1169            this.buffer = new byte[ bufferLength ];
1170            this.position = 0;
1171            this.lineLength = 0;
1172            this.suspendEncoding = false;
1173            this.b4 = new byte[4];
1174        } // end constructor
1175

1176        
1177        /**
1178         * Writes the byte to the output stream after
1179         * converting to/from Base64 notation.
1180         * When encoding, bytes are buffered three
1181         * at a time before the output stream actually
1182         * gets a write() call.
1183         * When decoding, bytes are buffered four
1184         * at a time.
1185         *
1186         * @param theByte the byte to write
1187         * @since 1.3
1188         */

1189        public void write(int theByte) throws java.io.IOException JavaDoc
1190        {
1191            // Encoding suspended?
1192
if( suspendEncoding )
1193            {
1194                super.out.write( theByte );
1195                return;
1196            } // end if: supsended
1197

1198            // Encode?
1199
if( encode )
1200            {
1201                buffer[ position++ ] = (byte)theByte;
1202                if( position >= bufferLength ) // Enough to encode.
1203
{
1204                    out.write( encode3to4( b4, buffer, bufferLength ) );
1205
1206                    lineLength += 4;
1207                    if( breakLines && lineLength >= MAX_LINE_LENGTH )
1208                    {
1209                        out.write( NEW_LINE );
1210                        lineLength = 0;
1211                    } // end if: end of line
1212

1213                    position = 0;
1214                } // end if: enough to output
1215
} // end if: encoding
1216

1217            // Else, Decoding
1218
else
1219            {
1220                // Meaningful Base64 character?
1221
if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1222                {
1223                    buffer[ position++ ] = (byte)theByte;
1224                    if( position >= bufferLength ) // Enough to output.
1225
{
1226                        int len = Base64.decode4to3( buffer, 0, b4, 0 );
1227                        out.write( b4, 0, len );
1228                        //out.write( Base64.decode4to3( buffer ) );
1229
position = 0;
1230                    } // end if: enough to output
1231
} // end if: meaningful base64 character
1232
else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1233                {
1234                    throw new java.io.IOException JavaDoc( "Invalid character in Base64 data." );
1235                } // end else: not white space either
1236
} // end else: decoding
1237
} // end write
1238

1239        
1240        
1241        /**
1242         * Calls {@link #write} repeatedly until <var>len</var>
1243         * bytes are written.
1244         *
1245         * @param theBytes array from which to read bytes
1246         * @param off offset for array
1247         * @param len max number of bytes to read into array
1248         * @since 1.3
1249         */

1250        public void write( byte[] theBytes, int off, int len ) throws java.io.IOException JavaDoc
1251        {
1252            // Encoding suspended?
1253
if( suspendEncoding )
1254            {
1255                super.out.write( theBytes, off, len );
1256                return;
1257            } // end if: supsended
1258

1259            for( int i = 0; i < len; i++ )
1260            {
1261                write( theBytes[ off + i ] );
1262            } // end for: each byte written
1263

1264        } // end write
1265

1266        
1267        
1268        /**
1269         * Method added by PHIL. [Thanks, PHIL. -Rob]
1270         * This pads the buffer without closing the stream.
1271         */

1272        public void flushBase64() throws java.io.IOException JavaDoc
1273        {
1274            if( position > 0 )
1275            {
1276                if( encode )
1277                {
1278                    out.write( encode3to4( b4, buffer, position ) );
1279                    position = 0;
1280                } // end if: encoding
1281
else
1282                {
1283                    throw new java.io.IOException JavaDoc( "Base64 input not properly padded." );
1284                } // end else: decoding
1285
} // end if: buffer partially full
1286

1287        } // end flush
1288

1289        
1290        /**
1291         * Flushes and closes (I think, in the superclass) the stream.
1292         *
1293         * @since 1.3
1294         */

1295        public void close() throws java.io.IOException JavaDoc
1296        {
1297            // 1. Ensure that pending characters are written
1298
flushBase64();
1299
1300            // 2. Actually close the stream
1301
// Base class both flushes and closes.
1302
super.close();
1303            
1304            buffer = null;
1305            out = null;
1306        } // end close
1307

1308        
1309        
1310        /**
1311         * Suspends encoding of the stream.
1312         * May be helpful if you need to embed a piece of
1313         * base640-encoded data in a stream.
1314         *
1315         * @since 1.5.1
1316         */

1317        public void suspendEncoding() throws java.io.IOException JavaDoc
1318        {
1319            flushBase64();
1320            this.suspendEncoding = true;
1321        } // end suspendEncoding
1322

1323        
1324        /**
1325         * Resumes encoding of the stream.
1326         * May be helpful if you need to embed a piece of
1327         * base640-encoded data in a stream.
1328         *
1329         * @since 1.5.1
1330         */

1331        public void resumeEncoding()
1332        {
1333            this.suspendEncoding = false;
1334        } // end resumeEncoding
1335

1336        
1337        
1338    } // end inner class OutputStream
1339

1340    
1341} // end class Base64
1342
Popular Tags