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