1 /** 2 * $RCSfile$ 3 * $Revision$ 4 * $Date$ 5 * 6 */ 7 package 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 (at) iharder.net 16 * @version 2.2.1 17 */ 18 public 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 1690