View Javadoc
1   package util;
2   
3   /***
4    * Encodes and decodes to and from Base64 notation.
5    *
6    * <p>
7    * Change Log:
8    * </p>
9    * <ul>
10   *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
11   *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
12   *      where last buffer being read, if not completely full, was not returned.</li>
13   *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
14   *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
15   * </ul>
16   *
17   * <p>
18   * I am placing this code in the Public Domain. Do with it as you will.
19   * This software comes with no guarantees or warranties but with
20   * plenty of well-wishing instead!
21   * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
22   * periodically to check for updates or to contribute improvements.
23   * </p>
24   *
25   * @author Robert Harder
26   * @author rob@iharder.net
27   * @version 1.3.4
28   */
29  public class Base64
30  {
31      
32      /*** Specify encoding (value is <tt>true</tt>). */
33      public final static boolean ENCODE = true;
34      
35      
36      /*** Specify decoding (value is <tt>false</tt>). */
37      public final static boolean DECODE = false;
38      
39      
40      /*** Maximum line length (76) of Base64 output. */
41      private final static int MAX_LINE_LENGTH = 76;
42      
43      
44      /*** The equals sign (=) as a byte. */
45      private final static byte EQUALS_SIGN = (byte)'=';
46      
47      
48      /*** The new line character (\n) as a byte. */
49      private final static byte NEW_LINE = (byte)'\n';
50      
51      
52      /*** The 64 valid Base64 values. */
53      private final static byte[] ALPHABET =
54      {
55          (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
56          (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
57          (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
58          (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
59          (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
60          (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
61          (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
62          (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
63          (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
64          (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
65      };
66      
67      /*** 
68       * Translates a Base64 value to either its 6-bit reconstruction value
69       * or a negative number indicating some other meaning.
70       **/
71      private final static byte[] DECODABET =
72      {   
73          -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
74          -5,-5,                                      // Whitespace: Tab and Linefeed
75          -9,-9,                                      // Decimal 11 - 12
76          -5,                                         // Whitespace: Carriage Return
77          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
78          -9,-9,-9,-9,-9,                             // Decimal 27 - 31
79          -5,                                         // Whitespace: Space
80          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
81          62,                                         // Plus sign at decimal 43
82          -9,-9,-9,                                   // Decimal 44 - 46
83          63,                                         // Slash at decimal 47
84          52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
85          -9,-9,-9,                                   // Decimal 58 - 60
86          -1,                                         // Equals sign at decimal 61
87          -9,-9,-9,                                      // Decimal 62 - 64
88          0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
89          14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
90          -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
91          26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
92          39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
93          -9,-9,-9,-9                                 // Decimal 123 - 126
94          /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
95          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
96          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
97          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
98          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
99          -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
100         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
101         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
102         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
103         -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
104     };
105     
106     private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
107     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
108     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
109 
110     
111     /*** Defeats instantiation. */
112     private Base64(){}
113     
114     
115     
116     /***
117      * Testing. Feel free--in fact I encourage you--to throw out 
118      * this entire "main" method when you actually deploy this code.
119      */
120     public static void main( String[] args )
121     {
122         try
123         {
124             // Test encoding/decoding byte arrays
125             {
126                 byte[] bytes1 = { (byte)2,(byte)2,(byte)3,(byte)0,(byte)9 }; // My zip code
127                 byte[] bytes2 = { (byte)99,(byte)2,(byte)2,(byte)3,(byte)0,(byte)9 };
128                 System.out.println( "Bytes 2,2,3,0,9 as Base64: " + encodeBytes( bytes1 ) );
129                 System.out.println( "Bytes 2,2,3,0,9 w/ offset: " + encodeBytes( bytes2, 1, bytes2.length-1 ) );
130                 byte[] dbytes = decode( encodeBytes( bytes1 ) );
131                 System.out.print( encodeBytes( bytes1 ) + " decoded: " );
132                 for( int i = 0; i < dbytes.length; i++ )
133                     System.out.print( dbytes[i] + (i<dbytes.length-1?",":"\n") );
134             }   // end testing byte arrays
135             
136             
137             
138             
139             // Test Input Stream
140             {
141                 // Read GIF stored in base64 form.
142                 java.io.FileInputStream fis = new java.io.FileInputStream( "test.gif.b64" );
143                 Base64.InputStream b64is = new Base64.InputStream( fis, DECODE );
144 
145                 byte[] bytes = new byte[0];
146                 int b = -1;
147                 while( (b = b64is.read()) >= 0 ){
148                     byte[] temp = new byte[ bytes.length + 1 ];
149                     System.arraycopy( bytes,0, temp,0,bytes.length );
150                     temp[bytes.length] = (byte)b;
151                     bytes = temp;
152                 }   // end while: terribly inefficient way to read data
153                 b64is.close();
154                 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon( bytes );
155                 javax.swing.JLabel jlabel = new javax.swing.JLabel( "Read from test.gif.b64", iicon,0 );
156                 javax.swing.JFrame jframe = new javax.swing.JFrame();
157                 jframe.getContentPane().add( jlabel );
158                 jframe.pack();
159                 jframe.show();
160 
161                 // Write raw bytes to file
162                 java.io.FileOutputStream fos = new java.io.FileOutputStream( "test.gif_out" );
163                 fos.write( bytes );
164                 fos.close();
165 
166                 // Read raw bytes and encode
167                 fis = new java.io.FileInputStream( "test.gif_out" );
168                 b64is = new Base64.InputStream( fis, ENCODE );
169                 byte[] ebytes = new byte[0];
170                 b = -1;
171                 while( (b = b64is.read()) >= 0 ){
172                     byte[] temp = new byte[ ebytes.length + 1 ];
173                     System.arraycopy( ebytes,0, temp,0,ebytes.length );
174                     temp[ebytes.length] = (byte)b;
175                     ebytes = temp;
176                 }   // end while: terribly inefficient way to read data
177                 b64is.close();
178                 String s = new String( ebytes );
179                 javax.swing.JTextArea jta = new javax.swing.JTextArea( s );
180                 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane( jta );
181                 jframe = new javax.swing.JFrame();
182                 jframe.setTitle( "Read from test.gif_out" );
183                 jframe.getContentPane().add( jsp );
184                 jframe.pack();
185                 jframe.show();
186 
187                 // Write encoded bytes to file
188                 fos = new java.io.FileOutputStream( "test.gif.b64_out" );
189                 fos.write( ebytes );
190 
191                 // Read GIF stored in base64 form.
192                 fis = new java.io.FileInputStream( "test.gif.b64_out" );
193                 b64is = new Base64.InputStream( fis, DECODE );
194                 byte[] edbytes = new byte[0]; 
195                 b = -1;
196                 while( (b = b64is.read()) >= 0 ){
197                     byte[] temp = new byte[ edbytes.length + 1 ];
198                     System.arraycopy( edbytes,0, temp,0,edbytes.length );
199                     temp[edbytes.length] = (byte)b;
200                     edbytes = temp;
201                 }   // end while: terribly inefficient way to read data
202                 b64is.close();
203                 iicon = new javax.swing.ImageIcon( edbytes );
204                 jlabel = new javax.swing.JLabel( "Read from test.gif.b64_out", iicon,0 );
205                 jframe = new javax.swing.JFrame();
206                 jframe.getContentPane().add( jlabel );
207                 jframe.pack();
208                 jframe.show();
209             }   // end: Test Input Stream
210             
211             
212             // Test Output Stream
213             {
214                 // Read raw bytes
215                 java.io.FileInputStream fis = new java.io.FileInputStream( "test.gif_out" );
216                 byte[] rbytes = new byte[0];
217                 int b = -1;
218                 while( (b = fis.read()) >= 0 ){
219                     byte[] temp = new byte[ rbytes.length + 1 ];
220                     System.arraycopy( rbytes,0, temp,0,rbytes.length );
221                     temp[rbytes.length] = (byte)b;
222                     rbytes = temp;
223                 }   // end while: terribly inefficient way to read data
224                 fis.close();
225                 
226                 // Write raw bytes to encoded file
227                 java.io.FileOutputStream fos = new java.io.FileOutputStream("test.gif.b64_out2");
228                 Base64.OutputStream b64os = new Base64.OutputStream( fos, ENCODE );
229                 b64os.write( rbytes );
230                 b64os.close();
231                 
232                 
233                 // Read raw bytes that are actually encoded (but we'll ignore that)
234                 fis = new java.io.FileInputStream( "test.gif.b64_out2" );
235                 byte[] rebytes = new byte[0];
236                 b = -1;
237                 while( (b = fis.read()) >= 0 ){
238                     byte[] temp = new byte[ rebytes.length + 1 ];
239                     System.arraycopy( rebytes,0, temp,0,rebytes.length );
240                     temp[rebytes.length] = (byte)b;
241                     rebytes = temp;
242                 }   // end while: terribly inefficient way to read data
243                 fis.close();
244                 String s = new String( rebytes );
245                 javax.swing.JTextArea jta = new javax.swing.JTextArea( s );
246                 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane( jta );
247                 javax.swing.JFrame jframe = new javax.swing.JFrame();
248                 jframe.setTitle( "Read from test.gif.b64_out2" );
249                 jframe.getContentPane().add( jsp );
250                 jframe.pack();
251                 jframe.show();
252                
253                 // Write encoded bytes to decoded raw file
254                 fos = new java.io.FileOutputStream("test.gif_out2");
255                 b64os = new Base64.OutputStream( fos, DECODE );
256                 b64os.write( rebytes );
257                 b64os.close();
258                 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon( "test.gif_out2" );
259                 javax.swing.JLabel jlabel = new javax.swing.JLabel( "Read from test.gif_out2", iicon,0 );
260                 jframe = new javax.swing.JFrame();
261                 jframe.getContentPane().add( jlabel );
262                 jframe.pack();
263                 jframe.show();
264                
265             }   // end: Test Output Stream
266             
267             
268             // Test wagner's files
269             {
270                 java.io.FileInputStream fis = new java.io.FileInputStream("D://temp//testencoding.txt");
271                 Base64.InputStream b64is = new Base64.InputStream( fis, DECODE );
272                 java.io.FileOutputStream fos = new java.io.FileOutputStream("D://temp//file.zip");
273                 int b;
274                 while( (b=b64is.read()) >= 0 )
275                     fos.write( b );
276                 fos.close();
277                 b64is.close();
278             
279             }   // end test wagner's file
280             
281         }   // end try
282         catch( Exception e)
283         {   e.printStackTrace();
284         }
285     }   // end main
286     
287     
288 /* ********  E N C O D I N G   M E T H O D S  ******** */    
289     
290     
291     /***
292      * Encodes the first three bytes of array <var>threeBytes</var>
293      * and returns a four-byte array in Base64 notation.
294      *
295      * @param threeBytes the array to convert
296      * @return four byte array in Base64 notation.
297      * @since 1.3
298      */
299     private static byte[] encode3to4( byte[] threeBytes )
300     {   return encode3to4( threeBytes, 3 );
301     }   // end encodeToBytes
302     
303     
304     
305     /***
306      * Encodes up to the first three bytes of array <var>threeBytes</var>
307      * and returns a four-byte array in Base64 notation.
308      * The actual number of significant bytes in your array is
309      * given by <var>numSigBytes</var>.
310      * The array <var>threeBytes</var> needs only be as big as
311      * <var>numSigBytes</var>.
312      *
313      * @param threeBytes the array to convert
314      * @param numSigBytes the number of significant bytes in your array
315      * @return four byte array in Base64 notation.
316      * @since 1.3
317      */
318     private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )
319     {   byte[] dest = new byte[4];
320         encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
321         return dest;
322     }
323     
324     
325     
326     /***
327      * Encodes up to three bytes of the array <var>source</var>
328      * and writes the resulting four Base64 bytes to <var>destination</var>.
329      * The source and destination arrays can be manipulated
330      * anywhere along their length by specifying 
331      * <var>srcOffset</var> and <var>destOffset</var>.
332      * This method does not check to make sure your arrays
333      * are large enough to accomodate <var>srcOffset</var> + 3 for
334      * the <var>source</var> array or <var>destOffset</var> + 4 for
335      * the <var>destination</var> array.
336      * The actual number of significant bytes in your array is
337      * given by <var>numSigBytes</var>.
338      *
339      * @param source the array to convert
340      * @param srcOffset the index where conversion begins
341      * @param numSigBytes the number of significant bytes in your array
342      * @param destination the array to hold the conversion
343      * @param destOffset the index where output will be put
344      * @return the <var>destination</var> array
345      * @since 1.3
346      */
347     private static byte[] encode3to4( 
348      byte[] source, int srcOffset, int numSigBytes,
349      byte[] destination, int destOffset )
350     {
351         //           1         2         3  
352         // 01234567890123456789012345678901 Bit position
353         // --------000000001111111122222222 Array position from threeBytes
354         // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
355         //          >>18  >>12  >> 6  >> 0  Right shift necessary
356         //                0x3f  0x3f  0x3f  Additional AND
357         
358         // Create buffer with zero-padding if there are only one or two
359         // significant bytes passed in the array.
360         // We have to shift left 24 in order to flush out the 1's that appear
361         // when Java treats a value as negative that is cast from a byte to an int.
362         int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
363                      | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
364                      | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
365 
366         switch( numSigBytes )
367         {
368             case 3:
369                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
370                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
371                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
372                 destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
373                 return destination;
374                 
375             case 2:
376                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
377                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
378                 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
379                 destination[ destOffset + 3 ] = EQUALS_SIGN;
380                 return destination;
381                 
382             case 1:
383                 destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
384                 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
385                 destination[ destOffset + 2 ] = EQUALS_SIGN;
386                 destination[ destOffset + 3 ] = EQUALS_SIGN;
387                 return destination;
388                 
389             default:
390                 return destination;
391         }   // end switch
392     }   // end encode3to4
393     
394     /***
395      * Serializes an object and returns the Base64-encoded
396      * version of that serialized object. If the object
397      * cannot be serialized or there is another error,
398      * the method will return <tt>null</tt>.
399      *
400      * @param serializableObject The object to encode
401      * @return The Base64-encoded object
402      * @since 1.4
403      */
404     public static String encodeObject( java.io.Serializable serializableObject )
405     {
406         return encodeObject( serializableObject, true );
407     }   // end encodeObject
408     
409     /***
410      * Serializes an object and returns the Base64-encoded
411      * version of that serialized object. If the object
412      * cannot be serialized or there is another error,
413      * the method will return <tt>null</tt>.
414      *
415      * @param serializableObject The object to encode
416      * @param breakLines Break lines at 80 characters or less.
417      * @return The Base64-encoded object
418      * @since 1.4
419      */
420     public static String encodeObject( java.io.Serializable serializableObject, boolean breakLines )
421     {
422         java.io.ByteArrayOutputStream baos  = null; 
423         java.io.OutputStream          b64os = null; 
424         java.io.ObjectOutputStream    oos   = null; 
425         
426         try
427         {
428             baos  = new java.io.ByteArrayOutputStream();
429             b64os = new Base64.OutputStream( baos, Base64.ENCODE, breakLines );
430             oos   = new java.io.ObjectOutputStream( b64os );
431             
432             oos.writeObject( serializableObject );
433         }   // end try
434         catch( java.io.IOException e )
435         {
436             e.printStackTrace();
437             return null;
438         }   // end catch
439         finally
440         {
441             try{ oos.close();   } catch( Exception e ){}
442             try{ b64os.close(); } catch( Exception e ){}
443             try{ baos.close();  } catch( Exception e ){}
444         }   // end finally
445         
446         return new String( baos.toByteArray() );
447     }   // end encode
448     
449     
450     /***
451      * Encodes a byte array into Base64 notation.
452      * Equivalen to calling
453      * <code>encodeBytes( source, 0, source.length )</code>
454      *
455      * @param source The data to convert
456      * @since 1.4
457      */
458     public static String encodeBytes( byte[] source )
459     {
460         return encodeBytes( source, true );
461     }   // end encodeBytes
462     
463     /***
464      * Encodes a byte array into Base64 notation.
465      * Equivalen to calling
466      * <code>encodeBytes( source, 0, source.length )</code>
467      *
468      * @param source The data to convert
469      * @param breakLines Break lines at 80 characters or less.
470      * @since 1.4
471      */
472     public static String encodeBytes( byte[] source, boolean breakLines )
473     {   
474         return encodeBytes( source, 0, source.length, breakLines );
475     }   // end encodeBytes
476     
477     
478     /***
479      * Encodes a byte array into Base64 notation.
480      *
481      * @param source The data to convert
482      * @param off Offset in array where conversion should begin
483      * @param len Length of data to convert
484      * @since 1.4
485      */
486     public static String encodeBytes( byte[] source, int off, int len )
487     {
488         return encodeBytes( source, off, len, true );
489     }   // end encodeBytes
490     
491     
492     /***
493      * Encodes a byte array into Base64 notation.
494      *
495      * @param source The data to convert
496      * @param off Offset in array where conversion should begin
497      * @param len Length of data to convert
498      * @param breakLines Break lines at 80 characters or less.
499      * @since 1.4
500      */
501     public static String encodeBytes( byte[] source, int off, int len, boolean breakLines )
502     {
503         int    len43   = len * 4 / 3;
504         byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
505                                    + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
506                                    + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
507         int d = 0;
508         int e = 0;
509         int len2 = len - 2;
510         int lineLength = 0;
511         for( ; d < len2; d+=3, e+=4 )
512         {
513             encode3to4( source, d+off, 3, outBuff, e );
514             
515             lineLength += 4;
516             if( breakLines && lineLength == MAX_LINE_LENGTH )
517             {   
518                 outBuff[e+4] = NEW_LINE;
519                 e++;
520                 lineLength = 0;
521             }   // end if: end of line
522         }   // en dfor: each piece of array
523         
524         if( d < len )
525         {
526             encode3to4( source, d+off, len - d, outBuff, e );
527             e += 4;
528         }   // end if: some padding needed
529         
530         return new String( outBuff, 0, e );
531     }   // end encodeBytes
532     
533     
534     /***
535      * Encodes a string in Base64 notation with line breaks
536      * after every 75 Base64 characters.
537      *
538      * @param s the string to encode
539      * @return the encoded string
540      * @since 1.3
541      */
542     public static String encodeString( String s )
543     {
544         return encodeString( s, true );
545     }   // end encodeString
546     
547     /***
548      * Encodes a string in Base64 notation with line breaks
549      * after every 75 Base64 characters.
550      *
551      * @param s the string to encode
552      * @param breakLines Break lines at 80 characters or less.
553      * @return the encoded string
554      * @since 1.3
555      */
556     public static String encodeString( String s, boolean breakLines )
557     {   
558         return encodeBytes( s.getBytes(), breakLines );
559     }   // end encodeString
560     
561     
562     
563     
564 /* ********  D E C O D I N G   M E T H O D S  ******** */
565     
566     
567     /***
568      * Decodes the first four bytes of array <var>fourBytes</var>
569      * and returns an array up to three bytes long with the
570      * decoded values.
571      *
572      * @param fourBytes the array with Base64 content
573      * @return array with decoded values
574      * @since 1.3
575      */
576     private static byte[] decode4to3( byte[] fourBytes )
577     {
578         byte[] outBuff1 = new byte[3];
579         int    count    = decode4to3( fourBytes, 0, outBuff1, 0 );
580         byte[] outBuff2 = new byte[ count ];
581         
582         for( int i = 0; i < count; i++ )
583             outBuff2[i] = outBuff1[i];
584         
585         return outBuff2;
586     }
587     
588     
589     
590     
591     /***
592      * Decodes four bytes from array <var>source</var>
593      * and writes the resulting bytes (up to three of them)
594      * to <var>destination</var>.
595      * The source and destination arrays can be manipulated
596      * anywhere along their length by specifying 
597      * <var>srcOffset</var> and <var>destOffset</var>.
598      * This method does not check to make sure your arrays
599      * are large enough to accomodate <var>srcOffset</var> + 4 for
600      * the <var>source</var> array or <var>destOffset</var> + 3 for
601      * the <var>destination</var> array.
602      * This method returns the actual number of bytes that 
603      * were converted from the Base64 encoding.
604      * 
605      *
606      * @param source the array to convert
607      * @param srcOffset the index where conversion begins
608      * @param destination the array to hold the conversion
609      * @param destOffset the index where output will be put
610      * @return the number of decoded bytes converted
611      * @since 1.3
612      */
613     private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
614     {
615         // Example: Dk==
616         if( source[ srcOffset + 2] == EQUALS_SIGN )
617         {
618             // Two ways to do the same thing. Don't know which way I like best.
619             //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
620             //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
621             int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
622                           | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
623             
624             destination[ destOffset ] = (byte)( outBuff >>> 16 );
625             return 1;
626         }
627         
628         // Example: DkL=
629         else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
630         {
631             // Two ways to do the same thing. Don't know which way I like best.
632             //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
633             //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
634             //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
635             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
636                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
637                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
638             
639             destination[ destOffset     ] = (byte)( outBuff >>> 16 );
640             destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
641             return 2;
642         }
643         
644         // Example: DkLE
645         else
646         {
647             try{
648             // Two ways to do the same thing. Don't know which way I like best.
649             //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
650             //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
651             //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
652             //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
653             int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
654                           | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
655                           | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
656                           | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
657 
658             
659             destination[ destOffset     ] = (byte)( outBuff >> 16 );
660             destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
661             destination[ destOffset + 2 ] = (byte)( outBuff       );
662 
663             return 3;
664             }catch( Exception e){
665                 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
666                 System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
667                 System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
668                 System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
669                 return -1;
670             }   //e nd catch
671         }
672     }   // end decodeToBytes
673     
674     
675     
676     /***
677      * Decodes data from Base64 notation.
678      *
679      * @param s the string to decode
680      * @return the decoded data
681      * @since 1.4
682      */
683     public static byte[] decode( String s )
684     {   
685         byte[] bytes = s.getBytes();
686         return decode( bytes, 0, bytes.length );
687     }   // end decode
688     
689     
690     /***
691      * Decodes data from Base64 notation and
692      * returns it as a string.
693      * Equivlaent to calling
694      * <code>new String( decode( s ) )</code>
695      *
696      * @param s the strind to decode
697      * @return The data as a string
698      * @since 1.4
699      */
700     public static String decodeToString( String s )
701     {   return new String( decode( s ) );
702     }   // end decodeToString
703     
704     
705     /***
706      * Attempts to decode Base64 data and deserialize a Java
707      * Object within. Returns <tt>null if there was an error.
708      *
709      * @param encodedObject The Base64 data to decode
710      * @return The decoded and deserialized object
711      * @since 1.4
712      */
713     public static Object decodeToObject( String encodedObject )
714     {
715         byte[] objBytes = decode( encodedObject );
716         
717         java.io.ByteArrayInputStream bais = null;
718         java.io.ObjectInputStream    ois  = null;
719         
720         try
721         {
722             bais = new java.io.ByteArrayInputStream( objBytes );
723             ois  = new java.io.ObjectInputStream( bais );
724         
725             return ois.readObject();
726         }   // end try
727         catch( java.io.IOException e )
728         {
729             e.printStackTrace();
730             return null;
731         }   // end catch
732         catch( java.lang.ClassNotFoundException e )
733         {
734             e.printStackTrace();
735             return null;
736         }   // end catch
737         finally
738         {
739             try{ bais.close(); } catch( Exception e ){}
740             try{ ois.close();  } catch( Exception e ){}
741         }   // end finally
742     }   // end decodeObject
743     
744     
745     /***
746      * Decodes Base64 content in byte array format and returns
747      * the decoded byte array.
748      *
749      * @param source The Base64 encoded data
750      * @param off    The offset of where to begin decoding
751      * @param len    The length of characters to decode
752      * @return decoded data
753      * @since 1.3
754      */
755     public static byte[] decode( byte[] source, int off, int len )
756     {
757         int    len34   = len * 3 / 4;
758         byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
759         int    outBuffPosn = 0;
760         
761         byte[] b4        = new byte[4];
762         int    b4Posn    = 0;
763         int    i         = 0;
764         byte   sbiCrop   = 0;
765         byte   sbiDecode = 0;
766         for( i = 0; i < len; i++ )
767         {
768             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
769             sbiDecode = DECODABET[ sbiCrop ];
770             
771             if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
772             {
773                 if( sbiDecode >= EQUALS_SIGN_ENC )
774                 {
775                     b4[ b4Posn++ ] = sbiCrop;
776                     if( b4Posn > 3 )
777                     {
778                         outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
779                         b4Posn = 0;
780                         
781                         // If that was the equals sign, break out of 'for' loop
782                         if( sbiCrop == EQUALS_SIGN )
783                             break;
784                     }   // end if: quartet built
785                     
786                 }   // end if: equals sign or better
787                 
788             }   // end if: white space, equals sign or better
789             else
790             {
791                 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
792                 return null;
793             }   // end else: 
794         }   // each input character
795                                    
796         byte[] out = new byte[ outBuffPosn ];
797         System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
798         return out;
799     }   // end decode
800     
801     
802     
803     
804     /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */
805     
806     
807     
808     /***
809      * A {@link Base64#InputStream} will read data from another
810      * {@link java.io.InputStream}, given in the constructor,
811      * and encode/decode to/from Base64 notation on the fly.
812      *
813      * @see Base64
814      * @see java.io.FilterInputStream
815      * @since 1.3
816      */
817     public static class InputStream extends java.io.FilterInputStream
818     {
819         private boolean encode;         // Encoding or decoding
820         private int     position;       // Current position in the buffer
821         private byte[]  buffer;         // Small buffer holding converted data
822         private int     bufferLength;   // Length of buffer (3 or 4)
823         private int     numSigBytes;    // Number of meaningful bytes in the buffer
824         private int     lineLength;
825         private boolean breakLines;     // Break lines at less than 80 characters
826         
827         
828         /***
829          * Constructs a {@link Base64#InputStream} in DECODE mode.
830          *
831          * @param in the {@link java.io.InputStream} from which to read data.
832          * @since 1.3
833          */
834         public InputStream( java.io.InputStream in )
835         {   
836             this( in, Base64.DECODE );
837         }   // end constructor
838         
839         
840         /***
841          * Constructs a {@link Base64#InputStream} in
842          * either ENCODE or DECODE mode.
843          *
844          * @param in the {@link java.io.InputStream} from which to read data.
845          * @param encode Conversion direction
846          * @see Base64#ENCODE
847          * @see Base64#DECODE
848          * @since 1.3
849          */
850         public InputStream( java.io.InputStream in, boolean encode )
851         {
852             this( in, encode, true );
853         }   // end constructor
854         
855         
856         /***
857          * Constructs a {@link Base64#InputStream} in
858          * either ENCODE or DECODE mode.
859          *
860          * @param in the {@link java.io.InputStream} from which to read data.
861          * @param encode Conversion direction
862          * @param breakLines Break lines at less than 80 characters.
863          * @see Base64#ENCODE
864          * @see Base64#DECODE
865          * @since 1.3
866          */
867         public InputStream( java.io.InputStream in, boolean encode, boolean breakLines )
868         {   
869             super( in );
870             this.breakLines = breakLines;
871             this.encode = encode;
872             this.bufferLength = encode ? 4 : 3;
873             this.buffer   = new byte[ bufferLength ];
874             this.position = -1;
875             this.lineLength = 0;
876         }   // end constructor
877         
878         /***
879          * Reads enough of the input stream to convert
880          * to/from Base64 and returns the next byte.
881          *
882          * @return next byte
883          * @since 1.3
884          */
885         public int read() throws java.io.IOException 
886         { 
887             // Do we need to get data?
888             if( position < 0 )
889             {
890                 if( encode )
891                 {
892                     byte[] b3 = new byte[3];
893                     int numBinaryBytes = 0;
894                     for( int i = 0; i < 3; i++ )
895                     {
896                         try
897                         { 
898                             int b = in.read();
899                             
900                             // If end of stream, b is -1.
901                             if( b >= 0 )
902                             {
903                                 b3[i] = (byte)b;
904                                 numBinaryBytes++;
905                             }   // end if: not end of stream
906                             
907                         }   // end try: read
908                         catch( java.io.IOException e )
909                         {   
910                             // Only a problem if we got no data at all.
911                             if( i == 0 )
912                                 throw e;
913                             
914                         }   // end catch
915                     }   // end for: each needed input byte
916                     
917                     if( numBinaryBytes > 0 )
918                     {
919                         encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
920                         position = 0;
921                         numSigBytes = 4;
922                     }   // end if: got data
923                     else
924                     {
925                         return -1;
926                     }   // end else
927                 }   // end if: encoding
928                 
929                 // Else decoding
930                 else
931                 {
932                     byte[] b4 = new byte[4];
933                     int i = 0;
934                     for( i = 0; i < 4; i++ )
935                     {
936                         // Read four "meaningful" bytes:
937                         int b = 0;
938                         do{ b = in.read(); }
939                         while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
940                         
941                         if( b < 0 )
942                             break; // Reads a -1 if end of stream
943                         
944                         b4[i] = (byte)b;
945                     }   // end for: each needed input byte
946                     
947                     if( i == 4 )
948                     {
949                         numSigBytes = decode4to3( b4, 0, buffer, 0 );
950                         position = 0;
951                     }   // end if: got four characters
952                     else if( i == 0 ){
953                         return -1;
954                     }   // end else if: also padded correctly
955                     else
956                     {
957                         // Must have broken out from above.
958                         throw new java.io.IOException( "Improperly padded Base64 input." );
959                     }   // end 
960                     
961                 }   // end else: decode
962             }   // end else: get data
963             
964             // Got data?
965             if( position >= 0 )
966             {
967                 // End of relevant data?
968                 if( /*!encode &&*/ position >= numSigBytes )
969                     return -1;
970                 
971                 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
972                 {
973                     lineLength = 0;
974                     return '\n';
975                 }   // end if
976                 else
977                 {
978                     lineLength++;   // This isn't important when decoding
979                                     // but throwing an extra "if" seems
980                                     // just as wasteful.
981                     
982                     int b = buffer[ position++ ];
983 
984                     if( position >= bufferLength )
985                         position = -1;
986 
987                     return b & 0xFF; // This is how you "cast" a byte that's
988                                      // intended to be unsigned.
989                 }   // end else
990             }   // end if: position >= 0
991             
992             // Else error
993             else
994             {   
995                 // When JDK1.4 is more accepted, use an assertion here.
996                 throw new java.io.IOException( "Error in Base64 code reading stream." );
997             }   // end else
998         }   // end read
999         
1000         
1001         /***
1002          * Calls {@link #read} repeatedly until the end of stream
1003          * is reached or <var>len</var> bytes are read.
1004          * Returns number of bytes read into array or -1 if
1005          * end of stream is encountered.
1006          *
1007          * @param dest array to hold values
1008          * @param off offset for array
1009          * @param len max number of bytes to read into array
1010          * @return bytes read into array or -1 if end of stream is encountered.
1011          * @since 1.3
1012          */
1013         public int read( byte[] dest, int off, int len ) throws java.io.IOException
1014         {
1015             int i;
1016             int b;
1017             for( i = 0; i < len; i++ )
1018             {
1019                 b = read();
1020                 
1021                 //if( b < 0 && i == 0 )
1022                 //    return -1;
1023                 
1024                 if( b >= 0 )
1025                     dest[off + i] = (byte)b;
1026                 else if( i == 0 )
1027                     return -1;
1028                 else
1029                     break; // Out of 'for' loop
1030             }   // end for: each byte read
1031             return i;
1032         }   // end read
1033         
1034     }   // end inner class InputStream
1035     
1036     
1037     
1038     
1039     
1040     
1041     /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */
1042     
1043     
1044     
1045     /***
1046      * A {@link Base64#OutputStream} will write data to another
1047      * {@link java.io.OutputStream}, given in the constructor,
1048      * and encode/decode to/from Base64 notation on the fly.
1049      *
1050      * @see Base64
1051      * @see java.io.FilterOutputStream
1052      * @since 1.3
1053      */
1054     public static class OutputStream extends java.io.FilterOutputStream
1055     {
1056         private boolean encode;
1057         private int     position;
1058         private byte[]  buffer;
1059         private int     bufferLength;
1060         private int     lineLength;
1061         private boolean breakLines;
1062         
1063         
1064         /***
1065          * Constructs a {@link Base64#OutputStream} in ENCODE mode.
1066          *
1067          * @param out the {@link java.io.OutputStream} to which data will be written.
1068          * @since 1.3
1069          */
1070         public OutputStream( java.io.OutputStream out )
1071         {   
1072             this( out, Base64.ENCODE );
1073         }   // end constructor
1074         
1075         
1076         /***
1077          * Constructs a {@link Base64#OutputStream} in
1078          * either ENCODE or DECODE mode.
1079          *
1080          * @param out the {@link java.io.OutputStream} to which data will be written.
1081          * @param encode Conversion direction
1082          * @see Base64#ENCODE
1083          * @see Base64#DECODE
1084          * @since 1.3
1085          */
1086         public OutputStream( java.io.OutputStream out, boolean encode )
1087         {
1088             this( out, encode, true );
1089         }   // end constructor
1090         
1091         
1092         /***
1093          * Constructs a {@link Base64#OutputStream} in
1094          * either ENCODE or DECODE mode.
1095          *
1096          * @param out the {@link java.io.OutputStream} to which data will be written.
1097          * @param encode Conversion direction
1098          * @param breakLines Break lines to be less than 80 characters.
1099          * @see Base64#ENCODE
1100          * @see Base64#DECODE
1101          * @since 1.3
1102          */
1103         public OutputStream( java.io.OutputStream out, boolean encode, boolean breakLines )
1104         {   
1105             super( out );
1106             this.breakLines   = breakLines;
1107             this.encode       = encode;
1108             this.bufferLength = encode ? 3 : 4;
1109             this.buffer       = new byte[ bufferLength ];
1110             this.position     = 0;
1111             this.lineLength   = 0;
1112         }   // end constructor
1113         
1114         
1115         /***
1116          * Writes the byte to the output stream after
1117          * converting to/from Base64 notation.
1118          * When encoding, bytes are buffered three
1119          * at a time before the output stream actually
1120          * gets a write() call.
1121          * When decoding, bytes are buffered four
1122          * at a time.
1123          *
1124          * @param theByte the byte to write
1125          * @since 1.3
1126          */
1127         public void write(int theByte) throws java.io.IOException 
1128         {
1129             if( encode )
1130             {
1131                 buffer[ position++ ] = (byte)theByte;
1132                 if( position >= bufferLength )  // Enough to encode.
1133                 {   
1134                     out.write( Base64.encode3to4( buffer, bufferLength ) );
1135                     
1136                     lineLength += 4;
1137                     if( breakLines && lineLength >= MAX_LINE_LENGTH )
1138                     {
1139                         out.write( NEW_LINE );
1140                         lineLength = 0;
1141                     }   // end if: end of line
1142                     
1143                     position = 0;
1144                 }   // end if: enough to output
1145             }   // end if: encoding
1146             
1147             // Else, Decoding
1148             else 
1149             {
1150                 // Meaningful Base64 character?
1151                 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1152                 {
1153                     buffer[ position++ ] = (byte)theByte;
1154                     if( position >= bufferLength )  // Enough to output.
1155                     {   
1156                         out.write( Base64.decode4to3( buffer ) );
1157                         position = 0;
1158                     }   // end if: enough to output
1159                 }   // end if: meaningful base64 character
1160                 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1161                 {
1162                     throw new java.io.IOException( "Invalid character in Base64 data." );
1163                 }   // end else: not white space either
1164             }   // end else: decoding
1165         }   // end write
1166         
1167         
1168         
1169         /***
1170          * Calls {@link #write} repeatedly until <var>len</var> 
1171          * bytes are written.
1172          *
1173          * @param theBytes array from which to read bytes
1174          * @param off offset for array
1175          * @param len max number of bytes to read into array
1176          * @since 1.3
1177          */
1178         public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1179         {
1180             for( int i = 0; i < len; i++ )
1181             {
1182                 write( theBytes[ off + i ] );
1183             }   // end for: each byte written
1184             
1185         }   // end write
1186         
1187         
1188         /***
1189          * Appropriately pads Base64 notation when encoding
1190          * or throws an exception if Base64 input is not
1191          * properly padded when decoding.
1192          *
1193          * @since 1.3
1194          */
1195         public void flush() throws java.io.IOException
1196         {
1197             super.flush();
1198             
1199             if( position > 0 )
1200             {
1201                 if( encode )
1202                 {
1203                     out.write( Base64.encode3to4( buffer, position ) );
1204                     position = 0;
1205                 }   // end if: encoding
1206                 else
1207                 {   
1208                     throw new java.io.IOException( "Base64 input not properly padded." );
1209                 }   // end else: decoding
1210             }   // end if: buffer partially full
1211             
1212             out.flush();
1213         }   // end flush
1214         
1215         
1216         /*** 
1217          * Flushes and closes (I think, in the superclass) the stream. 
1218          *
1219          * @since 1.3
1220          */
1221         public void close() throws java.io.IOException
1222         {
1223             super.close();
1224             //this.flush();
1225             
1226             out.close();
1227             
1228             buffer = null;
1229             out    = null;
1230         }   // end close
1231         
1232     }   // end inner class OutputStream
1233     
1234     
1235 }   // end class Base64