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

