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