1 package com.cyberfox.util.config; 2 3 /** 4 * Encodes and decodes to and from Base64 notation. 5 * 6 * <p> 7 * Change Log: 8 * </p> 9 * <ul> 10 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li> 11 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream 12 * where last buffer being read, if not completely full, was not returned.</li> 13 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li> 14 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li> 15 * </ul> 16 * 17 * <p> 18 * I am placing this code in the Public Domain. Do with it as you will. 19 * This software comes with no guarantees or warranties but with 20 * plenty of well-wishing instead! 21 * Please visit <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a> 22 * periodically to check for updates or to contribute improvements. 23 * </p> 24 * 25 * @author Robert Harder 26 * @author rob@iharder.net 27 * @version 1.3.4 28 */ 29 public class Base64 30 { 31 32 /** Specify encoding (value is <tt>true</tt>). */ 33 public final static boolean ENCODE = true; 34 35 36 /** Specify decoding (value is <tt>false</tt>). */ 37 public final static boolean DECODE = false; 38 39 40 /** Maximum line length (76) of Base64 output. */ 41 private final static int MAX_LINE_LENGTH = 76; 42 43 44 /** The equals sign (=) as a byte. */ 45 private final static byte EQUALS_SIGN = (byte)'='; 46 47 48 /** The new line character (\n) as a byte. */ 49 private final static byte NEW_LINE = (byte)'\n'; 50 51 52 /** The 64 valid Base64 values. */ 53 private final static byte[] ALPHABET = 54 { 55 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 56 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 57 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 58 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 59 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 60 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 61 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 62 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 63 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 64 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' 65 }; 66 67 /** 68 * Translates a Base64 value to either its 6-bit reconstruction value 69 * or a negative number indicating some other meaning. 70 **/ 71 private final static byte[] DECODABET = 72 { 73 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 74 -5,-5, // Whitespace: Tab and Linefeed 75 -9,-9, // Decimal 11 - 12 76 -5, // Whitespace: Carriage Return 77 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 78 -9,-9,-9,-9,-9, // Decimal 27 - 31 79 -5, // Whitespace: Space 80 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 81 62, // Plus sign at decimal 43 82 -9,-9,-9, // Decimal 44 - 46 83 63, // Slash at decimal 47 84 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 85 -9,-9,-9, // Decimal 58 - 60 86 -1, // Equals sign at decimal 61 87 -9,-9,-9, // Decimal 62 - 64 88 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 89 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 90 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 91 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 92 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 93 -9,-9,-9,-9 // Decimal 123 - 126 94 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 95 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 96 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 97 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 98 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 99 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 100 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 101 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 102 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 103 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ 104 }; 105 106 // Unused. 107 // private final static byte BAD_ENCODING = -9; // Indicates error in encoding 108 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding 109 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding 110 111 112 /** Defeats instantiation. */ Base64()113 private Base64(){} 114 115 /* ******** E N C O D I N G M E T H O D S ******** */ 116 /** 117 * Encodes up to the first three bytes of array <var>threeBytes</var> 118 * and returns a four-byte array in Base64 notation. 119 * The actual number of significant bytes in your array is 120 * given by <var>numSigBytes</var>. 121 * The array <var>threeBytes</var> needs only be as big as 122 * <var>numSigBytes</var>. 123 * 124 * @param threeBytes the array to convert 125 * @param numSigBytes the number of significant bytes in your array 126 * @return four byte array in Base64 notation. 127 * @since 1.3 128 */ encode3to4( byte[] threeBytes, int numSigBytes )129 private static byte[] encode3to4( byte[] threeBytes, int numSigBytes ) 130 { byte[] dest = new byte[4]; 131 encode3to4( threeBytes, 0, numSigBytes, dest, 0 ); 132 return dest; 133 } 134 135 136 137 /** 138 * Encodes up to three bytes of the array <var>source</var> 139 * and writes the resulting four Base64 bytes to <var>destination</var>. 140 * The source and destination arrays can be manipulated 141 * anywhere along their length by specifying 142 * <var>srcOffset</var> and <var>destOffset</var>. 143 * This method does not check to make sure your arrays 144 * are large enough to accomodate <var>srcOffset</var> + 3 for 145 * the <var>source</var> array or <var>destOffset</var> + 4 for 146 * the <var>destination</var> array. 147 * The actual number of significant bytes in your array is 148 * given by <var>numSigBytes</var>. 149 * 150 * @param source the array to convert 151 * @param srcOffset the index where conversion begins 152 * @param numSigBytes the number of significant bytes in your array 153 * @param destination the array to hold the conversion 154 * @param destOffset the index where output will be put 155 * @return the <var>destination</var> array 156 * @since 1.3 157 */ encode3to4( byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset )158 private static byte[] encode3to4( 159 byte[] source, int srcOffset, int numSigBytes, 160 byte[] destination, int destOffset ) 161 { 162 // 1 2 3 163 // 01234567890123456789012345678901 Bit position 164 // --------000000001111111122222222 Array position from threeBytes 165 // --------| || || || | Six bit groups to index ALPHABET 166 // >>18 >>12 >> 6 >> 0 Right shift necessary 167 // 0x3f 0x3f 0x3f Additional AND 168 169 // Create buffer with zero-padding if there are only one or two 170 // significant bytes passed in the array. 171 // We have to shift left 24 in order to flush out the 1's that appear 172 // when Java treats a value as negative that is cast from a byte to an int. 173 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) 174 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) 175 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); 176 177 switch( numSigBytes ) 178 { 179 case 3: 180 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 181 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 182 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 183 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; 184 return destination; 185 186 case 2: 187 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 188 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 189 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 190 destination[ destOffset + 3 ] = EQUALS_SIGN; 191 return destination; 192 193 case 1: 194 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 195 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 196 destination[ destOffset + 2 ] = EQUALS_SIGN; 197 destination[ destOffset + 3 ] = EQUALS_SIGN; 198 return destination; 199 200 default: 201 return destination; 202 } // end switch 203 } // end encode3to4 204 205 /** 206 * Serializes an object and returns the Base64-encoded 207 * version of that serialized object. If the object 208 * cannot be serialized or there is another error, 209 * the method will return <tt>null</tt>. 210 * 211 * @param serializableObject The object to encode 212 * @return The Base64-encoded object 213 * @since 1.4 214 */ encodeObject( java.io.Serializable serializableObject )215 public static String encodeObject( java.io.Serializable serializableObject ) 216 { 217 return encodeObject( serializableObject, true ); 218 } // end encodeObject 219 220 /** 221 * Serializes an object and returns the Base64-encoded 222 * version of that serialized object. If the object 223 * cannot be serialized or there is another error, 224 * the method will return <tt>null</tt>. 225 * 226 * @param serializableObject The object to encode 227 * @param breakLines Break lines at 80 characters or less. 228 * @return The Base64-encoded object 229 * @since 1.4 230 */ encodeObject( java.io.Serializable serializableObject, boolean breakLines )231 public static String encodeObject( java.io.Serializable serializableObject, boolean breakLines ) 232 { 233 java.io.ByteArrayOutputStream baos = null; 234 java.io.OutputStream b64os = null; 235 java.io.ObjectOutputStream oos = null; 236 237 try 238 { 239 baos = new java.io.ByteArrayOutputStream(); 240 b64os = new Base64.OutputStream( baos, Base64.ENCODE, breakLines ); 241 oos = new java.io.ObjectOutputStream( b64os ); 242 243 oos.writeObject( serializableObject ); 244 } // end try 245 catch( java.io.IOException e ) 246 { 247 e.printStackTrace(); 248 return null; 249 } // end catch 250 finally 251 { 252 try{ oos.close(); } catch( Exception ignored ){ /* Ignore close errors */ } 253 try{ b64os.close(); } catch( Exception ignored ){ /* Ignore close errors */ } 254 try{ baos.close(); } catch( Exception ignored ){ /* Ignore close errors */ } 255 } // end finally 256 257 return new String( baos.toByteArray() ); 258 } // end encode 259 260 261 /** 262 * Encodes a byte array into Base64 notation. 263 * Equivalen to calling 264 * <code>encodeBytes( source, 0, source.length )</code> 265 * 266 * @param source The data to convert 267 * 268 * @return The Base64 encoded text. 269 * 270 * @since 1.4 271 */ encodeBytes( byte[] source )272 public static String encodeBytes( byte[] source ) 273 { 274 return encodeBytes( source, true ); 275 } // end encodeBytes 276 277 /** 278 * Encodes a byte array into Base64 notation. 279 * Equivalen to calling 280 * <code>encodeBytes( source, 0, source.length )</code> 281 * 282 * @param source The data to convert 283 * @param breakLines Break lines at 80 characters or less. 284 * 285 * @return The Base64 encoded text. 286 * 287 * @since 1.4 288 */ encodeBytes( byte[] source, boolean breakLines )289 public static String encodeBytes( byte[] source, boolean breakLines ) 290 { 291 return encodeBytes( source, 0, source.length, breakLines ); 292 } // end encodeBytes 293 294 295 /** 296 * Encodes a byte array into Base64 notation. 297 * 298 * @param source The data to convert 299 * @param off Offset in array where conversion should begin 300 * @param len Length of data to convert 301 * 302 * @return The Base64 encoded text. 303 * 304 * @since 1.4 305 */ encodeBytes( byte[] source, int off, int len )306 public static String encodeBytes( byte[] source, int off, int len ) 307 { 308 return encodeBytes( source, off, len, true ); 309 } // end encodeBytes 310 311 312 /** 313 * Encodes a byte array into Base64 notation. 314 * 315 * @param source The data to convert 316 * @param off Offset in array where conversion should begin 317 * @param len Length of data to convert 318 * @param breakLines Break lines at 80 characters or less. 319 * 320 * @return The Base64 encoded text. 321 * 322 * @since 1.4 323 */ encodeBytes( byte[] source, int off, int len, boolean breakLines )324 public static String encodeBytes( byte[] source, int off, int len, boolean breakLines ) 325 { 326 int len43 = len * 4 / 3; 327 byte[] outBuff = new byte[ ( len43 ) // Main 4:3 328 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding 329 + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines 330 int d = 0; 331 int e = 0; 332 int len2 = len - 2; 333 int lineLength = 0; 334 for( ; d < len2; d+=3, e+=4 ) 335 { 336 encode3to4( source, d+off, 3, outBuff, e ); 337 338 lineLength += 4; 339 if( breakLines && lineLength == MAX_LINE_LENGTH ) 340 { 341 outBuff[e+4] = NEW_LINE; 342 e++; 343 lineLength = 0; 344 } // end if: end of line 345 } // en dfor: each piece of array 346 347 if( d < len ) 348 { 349 encode3to4( source, d+off, len - d, outBuff, e ); 350 e += 4; 351 } // end if: some padding needed 352 353 return new String( outBuff, 0, e ); 354 } // end encodeBytes 355 356 357 /** 358 * Encodes a string in Base64 notation with line breaks 359 * after every 75 Base64 characters. 360 * 361 * @param s the string to encode 362 * @return the encoded string 363 * @since 1.3 364 */ encodeString( String s )365 public static String encodeString( String s ) 366 { 367 return encodeString( s, true ); 368 } // end encodeString 369 370 /** 371 * Encodes a string in Base64 notation with line breaks 372 * after every 75 Base64 characters. 373 * 374 * @param s the string to encode 375 * @param breakLines Break lines at 80 characters or less. 376 * @return the encoded string 377 * @since 1.3 378 */ encodeString( String s, boolean breakLines )379 public static String encodeString( String s, boolean breakLines ) 380 { 381 return encodeBytes( s.getBytes(), breakLines ); 382 } // end encodeString 383 384 385 386 387 /* ******** D E C O D I N G M E T H O D S ******** */ 388 389 390 /** 391 * Decodes the first four bytes of array <var>fourBytes</var> 392 * and returns an array up to three bytes long with the 393 * decoded values. 394 * 395 * @param fourBytes the array with Base64 content 396 * @return array with decoded values 397 * @since 1.3 398 */ decode4to3( byte[] fourBytes )399 private static byte[] decode4to3( byte[] fourBytes ) 400 { 401 byte[] outBuff1 = new byte[3]; 402 int count = decode4to3( fourBytes, 0, outBuff1, 0 ); 403 byte[] outBuff2 = new byte[ count ]; 404 405 System.arraycopy(outBuff1, 0, outBuff2, 0, count); 406 407 return outBuff2; 408 } 409 410 411 412 413 /** 414 * Decodes four bytes from array <var>source</var> 415 * and writes the resulting bytes (up to three of them) 416 * to <var>destination</var>. 417 * The source and destination arrays can be manipulated 418 * anywhere along their length by specifying 419 * <var>srcOffset</var> and <var>destOffset</var>. 420 * This method does not check to make sure your arrays 421 * are large enough to accomodate <var>srcOffset</var> + 4 for 422 * the <var>source</var> array or <var>destOffset</var> + 3 for 423 * the <var>destination</var> array. 424 * This method returns the actual number of bytes that 425 * were converted from the Base64 encoding. 426 * 427 * 428 * @param source the array to convert 429 * @param srcOffset the index where conversion begins 430 * @param destination the array to hold the conversion 431 * @param destOffset the index where output will be put 432 * @return the number of decoded bytes converted 433 * @since 1.3 434 */ decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )435 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) 436 { 437 // Example: Dk== 438 if( source[ srcOffset + 2] == EQUALS_SIGN ) 439 { 440 // Two ways to do the same thing. Don't know which way I like best. 441 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 442 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 443 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 444 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); 445 446 destination[ destOffset ] = (byte)( outBuff >>> 16 ); 447 return 1; 448 } 449 450 // Example: DkL= 451 else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) 452 { 453 // Two ways to do the same thing. Don't know which way I like best. 454 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 455 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 456 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 457 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 458 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 459 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); 460 461 destination[ destOffset ] = (byte)( outBuff >>> 16 ); 462 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); 463 return 2; 464 } 465 466 // Example: DkLE 467 else 468 { 469 try{ 470 // Two ways to do the same thing. Don't know which way I like best. 471 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 472 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 473 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 474 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 475 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 476 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 477 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) 478 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); 479 480 481 destination[ destOffset ] = (byte)( outBuff >> 16 ); 482 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); 483 destination[ destOffset + 2 ] = (byte)( outBuff ); 484 485 return 3; 486 }catch( Exception e){ 487 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); 488 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); 489 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); 490 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); 491 return -1; 492 } //e nd catch 493 } 494 } // end decodeToBytes 495 496 497 498 /** 499 * Decodes data from Base64 notation. 500 * 501 * @param s the string to decode 502 * @return the decoded data 503 * @since 1.4 504 */ decode( String s )505 public static byte[] decode( String s ) 506 { 507 byte[] bytes = s.getBytes(); 508 return decode( bytes, bytes.length ); 509 } // end decode 510 511 512 /** 513 * Decodes data from Base64 notation and 514 * returns it as a string. 515 * Equivlaent to calling 516 * <code>new String( decode( s ) )</code> 517 * 518 * @param s the strind to decode 519 * @return The data as a string 520 * @since 1.4 521 */ decodeToString( String s )522 public static String decodeToString( String s ) 523 { return new String( decode( s ) ); 524 } // end decodeToString 525 526 527 /** 528 * Attempts to decode Base64 data and deserialize a Java 529 * Object within. Returns <tt>null if there was an error. 530 * 531 * @param encodedObject The Base64 data to decode 532 * @return The decoded and deserialized object 533 * @since 1.4 534 */ decodeToObject( String encodedObject )535 public static Object decodeToObject( String encodedObject ) 536 { 537 byte[] objBytes = decode( encodedObject ); 538 539 java.io.ByteArrayInputStream bais = null; 540 java.io.ObjectInputStream ois = null; 541 542 try 543 { 544 bais = new java.io.ByteArrayInputStream( objBytes ); 545 ois = new java.io.ObjectInputStream( bais ); 546 547 return ois.readObject(); 548 } // end try 549 catch( java.io.IOException e ) 550 { 551 e.printStackTrace(); 552 return null; 553 } // end catch 554 catch( java.lang.ClassNotFoundException e ) 555 { 556 e.printStackTrace(); 557 return null; 558 } // end catch 559 finally 560 { 561 try{ if(bais != null) bais.close(); } catch( Exception ignored ){ /* Ignore close errors */ } 562 try{ if(ois != null) ois.close(); } catch( Exception ignored ){ /* Ignore close errors */ } 563 } // end finally 564 } // end decodeObject 565 566 567 /** 568 * Decodes Base64 content in byte array format and returns 569 * the decoded byte array. 570 * 571 * @param source The Base64 encoded data 572 * @param len The length of characters to decode 573 * @return decoded data 574 * @since 1.3 575 */ decode(byte[] source, int len)576 public static byte[] decode(byte[] source, int len) 577 { 578 int len34 = len * 3 / 4; 579 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output 580 int outBuffPosn = 0; 581 582 byte[] b4 = new byte[4]; 583 int b4Posn = 0; 584 int i; 585 byte sbiCrop, sbiDecode; 586 for( i = 0; i < len; i++ ) 587 { 588 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits 589 sbiDecode = DECODABET[ sbiCrop ]; 590 591 if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better 592 { 593 if( sbiDecode >= EQUALS_SIGN_ENC ) 594 { 595 b4[ b4Posn++ ] = sbiCrop; 596 if( b4Posn > 3 ) 597 { 598 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn ); 599 b4Posn = 0; 600 601 // If that was the equals sign, break out of 'for' loop 602 if( sbiCrop == EQUALS_SIGN ) 603 break; 604 } // end if: quartet built 605 606 } // end if: equals sign or better 607 608 } // end if: white space, equals sign or better 609 else 610 { 611 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); 612 throw new RuntimeException("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); 613 } // end else: 614 } // each input character 615 616 byte[] out = new byte[ outBuffPosn ]; 617 System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 618 return out; 619 } // end decode 620 621 622 623 624 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 625 626 627 628 /** 629 * A {@link Base64#InputStream} will read data from another 630 * {@link java.io.InputStream}, given in the constructor, 631 * and encode/decode to/from Base64 notation on the fly. 632 * 633 * @see Base64 634 * @see java.io.FilterInputStream 635 * @since 1.3 636 * @noinspection JavadocReference 637 */ 638 public static class InputStream extends java.io.FilterInputStream 639 { 640 private boolean encode; // Encoding or decoding 641 private int position; // Current position in the buffer 642 private byte[] buffer; // Small buffer holding converted data 643 private int bufferLength; // Length of buffer (3 or 4) 644 private int numSigBytes; // Number of meaningful bytes in the buffer 645 private int lineLength; 646 private boolean breakLines; // Break lines at less than 80 characters 647 648 649 /** 650 * Constructs a {@link Base64#InputStream} in DECODE mode. 651 * 652 * @param in the {@link java.io.InputStream} from which to read data. 653 * @since 1.3 654 */ InputStream( java.io.InputStream in )655 public InputStream( java.io.InputStream in ) 656 { 657 this( in, Base64.DECODE ); 658 } // end constructor 659 660 661 /** 662 * Constructs a {@link Base64#InputStream} in 663 * either ENCODE or DECODE mode. 664 * 665 * @param in the {@link java.io.InputStream} from which to read data. 666 * @param encode Conversion direction 667 * @see Base64#ENCODE 668 * @see Base64#DECODE 669 * @since 1.3 670 */ InputStream( java.io.InputStream in, boolean encode )671 public InputStream( java.io.InputStream in, boolean encode ) 672 { 673 this( in, encode, true ); 674 } // end constructor 675 676 677 /** 678 * Constructs a {@link Base64#InputStream} in 679 * either ENCODE or DECODE mode. 680 * 681 * @param in the {@link java.io.InputStream} from which to read data. 682 * @param encode Conversion direction 683 * @param breakLines Break lines at less than 80 characters. 684 * @see Base64#ENCODE 685 * @see Base64#DECODE 686 * @since 1.3 687 */ InputStream( java.io.InputStream in, boolean encode, boolean breakLines )688 public InputStream( java.io.InputStream in, boolean encode, boolean breakLines ) 689 { 690 super( in ); 691 this.breakLines = breakLines; 692 this.encode = encode; 693 this.bufferLength = encode ? 4 : 3; 694 this.buffer = new byte[ bufferLength ]; 695 this.position = -1; 696 this.lineLength = 0; 697 } // end constructor 698 699 /** 700 * Reads enough of the input stream to convert 701 * to/from Base64 and returns the next byte. 702 * 703 * @return next byte 704 * @since 1.3 705 */ read()706 public int read() throws java.io.IOException 707 { 708 // Do we need to get data? 709 if( position < 0 ) 710 { 711 if( encode ) 712 { 713 byte[] b3 = new byte[3]; 714 int numBinaryBytes = 0; 715 for( int i = 0; i < 3; i++ ) 716 { 717 try 718 { 719 int b = in.read(); 720 721 // If end of stream, b is -1. 722 if( b >= 0 ) 723 { 724 b3[i] = (byte)b; 725 numBinaryBytes++; 726 } // end if: not end of stream 727 728 } // end try: read 729 catch( java.io.IOException e ) 730 { 731 // Only a problem if we got no data at all. 732 if( i == 0 ) 733 throw e; 734 735 } // end catch 736 } // end for: each needed input byte 737 738 if( numBinaryBytes > 0 ) 739 { 740 encode3to4( b3, 0, numBinaryBytes, buffer, 0 ); 741 position = 0; 742 numSigBytes = 4; 743 } // end if: got data 744 else 745 { 746 return -1; 747 } // end else 748 } // end if: encoding 749 750 // Else decoding 751 else 752 { 753 byte[] b4 = new byte[4]; 754 int i; 755 for( i = 0; i < 4; i++ ) 756 { 757 // Read four "meaningful" bytes: 758 int b; 759 do{ b = in.read(); } 760 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC ); 761 762 if( b < 0 ) 763 break; // Reads a -1 if end of stream 764 765 b4[i] = (byte)b; 766 } // end for: each needed input byte 767 768 if( i == 4 ) 769 { 770 numSigBytes = decode4to3( b4, 0, buffer, 0 ); 771 position = 0; 772 } // end if: got four characters 773 else if( i == 0 ){ 774 return -1; 775 } // end else if: also padded correctly 776 else 777 { 778 // Must have broken out from above. 779 throw new java.io.IOException( "Improperly padded Base64 input." ); 780 } // end 781 782 } // end else: decode 783 } // end else: get data 784 785 // Got data? 786 if( position >= 0 ) 787 { 788 // End of relevant data? 789 if( /*!encode &&*/ position >= numSigBytes ) 790 return -1; 791 792 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) 793 { 794 lineLength = 0; 795 return '\n'; 796 } // end if 797 else 798 { 799 lineLength++; // This isn't important when decoding 800 // but throwing an extra "if" seems 801 // just as wasteful. 802 803 int b = buffer[ position++ ]; 804 805 if( position >= bufferLength ) 806 position = -1; 807 808 return b & 0xFF; // This is how you "cast" a byte that's 809 // intended to be unsigned. 810 } // end else 811 } // end if: position >= 0 812 813 // Else error 814 else 815 { 816 // When JDK1.4 is more accepted, use an assertion here. 817 throw new java.io.IOException( "Error in Base64 code reading stream." ); 818 } // end else 819 } // end read 820 821 822 /** 823 * Calls {@link #read} repeatedly until the end of stream 824 * is reached or <var>len</var> bytes are read. 825 * Returns number of bytes read into array or -1 if 826 * end of stream is encountered. 827 * 828 * @param dest array to hold values 829 * @param off offset for array 830 * @param len max number of bytes to read into array 831 * @return bytes read into array or -1 if end of stream is encountered. 832 * @since 1.3 833 */ read( byte[] dest, int off, int len )834 public int read( byte[] dest, int off, int len ) throws java.io.IOException 835 { 836 int i; 837 int b; 838 for( i = 0; i < len; i++ ) 839 { 840 b = read(); 841 842 //if( b < 0 && i == 0 ) 843 // return -1; 844 845 if( b >= 0 ) 846 dest[off + i] = (byte)b; 847 else if( i == 0 ) 848 return -1; 849 else 850 break; // Out of 'for' loop 851 } // end for: each byte read 852 return i; 853 } // end read 854 855 } // end inner class InputStream 856 857 858 859 860 861 862 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ 863 864 865 866 /** 867 * A {@link Base64#OutputStream} will write data to another 868 * {@link java.io.OutputStream}, given in the constructor, 869 * and encode/decode to/from Base64 notation on the fly. 870 * 871 * @see Base64 872 * @see java.io.FilterOutputStream 873 * @since 1.3 874 * @noinspection JavadocReference 875 */ 876 public static class OutputStream extends java.io.FilterOutputStream 877 { 878 private boolean encode; 879 private int position; 880 private byte[] buffer; 881 private int bufferLength; 882 private int lineLength; 883 private boolean breakLines; 884 885 886 /** 887 * Constructs a {@link Base64#OutputStream} in ENCODE mode. 888 * 889 * @param out the {@link java.io.OutputStream} to which data will be written. 890 * @since 1.3 891 */ OutputStream( java.io.OutputStream out )892 public OutputStream( java.io.OutputStream out ) 893 { 894 this( out, Base64.ENCODE ); 895 } // end constructor 896 897 898 /** 899 * Constructs a {@link Base64#OutputStream} in 900 * either ENCODE or DECODE mode. 901 * 902 * @param out the {@link java.io.OutputStream} to which data will be written. 903 * @param encode Conversion direction 904 * @see Base64#ENCODE 905 * @see Base64#DECODE 906 * @since 1.3 907 */ OutputStream( java.io.OutputStream out, boolean encode )908 public OutputStream( java.io.OutputStream out, boolean encode ) 909 { 910 this( out, encode, true ); 911 } // end constructor 912 913 914 /** 915 * Constructs a {@link Base64#OutputStream} in 916 * either ENCODE or DECODE mode. 917 * 918 * @param out the {@link java.io.OutputStream} to which data will be written. 919 * @param encode Conversion direction 920 * @param breakLines Break lines to be less than 80 characters. 921 * @see Base64#ENCODE 922 * @see Base64#DECODE 923 * @since 1.3 924 */ OutputStream( java.io.OutputStream out, boolean encode, boolean breakLines )925 public OutputStream( java.io.OutputStream out, boolean encode, boolean breakLines ) 926 { 927 super( out ); 928 this.breakLines = breakLines; 929 this.encode = encode; 930 this.bufferLength = encode ? 3 : 4; 931 this.buffer = new byte[ bufferLength ]; 932 this.position = 0; 933 this.lineLength = 0; 934 } // end constructor 935 936 937 /** 938 * Writes the byte to the output stream after 939 * converting to/from Base64 notation. 940 * When encoding, bytes are buffered three 941 * at a time before the output stream actually 942 * gets a write() call. 943 * When decoding, bytes are buffered four 944 * at a time. 945 * 946 * @param theByte the byte to write 947 * @since 1.3 948 */ write(int theByte)949 public void write(int theByte) throws java.io.IOException 950 { 951 if( encode ) 952 { 953 buffer[ position++ ] = (byte)theByte; 954 if( position >= bufferLength ) // Enough to encode. 955 { 956 out.write( Base64.encode3to4( buffer, bufferLength ) ); 957 958 lineLength += 4; 959 if( breakLines && lineLength >= MAX_LINE_LENGTH ) 960 { 961 out.write( NEW_LINE ); 962 lineLength = 0; 963 } // end if: end of line 964 965 position = 0; 966 } // end if: enough to output 967 } // end if: encoding 968 969 // Else, Decoding 970 else 971 { 972 // Meaningful Base64 character? 973 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC ) 974 { 975 buffer[ position++ ] = (byte)theByte; 976 if( position >= bufferLength ) // Enough to output. 977 { 978 out.write( Base64.decode4to3( buffer ) ); 979 position = 0; 980 } // end if: enough to output 981 } // end if: meaningful base64 character 982 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC ) 983 { 984 throw new java.io.IOException( "Invalid character in Base64 data." ); 985 } // end else: not white space either 986 } // end else: decoding 987 } // end write 988 989 990 991 /** 992 * Calls {@link #write} repeatedly until <var>len</var> 993 * bytes are written. 994 * 995 * @param theBytes array from which to read bytes 996 * @param off offset for array 997 * @param len max number of bytes to read into array 998 * @since 1.3 999 */ write( byte[] theBytes, int off, int len )1000 public void write( byte[] theBytes, int off, int len ) throws java.io.IOException 1001 { 1002 for( int i = 0; i < len; i++ ) 1003 { 1004 write( theBytes[ off + i ] ); 1005 } // end for: each byte written 1006 1007 } // end write 1008 1009 1010 /** 1011 * Appropriately pads Base64 notation when encoding 1012 * or throws an exception if Base64 input is not 1013 * properly padded when decoding. 1014 * 1015 * @since 1.3 1016 */ flush()1017 public void flush() throws java.io.IOException 1018 { 1019 super.flush(); 1020 1021 if( position > 0 ) 1022 { 1023 if( encode ) 1024 { 1025 out.write( Base64.encode3to4( buffer, position ) ); 1026 position = 0; 1027 } // end if: encoding 1028 else 1029 { 1030 throw new java.io.IOException( "Base64 input not properly padded." ); 1031 } // end else: decoding 1032 } // end if: buffer partially full 1033 1034 out.flush(); 1035 } // end flush 1036 1037 1038 /** 1039 * Flushes and closes (I think, in the superclass) the stream. 1040 * 1041 * @since 1.3 1042 */ close()1043 public void close() throws java.io.IOException 1044 { 1045 super.close(); 1046 //this.flush(); 1047 1048 out.close(); 1049 1050 buffer = null; 1051 out = null; 1052 } // end close 1053 1054 } // end inner class OutputStream 1055 1056 1057 } // end class Base64 1058