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