1 /*
2 * ModeShape (http://www.modeshape.org)
3 * See the COPYRIGHT.txt file distributed with this work for information
4 * regarding copyright ownership. Some portions may be licensed
5 * to Red Hat, Inc. under one or more contributor license agreements.
6 * See the AUTHORS.txt file in the distribution for a full listing of
7 * individual contributors.
8 *
9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10 * is licensed to you under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * ModeShape is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this software; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24 package org.modeshape.common.util;
25
26 import java.io.BufferedInputStream;
27 import java.io.IOException;
28 import org.modeshape.common.SystemFailureException;
29
30 /**
31 * <p>
32 * Encodes and decodes to and from Base64 notation.
33 * </p>
34 * <p>
35 * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.
36 * </p>
37 * <p>
38 * Example:
39 * </p>
40 * <code>String encoded = Base64.encode( myByteArray );</code> <br /> <code>byte[] myByteArray = Base64.decode( encoded );</code>
41 * <p>
42 * The <tt>options</tt> parameter, which appears in a few places, is used to pass several pieces of information to the encoder. In
43 * the "higher level" methods such as encodeBytes( bytes, options ) the options parameter can be used to indicate such things as
44 * first gzipping the bytes before encoding them, not inserting linefeeds, and encoding using the URL-safe and Ordered dialects.
45 * </p>
46 * <p>
47 * Note, according to <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>, Section 2.1, implementations should not add
48 * line feeds unless explicitly told to do so. I've got Base64 set to this behavior now, although earlier versions broke lines by
49 * default.
50 * </p>
51 * <p>
52 * The constants defined in Base64 can be OR-ed together to combine options, so you might make a call like this:
53 * </p>
54 * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );</code>
55 * <p>
56 * to compress the data before encoding it and then making the output have newline characters.
57 * </p>
58 * <p>
59 * Also...
60 * </p>
61 * <code>String encoded = Base64.encodeBytes( crazyString.getBytes() );</code>
62 * <p>
63 * Change Log:
64 * </p>
65 * <ul>
66 * <li>v2.3.7 - Fixed subtle bug when base 64 input stream contained the value 01111111, which is an invalid base 64 character but
67 * should not throw an ArrayIndexOutOfBoundsException either. Led to discovery of mishandling (or potential for better handling)
68 * of other bad input characters. You should now get an IOException if you try decoding something that has bad characters in it.</li>
69 * <li>v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded string ended in the last column; the buffer was
70 * not properly shrunk and contained an extra (null) byte that made it into the string.</li>
71 * <li>v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size was wrong for files of size 31, 34, and 37 bytes.
72 * </li>
73 * <li>v2.3.4 - Fixed bug when working with gzipped streams whereby flushing the Base64.OutputStream closed the Base64 encoding
74 * (by padding with equals signs) too soon. Also added an option to suppress the automatic decoding of gzipped streams. Also added
75 * experimental support for specifying a class loader when using the
76 * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)} method.</li>
77 * <li>v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java footprint with its CharEncoders and so
78 * forth. Fixed some javadocs that were inconsistent. Removed imports and specified things like java.io.IOException explicitly
79 * inline.</li>
80 * <li>v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the final encoded data will be so that the
81 * code doesn't have to create two output arrays: an oversized initial one and then a final, exact-sized one. Big win when using
82 * the {@link #encodeBytesToBytes(byte[])} family of methods (and not using the gzip options which uses a different mechanism with
83 * streams and stuff).</li>
84 * <li>v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some similar helper methods to be more efficient with
85 * memory by not returning a String but just a byte array.</li>
86 * <li>v2.3 - <strong>This is not a drop-in replacement!</strong> This is two years of comments and bug fixes queued up and
87 * finally executed. Thanks to everyone who sent me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else.
88 * Much bad coding was cleaned up including throwing exceptions where necessary instead of returning null values or something
89 * similar. Here are some changes that may affect you:
90 * <ul>
91 * <li><em>Does not break lines, by default.</em> This is to keep in compliance with <a
92 * href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
93 * <li><em>Throws exceptions instead of returning null values.</em> Because some operations (especially those that may permit the
94 * GZIP option) use IO streams, there is a possiblity of an java.io.IOException being thrown. After some discussion and thought,
95 * I've changed the behavior of the methods to throw java.io.IOExceptions rather than return null if ever there's an error. I
96 * think this is more appropriate, though it will require some changes to your code. Sorry, it should have been done this way to
97 * begin with.</li>
98 * <li><em>Removed all references to System.out, System.err, and the like.</em> Shame on me. All I can say is sorry they were ever
99 * there.</li>
100 * <li><em>Throws NullPointerExceptions and IllegalArgumentExceptions</em> as needed such as when passed arrays are null or
101 * offsets are invalid.</li>
102 * <li>Cleaned up as much javadoc as I could to avoid any javadoc warnings. This was especially annoying before for people who
103 * were thorough in their own projects and then had gobs of javadoc warnings on this file.</li>
104 * </ul>
105 * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug when using very small files (~< 40 bytes).</li>
106 * <li>v2.2 - Added some helper methods for encoding/decoding directly from one file to the next. Also added a main() method to
107 * support command line encoding/decoding from one file to the next. Also added these Base64 dialects:
108 * <ol>
109 * <li>The default is RFC3548 format.</li>
110 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates URL and file name friendly format as described in
111 * Section 4 of RFC3548. http://www.faqs.org/rfcs/rfc3548.html</li>
112 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates URL and file name friendly format that preserves
113 * lexical ordering as described in http://www.faqs.org/qa/rfcc-1940.html</li>
114 * </ol>
115 * Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a> for contributing the new
116 * Base64 dialects.</li>
117 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added some convenience methods for reading and writing
118 * to and from files.</li>
119 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems with other encodings (like EBCDIC).</li>
120 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the encoded data was a single byte.</li>
121 * <li>v2.0 - I got rid of methods that used booleans to set options. Now everything is more consolidated and cleaner. The code
122 * now detects when data that's being decoded is gzip-compressed and will decompress it automatically. Generally things are
123 * cleaner. You'll probably have to change some method calls that you were making to support the new options format (<tt>int</tt>s
124 * that you "OR" together).</li>
125 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
126 * Added the ability to "suspend" encoding in the Output Stream so you can turn on and off the encoding if you need to embed
127 * base64 data in an otherwise "normal" stream (like an XML file).</li>
128 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself. This helps when using GZIP streams. Added the
129 * ability to GZip-compress objects before encoding them.</li>
130 * <li>v1.4 - Added helper methods to read/write files.</li>
131 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
132 * <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
133 * completely full, was not returned.</li>
134 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
135 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
136 * </ul>
137 * <p>
138 * I am placing this code in the Public Domain. Do with it as you will. This software comes with no guarantees or warranties but
139 * with plenty of well-wishing instead! Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
140 * periodically to check for updates or to contribute improvements.
141 * </p>
142 *
143 * @author Robert Harder
144 * @author rob@iharder.net
145 * @version 2.3.7
146 */
147 public class Base64 {
148
149 /* ******** P U B L I C F I E L D S ******** */
150
151 /** No options specified. Value is zero. */
152 public final static int NO_OPTIONS = 0;
153
154 /** Specify encoding in first bit. Value is one. */
155 public final static int ENCODE = 1;
156
157 /** Specify decoding in first bit. Value is zero. */
158 public final static int DECODE = 0;
159
160 /** Specify that data should be gzip-compressed in second bit. Value is two. */
161 public final static int GZIP = 2;
162
163 /** Specify that gzipped data should <em>not</em> be automatically gunzipped. */
164 public final static int DONT_GUNZIP = 4;
165
166 /** Do break lines when encoding. Value is 8. */
167 public final static int DO_BREAK_LINES = 8;
168
169 /**
170 * Encode using Base64-like encoding that is URL- and Filename-safe as described in Section 4 of RFC3548: <a
171 * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. It is important to note that data
172 * encoded this way is <em>not</em> officially valid Base64, or at the very least should not be called Base64 without also
173 * specifying that is was encoded using the URL- and Filename-safe dialect.
174 */
175 public final static int URL_SAFE = 16;
176
177 /**
178 * Encode using the special "ordered" dialect of Base64 described here: <a
179 * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
180 */
181 public final static int ORDERED = 32;
182
183 /* ******** P R I V A T E F I E L D S ******** */
184
185 /** Maximum line length (76) of Base64 output. */
186 private final static int MAX_LINE_LENGTH = 76;
187
188 /** The equals sign (=) as a byte. */
189 private final static byte EQUALS_SIGN = (byte)'=';
190
191 /** The new line character (\n) as a byte. */
192 private final static byte NEW_LINE = (byte)'\n';
193
194 /** Preferred encoding. */
195 private final static String PREFERRED_ENCODING = "US-ASCII";
196
197 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
198 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
199
200 /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
201
202 /** The 64 valid Base64 values. */
203 /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
204 private final static byte[] _STANDARD_ALPHABET = {(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F',
205 (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q',
206 (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b',
207 (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',
208 (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
209 (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8',
210 (byte)'9', (byte)'+', (byte)'/'};
211
212 /**
213 * Translates a Base64 value to either its 6-bit reconstruction value or a negative number indicating some other meaning.
214 **/
215 private final static byte[] _STANDARD_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
216 -5, -5, // Whitespace: Tab and Linefeed
217 -9, -9, // Decimal 11 - 12
218 -5, // Whitespace: Carriage Return
219 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
220 -9, -9, -9, -9, -9, // Decimal 27 - 31
221 -5, // Whitespace: Space
222 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
223 62, // Plus sign at decimal 43
224 -9, -9, -9, // Decimal 44 - 46
225 63, // Slash at decimal 47
226 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
227 -9, -9, -9, // Decimal 58 - 60
228 -1, // Equals sign at decimal 61
229 -9, -9, -9, // Decimal 62 - 64
230 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
231 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
232 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
233 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
234 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
235 -9, -9, -9, -9, -9 // Decimal 123 - 127
236 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
237 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
238 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
239 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
240 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
241 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
242 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
243 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
244 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
245 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
246 };
247
248 /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
249
250 /**
251 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: <a
252 * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. Notice that the last two bytes
253 * become "hyphen" and "underscore" instead of "plus" and "slash."
254 */
255 private final static byte[] _URL_SAFE_ALPHABET = {(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F',
256 (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q',
257 (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b',
258 (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',
259 (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
260 (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8',
261 (byte)'9', (byte)'-', (byte)'_'};
262
263 /**
264 * Used in decoding URL- and Filename-safe dialects of Base64.
265 */
266 private final static byte[] _URL_SAFE_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
267 -5, -5, // Whitespace: Tab and Linefeed
268 -9, -9, // Decimal 11 - 12
269 -5, // Whitespace: Carriage Return
270 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
271 -9, -9, -9, -9, -9, // Decimal 27 - 31
272 -5, // Whitespace: Space
273 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
274 -9, // Plus sign at decimal 43
275 -9, // Decimal 44
276 62, // Minus sign at decimal 45
277 -9, // Decimal 46
278 -9, // Slash at decimal 47
279 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
280 -9, -9, -9, // Decimal 58 - 60
281 -1, // Equals sign at decimal 61
282 -9, -9, -9, // Decimal 62 - 64
283 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
284 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
285 -9, -9, -9, -9, // Decimal 91 - 94
286 63, // Underscore at decimal 95
287 -9, // Decimal 96
288 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
289 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
290 -9, -9, -9, -9, -9 // Decimal 123 - 127
291 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
292 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
293 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
294 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
295 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
296 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
297 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
298 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
299 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
300 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
301 };
302
303 /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
304
305 /**
306 * I don't get the point of this technique, but someone requested it, and it is described here: <a
307 * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
308 */
309 private final static byte[] _ORDERED_ALPHABET = {(byte)'-', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
310 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
311 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R',
312 (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'_', (byte)'a', (byte)'b',
313 (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',
314 (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
315 (byte)'y', (byte)'z'};
316
317 /**
318 * Used in decoding the "ordered" dialect of Base64.
319 */
320 private final static byte[] _ORDERED_DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
321 -5, -5, // Whitespace: Tab and Linefeed
322 -9, -9, // Decimal 11 - 12
323 -5, // Whitespace: Carriage Return
324 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
325 -9, -9, -9, -9, -9, // Decimal 27 - 31
326 -5, // Whitespace: Space
327 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
328 -9, // Plus sign at decimal 43
329 -9, // Decimal 44
330 0, // Minus sign at decimal 45
331 -9, // Decimal 46
332 -9, // Slash at decimal 47
333 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
334 -9, -9, -9, // Decimal 58 - 60
335 -1, // Equals sign at decimal 61
336 -9, -9, -9, // Decimal 62 - 64
337 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M'
338 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z'
339 -9, -9, -9, -9, // Decimal 91 - 94
340 37, // Underscore at decimal 95
341 -9, // Decimal 96
342 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm'
343 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z'
344 -9, -9, -9, -9, -9 // Decimal 123 - 127
345 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139
346 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152
347 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165
348 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178
349 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191
350 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204
351 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217
352 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230
353 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243
354 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
355 };
356
357 /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
358
359 /**
360 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options specified. It's possible, though silly, to
361 * specify ORDERED <b>and</b> URLSAFE in which case one of them will be picked, though there is no guarantee as to which one
362 * will be picked.
363 *
364 * @param options the options
365 * @return the byte array; never null
366 */
367 private final static byte[] getAlphabet( int options ) {
368 if ((options & URL_SAFE) == URL_SAFE) {
369 return _URL_SAFE_ALPHABET;
370 } else if ((options & ORDERED) == ORDERED) {
371 return _ORDERED_ALPHABET;
372 } else {
373 return _STANDARD_ALPHABET;
374 }
375 } // end getAlphabet
376
377 /**
378 * Returns one of the _SOMETHING_DECODABET byte arrays depending on the options specified. It's possible, though silly, to
379 * 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
380 * picked.
381 *
382 * @param options the options
383 * @return the byte array; never null
384 */
385 private final static byte[] getDecodabet( int options ) {
386 if ((options & URL_SAFE) == URL_SAFE) {
387 return _URL_SAFE_DECODABET;
388 } else if ((options & ORDERED) == ORDERED) {
389 return _ORDERED_DECODABET;
390 } else {
391 return _STANDARD_DECODABET;
392 }
393 } // end getAlphabet
394
395 /** Defeats instantiation. */
396 private Base64() {
397 }
398
399 /* ******** E N C O D I N G M E T H O D S ******** */
400
401 /**
402 * Encodes up to the first three bytes of array <var>threeBytes</var> and returns a four-byte array in Base64 notation. The
403 * actual number of significant bytes in your array is given by <var>numSigBytes</var>. The array <var>threeBytes</var> needs
404 * only be as big as <var>numSigBytes</var>. Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
405 *
406 * @param b4 A reusable byte array to reduce array instantiation
407 * @param threeBytes the array to convert
408 * @param numSigBytes the number of significant bytes in your array
409 * @param options the options
410 * @return four byte array in Base64 notation.
411 * @since 1.5.1
412 */
413 private static byte[] encode3to4( byte[] b4,
414 byte[] threeBytes,
415 int numSigBytes,
416 int options ) {
417 encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
418 return b4;
419 } // end encode3to4
420
421 /**
422 * <p>
423 * Encodes up to three bytes of the array <var>source</var> and writes the resulting four Base64 bytes to
424 * <var>destination</var>. The source and destination arrays can be manipulated anywhere along their length by specifying
425 * <var>srcOffset</var> and <var>destOffset</var>. This method does not check to make sure your arrays are large enough to
426 * accomodate <var>srcOffset</var> + 3 for the <var>source</var> array or <var>destOffset</var> + 4 for the
427 * <var>destination</var> array. The actual number of significant bytes in your array is given by <var>numSigBytes</var>.
428 * </p>
429 * <p>
430 * This is the lowest level of the encoding methods with all possible parameters.
431 * </p>
432 *
433 * @param source the array to convert
434 * @param srcOffset the index where conversion begins
435 * @param numSigBytes the number of significant bytes in your array
436 * @param destination the array to hold the conversion
437 * @param destOffset the index where output will be put
438 * @param options the options
439 * @return the <var>destination</var> array
440 * @since 1.3
441 */
442 private static byte[] encode3to4( byte[] source,
443 int srcOffset,
444 int numSigBytes,
445 byte[] destination,
446 int destOffset,
447 int options ) {
448
449 byte[] ALPHABET = getAlphabet(options);
450
451 // 1 2 3
452 // 01234567890123456789012345678901 Bit position
453 // --------000000001111111122222222 Array position from threeBytes
454 // --------| || || || | Six bit groups to index ALPHABET
455 // >>18 >>12 >> 6 >> 0 Right shift necessary
456 // 0x3f 0x3f 0x3f Additional AND
457
458 // Create buffer with zero-padding if there are only one or two
459 // significant bytes passed in the array.
460 // We have to shift left 24 in order to flush out the 1's that appear
461 // when Java treats a value as negative that is cast from a byte to an int.
462 int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
463 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
464 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
465
466 switch (numSigBytes) {
467 case 3:
468 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
469 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
470 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
471 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
472 return destination;
473
474 case 2:
475 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
476 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
477 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
478 destination[destOffset + 3] = EQUALS_SIGN;
479 return destination;
480
481 case 1:
482 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
483 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
484 destination[destOffset + 2] = EQUALS_SIGN;
485 destination[destOffset + 3] = EQUALS_SIGN;
486 return destination;
487
488 default:
489 return destination;
490 } // end switch
491 } // end encode3to4
492
493 /**
494 * Encodes content of the supplied InputStream into Base64 notation. Does not GZip-compress data.
495 *
496 * @param source The data to convert
497 * @return the encoded bytes
498 */
499 public static String encode( java.io.InputStream source ) {
500 return encode(source, NO_OPTIONS);
501 }
502
503 /**
504 * Encodes the content of the supplied InputStream into Base64 notation.
505 * <p>
506 * Valid options:
507 *
508 * <pre>
509 * GZIP: gzip-compresses object before encoding it.
510 * DONT_BREAK_LINES: don't break lines at 76 characters
511 * <i>Note: Technically, this makes your encoding non-compliant.</i>
512 * </pre>
513 * <p>
514 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
515 * <p>
516 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
517 *
518 * @param source The data to convert
519 * @param options Specified options- the alphabet type is pulled from this (standard, url-safe, ordered)
520 * @return the encoded bytes
521 * @see Base64#GZIP
522 */
523 public static String encode( java.io.InputStream source,
524 int options ) {
525 CheckArg.isNotNull(source, "source");
526 java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
527 Base64.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
528 BufferedInputStream input = new BufferedInputStream(source);
529 java.io.OutputStream output = b64os;
530
531 boolean error = false;
532 try {
533 if ((options & GZIP) == GZIP) {
534 output = new java.util.zip.GZIPOutputStream(output);
535 }
536 int numRead = 0;
537 byte[] buffer = new byte[1024];
538 while ((numRead = input.read(buffer)) > -1) {
539 output.write(buffer, 0, numRead);
540 }
541 output.close();
542 } catch (IOException e) {
543 error = true;
544 throw new SystemFailureException(e); // error using reading from byte array!
545 } finally {
546 try {
547 input.close();
548 } catch (IOException e) {
549 if (!error) new SystemFailureException(e); // error closing input stream
550 }
551 }
552
553 // Return value according to relevant encoding.
554 try {
555 return new String(baos.toByteArray(), PREFERRED_ENCODING);
556 } catch (java.io.UnsupportedEncodingException uue) {
557 return new String(baos.toByteArray());
558 }
559 }
560
561 /**
562 * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it to the <code>encoded</code> ByteBuffer. This is an
563 * experimental feature. Currently it does not pass along any options (such as {@link #DO_BREAK_LINES} or {@link #GZIP}.
564 *
565 * @param raw input buffer
566 * @param encoded output buffer
567 * @since 2.3
568 */
569 public static void encode( java.nio.ByteBuffer raw,
570 java.nio.ByteBuffer encoded ) {
571 byte[] raw3 = new byte[3];
572 byte[] enc4 = new byte[4];
573
574 while (raw.hasRemaining()) {
575 int rem = Math.min(3, raw.remaining());
576 raw.get(raw3, 0, rem);
577 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
578 encoded.put(enc4);
579 } // end input remaining
580 }
581
582 /**
583 * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it to the <code>encoded</code> CharBuffer. This is an
584 * experimental feature. Currently it does not pass along any options (such as {@link #DO_BREAK_LINES} or {@link #GZIP}.
585 *
586 * @param raw input buffer
587 * @param encoded output buffer
588 * @since 2.3
589 */
590 public static void encode( java.nio.ByteBuffer raw,
591 java.nio.CharBuffer encoded ) {
592 byte[] raw3 = new byte[3];
593 byte[] enc4 = new byte[4];
594
595 while (raw.hasRemaining()) {
596 int rem = Math.min(3, raw.remaining());
597 raw.get(raw3, 0, rem);
598 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
599 for (int i = 0; i < 4; i++) {
600 encoded.put((char)(enc4[i] & 0xFF));
601 }
602 } // end input remaining
603 }
604
605 /**
606 * Serializes an object and returns the Base64-encoded version of that serialized object.
607 * <p>
608 * As of v 2.3, if the object cannot be serialized or there is another error, the method will throw an java.io.IOException.
609 * <b>This is new to v2.3!</b> In earlier versions, it just returned a null value, but in retrospect that's a pretty poor way
610 * to handle it.
611 * </p>
612 * The object is not GZip-compressed before being encoded.
613 *
614 * @param serializableObject The object to encode
615 * @return The Base64-encoded object
616 * @throws java.io.IOException if there is an error
617 * @throws NullPointerException if serializedObject is null
618 * @since 1.4
619 */
620 public static String encodeObject( java.io.Serializable serializableObject ) throws java.io.IOException {
621 return encodeObject(serializableObject, NO_OPTIONS);
622 } // end encodeObject
623
624 /**
625 * Serializes an object and returns the Base64-encoded version of that serialized object.
626 * <p>
627 * As of v 2.3, if the object cannot be serialized or there is another error, the method will throw an java.io.IOException.
628 * <b>This is new to v2.3!</b> In earlier versions, it just returned a null value, but in retrospect that's a pretty poor way
629 * to handle it.
630 * </p>
631 * The object is not GZip-compressed before being encoded.
632 * <p>
633 * Example options:
634 *
635 * <pre>
636 * GZIP: gzip-compresses object before encoding it.
637 * DO_BREAK_LINES: break lines at 76 characters
638 * </pre>
639 * <p>
640 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
641 * <p>
642 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
643 *
644 * @param serializableObject The object to encode
645 * @param options Specified options
646 * @return The Base64-encoded object
647 * @see Base64#GZIP
648 * @see Base64#DO_BREAK_LINES
649 * @throws java.io.IOException if there is an error
650 * @since 2.0
651 */
652 public static String encodeObject( java.io.Serializable serializableObject,
653 int options ) throws java.io.IOException {
654
655 if (serializableObject == null) {
656 throw new NullPointerException("Cannot serialize a null object.");
657 } // end if: null
658
659 // Streams
660 java.io.ByteArrayOutputStream baos = null;
661 java.io.OutputStream b64os = null;
662 java.util.zip.GZIPOutputStream gzos = null;
663 java.io.ObjectOutputStream oos = null;
664
665 try {
666 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
667 baos = new java.io.ByteArrayOutputStream();
668 b64os = new Base64.OutputStream(baos, ENCODE | options);
669 if ((options & GZIP) != 0) {
670 // Gzip
671 gzos = new java.util.zip.GZIPOutputStream(b64os);
672 oos = new java.io.ObjectOutputStream(gzos);
673 } else {
674 // Not gzipped
675 oos = new java.io.ObjectOutputStream(b64os);
676 }
677 oos.writeObject(serializableObject);
678 } // end try
679 catch (java.io.IOException e) {
680 // Catch it and then throw it immediately so that
681 // the finally{} block is called for cleanup.
682 throw e;
683 } // end catch
684 finally {
685 try {
686 if (oos != null) oos.close();
687 } catch (Exception e) {
688 }
689 try {
690 if (gzos != null) gzos.close();
691 } catch (Exception e) {
692 }
693 try {
694 if (b64os != null) b64os.close();
695 } catch (Exception e) {
696 }
697 try {
698 if (baos != null) baos.close();
699 } catch (Exception e) {
700 }
701 } // end finally
702
703 // Return value according to relevant encoding.
704 assert baos != null;
705 try {
706 return new String(baos.toByteArray(), PREFERRED_ENCODING);
707 } // end try
708 catch (java.io.UnsupportedEncodingException uue) {
709 // Fall back to some Java default
710 return new String(baos.toByteArray());
711 } // end catch
712
713 } // end encode
714
715 /**
716 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
717 *
718 * @param source The data to convert
719 * @return The data in Base64-encoded form
720 * @throws NullPointerException if source array is null
721 * @since 1.4
722 */
723 public static String encodeBytes( byte[] source ) {
724 // Since we're not going to have the GZIP encoding turned on,
725 // we're not going to have an java.io.IOException thrown, so
726 // we should not force the user to have to catch it.
727 String encoded = null;
728 try {
729 encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
730 } catch (java.io.IOException ex) {
731 assert false : ex.getMessage();
732 } // end catch
733 assert encoded != null;
734 return encoded;
735 } // end encodeBytes
736
737 /**
738 * Encodes a byte array into Base64 notation.
739 * <p>
740 * Example options:
741 *
742 * <pre>
743 * GZIP: gzip-compresses object before encoding it.
744 * DO_BREAK_LINES: break lines at 76 characters
745 * <i>Note: Technically, this makes your encoding non-compliant.</i>
746 * </pre>
747 * <p>
748 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
749 * <p>
750 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
751 * <p>
752 * As of v 2.3, if there is an error with the GZIP stream, the method will throw an java.io.IOException. <b>This is new to
753 * v2.3!</b> In earlier versions, it just returned a null value, but in retrospect that's a pretty poor way to handle it.
754 * </p>
755 *
756 * @param source The data to convert
757 * @param options Specified options
758 * @return The Base64-encoded data as a String
759 * @see Base64#GZIP
760 * @see Base64#DO_BREAK_LINES
761 * @throws java.io.IOException if there is an error
762 * @throws NullPointerException if source array is null
763 * @since 2.0
764 */
765 public static String encodeBytes( byte[] source,
766 int options ) throws java.io.IOException {
767 return encodeBytes(source, 0, source.length, options);
768 } // end encodeBytes
769
770 /**
771 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
772 * <p>
773 * As of v 2.3, if there is an error, the method will throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
774 * versions, it just returned a null value, but in retrospect that's a pretty poor way to handle it.
775 * </p>
776 *
777 * @param source The data to convert
778 * @param off Offset in array where conversion should begin
779 * @param len Length of data to convert
780 * @return The Base64-encoded data as a String
781 * @throws NullPointerException if source array is null
782 * @throws IllegalArgumentException if source array, offset, or length are invalid
783 * @since 1.4
784 */
785 public static String encodeBytes( byte[] source,
786 int off,
787 int len ) {
788 // Since we're not going to have the GZIP encoding turned on,
789 // we're not going to have an java.io.IOException thrown, so
790 // we should not force the user to have to catch it.
791 String encoded = null;
792 try {
793 encoded = encodeBytes(source, off, len, NO_OPTIONS);
794 } catch (java.io.IOException ex) {
795 assert false : ex.getMessage();
796 } // end catch
797 assert encoded != null;
798 return encoded;
799 } // end encodeBytes
800
801 /**
802 * Encodes a byte array into Base64 notation.
803 * <p>
804 * Example options:
805 *
806 * <pre>
807 * GZIP: gzip-compresses object before encoding it.
808 * DO_BREAK_LINES: break lines at 76 characters
809 * <i>Note: Technically, this makes your encoding non-compliant.</i>
810 * </pre>
811 * <p>
812 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
813 * <p>
814 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
815 * <p>
816 * As of v 2.3, if there is an error with the GZIP stream, the method will throw an java.io.IOException. <b>This is new to
817 * v2.3!</b> In earlier versions, it just returned a null value, but in retrospect that's a pretty poor way to handle it.
818 * </p>
819 *
820 * @param source The data to convert
821 * @param off Offset in array where conversion should begin
822 * @param len Length of data to convert
823 * @param options Specified options
824 * @return The Base64-encoded data as a String
825 * @see Base64#GZIP
826 * @see Base64#DO_BREAK_LINES
827 * @throws java.io.IOException if there is an error
828 * @throws NullPointerException if source array is null
829 * @throws IllegalArgumentException if source array, offset, or length are invalid
830 * @since 2.0
831 */
832 public static String encodeBytes( byte[] source,
833 int off,
834 int len,
835 int options ) throws java.io.IOException {
836 byte[] encoded = encodeBytesToBytes(source, off, len, options);
837
838 // Return value according to relevant encoding.
839 try {
840 return new String(encoded, PREFERRED_ENCODING);
841 } // end try
842 catch (java.io.UnsupportedEncodingException uue) {
843 return new String(encoded);
844 } // end catch
845
846 } // end encodeBytes
847
848 /**
849 * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead of instantiating a String. This is more efficient
850 * if you're working with I/O streams and have large data sets to encode.
851 *
852 * @param source The data to convert
853 * @return The Base64-encoded data as a byte[] (of ASCII characters)
854 * @throws NullPointerException if source array is null
855 * @since 2.3.1
856 */
857 public static byte[] encodeBytesToBytes( byte[] source ) {
858 byte[] encoded = null;
859 try {
860 encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS);
861 } catch (java.io.IOException ex) {
862 assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
863 }
864 return encoded;
865 }
866
867 /**
868 * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte array instead of instantiating a String. This is
869 * more efficient if you're working with I/O streams and have large data sets to encode.
870 *
871 * @param source The data to convert
872 * @param off Offset in array where conversion should begin
873 * @param len Length of data to convert
874 * @param options Specified options
875 * @return The Base64-encoded data as a String
876 * @see Base64#GZIP
877 * @see Base64#DO_BREAK_LINES
878 * @throws java.io.IOException if there is an error
879 * @throws NullPointerException if source array is null
880 * @throws IllegalArgumentException if source array, offset, or length are invalid
881 * @since 2.3.1
882 */
883 public static byte[] encodeBytesToBytes( byte[] source,
884 int off,
885 int len,
886 int options ) throws java.io.IOException {
887
888 if (source == null) {
889 throw new NullPointerException("Cannot serialize a null array.");
890 } // end if: null
891
892 if (off < 0) {
893 throw new IllegalArgumentException("Cannot have negative offset: " + off);
894 } // end if: off < 0
895
896 if (len < 0) {
897 throw new IllegalArgumentException("Cannot have length offset: " + len);
898 } // end if: len < 0
899
900 if (off + len > source.length) {
901 throw new IllegalArgumentException(String.format("Cannot have offset of %d and length of %d with array of length %d",
902 off,
903 len,
904 source.length));
905 } // end if: off < 0
906
907 // Compress?
908 if ((options & GZIP) != 0) {
909 java.io.ByteArrayOutputStream baos = null;
910 java.util.zip.GZIPOutputStream gzos = null;
911 Base64.OutputStream b64os = null;
912
913 try {
914 // GZip -> Base64 -> ByteArray
915 baos = new java.io.ByteArrayOutputStream();
916 b64os = new Base64.OutputStream(baos, ENCODE | options);
917 gzos = new java.util.zip.GZIPOutputStream(b64os);
918
919 gzos.write(source, off, len);
920 gzos.close();
921 } // end try
922 catch (java.io.IOException e) {
923 // Catch it and then throw it immediately so that
924 // the finally{} block is called for cleanup.
925 throw e;
926 } // end catch
927 finally {
928 try {
929 if (gzos != null) gzos.close();
930 } catch (Exception e) {
931 }
932 try {
933 if (b64os != null) b64os.close();
934 } catch (Exception e) {
935 }
936 try {
937 if (baos != null) baos.close();
938 } catch (Exception e) {
939 }
940 } // end finally
941
942 assert baos != null;
943 return baos.toByteArray();
944 } // end if: compress
945
946 // Else, don't compress. Better not to use streams at all then.
947 boolean breakLines = (options & DO_BREAK_LINES) != 0;
948
949 // int len43 = len * 4 / 3;
950 // byte[] outBuff = new byte[ ( len43 ) // Main 4:3
951 // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
952 // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
953 // Try to determine more precisely how big the array needs to be.
954 // If we get it right, we don't have to do an array copy, and
955 // we save a bunch of memory.
956 int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding
957 if (breakLines) {
958 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
959 }
960 byte[] outBuff = new byte[encLen];
961
962 int d = 0;
963 int e = 0;
964 int len2 = len - 2;
965 int lineLength = 0;
966 for (; d < len2; d += 3, e += 4) {
967 encode3to4(source, d + off, 3, outBuff, e, options);
968
969 lineLength += 4;
970 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
971 outBuff[e + 4] = NEW_LINE;
972 e++;
973 lineLength = 0;
974 } // end if: end of line
975 } // en dfor: each piece of array
976
977 if (d < len) {
978 encode3to4(source, d + off, len - d, outBuff, e, options);
979 e += 4;
980 } // end if: some padding needed
981
982 // Only resize array if we didn't guess it right.
983 if (e <= outBuff.length - 1) {
984 // If breaking lines and the last byte falls right at
985 // the line length (76 bytes per line), there will be
986 // one extra byte, and the array will need to be resized.
987 // Not too bad of an estimate on array size, I'd say.
988 byte[] finalOut = new byte[e];
989 System.arraycopy(outBuff, 0, finalOut, 0, e);
990 // System.err.println("Having to resize array from " + outBuff.length + " to " + e );
991 return finalOut;
992 }
993 // System.err.println("No need to resize array.");
994 return outBuff;
995
996 } // end encodeBytesToBytes
997
998 /* ******** D E C O D I N G M E T H O D S ******** */
999
1000 /**
1001 * Decodes four bytes from array <var>source</var> and writes the resulting bytes (up to three of them) to
1002 * <var>destination</var>. The source and destination arrays can be manipulated anywhere along their length by specifying
1003 * <var>srcOffset</var> and <var>destOffset</var>. This method does not check to make sure your arrays are large enough to
1004 * accomodate <var>srcOffset</var> + 4 for the <var>source</var> array or <var>destOffset</var> + 3 for the
1005 * <var>destination</var> array. This method returns the actual number of bytes that were converted from the Base64 encoding.
1006 * <p>
1007 * This is the lowest level of the decoding methods with all possible parameters.
1008 * </p>
1009 *
1010 * @param source the array to convert
1011 * @param srcOffset the index where conversion begins
1012 * @param destination the array to hold the conversion
1013 * @param destOffset the index where output will be put
1014 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
1015 * @return the number of decoded bytes converted
1016 * @throws NullPointerException if source or destination arrays are null
1017 * @throws IllegalArgumentException if srcOffset or destOffset are invalid or there is not enough room in the array.
1018 * @since 1.3
1019 */
1020 private static int decode4to3( byte[] source,
1021 int srcOffset,
1022 byte[] destination,
1023 int destOffset,
1024 int options ) {
1025
1026 // Lots of error checking and exception throwing
1027 if (source == null) {
1028 throw new NullPointerException("Source array was null.");
1029 } // end if
1030 if (destination == null) {
1031 throw new NullPointerException("Destination array was null.");
1032 } // end if
1033 if (srcOffset < 0 || srcOffset + 3 >= source.length) {
1034 throw new IllegalArgumentException(
1035 String.format("Source array with length %d cannot have offset of %d and still process four bytes.",
1036 source.length,
1037 srcOffset));
1038 } // end if
1039 if (destOffset < 0 || destOffset + 2 >= destination.length) {
1040 throw new IllegalArgumentException(
1041 String.format("Destination array with length %d cannot have offset of %d and still store three bytes.",
1042 destination.length,
1043 destOffset));
1044 } // end if
1045
1046 byte[] DECODABET = getDecodabet(options);
1047
1048 // Example: Dk==
1049 if (source[srcOffset + 2] == EQUALS_SIGN) {
1050 // Two ways to do the same thing. Don't know which way I like best.
1051 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1052 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
1053 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
1054
1055 destination[destOffset] = (byte)(outBuff >>> 16);
1056 return 1;
1057 }
1058
1059 // Example: DkL=
1060 else if (source[srcOffset + 3] == EQUALS_SIGN) {
1061 // Two ways to do the same thing. Don't know which way I like best.
1062 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1063 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1064 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
1065 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1066 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
1067
1068 destination[destOffset] = (byte)(outBuff >>> 16);
1069 destination[destOffset + 1] = (byte)(outBuff >>> 8);
1070 return 2;
1071 }
1072
1073 // Example: DkLE
1074 else {
1075 // Two ways to do the same thing. Don't know which way I like best.
1076 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1077 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1078 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
1079 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
1080 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1081 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
1082
1083 destination[destOffset] = (byte)(outBuff >> 16);
1084 destination[destOffset + 1] = (byte)(outBuff >> 8);
1085 destination[destOffset + 2] = (byte)(outBuff);
1086
1087 return 3;
1088 }
1089 } // end decodeToBytes
1090
1091 /**
1092 * Low-level access to decoding ASCII characters in the form of a byte array. <strong>Ignores GUNZIP option, if it's
1093 * set.</strong> This is not generally a recommended method, although it is used internally as part of the decoding process.
1094 * Special case: if len = 0, an empty array is returned. Still, if you need more speed and reduced memory footprint (and
1095 * aren't gzipping), consider this method.
1096 *
1097 * @param source The Base64 encoded data
1098 * @return decoded data
1099 * @throws IOException if there is an error decoding the supplied byte array
1100 * @since 2.3.1
1101 */
1102 public static byte[] decode( byte[] source ) throws IOException {
1103 byte[] decoded = null;
1104 // try {
1105 decoded = decode(source, 0, source.length, Base64.NO_OPTIONS);
1106 // } catch( java.io.IOException ex ) {
1107 // assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
1108 // }
1109 return decoded;
1110 }
1111
1112 /**
1113 * Low-level access to decoding ASCII characters in the form of a byte array. <strong>Ignores GUNZIP option, if it's
1114 * set.</strong> This is not generally a recommended method, although it is used internally as part of the decoding process.
1115 * Special case: if len = 0, an empty array is returned. Still, if you need more speed and reduced memory footprint (and
1116 * aren't gzipping), consider this method.
1117 *
1118 * @param source The Base64 encoded data
1119 * @param off The offset of where to begin decoding
1120 * @param len The length of characters to decode
1121 * @param options Can specify options such as alphabet type to use
1122 * @return decoded data
1123 * @throws java.io.IOException If bogus characters exist in source data
1124 * @since 1.3
1125 */
1126 public static byte[] decode( byte[] source,
1127 int off,
1128 int len,
1129 int options ) throws java.io.IOException {
1130
1131 // Lots of error checking and exception throwing
1132 if (source == null) {
1133 throw new NullPointerException("Cannot decode null source array.");
1134 } // end if
1135 if (off < 0 || off + len > source.length) {
1136 throw new IllegalArgumentException(
1137 String.format("Source array with length %d cannot have offset of %d and process %d bytes.",
1138 source.length,
1139 off,
1140 len));
1141 } // end if
1142
1143 if (len == 0) {
1144 return new byte[0];
1145 } else if (len < 4) {
1146 throw new IllegalArgumentException(
1147 "Base64-encoded string must have at least four characters, but length specified was "
1148 + len);
1149 } // end if
1150
1151 byte[] DECODABET = getDecodabet(options);
1152
1153 int len34 = len * 3 / 4; // Estimate on array size
1154 byte[] outBuff = new byte[len34]; // Upper limit on size of output
1155 int outBuffPosn = 0; // Keep track of where we're writing
1156
1157 byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
1158 int b4Posn = 0; // Keep track of four byte input buffer
1159 int i = 0; // Source array counter
1160 byte sbiDecode = 0; // Special value from DECODABET
1161
1162 for (i = off; i < off + len; i++) { // Loop through source
1163
1164 sbiDecode = DECODABET[source[i] & 0xFF];
1165
1166 // White space, Equals sign, or legit Base64 character
1167 // Note the values such as -5 and -9 in the
1168 // DECODABETs at the top of the file.
1169 if (sbiDecode >= WHITE_SPACE_ENC) {
1170 if (sbiDecode >= EQUALS_SIGN_ENC) {
1171 b4[b4Posn++] = source[i]; // Save non-whitespace
1172 if (b4Posn > 3) { // Time to decode?
1173 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
1174 b4Posn = 0;
1175
1176 // If that was the equals sign, break out of 'for' loop
1177 if (source[i] == EQUALS_SIGN) {
1178 break;
1179 } // end if: equals sign
1180 } // end if: quartet built
1181 } // end if: equals sign or better
1182 } // end if: white space, equals sign or better
1183 else {
1184 // There's a bad input character in the Base64 stream.
1185 throw new java.io.IOException(String.format("Bad Base64 input character decimal %d in array position %d",
1186 source[i] & 0xFF,
1187 i));
1188 } // end else:
1189 } // each input character
1190
1191 byte[] out = new byte[outBuffPosn];
1192 System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
1193 return out;
1194 } // end decode
1195
1196 /**
1197 * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
1198 *
1199 * @param s the string to decode
1200 * @return the decoded data
1201 * @throws java.io.IOException If there is a problem
1202 * @since 1.4
1203 */
1204 public static byte[] decode( String s ) throws java.io.IOException {
1205 return decode(s, NO_OPTIONS);
1206 }
1207
1208 /**
1209 * Decodes data from Base64 notation, automatically detecting gzip-compressed data and decompressing it.
1210 *
1211 * @param s the string to decode
1212 * @param options encode options such as URL_SAFE
1213 * @return the decoded data
1214 * @throws java.io.IOException if there is an error
1215 * @throws NullPointerException if <tt>s</tt> is null
1216 * @since 1.4
1217 */
1218 public static byte[] decode( String s,
1219 int options ) throws java.io.IOException {
1220
1221 if (s == null) {
1222 throw new NullPointerException("Input string was null.");
1223 } // end if
1224
1225 byte[] bytes;
1226 try {
1227 bytes = s.getBytes(PREFERRED_ENCODING);
1228 } // end try
1229 catch (java.io.UnsupportedEncodingException uee) {
1230 bytes = s.getBytes();
1231 } // end catch
1232 // </change>
1233
1234 // Decode
1235 bytes = decode(bytes, 0, bytes.length, options);
1236
1237 // Check to see if it's gzip-compressed
1238 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1239 boolean dontGunzip = (options & DONT_GUNZIP) != 0;
1240 if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) {
1241
1242 int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1243 if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
1244 java.io.ByteArrayInputStream bais = null;
1245 java.util.zip.GZIPInputStream gzis = null;
1246 java.io.ByteArrayOutputStream baos = null;
1247 byte[] buffer = new byte[2048];
1248 int length = 0;
1249
1250 try {
1251 baos = new java.io.ByteArrayOutputStream();
1252 bais = new java.io.ByteArrayInputStream(bytes);
1253 gzis = new java.util.zip.GZIPInputStream(bais);
1254
1255 while ((length = gzis.read(buffer)) >= 0) {
1256 baos.write(buffer, 0, length);
1257 } // end while: reading input
1258
1259 // No error? Get new bytes.
1260 bytes = baos.toByteArray();
1261
1262 } // end try
1263 catch (java.io.IOException e) {
1264 e.printStackTrace();
1265 // Just return originally-decoded bytes
1266 } // end catch
1267 finally {
1268 try {
1269 if (baos != null) baos.close();
1270 } catch (Exception e) {
1271 }
1272 try {
1273 if (gzis != null) gzis.close();
1274 } catch (Exception e) {
1275 }
1276 try {
1277 if (bais != null) bais.close();
1278 } catch (Exception e) {
1279 }
1280 } // end finally
1281
1282 } // end if: gzipped
1283 } // end if: bytes.length >= 2
1284
1285 return bytes;
1286 } // end decode
1287
1288 /**
1289 * Attempts to decode Base64 data and deserialize a Java Object within. Returns <tt>null</tt> if there was an error.
1290 *
1291 * @param encodedObject The Base64 data to decode
1292 * @return The decoded and deserialized object
1293 * @throws NullPointerException if encodedObject is null
1294 * @throws java.io.IOException if there is a general error
1295 * @throws ClassNotFoundException if the decoded object is of a class that cannot be found by the JVM
1296 * @since 1.5
1297 */
1298 public static Object decodeToObject( String encodedObject ) throws java.io.IOException, java.lang.ClassNotFoundException {
1299 return decodeToObject(encodedObject, NO_OPTIONS, null);
1300 }
1301
1302 /**
1303 * Attempts to decode Base64 data and deserialize a Java Object within. Returns <tt>null</tt> if there was an error. If
1304 * <tt>loader</tt> is not null, it will be the class loader used when deserializing.
1305 *
1306 * @param encodedObject The Base64 data to decode
1307 * @param options Various parameters related to decoding
1308 * @param loader Optional class loader to use in deserializing classes.
1309 * @return The decoded and deserialized object
1310 * @throws NullPointerException if encodedObject is null
1311 * @throws java.io.IOException if there is a general error
1312 * @throws ClassNotFoundException if the decoded object is of a class that cannot be found by the JVM
1313 * @since 2.3.4
1314 */
1315 public static Object decodeToObject( String encodedObject,
1316 int options,
1317 final ClassLoader loader ) throws java.io.IOException, java.lang.ClassNotFoundException {
1318
1319 // Decode and gunzip if necessary
1320 byte[] objBytes = decode(encodedObject, options);
1321
1322 java.io.ByteArrayInputStream bais = null;
1323 java.io.ObjectInputStream ois = null;
1324 Object obj = null;
1325
1326 try {
1327 bais = new java.io.ByteArrayInputStream(objBytes);
1328
1329 // If no custom class loader is provided, use Java's builtin OIS.
1330 if (loader == null) {
1331 ois = new java.io.ObjectInputStream(bais);
1332 } // end if: no loader provided
1333
1334 // Else make a customized object input stream that uses
1335 // the provided class loader.
1336 else {
1337 ois = new java.io.ObjectInputStream(bais) {
1338 @Override
1339 public Class<?> resolveClass( java.io.ObjectStreamClass streamClass )
1340 throws java.io.IOException, ClassNotFoundException {
1341 Class<?> c = Class.forName(streamClass.getName(), false, loader);
1342 if (c == null) {
1343 return super.resolveClass(streamClass);
1344 }
1345 return c; // Class loader knows of this class.
1346 } // end resolveClass
1347 }; // end ois
1348 } // end else: no custom class loader
1349
1350 obj = ois.readObject();
1351 } // end try
1352 catch (java.io.IOException e) {
1353 throw e; // Catch and throw in order to execute finally{}
1354 } // end catch
1355 catch (java.lang.ClassNotFoundException e) {
1356 throw e; // Catch and throw in order to execute finally{}
1357 } // end catch
1358 finally {
1359 try {
1360 if (bais != null) bais.close();
1361 } catch (Exception e) {
1362 }
1363 try {
1364 if (ois != null) ois.close();
1365 } catch (Exception e) {
1366 }
1367 } // end finally
1368
1369 return obj;
1370 } // end decodeObject
1371
1372 /**
1373 * Convenience method for encoding data to a file.
1374 * <p>
1375 * As of v 2.3, if there is a error, the method will throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
1376 * versions, it just returned false, but in retrospect that's a pretty poor way to handle it.
1377 * </p>
1378 *
1379 * @param dataToEncode byte array of data to encode in base64 form
1380 * @param filename Filename for saving encoded data
1381 * @throws java.io.IOException if there is an error
1382 * @throws NullPointerException if dataToEncode is null
1383 * @since 2.1
1384 */
1385 public static void encodeToFile( byte[] dataToEncode,
1386 String filename ) throws java.io.IOException {
1387
1388 if (dataToEncode == null) {
1389 throw new NullPointerException("Data to encode was null.");
1390 } // end iff
1391
1392 Base64.OutputStream bos = null;
1393 try {
1394 bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
1395 bos.write(dataToEncode);
1396 } // end try
1397 catch (java.io.IOException e) {
1398 throw e; // Catch and throw to execute finally{} block
1399 } // end catch: java.io.IOException
1400 finally {
1401 try {
1402 if (bos != null) bos.close();
1403 } catch (Exception e) {
1404 }
1405 } // end finally
1406
1407 } // end encodeToFile
1408
1409 /**
1410 * Convenience method for decoding data to a file.
1411 * <p>
1412 * As of v 2.3, if there is a error, the method will throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
1413 * versions, it just returned false, but in retrospect that's a pretty poor way to handle it.
1414 * </p>
1415 *
1416 * @param dataToDecode Base64-encoded data as a string
1417 * @param filename Filename for saving decoded data
1418 * @throws java.io.IOException if there is an error
1419 * @since 2.1
1420 */
1421 public static void decodeToFile( String dataToDecode,
1422 String filename ) throws java.io.IOException {
1423
1424 Base64.OutputStream bos = null;
1425 try {
1426 bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
1427 bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
1428 } // end try
1429 catch (java.io.IOException e) {
1430 throw e; // Catch and throw to execute finally{} block
1431 } // end catch: java.io.IOException
1432 finally {
1433 try {
1434 if (bos != null) bos.close();
1435 } catch (Exception e) {
1436 }
1437 } // end finally
1438
1439 } // end decodeToFile
1440
1441 /**
1442 * Convenience method for reading a base64-encoded file and decoding it.
1443 * <p>
1444 * As of v 2.3, if there is a error, the method will throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
1445 * versions, it just returned false, but in retrospect that's a pretty poor way to handle it.
1446 * </p>
1447 *
1448 * @param filename Filename for reading encoded data
1449 * @return decoded byte array
1450 * @throws java.io.IOException if there is an error
1451 * @since 2.1
1452 */
1453 public static byte[] decodeFromFile( String filename ) throws java.io.IOException {
1454
1455 byte[] decodedData = null;
1456 Base64.InputStream bis = null;
1457 try {
1458 // Set up some useful variables
1459 java.io.File file = new java.io.File(filename);
1460 byte[] buffer = null;
1461 int length = 0;
1462 int numBytes = 0;
1463
1464 // Check for size of file
1465 if (file.length() > Integer.MAX_VALUE) {
1466 throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes).");
1467 } // end if: file too big for int index
1468 buffer = new byte[(int)file.length()];
1469
1470 // Open a stream
1471 bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.DECODE);
1472
1473 // Read until done
1474 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1475 length += numBytes;
1476 } // end while
1477
1478 // Save in a variable to return
1479 decodedData = new byte[length];
1480 System.arraycopy(buffer, 0, decodedData, 0, length);
1481
1482 } // end try
1483 catch (java.io.IOException e) {
1484 throw e; // Catch and release to execute finally{}
1485 } // end catch: java.io.IOException
1486 finally {
1487 try {
1488 if (bis != null) bis.close();
1489 } catch (Exception e) {
1490 }
1491 } // end finally
1492
1493 return decodedData;
1494 } // end decodeFromFile
1495
1496 /**
1497 * Convenience method for reading a binary file and base64-encoding it.
1498 * <p>
1499 * As of v 2.3, if there is a error, the method will throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
1500 * versions, it just returned false, but in retrospect that's a pretty poor way to handle it.
1501 * </p>
1502 *
1503 * @param filename Filename for reading binary data
1504 * @return base64-encoded string
1505 * @throws java.io.IOException if there is an error
1506 * @since 2.1
1507 */
1508 public static String encodeFromFile( String filename ) throws java.io.IOException {
1509
1510 String encodedData = null;
1511 Base64.InputStream bis = null;
1512 try {
1513 // Set up some useful variables
1514 java.io.File file = new java.io.File(filename);
1515 byte[] buffer = new byte[Math.max((int)(file.length() * 1.4 + 1), 40)]; // Need max() for math on small files
1516 // (v2.2.1); Need +1 for a few corner cases
1517 // (v2.3.5)
1518 int length = 0;
1519 int numBytes = 0;
1520
1521 // Open a stream
1522 bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)), Base64.ENCODE);
1523
1524 // Read until done
1525 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1526 length += numBytes;
1527 } // end while
1528
1529 // Save in a variable to return
1530 encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
1531
1532 } // end try
1533 catch (java.io.IOException e) {
1534 throw e; // Catch and release to execute finally{}
1535 } // end catch: java.io.IOException
1536 finally {
1537 try {
1538 if (bis != null) bis.close();
1539 } catch (Exception e) {
1540 }
1541 } // end finally
1542
1543 return encodedData;
1544 } // end encodeFromFile
1545
1546 /**
1547 * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1548 *
1549 * @param infile Input file
1550 * @param outfile Output file
1551 * @throws java.io.IOException if there is an error
1552 * @since 2.2
1553 */
1554 public static void encodeFileToFile( String infile,
1555 String outfile ) throws java.io.IOException {
1556
1557 String encoded = Base64.encodeFromFile(infile);
1558 java.io.OutputStream out = null;
1559 try {
1560 out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1561 out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
1562 } // end try
1563 catch (java.io.IOException e) {
1564 throw e; // Catch and release to execute finally{}
1565 } // end catch
1566 finally {
1567 try {
1568 if (out != null) out.close();
1569 } catch (Exception ex) {
1570 }
1571 } // end finally
1572 } // end encodeFileToFile
1573
1574 /**
1575 * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1576 *
1577 * @param infile Input file
1578 * @param outfile Output file
1579 * @throws java.io.IOException if there is an error
1580 * @since 2.2
1581 */
1582 public static void decodeFileToFile( String infile,
1583 String outfile ) throws java.io.IOException {
1584
1585 byte[] decoded = Base64.decodeFromFile(infile);
1586 java.io.OutputStream out = null;
1587 try {
1588 out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1589 out.write(decoded);
1590 } // end try
1591 catch (java.io.IOException e) {
1592 throw e; // Catch and release to execute finally{}
1593 } // end catch
1594 finally {
1595 try {
1596 if (out != null) out.close();
1597 } catch (Exception ex) {
1598 }
1599 } // end finally
1600 } // end decodeFileToFile
1601
1602 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1603
1604 /**
1605 * A {@link Base64.InputStream} will read data from another <tt>java.io.InputStream</tt>, given in the constructor, and
1606 * encode/decode to/from Base64 notation on the fly.
1607 *
1608 * @see Base64
1609 * @since 1.3
1610 */
1611 public static class InputStream extends java.io.FilterInputStream {
1612
1613 private boolean encode; // Encoding or decoding
1614 private int position; // Current position in the buffer
1615 private byte[] buffer; // Small buffer holding converted data
1616 private int bufferLength; // Length of buffer (3 or 4)
1617 private int numSigBytes; // Number of meaningful bytes in the buffer
1618 private int lineLength;
1619 private boolean breakLines; // Break lines at less than 80 characters
1620 private int options; // Record options used to create the stream.
1621 private byte[] decodabet; // Local copies to avoid extra method calls
1622
1623 /**
1624 * Constructs a {@link Base64.InputStream} in DECODE mode.
1625 *
1626 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1627 * @since 1.3
1628 */
1629 public InputStream( java.io.InputStream in ) {
1630 this(in, DECODE);
1631 } // end constructor
1632
1633 /**
1634 * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE mode.
1635 * <p>
1636 * Valid options:
1637 *
1638 * <pre>
1639 * ENCODE or DECODE: Encode or Decode as data is read.
1640 * DO_BREAK_LINES: break lines at 76 characters
1641 * (only meaningful when encoding)</i>
1642 * </pre>
1643 * <p>
1644 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1645 *
1646 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1647 * @param options Specified options
1648 * @see Base64#ENCODE
1649 * @see Base64#DECODE
1650 * @see Base64#DO_BREAK_LINES
1651 * @since 2.0
1652 */
1653 @SuppressWarnings( "synthetic-access" )
1654 public InputStream( java.io.InputStream in,
1655 int options ) {
1656
1657 super(in);
1658 this.options = options; // Record for later
1659 this.breakLines = (options & DO_BREAK_LINES) > 0;
1660 this.encode = (options & ENCODE) > 0;
1661 this.bufferLength = encode ? 4 : 3;
1662 this.buffer = new byte[bufferLength];
1663 this.position = -1;
1664 this.lineLength = 0;
1665 this.decodabet = getDecodabet(options);
1666 } // end constructor
1667
1668 /**
1669 * Reads enough of the input stream to convert to/from Base64 and returns the next byte.
1670 *
1671 * @return next byte
1672 * @since 1.3
1673 */
1674 @SuppressWarnings( "synthetic-access" )
1675 @Override
1676 public int read() throws java.io.IOException {
1677
1678 // Do we need to get data?
1679 if (position < 0) {
1680 if (encode) {
1681 byte[] b3 = new byte[3];
1682 int numBinaryBytes = 0;
1683 for (int i = 0; i < 3; i++) {
1684 int b = in.read();
1685
1686 // If end of stream, b is -1.
1687 if (b >= 0) {
1688 b3[i] = (byte)b;
1689 numBinaryBytes++;
1690 } else {
1691 break; // out of for loop
1692 } // end else: end of stream
1693
1694 } // end for: each needed input byte
1695
1696 if (numBinaryBytes > 0) {
1697 encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1698 position = 0;
1699 numSigBytes = 4;
1700 } // end if: got data
1701 else {
1702 return -1; // Must be end of stream
1703 } // end else
1704 } // end if: encoding
1705
1706 // Else decoding
1707 else {
1708 byte[] b4 = new byte[4];
1709 int i = 0;
1710 for (i = 0; i < 4; i++) {
1711 // Read four "meaningful" bytes:
1712 int b = 0;
1713 do {
1714 b = in.read();
1715 } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1716
1717 if (b < 0) {
1718 break; // Reads a -1 if end of stream
1719 } // end if: end of stream
1720
1721 b4[i] = (byte)b;
1722 } // end for: each needed input byte
1723
1724 if (i == 4) {
1725 numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1726 position = 0;
1727 } // end if: got four characters
1728 else if (i == 0) {
1729 return -1;
1730 } // end else if: also padded correctly
1731 else {
1732 // Must have broken out from above.
1733 throw new java.io.IOException("Improperly padded Base64 input.");
1734 } // end
1735
1736 } // end else: decode
1737 } // end else: get data
1738
1739 // Got data?
1740 if (position >= 0) {
1741 // End of relevant data?
1742 if ( /*!encode &&*/position >= numSigBytes) {
1743 return -1;
1744 } // end if: got data
1745
1746 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1747 lineLength = 0;
1748 return '\n';
1749 } // end if
1750 lineLength++; // This isn't important when decoding
1751 // but throwing an extra "if" seems
1752 // just as wasteful.
1753
1754 int b = buffer[position++];
1755
1756 if (position >= bufferLength) {
1757 position = -1;
1758 } // end if: end
1759
1760 return b & 0xFF; // This is how you "cast" a byte that's
1761 // intended to be unsigned.
1762 } // end if: position >= 0
1763
1764 // Else error
1765 throw new java.io.IOException("Error in Base64 code reading stream.");
1766 } // end read
1767
1768 /**
1769 * Calls {@link #read()} repeatedly until the end of stream is reached or <var>len</var> bytes are read. Returns number of
1770 * bytes read into array or -1 if end of stream is encountered.
1771 *
1772 * @param dest array to hold values
1773 * @param off offset for array
1774 * @param len max number of bytes to read into array
1775 * @return bytes read into array or -1 if end of stream is encountered.
1776 * @since 1.3
1777 */
1778 @Override
1779 public int read( byte[] dest,
1780 int off,
1781 int len ) throws java.io.IOException {
1782 int i;
1783 int b;
1784 for (i = 0; i < len; i++) {
1785 b = read();
1786
1787 if (b >= 0) {
1788 dest[off + i] = (byte)b;
1789 } else if (i == 0) {
1790 return -1;
1791 } else {
1792 break; // Out of 'for' loop
1793 } // Out of 'for' loop
1794 } // end for: each byte read
1795 return i;
1796 } // end read
1797
1798 } // end inner class InputStream
1799
1800 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1801
1802 /**
1803 * A {@link Base64.OutputStream} will write data to another <tt>java.io.OutputStream</tt>, given in the constructor, and
1804 * encode/decode to/from Base64 notation on the fly.
1805 *
1806 * @see Base64
1807 * @since 1.3
1808 */
1809 public static class OutputStream extends java.io.FilterOutputStream {
1810
1811 private boolean encode;
1812 private int position;
1813 private byte[] buffer;
1814 private int bufferLength;
1815 private int lineLength;
1816 private boolean breakLines;
1817 private byte[] b4; // Scratch used in a few places
1818 private boolean suspendEncoding;
1819 private int options; // Record for later
1820 private byte[] decodabet; // Local copies to avoid extra method calls
1821
1822 /**
1823 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1824 *
1825 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1826 * @since 1.3
1827 */
1828 public OutputStream( java.io.OutputStream out ) {
1829 this(out, ENCODE);
1830 } // end constructor
1831
1832 /**
1833 * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE mode.
1834 * <p>
1835 * Valid options:
1836 *
1837 * <pre>
1838 * ENCODE or DECODE: Encode or Decode as data is read.
1839 * DO_BREAK_LINES: don't break lines at 76 characters
1840 * (only meaningful when encoding)</i>
1841 * </pre>
1842 * <p>
1843 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1844 *
1845 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1846 * @param options Specified options.
1847 * @see Base64#ENCODE
1848 * @see Base64#DECODE
1849 * @see Base64#DO_BREAK_LINES
1850 * @since 1.3
1851 */
1852 @SuppressWarnings( "synthetic-access" )
1853 public OutputStream( java.io.OutputStream out,
1854 int options ) {
1855 super(out);
1856 this.breakLines = (options & DO_BREAK_LINES) != 0;
1857 this.encode = (options & ENCODE) != 0;
1858 this.bufferLength = encode ? 3 : 4;
1859 this.buffer = new byte[bufferLength];
1860 this.position = 0;
1861 this.lineLength = 0;
1862 this.suspendEncoding = false;
1863 this.b4 = new byte[4];
1864 this.options = options;
1865 this.decodabet = getDecodabet(options);
1866 } // end constructor
1867
1868 /**
1869 * Writes the byte to the output stream after converting to/from Base64 notation. When encoding, bytes are buffered three
1870 * at a time before the output stream actually gets a write() call. When decoding, bytes are buffered four at a time.
1871 *
1872 * @param theByte the byte to write
1873 * @since 1.3
1874 */
1875 @SuppressWarnings( "synthetic-access" )
1876 @Override
1877 public void write( int theByte ) throws java.io.IOException {
1878 // Encoding suspended?
1879 if (suspendEncoding) {
1880 this.out.write(theByte);
1881 return;
1882 } // end if: supsended
1883
1884 // Encode?
1885 if (encode) {
1886 buffer[position++] = (byte)theByte;
1887 if (position >= bufferLength) { // Enough to encode.
1888
1889 this.out.write(encode3to4(b4, buffer, bufferLength, options));
1890
1891 lineLength += 4;
1892 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1893 this.out.write(NEW_LINE);
1894 lineLength = 0;
1895 } // end if: end of line
1896
1897 position = 0;
1898 } // end if: enough to output
1899 } // end if: encoding
1900
1901 // Else, Decoding
1902 else {
1903 // Meaningful Base64 character?
1904 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1905 buffer[position++] = (byte)theByte;
1906 if (position >= bufferLength) { // Enough to output.
1907
1908 int len = Base64.decode4to3(buffer, 0, b4, 0, options);
1909 out.write(b4, 0, len);
1910 position = 0;
1911 } // end if: enough to output
1912 } // end if: meaningful base64 character
1913 else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1914 throw new java.io.IOException("Invalid character in Base64 data.");
1915 } // end else: not white space either
1916 } // end else: decoding
1917 } // end write
1918
1919 /**
1920 * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are written.
1921 *
1922 * @param theBytes array from which to read bytes
1923 * @param off offset for array
1924 * @param len max number of bytes to read into array
1925 * @since 1.3
1926 */
1927 @Override
1928 public void write( byte[] theBytes,
1929 int off,
1930 int len ) throws java.io.IOException {
1931 // Encoding suspended?
1932 if (suspendEncoding) {
1933 this.out.write(theBytes, off, len);
1934 return;
1935 } // end if: supsended
1936
1937 for (int i = 0; i < len; i++) {
1938 write(theBytes[off + i]);
1939 } // end for: each byte written
1940
1941 } // end write
1942
1943 /**
1944 * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without closing the stream.
1945 *
1946 * @throws java.io.IOException if there's an error.
1947 */
1948 @SuppressWarnings( "synthetic-access" )
1949 public void flushBase64() throws java.io.IOException {
1950 if (position > 0) {
1951 if (encode) {
1952 out.write(encode3to4(b4, buffer, position, options));
1953 position = 0;
1954 } // end if: encoding
1955 else {
1956 throw new java.io.IOException("Base64 input not properly padded.");
1957 } // end else: decoding
1958 } // end if: buffer partially full
1959
1960 } // end flush
1961
1962 /**
1963 * Flushes and closes (I think, in the superclass) the stream.
1964 *
1965 * @since 1.3
1966 */
1967 @Override
1968 public void close() throws java.io.IOException {
1969 // 1. Ensure that pending characters are written
1970 flushBase64();
1971
1972 // 2. Actually close the stream
1973 // Base class both flushes and closes.
1974 super.close();
1975
1976 buffer = null;
1977 out = null;
1978 } // end close
1979
1980 /**
1981 * Suspends encoding of the stream. May be helpful if you need to embed a piece of base64-encoded data in a stream.
1982 *
1983 * @throws java.io.IOException if there's an error flushing
1984 * @since 1.5.1
1985 */
1986 public void suspendEncoding() throws java.io.IOException {
1987 flushBase64();
1988 this.suspendEncoding = true;
1989 } // end suspendEncoding
1990
1991 /**
1992 * Resumes encoding of the stream. May be helpful if you need to embed a piece of base64-encoded data in a stream.
1993 *
1994 * @since 1.5.1
1995 */
1996 public void resumeEncoding() {
1997 this.suspendEncoding = false;
1998 } // end resumeEncoding
1999
2000 } // end inner class OutputStream
2001
2002 } // end class Base64