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