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