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