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