JavaDoc


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





























































Powered by Drupal - Theme by Danger4k