Source code: org/mobicents/slee/test/Base64.java
1
2 package org.mobicents.slee.test;
3
4 /**
5 * Encodes and decodes to and from Base64 notation.
6 *
7 * <p>
8 * Change Log:
9 * </p>
10 * <ul>
11 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
12 * some convenience methods for reading and writing to and from files.</li>
13 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
14 * with other encodings (like EBCDIC).</li>
15 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
16 * encoded data was a single byte.</li>
17 * <li>v2.0 - I got rid of methods that used booleans to set options.
18 * Now everything is more consolidated and cleaner. The code now detects
19 * when data that's being decoded is gzip-compressed and will decompress it
20 * automatically. Generally things are cleaner. You'll probably have to
21 * change some method calls that you were making to support the new
22 * options format (<tt>int</tt>s that you "OR" together).</li>
23 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
24 * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
25 * Added the ability to "suspend" encoding in the Output Stream so
26 * you can turn on and off the encoding if you need to embed base64
27 * data in an otherwise "normal" stream (like an XML file).</li>
28 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
29 * This helps when using GZIP streams.
30 * Added the ability to GZip-compress objects before encoding them.</li>
31 * <li>v1.4 - Added helper methods to read/write files.</li>
32 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
33 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
34 * where last buffer being read, if not completely full, was not returned.</li>
35 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
36 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
37 * </ul>
38 *
39 * <p>
40 * I am placing this code in the Public Domain. Do with it as you will.
41 * This software comes with no guarantees or warranties but with
42 * plenty of well-wishing instead!
43 * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
44 * periodically to check for updates or to contribute improvements.
45 * </p>
46 *
47 * @author Robert Harder
48 * @author rob@iharder.net
49 * @version 2.1
50 */
51
52 public class Base64
53 {
54
55 /* ******** P U B L I C F I E L D S ******** */
56
57
58 /** No options specified. Value is zero. */
59 public final static int NO_OPTIONS = 0;
60
61 /** Specify encoding. */
62 public final static int ENCODE = 1;
63
64
65 /** Specify decoding. */
66 public final static int DECODE = 0;
67
68
69 /** Specify that data should be gzip-compressed. */
70 public final static int GZIP = 2;
71
72
73 /** Don't break lines when encoding (violates strict Base64 specification) */
74 public final static int DONT_BREAK_LINES = 8;
75
76
77 /* ******** P R I V A T E F I E L D S ******** */
78
79
80 /** Maximum line length (76) of Base64 output. */
81 private final static int MAX_LINE_LENGTH = 76;
82
83
84 /** The equals sign (=) as a byte. */
85 private final static byte EQUALS_SIGN = (byte)'=';
86
87
88 /** The new line character (\n) as a byte. */
89 private final static byte NEW_LINE = (byte)'\n';
90
91
92 /** Preferred encoding. */
93 private final static String PREFERRED_ENCODING = "UTF-8";
94
95
96 /** The 64 valid Base64 values. */
97 private final static byte[] ALPHABET;
98 private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
99 {
100 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
101 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
102 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
103 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
104 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
105 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
106 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
107 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
108 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
109 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
110 };
111
112 /** Determine which ALPHABET to use. */
113 static
114 {
115 byte[] __bytes;
116 try
117 {
118 __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
119 } // end try
120 catch (java.io.UnsupportedEncodingException use)
121 {
122 __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
123 } // end catch
124 ALPHABET = __bytes;
125 } // end static
126
127
128 /**
129 * Translates a Base64 value to either its 6-bit reconstruction value
130 * or a negative number indicating some other meaning.
131 **/
132 private final static byte[] DECODABET =
133 {
134 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
135 -5,-5, // Whitespace: Tab and Linefeed
136 -9,-9, // Decimal 11 - 12
137 -5, // Whitespace: Carriage Return
138 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
139 -9,-9,-9,-9,-9, // Decimal 27 - 31
140 -5, // Whitespace: Space
141 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
142 62, // Plus sign at decimal 43
143 -9,-9,-9, // Decimal 44 - 46
144 63, // Slash at decimal 47
145 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
146 -9,-9,-9, // Decimal 58 - 60
147 -1, // Equals sign at decimal 61
148 -9,-9,-9, // Decimal 62 - 64
149 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
150 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
151 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
152 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
153 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
154 -9,-9,-9,-9 // Decimal 123 - 126
155 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
156 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
157 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
158 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
159 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
160 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
161 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
162 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
163 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
164 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
165 };
166
167 // I think I end up not using the BAD_ENCODING indicator.
168 //private final static byte BAD_ENCODING = -9; // Indicates error in encoding
169 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
170 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
171
172
173 /** Defeats instantiation. */
174 private Base64(){}
175
176
177
178 /* ******** E N C O D I N G M E T H O D S ******** */
179
180
181 /**
182 * Encodes up to the first three bytes of array <var>threeBytes</var>
183 * and returns a four-byte array in Base64 notation.
184 * The actual number of significant bytes in your array is
185 * given by <var>numSigBytes</var>.
186 * The array <var>threeBytes</var> needs only be as big as
187 * <var>numSigBytes</var>.
188 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
189 *
190 * @param b4 A reusable byte array to reduce array instantiation
191 * @param threeBytes the array to convert
192 * @param numSigBytes the number of significant bytes in your array
193 * @return four byte array in Base64 notation.
194 * @since 1.5.1
195 */
196 private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
197 {
198 encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
199 return b4;
200 } // end encode3to4
201
202
203 /**
204 * Encodes up to three bytes of the array <var>source</var>
205 * and writes the resulting four Base64 bytes to <var>destination</var>.
206 * The source and destination arrays can be manipulated
207 * anywhere along their length by specifying
208 * <var>srcOffset</var> and <var>destOffset</var>.
209 * This method does not check to make sure your arrays
210 * are large enough to accomodate <var>srcOffset</var> + 3 for
211 * the <var>source</var> array or <var>destOffset</var> + 4 for
212 * the <var>destination</var> array.
213 * The actual number of significant bytes in your array is
214 * given by <var>numSigBytes</var>.
215 *
216 * @param source the array to convert
217 * @param srcOffset the index where conversion begins
218 * @param numSigBytes the number of significant bytes in your array
219 * @param destination the array to hold the conversion
220 * @param destOffset the index where output will be put
221 * @return the <var>destination</var> array
222 * @since 1.3
223 */
224 private static byte[] encode3to4(
225 byte[] source, int srcOffset, int numSigBytes,
226 byte[] destination, int destOffset )
227 {
228 // 1 2 3
229 // 01234567890123456789012345678901 Bit position
230 // --------000000001111111122222222 Array position from threeBytes
231 // --------| || || || | Six bit groups to index ALPHABET
232 // >>18 >>12 >> 6 >> 0 Right shift necessary
233 // 0x3f 0x3f 0x3f Additional AND
234
235 // Create buffer with zero-padding if there are only one or two
236 // significant bytes passed in the array.
237 // We have to shift left 24 in order to flush out the 1's that appear
238 // when Java treats a value as negative that is cast from a byte to an int.
239 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
240 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
241 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
242
243 switch( numSigBytes )
244 {
245 case 3:
246 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
247 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
248 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
249 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
250 return destination;
251
252 case 2:
253 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
254 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
255 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
256 destination[ destOffset + 3 ] = EQUALS_SIGN;
257 return destination;
258
259 case 1:
260 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
261 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
262 destination[ destOffset + 2 ] = EQUALS_SIGN;
263 destination[ destOffset + 3 ] = EQUALS_SIGN;
264 return destination;
265
266 default:
267 return destination;
268 } // end switch
269 } // end encode3to4
270
271
272
273 /**
274 * Serializes an object and returns the Base64-encoded
275 * version of that serialized object. If the object
276 * cannot be serialized or there is another error,
277 * the method will return <tt>null</tt>.
278 * The object is not GZip-compressed before being encoded.
279 *
280 * @param serializableObject The object to encode
281 * @return The Base64-encoded object
282 * @since 1.4
283 */
284 public static String encodeObject( java.io.Serializable serializableObject )
285 {
286 return encodeObject( serializableObject, NO_OPTIONS );
287 } // end encodeObject
288
289
290
291 /**
292 * Serializes an object and returns the Base64-encoded
293 * version of that serialized object. If the object
294 * cannot be serialized or there is another error,
295 * the method will return <tt>null</tt>.
296 * <p>
297 * Valid options:<pre>
298 * GZIP: gzip-compresses object before encoding it.
299 * DONT_BREAK_LINES: don't break lines at 76 characters
300 * <i>Note: Technically, this makes your encoding non-compliant.</i>
301 * </pre>
302 * <p>
303 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
304 * <p>
305 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
306 *
307 * @param serializableObject The object to encode
308 * @param options Specified options
309 * @return The Base64-encoded object
310 * @see Base64#GZIP
311 * @see Base64#DONT_BREAK_LINES
312 * @since 2.0
313 */
314 public static String encodeObject( java.io.Serializable serializableObject, int options )
315 {
316 // Streams
317 java.io.ByteArrayOutputStream baos = null;
318 java.io.OutputStream b64os = null;
319 java.io.ObjectOutputStream oos = null;
320 java.util.zip.GZIPOutputStream gzos = null;
321
322 // Isolate options
323 int gzip = (options & GZIP);
324 int dontBreakLines = (options & DONT_BREAK_LINES);
325
326 try
327 {
328 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
329 baos = new java.io.ByteArrayOutputStream();
330 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
331
332 // GZip?
333 if( gzip == GZIP )
334 {
335 gzos = new java.util.zip.GZIPOutputStream( b64os );
336 oos = new java.io.ObjectOutputStream( gzos );
337 } // end if: gzip
338 else
339 oos = new java.io.ObjectOutputStream( b64os );
340
341 oos.writeObject( serializableObject );
342 } // end try
343 catch( java.io.IOException e )
344 {
345 e.printStackTrace();
346 return null;
347 } // end catch
348 finally
349 {
350 try{ oos.close(); } catch( Exception e ){}
351 try{ gzos.close(); } catch( Exception e ){}
352 try{ b64os.close(); } catch( Exception e ){}
353 try{ baos.close(); } catch( Exception e ){}
354 } // end finally
355
356 // Return value according to relevant encoding.
357 try
358 {
359 return new String( baos.toByteArray(), PREFERRED_ENCODING );
360 } // end try
361 catch (java.io.UnsupportedEncodingException uue)
362 {
363 return new String( baos.toByteArray() );
364 } // end catch
365
366 } // end encode
367
368
369
370 /**
371 * Encodes a byte array into Base64 notation.
372 * Does not GZip-compress data.
373 *
374 * @param source The data to convert
375 * @since 1.4
376 */
377 public static String encodeBytes( byte[] source )
378 {
379 return encodeBytes( source, 0, source.length, NO_OPTIONS );
380 } // end encodeBytes
381
382
383
384 /**
385 * Encodes a byte array into Base64 notation.
386 * <p>
387 * Valid options:<pre>
388 * GZIP: gzip-compresses object before encoding it.
389 * DONT_BREAK_LINES: don't break lines at 76 characters
390 * <i>Note: Technically, this makes your encoding non-compliant.</i>
391 * </pre>
392 * <p>
393 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
394 * <p>
395 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
396 *
397 *
398 * @param source The data to convert
399 * @param options Specified options
400 * @see Base64#GZIP
401 * @see Base64#DONT_BREAK_LINES
402 * @since 2.0
403 */
404 public static String encodeBytes( byte[] source, int options )
405 {
406 return encodeBytes( source, 0, source.length, options );
407 } // end encodeBytes
408
409
410 /**
411 * Encodes a byte array into Base64 notation.
412 * Does not GZip-compress data.
413 *
414 * @param source The data to convert
415 * @param off Offset in array where conversion should begin
416 * @param len Length of data to convert
417 * @since 1.4
418 */
419 public static String encodeBytes( byte[] source, int off, int len )
420 {
421 return encodeBytes( source, off, len, NO_OPTIONS );
422 } // end encodeBytes
423
424
425
426 /**
427 * Encodes a byte array into Base64 notation.
428 * <p>
429 * Valid options:<pre>
430 * GZIP: gzip-compresses object before encoding it.
431 * DONT_BREAK_LINES: don't break lines at 76 characters
432 * <i>Note: Technically, this makes your encoding non-compliant.</i>
433 * </pre>
434 * <p>
435 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
436 * <p>
437 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
438 *
439 *
440 * @param source The data to convert
441 * @param off Offset in array where conversion should begin
442 * @param len Length of data to convert
443 * @param options Specified options
444 * @see Base64#GZIP
445 * @see Base64#DONT_BREAK_LINES
446 * @since 2.0
447 */
448 public static String encodeBytes( byte[] source, int off, int len, int options )
449 {
450 // Isolate options
451 int dontBreakLines = ( options & DONT_BREAK_LINES );
452 int gzip = ( options & GZIP );
453
454 // Compress?
455 if( gzip == GZIP )
456 {
457 java.io.ByteArrayOutputStream baos = null;
458 java.util.zip.GZIPOutputStream gzos = null;
459 Base64.OutputStream b64os = null;
460
461
462 try
463 {
464 // GZip -> Base64 -> ByteArray
465 baos = new java.io.ByteArrayOutputStream();
466 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
467 gzos = new java.util.zip.GZIPOutputStream( b64os );
468
469 gzos.write( source, off, len );
470 gzos.close();
471 } // end try
472 catch( java.io.IOException e )
473 {
474 e.printStackTrace();
475 return null;
476 } // end catch
477 finally
478 {
479 try{ gzos.close(); } catch( Exception e ){}
480 try{ b64os.close(); } catch( Exception e ){}
481 try{ baos.close(); } catch( Exception e ){}
482 } // end finally
483
484 // Return value according to relevant encoding.
485 try
486 {
487 return new String( baos.toByteArray(), PREFERRED_ENCODING );
488 } // end try
489 catch (java.io.UnsupportedEncodingException uue)
490 {
491 return new String( baos.toByteArray() );
492 } // end catch
493 } // end if: compress
494
495 // Else, don't compress. Better not to use streams at all then.
496 else
497 {
498 // Convert option to boolean in way that code likes it.
499 boolean breakLines = dontBreakLines == 0;
500
501 int len43 = len * 4 / 3;
502 byte[] outBuff = new byte[ ( len43 ) // Main 4:3
503 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
504 + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
505 int d = 0;
506 int e = 0;
507 int len2 = len - 2;
508 int lineLength = 0;
509 for( ; d < len2; d+=3, e+=4 )
510 {
511 encode3to4( source, d+off, 3, outBuff, e );
512
513 lineLength += 4;
514 if( breakLines && lineLength == MAX_LINE_LENGTH )
515 {
516 outBuff[e+4] = NEW_LINE;
517 e++;
518 lineLength = 0;
519 } // end if: end of line
520 } // en dfor: each piece of array
521
522 if( d < len )
523 {
524 encode3to4( source, d+off, len - d, outBuff, e );
525 e += 4;
526 } // end if: some padding needed
527
528
529 // Return value according to relevant encoding.
530 try
531 {
532 return new String( outBuff, 0, e, PREFERRED_ENCODING );
533 } // end try
534 catch (java.io.UnsupportedEncodingException uue)
535 {
536 return new String( outBuff, 0, e );
537 } // end catch
538
539 } // end else: don't compress
540
541 } // end encodeBytes
542
543
544
545
546
547 /* ******** D E C O D I N G M E T H O D S ******** */
548
549
550 /**
551 * Decodes four bytes from array <var>source</var>
552 * and writes the resulting bytes (up to three of them)
553 * to <var>destination</var>.
554 * The source and destination arrays can be manipulated
555 * anywhere along their length by specifying
556 * <var>srcOffset</var> and <var>destOffset</var>.
557 * This method does not check to make sure your arrays
558 * are large enough to accomodate <var>srcOffset</var> + 4 for
559 * the <var>source</var> array or <var>destOffset</var> + 3 for
560 * the <var>destination</var> array.
561 * This method returns the actual number of bytes that
562 * were converted from the Base64 encoding.
563 *
564 *
565 * @param source the array to convert
566 * @param srcOffset the index where conversion begins
567 * @param destination the array to hold the conversion
568 * @param destOffset the index where output will be put
569 * @return the number of decoded bytes converted
570 * @since 1.3
571 */
572 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
573 {
574 // Example: Dk==
575 if( source[ srcOffset + 2] == EQUALS_SIGN )
576 {
577 // Two ways to do the same thing. Don't know which way I like best.
578 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
579 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
580 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
581 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
582
583 destination[ destOffset ] = (byte)( outBuff >>> 16 );
584 return 1;
585 }
586
587 // Example: DkL=
588 else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
589 {
590 // Two ways to do the same thing. Don't know which way I like best.
591 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
592 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
593 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
594 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
595 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
596 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
597
598 destination[ destOffset ] = (byte)( outBuff >>> 16 );
599 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
600 return 2;
601 }
602
603 // Example: DkLE
604 else
605 {
606 try{
607 // Two ways to do the same thing. Don't know which way I like best.
608 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
609 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
610 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
611 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
612 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
613 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
614 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
615 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
616
617
618 destination[ destOffset ] = (byte)( outBuff >> 16 );
619 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
620 destination[ destOffset + 2 ] = (byte)( outBuff );
621
622 return 3;
623 }catch( Exception e){
624 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
625 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
626 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
627 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
628 return -1;
629 } //e nd catch
630 }
631 } // end decodeToBytes
632
633
634
635
636 /**
637 * Very low-level access to decoding ASCII characters in
638 * the form of a byte array. Does not support automatically
639 * gunzipping or any other "fancy" features.
640 *
641 * @param source The Base64 encoded data
642 * @param off The offset of where to begin decoding
643 * @param len The length of characters to decode
644 * @return decoded data
645 * @since 1.3
646 */
647 public static byte[] decode( byte[] source, int off, int len )
648 {
649 int len34 = len * 3 / 4;
650 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
651 int outBuffPosn = 0;
652
653 byte[] b4 = new byte[4];
654 int b4Posn = 0;
655 int i = 0;
656 byte sbiCrop = 0;
657 byte sbiDecode = 0;
658 for( i = off; i < off+len; i++ )
659 {
660 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
661 sbiDecode = DECODABET[ sbiCrop ];
662
663 if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
664 {
665 if( sbiDecode >= EQUALS_SIGN_ENC )
666 {
667 b4[ b4Posn++ ] = sbiCrop;
668 if( b4Posn > 3 )
669 {
670 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
671 b4Posn = 0;
672
673 // If that was the equals sign, break out of 'for' loop
674 if( sbiCrop == EQUALS_SIGN )
675 break;
676 } // end if: quartet built
677
678 } // end if: equals sign or better
679
680 } // end if: white space, equals sign or better
681 else
682 {
683 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
684 return null;
685 } // end else:
686 } // each input character
687
688 byte[] out = new byte[ outBuffPosn ];
689 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
690 return out;
691 } // end decode
692
693
694
695
696 /**
697 * Decodes data from Base64 notation, automatically
698 * detecting gzip-compressed data and decompressing it.
699 *
700 * @param s the string to decode
701 * @return the decoded data
702 * @since 1.4
703 */
704 public static byte[] decode( String s )
705 {
706 byte[] bytes;
707 try
708 {
709 bytes = s.getBytes( PREFERRED_ENCODING );
710 } // end try
711 catch( java.io.UnsupportedEncodingException uee )
712 {
713 bytes = s.getBytes();
714 } // end catch
715 //</change>
716
717 // Decode
718 bytes = decode( bytes, 0, bytes.length );
719
720
721 // Check to see if it's gzip-compressed
722 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
723 if( bytes != null && bytes.length >= 4 )
724 {
725
726 int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
727 if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
728 {
729 java.io.ByteArrayInputStream bais = null;
730 java.util.zip.GZIPInputStream gzis = null;
731 java.io.ByteArrayOutputStream baos = null;
732 byte[] buffer = new byte[2048];
733 int length = 0;
734
735 try
736 {
737 baos = new java.io.ByteArrayOutputStream();
738 bais = new java.io.ByteArrayInputStream( bytes );
739 gzis = new java.util.zip.GZIPInputStream( bais );
740
741 while( ( length = gzis.read( buffer ) ) >= 0 )
742 {
743 baos.write(buffer,0,length);
744 } // end while: reading input
745
746 // No error? Get new bytes.
747 bytes = baos.toByteArray();
748
749 } // end try
750 catch( java.io.IOException e )
751 {
752 // Just return originally-decoded bytes
753 } // end catch
754 finally
755 {
756 try{ baos.close(); } catch( Exception e ){}
757 try{ gzis.close(); } catch( Exception e ){}
758 try{ bais.close(); } catch( Exception e ){}
759 } // end finally
760
761 } // end if: gzipped
762 } // end if: bytes.length >= 2
763
764 return bytes;
765 } // end decode
766
767
768
769
770 /**
771 * Attempts to decode Base64 data and deserialize a Java
772 * Object within. Returns <tt>null</tt> if there was an error.
773 *
774 * @param encodedObject The Base64 data to decode
775 * @return The decoded and deserialized object
776 * @since 1.5
777 */
778 public static Object decodeToObject( String encodedObject )
779 {
780 // Decode and gunzip if necessary
781 byte[] objBytes = decode( encodedObject );
782
783 java.io.ByteArrayInputStream bais = null;
784 java.io.ObjectInputStream ois = null;
785 Object obj = null;
786
787 try
788 {
789 bais = new java.io.ByteArrayInputStream( objBytes );
790 ois = new java.io.ObjectInputStream( bais );
791
792 obj = ois.readObject();
793 } // end try
794 catch( java.io.IOException e )
795 {
796 e.printStackTrace();
797 obj = null;
798 } // end catch
799 catch( java.lang.ClassNotFoundException e )
800 {
801 e.printStackTrace();
802 obj = null;
803 } // end catch
804 finally
805 {
806 try{ bais.close(); } catch( Exception e ){}
807 try{ ois.close(); } catch( Exception e ){}
808 } // end finally
809
810 return obj;
811 } // end decodeObject
812
813
814
815 /**
816 * Convenience method for encoding data to a file.
817 *
818 * @param dataToEncode byte array of data to encode in base64 form
819 * @param filename Filename for saving encoded data
820 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
821 *
822 * @since 2.1
823 */
824 public static boolean encodeToFile( byte[] dataToEncode, String filename )
825 {
826 boolean success = false;
827 Base64.OutputStream bos = null;
828 try
829 {
830 bos = new Base64.OutputStream(
831 new java.io.FileOutputStream( filename ), Base64.ENCODE );
832 bos.write( dataToEncode );
833 success = true;
834 } // end try
835 catch( java.io.IOException e )
836 {
837
838 success = false;
839 } // end catch: IOException
840 finally
841 {
842 try{ bos.close(); } catch( Exception e ){}
843 } // end finally
844
845 return success;
846 } // end encodeToFile
847
848
849 /**
850 * Convenience method for decoding data to a file.
851 *
852 * @param dataToDecode Base64-encoded data as a string
853 * @param filename Filename for saving decoded data
854 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
855 *
856 * @since 2.1
857 */
858 public static boolean decodeToFile( String dataToDecode, String filename )
859 {
860 boolean success = false;
861 Base64.OutputStream bos = null;
862 try
863 {
864 bos = new Base64.OutputStream(
865 new java.io.FileOutputStream( filename ), Base64.DECODE );
866 bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
867 success = true;
868 } // end try
869 catch( java.io.IOException e )
870 {
871 success = false;
872 } // end catch: IOException
873 finally
874 {
875 try{ bos.close(); } catch( Exception e ){}
876 } // end finally
877
878 return success;
879 } // end decodeToFile
880
881
882
883
884 /**
885 * Convenience method for reading a base64-encoded
886 * file and decoding it.
887 *
888 * @param filename Filename for reading encoded data
889 * @return decoded byte array or null if unsuccessful
890 *
891 * @since 2.1
892 */
893 public static byte[] decodeFromFile( String filename )
894 {
895 byte[] decodedData = null;
896 Base64.InputStream bis = null;
897 try
898 {
899 // Set up some useful variables
900 java.io.File file = new java.io.File( filename );
901 byte[] buffer = null;
902 int length = 0;
903 int numBytes = 0;
904
905 // Check for size of file
906 if( file.length() > Integer.MAX_VALUE )
907 {
908 System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
909 return null;
910 } // end if: file too big for int index
911 buffer = new byte[ (int)file.length() ];
912
913 // Open a stream
914 bis = new Base64.InputStream(
915 new java.io.BufferedInputStream(
916 new java.io.FileInputStream( file ) ), Base64.DECODE );
917
918 // Read until done
919 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
920 length += numBytes;
921
922 // Save in a variable to return
923 decodedData = new byte[ length ];
924 System.arraycopy( buffer, 0, decodedData, 0, length );
925
926 } // end try
927 catch( java.io.IOException e )
928 {
929 System.err.println( "Error decoding from file " + filename );
930 } // end catch: IOException
931 finally
932 {
933 try{ bis.close(); } catch( Exception e) {}
934 } // end finally
935
936 return decodedData;
937 } // end decodeFromFile
938
939
940
941 /**
942 * Convenience method for reading a binary file
943 * and base64-encoding it.
944 *
945 * @param filename Filename for reading binary data
946 * @return base64-encoded string or null if unsuccessful
947 *
948 * @since 2.1
949 */
950 public static String encodeFromFile( String filename )
951 {
952 String encodedData = null;
953 Base64.InputStream bis = null;
954 try
955 {
956 // Set up some useful variables
957 java.io.File file = new java.io.File( filename );
958 byte[] buffer = new byte[ (int)(file.length() * 1.4) ];
959 int length = 0;
960 int numBytes = 0;
961
962 // Open a stream
963 bis = new Base64.InputStream(
964 new java.io.BufferedInputStream(
965 new java.io.FileInputStream( file ) ), Base64.ENCODE );
966
967 // Read until done
968 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
969 length += numBytes;
970
971 // Save in a variable to return
972 encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
973
974 } // end try
975 catch( java.io.IOException e )
976 {
977 System.err.println( "Error encoding from file " + filename );
978 } // end catch: IOException
979 finally
980 {
981 try{ bis.close(); } catch( Exception e) {}
982 } // end finally
983
984 return encodedData;
985 } // end encodeFromFile
986
987
988
989
990 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
991
992
993
994 /**
995 * A {@link Base64.InputStream} will read data from another
996 * <tt>java.io.InputStream</tt>, given in the constructor,
997 * and encode/decode to/from Base64 notation on the fly.
998 *
999 * @see Base64
1000 * @since 1.3
1001 */
1002 public static class InputStream extends java.io.FilterInputStream
1003 {
1004 private boolean encode; // Encoding or decoding
1005 private int position; // Current position in the buffer
1006 private byte[] buffer; // Small buffer holding converted data
1007 private int bufferLength; // Length of buffer (3 or 4)
1008 private int numSigBytes; // Number of meaningful bytes in the buffer
1009 private int lineLength;
1010 private boolean breakLines; // Break lines at less than 80 characters
1011
1012
1013 /**
1014 * Constructs a {@link Base64.InputStream} in DECODE mode.
1015 *
1016 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1017 * @since 1.3
1018 */
1019 public InputStream( java.io.InputStream in )
1020 {
1021 this( in, DECODE );
1022 } // end constructor
1023
1024
1025 /**
1026 * Constructs a {@link Base64.InputStream} in
1027 * either ENCODE or DECODE mode.
1028 * <p>
1029 * Valid options:<pre>
1030 * ENCODE or DECODE: Encode or Decode as data is read.
1031 * DONT_BREAK_LINES: don't break lines at 76 characters
1032 * (only meaningful when encoding)
1033 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1034 * </pre>
1035 * <p>
1036 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1037 *
1038 *
1039 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1040 * @param options Specified options
1041 * @see Base64#ENCODE
1042 * @see Base64#DECODE
1043 * @see Base64#DONT_BREAK_LINES
1044 * @since 2.0
1045 */
1046 public InputStream( java.io.InputStream in, int options )
1047 {
1048 super( in );
1049 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1050 this.encode = (options & ENCODE) == ENCODE;
1051 this.bufferLength = encode ? 4 : 3;
1052 this.buffer = new byte[ bufferLength ];
1053 this.position = -1;
1054 this.lineLength = 0;
1055 } // end constructor
1056
1057 /**
1058 * Reads enough of the input stream to convert
1059 * to/from Base64 and returns the next byte.
1060 *
1061 * @return next byte
1062 * @since 1.3
1063 */
1064 public int read() throws java.io.IOException
1065 {
1066 // Do we need to get data?
1067 if( position < 0 )
1068 {
1069 if( encode )
1070 {
1071 byte[] b3 = new byte[3];
1072 int numBinaryBytes = 0;
1073 for( int i = 0; i < 3; i++ )
1074 {
1075 try
1076 {
1077 int b = in.read();
1078
1079 // If end of stream, b is -1.
1080 if( b >= 0 )
1081 {
1082 b3[i] = (byte)b;
1083 numBinaryBytes++;
1084 } // end if: not end of stream
1085
1086 } // end try: read
1087 catch( java.io.IOException e )
1088 {
1089 // Only a problem if we got no data at all.
1090 if( i == 0 )
1091 throw e;
1092
1093 } // end catch
1094 } // end for: each needed input byte
1095
1096 if( numBinaryBytes > 0 )
1097 {
1098 encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
1099 position = 0;
1100 numSigBytes = 4;
1101 } // end if: got data
1102 else
1103 {
1104 return -1;
1105 } // end else
1106 } // end if: encoding
1107
1108 // Else decoding
1109 else
1110 {
1111 byte[] b4 = new byte[4];
1112 int i = 0;
1113 for( i = 0; i < 4; i++ )
1114 {
1115 // Read four "meaningful" bytes:
1116 int b = 0;
1117 do{ b = in.read(); }
1118 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1119
1120 if( b < 0 )
1121 break; // Reads a -1 if end of stream
1122
1123 b4[i] = (byte)b;
1124 } // end for: each needed input byte
1125
1126 if( i == 4 )
1127 {
1128 numSigBytes = decode4to3( b4, 0, buffer, 0 );
1129 position = 0;
1130 } // end if: got four characters
1131 else if( i == 0 ){
1132 return -1;
1133 } // end else if: also padded correctly
1134 else
1135 {
1136 // Must have broken out from above.
1137 throw new java.io.IOException( "Improperly padded Base64 input." );
1138 } // end
1139
1140 } // end else: decode
1141 } // end else: get data
1142
1143 // Got data?
1144 if( position >= 0 )
1145 {
1146 // End of relevant data?
1147 if( /*!encode &&*/ position >= numSigBytes )
1148 return -1;
1149
1150 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1151 {
1152 lineLength = 0;
1153 return '\n';
1154 } // end if
1155 else
1156 {
1157 lineLength++; // This isn't important when decoding
1158 // but throwing an extra "if" seems
1159 // just as wasteful.
1160
1161 int b = buffer[ position++ ];
1162
1163 if( position >= bufferLength )
1164 position = -1;
1165
1166 return b & 0xFF; // This is how you "cast" a byte that's
1167 // intended to be unsigned.
1168 } // end else
1169 } // end if: position >= 0
1170
1171 // Else error
1172 else
1173 {
1174 // When JDK1.4 is more accepted, use an assertion here.
1175 throw new java.io.IOException( "Error in Base64 code reading stream." );
1176 } // end else
1177 } // end read
1178
1179
1180 /**
1181 * Calls {@link #read()} repeatedly until the end of stream
1182 * is reached or <var>len</var> bytes are read.
1183 * Returns number of bytes read into array or -1 if
1184 * end of stream is encountered.
1185 *
1186 * @param dest array to hold values
1187 * @param off offset for array
1188 * @param len max number of bytes to read into array
1189 * @return bytes read into array or -1 if end of stream is encountered.
1190 * @since 1.3
1191 */
1192 public int read( byte[] dest, int off, int len ) throws java.io.IOException
1193 {
1194 int i;
1195 int b;
1196 for( i = 0; i < len; i++ )
1197 {
1198 b = read();
1199
1200 //if( b < 0 && i == 0 )
1201 // return -1;
1202
1203 if( b >= 0 )
1204 dest[off + i] = (byte)b;
1205 else if( i == 0 )
1206 return -1;
1207 else
1208 break; // Out of 'for' loop
1209 } // end for: each byte read
1210 return i;
1211 } // end read
1212
1213 } // end inner class InputStream
1214
1215
1216
1217
1218
1219
1220 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1221
1222
1223
1224 /**
1225 * A {@link Base64.OutputStream} will write data to another
1226 * <tt>java.io.OutputStream</tt>, given in the constructor,
1227 * and encode/decode to/from Base64 notation on the fly.
1228 *
1229 * @see Base64
1230 * @since 1.3
1231 */
1232 public static class OutputStream extends java.io.FilterOutputStream
1233 {
1234 private boolean encode;
1235 private int position;
1236 private byte[] buffer;
1237 private int bufferLength;
1238 private int lineLength;
1239 private boolean breakLines;
1240 private byte[] b4; // Scratch used in a few places
1241 private boolean suspendEncoding;
1242
1243 /**
1244 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1245 *
1246 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1247 * @since 1.3
1248 */
1249 public OutputStream( java.io.OutputStream out )
1250 {
1251 this( out, ENCODE );
1252 } // end constructor
1253
1254
1255 /**
1256 * Constructs a {@link Base64.OutputStream} in
1257 * either ENCODE or DECODE mode.
1258 * <p>
1259 * Valid options:<pre>
1260 * ENCODE or DECODE: Encode or Decode as data is read.
1261 * DONT_BREAK_LINES: don't break lines at 76 characters
1262 * (only meaningful when encoding)
1263 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1264 * </pre>
1265 * <p>
1266 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1267 *
1268 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1269 * @param options Specified options.
1270 * @see Base64#ENCODE
1271 * @see Base64#DECODE
1272 * @see Base64#DONT_BREAK_LINES
1273 * @since 1.3
1274 */
1275 public OutputStream( java.io.OutputStream out, int options )
1276 {
1277 super( out );
1278 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1279 this.encode = (options & ENCODE) == ENCODE;
1280 this.bufferLength = encode ? 3 : 4;
1281 this.buffer = new byte[ bufferLength ];
1282 this.position = 0;
1283 this.lineLength = 0;
1284 this.suspendEncoding = false;
1285 this.b4 = new byte[4];
1286 } // end constructor
1287
1288
1289 /**
1290 * Writes the byte to the output stream after
1291 * converting to/from Base64 notation.
1292 * When encoding, bytes are buffered three
1293 * at a time before the output stream actually
1294 * gets a write() call.
1295 * When decoding, bytes are buffered four
1296 * at a time.
1297 *
1298 * @param theByte the byte to write
1299 * @since 1.3
1300 */
1301 public void write(int theByte) throws java.io.IOException
1302 {
1303 // Encoding suspended?
1304 if( suspendEncoding )
1305 {
1306 super.out.write( theByte );
1307 return;
1308 } // end if: supsended
1309
1310 // Encode?
1311 if( encode )
1312 {
1313 buffer[ position++ ] = (byte)theByte;
1314 if( position >= bufferLength ) // Enough to encode.
1315 {
1316 out.write( encode3to4( b4, buffer, bufferLength ) );
1317
1318 lineLength += 4;
1319 if( breakLines && lineLength >= MAX_LINE_LENGTH )
1320 {
1321 out.write( NEW_LINE );
1322 lineLength = 0;
1323 } // end if: end of line
1324
1325 position = 0;
1326 } // end if: enough to output
1327 } // end if: encoding
1328
1329 // Else, Decoding
1330 else
1331 {
1332 // Meaningful Base64 character?
1333 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1334 {
1335 buffer[ position++ ] = (byte)theByte;
1336 if( position >= bufferLength ) // Enough to output.
1337 {
1338 int len = Base64.decode4to3( buffer, 0, b4, 0 );
1339 out.write( b4, 0, len );
1340 //out.write( Base64.decode4to3( buffer ) );
1341 position = 0;
1342 } // end if: enough to output
1343 } // end if: meaningful base64 character
1344 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1345 {
1346 throw new java.io.IOException( "Invalid character in Base64 data." );
1347 } // end else: not white space either
1348 } // end else: decoding
1349 } // end write
1350
1351
1352
1353 /**
1354 * Calls {@link #write(int)} repeatedly until <var>len</var>
1355 * bytes are written.
1356 *
1357 * @param theBytes array from which to read bytes
1358 * @param off offset for array
1359 * @param len max number of bytes to read into array
1360 * @since 1.3
1361 */
1362 public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1363 {
1364 // Encoding suspended?
1365 if( suspendEncoding )
1366 {
1367 super.out.write( theBytes, off, len );
1368 return;
1369 } // end if: supsended
1370
1371 for( int i = 0; i &l