1 /* 2 * This file is part of Waarp Project (named also Waarp or GG). 3 * 4 * Copyright (c) 2019, Waarp SAS, and individual contributors by the @author 5 * tags. See the COPYRIGHT.txt in the distribution for a full listing of 6 * individual contributors. 7 * 8 * All Waarp Project is free software: you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or (at your 11 * option) any later version. 12 * 13 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY 14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 15 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * Waarp . If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 package org.waarp.common.utility; 22 23 import org.waarp.common.file.FileUtils; 24 import org.waarp.common.logging.SysErrLogger; 25 26 import java.io.BufferedInputStream; 27 import java.io.BufferedOutputStream; 28 import java.io.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.FileOutputStream; 33 import java.io.FilterInputStream; 34 import java.io.FilterOutputStream; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.ObjectInputStream; 38 import java.io.ObjectOutputStream; 39 import java.io.ObjectStreamClass; 40 import java.io.OutputStream; 41 import java.io.Serializable; 42 import java.io.UnsupportedEncodingException; 43 import java.nio.ByteBuffer; 44 import java.nio.CharBuffer; 45 import java.util.zip.GZIPInputStream; 46 import java.util.zip.GZIPOutputStream; 47 48 /** 49 * <p> 50 * Encodes and decodes to and from Base64 notation. 51 * </p> 52 * <p> 53 * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>. 54 * </p> 55 * 56 * <p> 57 * Example: 58 * </p> 59 * 60 * {@code String encoded = Base64.encode( myByteArray );} <br /> 61 * {@code byte[] myByteArray = Base64.decode( encoded );} 62 * 63 * <p> 64 * The <tt>options</tt> parameter, which appears in a few places, is used to 65 * pass several pieces of 66 * information to the encoder. In the "higher level" methods such as 67 * encodeBytes( bytes, options ) the options 68 * parameter can be used to indicate such things as first gzipping the bytes 69 * before encoding them, not 70 * inserting linefeeds, and encoding using the URL-safe and Ordered dialects. 71 * </p> 72 * 73 * <p> 74 * Note, according to <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>, 75 * Section 2.1, 76 * implementations should not add line feeds unless explicitly told to do so. 77 * I've got Base64 set to this 78 * behavior now, although earlier versions broke lines by default. 79 * </p> 80 * 81 * <p> 82 * The constants defined in Base64 can be OR-ed together to combine options, so 83 * you might make a call like 84 * this: 85 * </p> 86 * 87 * {@code String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | 88 * Base64.DO_BREAK_LINES );} 89 * <p> 90 * to compress the data before encoding it and then making the output have 91 * newline characters. 92 * </p> 93 * <p> 94 * Also... 95 * </p> 96 * {@code String encoded = Base64.encodeBytes( crazyString.getBytes() );} 97 * 98 * 99 * 100 * <p> 101 * Change Log: 102 * </p> 103 * <ul> 104 * <li>v2.3.7 - Fixed subtle bug when base 64 input stream contained the value 105 * 01111111, which is an invalid 106 * base 64 character but should not throw an ArrayIndexOutOfBoundsException 107 * either. Led to discovery of 108 * mishandling (or potential for better handling) of other bad input 109 * characters. 110 * You should now get an 111 * IOException if you try decoding something that has bad characters in 112 * it.</li> 113 * <li>v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded 114 * string ended in the last 115 * column; the buffer was not properly shrunk and contained an extra (null) 116 * byte 117 * that made it into the 118 * string.</li> 119 * <li>v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer 120 * size 121 * was wrong for files of size 122 * 31, 34, and 37 bytes.</li> 123 * <li>v2.3.4 - Fixed bug when working with gzipped streams whereby flushing 124 * the 125 * Base64.OutputStream closed 126 * the Base64 encoding (by padding with equals signs) too soon. Also added an 127 * option to suppress the automatic 128 * decoding of gzipped streams. Also added experimental support for specifying 129 * a 130 * class loader when using the 131 * {@link #decodeToObject(String, int, ClassLoader)} 132 * method.</li> 133 * <li>v2.3.3 - Changed default char encoding to US-ASCII which reduces the 134 * internal Java footprint with its 135 * CharEncoders and so forth. Fixed some javadocs that were inconsistent. 136 * Removed imports and specified things 137 * like java.io.IOException explicitly inline.</li> 138 * <li>v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how 139 * big the final encoded data 140 * will be so that the code doesn't have to create two output arrays: an 141 * oversized initial one and then a 142 * final, exact-sized one. Big win when using the {@link 143 * #encodeBytesToBytes(byte[])} family of methods (and 144 * not using the gzip options which uses a different mechanism with streams and 145 * stuff).</li> 146 * <li>v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and 147 * some similar helper methods to be 148 * more efficient with memory by not returning a String but just a byte 149 * array.</li> 150 * <li>v2.3 - <strong>This is not a drop-in replacement!</strong> This is two 151 * years of comments and bug fixes 152 * queued up and finally executed. Thanks to everyone who sent me stuff, and 153 * I'm 154 * sorry I wasn't able to 155 * distribute your fixes to everyone else. Much bad coding was cleaned up 156 * including throwing exceptions where 157 * necessary instead of returning null values or something similar. Here are 158 * some changes that may affect you: 159 * <ul> 160 * <li><em>Does not break lines, by default.</em> This is to keep in compliance 161 * with 162 * <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li> 163 * <li><em>Throws exceptions instead of returning null values.</em> Because 164 * some 165 * operations (especially those 166 * that may permit the GZIP option) use IO streams, there is a possiblity of an 167 * java.io.IOException being 168 * thrown. After some discussion and thought, I've changed the behavior of the 169 * methods to throw 170 * java.io.IOExceptions rather than return null if ever there's an error. I 171 * think this is more appropriate, 172 * though it will require some changes to your code. Sorry, it should have been 173 * done this way to begin 174 * with.</li> 175 * <li><em>Removed all references to System.out, System.err, and the like.</em> 176 * Shame on me. All I can say is 177 * sorry they were ever there.</li> 178 * <li><em>Throws NullPointerExceptions and IllegalArgumentExceptions</em> as 179 * needed such as when passed 180 * arrays are null or offsets are invalid.</li> 181 * <li>Cleaned up as much javadoc as I could to avoid any javadoc warnings. 182 * This 183 * was especially annoying 184 * before for people who were thorough in their own projects and then had gobs 185 * of javadoc warnings on this 186 * file.</li> 187 * </ul> 188 * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug when 189 * using very small files (~< 190 * 40 bytes).</li> 191 * <li>v2.2 - Added some helper methods for encoding/decoding directly from one 192 * file to the next. Also added a 193 * main() method to support command line encoding/decoding from one file to the 194 * next. Also added these Base64 195 * dialects: 196 * <ol> 197 * <li>The default is RFC3548 format.</li> 198 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates 199 * URL and file name friendly 200 * format as described in Section 4 of RFC3548. http://www.faqs.org/rfcs/rfc3548.html</li> 201 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates 202 * URL and file name friendly 203 * format that preserves lexical ordering as described in 204 * http://www.faqs.org/qa/rfcc-1940.html</li> 205 * </ol> 206 * Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a> 207 * for 208 * contributing the new Base64 dialects.</li> 209 * 210 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. 211 * Added some convenience methods for 212 * reading and writing to and from files.</li> 213 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on 214 * systems with other encodings 215 * (like EBCDIC).</li> 216 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the 217 * encoded data was a single 218 * byte.</li> 219 * <li>v2.0 - I got rid of methods that used booleans to set options. Now 220 * everything is more consolidated and 221 * cleaner. The code now detects when data that's being decoded is 222 * gzip-compressed and will decompress it 223 * automatically. Generally things are cleaner. You'll probably have to change 224 * some method calls that you were 225 * making to support the new options format (<tt>int</tt>s that you "OR" 226 * together).</li> 227 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using 228 * <tt>decode( String s, boolean gzipCompressed )</tt>. Added the ability to 229 * "suspend" encoding in the Output 230 * Stream so you can turn on and off the encoding if you need to embed base64 231 * data in an otherwise "normal" 232 * stream (like an XML file).</li> 233 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything 234 * itself. This helps when using 235 * GZIP streams. Added the ability to GZip-compress objects before encoding 236 * them.</li> 237 * <li>v1.4 - Added helper methods to read/write files.</li> 238 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li> 239 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input 240 * stream where last buffer being 241 * read, if not completely full, was not returned.</li> 242 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the 243 * wrong time.</li> 244 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li> 245 * </ul> 246 * 247 * <p> 248 * I am placing this code in the Public Domain. Do with it as you will. This 249 * software comes with no guarantees 250 * or warranties but with plenty of well-wishing instead! Please visit 251 * <a href="http://iharder.net/base64">http://iharder.net/base64</a> 252 * periodically to check for updates or to 253 * contribute improvements. 254 * </p> 255 * 256 * @author Robert Harder 257 * @author rob@iharder.net 258 * @version 2.3.7 259 */ 260 public final class Base64 { 261 262 /* ******** P U B L I C F I E L D S ******** */ 263 264 /** 265 * No options specified. Value is zero. 266 */ 267 public static final int NO_OPTIONS = 0; 268 269 /** 270 * Specify encoding in first bit. Value is one. 271 */ 272 public static final int ENCODE = 1; 273 274 /** 275 * Specify decoding in first bit. Value is zero. 276 */ 277 public static final int DECODE = 0; 278 279 /** 280 * Specify that data should be gzip-compressed in second bit. Value is two. 281 */ 282 public static final int GZIP = 2; 283 284 /** 285 * Specify that gzipped data should <em>not</em> be automatically gunzipped. 286 */ 287 public static final int DONT_GUNZIP = 4; 288 289 /** 290 * Do break lines when encoding. Value is 8. 291 */ 292 public static final int DO_BREAK_LINES = 8; 293 294 /** 295 * Encode using Base64-like encoding that is URL- and Filename-safe as 296 * described in Section 4 of RFC3548: 297 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. 298 * It is important 299 * to note that data encoded this way is <em>not</em> officially valid 300 * Base64, or at the very least should not 301 * be called Base64 without also specifying that is was encoded using the 302 * URL- and Filename-safe dialect. 303 */ 304 public static final int URL_SAFE = 16; 305 306 /** 307 * Encode using the special "ordered" dialect of Base64 described here: 308 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>. 309 */ 310 public static final int ORDERED = 32; 311 312 /* ******** P R I V A T E F I E L D S ******** */ 313 314 /** 315 * Maximum line length (76) of Base64 output. 316 */ 317 private static final int MAX_LINE_LENGTH = 76; 318 319 /** 320 * The equals sign (=) as a byte. 321 */ 322 private static final byte EQUALS_SIGN = (byte) '='; 323 324 /** 325 * The new line character (\n) as a byte. 326 */ 327 private static final byte NEW_LINE = (byte) '\n'; 328 329 /** 330 * Preferred encoding. 331 */ 332 private static final String PREFERRED_ENCODING = "US-ASCII"; 333 334 private static final byte WHITE_SPACE_ENC = -5; 335 // Indicates white space in encoding 336 private static final byte EQUALS_SIGN_ENC = -1; 337 // Indicates equals sign in encoding 338 339 /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ 340 341 /** 342 * The 64 valid Base64 values. 343 */ 344 /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ 345 private static final byte[] _STANDARD_ALPHABET = { 346 (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', 347 (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', 348 (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', 349 (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', 350 (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', 351 (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', 352 (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', 353 (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', 354 (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', 355 (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', 356 (byte) '8', (byte) '9', (byte) '+', (byte) '/' 357 }; 358 359 /** 360 * Translates a Base64 value to either its 6-bit reconstruction value or a 361 * negative number indicating some 362 * other meaning. 363 **/ 364 private static final byte[] _STANDARD_DECODABET = { 365 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 366 -5, -5, // Whitespace: Tab and Linefeed 367 -9, -9, // Decimal 11 - 12 368 -5, // Whitespace: Carriage Return 369 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 370 // Decimal 14 - 26 371 -9, -9, -9, -9, -9, // Decimal 27 - 31 372 -5, // Whitespace: Space 373 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 374 62, // Plus sign at decimal 43 375 -9, -9, -9, // Decimal 44 - 46 376 63, // Slash at decimal 47 377 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine 378 -9, -9, -9, // Decimal 58 - 60 379 -1, // Equals sign at decimal 61 380 -9, -9, -9, // Decimal 62 - 64 381 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 382 // Letters 'A' through 'N' 383 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 384 // Letters 'O' through 'Z' 385 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 386 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 387 // Letters 'a' through 'm' 388 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 389 // Letters 'n' through 'z' 390 -9, -9, -9, -9, -9 // Decimal 123 - 127 391 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 392 // Decimal 128 - 139 393 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 394 // Decimal 140 - 152 395 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 396 // Decimal 153 - 165 397 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 398 // Decimal 166 - 178 399 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 400 // Decimal 179 - 191 401 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 402 // Decimal 192 - 204 403 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 404 // Decimal 205 - 217 405 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 406 // Decimal 218 - 230 407 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 408 // Decimal 231 - 243 409 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 410 }; 411 412 /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ 413 414 /** 415 * Used in the URL- and Filename-safe dialect described in Section 4 of 416 * RFC3548: 417 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. 418 * Notice that the 419 * last two bytes become "hyphen" and "underscore" instead of "plus" and 420 * "slash." 421 */ 422 private static final byte[] _URL_SAFE_ALPHABET = { 423 (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', 424 (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', 425 (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', 426 (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', 427 (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', 428 (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', 429 (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', 430 (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', 431 (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', 432 (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', 433 (byte) '8', (byte) '9', (byte) '-', (byte) '_' 434 }; 435 436 /** 437 * Used in decoding URL- and Filename-safe dialects of Base64. 438 */ 439 private static final byte[] _URL_SAFE_DECODABET = { 440 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 441 -5, -5, // Whitespace: Tab and Linefeed 442 -9, -9, // Decimal 11 - 12 443 -5, // Whitespace: Carriage Return 444 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 445 // Decimal 14 - 26 446 -9, -9, -9, -9, -9, // Decimal 27 - 31 447 -5, // Whitespace: Space 448 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 449 -9, // Plus sign at decimal 43 450 -9, // Decimal 44 451 62, // Minus sign at decimal 45 452 -9, // Decimal 46 453 -9, // Slash at decimal 47 454 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine 455 -9, -9, -9, // Decimal 58 - 60 456 -1, // Equals sign at decimal 61 457 -9, -9, -9, // Decimal 62 - 64 458 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 459 // Letters 'A' through 'N' 460 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 461 // Letters 'O' through 'Z' 462 -9, -9, -9, -9, // Decimal 91 - 94 463 63, // Underscore at decimal 95 464 -9, // Decimal 96 465 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 466 // Letters 'a' through 'm' 467 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 468 // Letters 'n' through 'z' 469 -9, -9, -9, -9, -9 // Decimal 123 - 127 470 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 471 // Decimal 128 - 139 472 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 473 // Decimal 140 - 152 474 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 475 // Decimal 153 - 165 476 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 477 // Decimal 166 - 178 478 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 479 // Decimal 179 - 191 480 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 481 // Decimal 192 - 204 482 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 483 // Decimal 205 - 217 484 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 485 // Decimal 218 - 230 486 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 487 // Decimal 231 - 243 488 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 489 }; 490 491 /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ 492 493 /** 494 * I don't get the point of this technique, but someone requested it, and 495 * it 496 * is described here: 497 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>. 498 */ 499 private static final byte[] _ORDERED_ALPHABET = { 500 (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', 501 (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', 502 (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', 503 (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', 504 (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', 505 (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', 506 (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', 507 (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', 508 (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', 509 (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', 510 (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' 511 }; 512 513 /** 514 * Used in decoding the "ordered" dialect of Base64. 515 */ 516 private static final byte[] _ORDERED_DECODABET = { 517 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 518 -5, -5, // Whitespace: Tab and Linefeed 519 -9, -9, // Decimal 11 - 12 520 -5, // Whitespace: Carriage Return 521 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 522 // Decimal 14 - 26 523 -9, -9, -9, -9, -9, // Decimal 27 - 31 524 -5, // Whitespace: Space 525 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 526 -9, // Plus sign at decimal 43 527 -9, // Decimal 44 528 0, // Minus sign at decimal 45 529 -9, // Decimal 46 530 -9, // Slash at decimal 47 531 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine 532 -9, -9, -9, // Decimal 58 - 60 533 -1, // Equals sign at decimal 61 534 -9, -9, -9, // Decimal 62 - 64 535 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 536 // Letters 'A' through 'M' 537 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 538 // Letters 'N' through 'Z' 539 -9, -9, -9, -9, // Decimal 91 - 94 540 37, // Underscore at decimal 95 541 -9, // Decimal 96 542 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 543 // Letters 'a' through 'm' 544 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 545 // Letters 'n' through 'z' 546 -9, -9, -9, -9, -9 // Decimal 123 - 127 547 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 548 // Decimal 128 - 139 549 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 550 // Decimal 140 - 152 551 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 552 // Decimal 153 - 165 553 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 554 // Decimal 166 - 178 555 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 556 // Decimal 179 - 191 557 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 558 // Decimal 192 - 204 559 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 560 // Decimal 205 - 217 561 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 562 // Decimal 218 - 230 563 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 564 // Decimal 231 - 243 565 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 566 }; 567 568 /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ 569 570 /** 571 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the 572 * options specified. It's possible, 573 * though silly, to specify ORDERED <b>and</b> URLSAFE in which case one of 574 * them will be picked, though there 575 * is no guarantee as to which one will be picked. 576 */ 577 private static byte[] getAlphabet(final int options) { 578 if ((options & URL_SAFE) == URL_SAFE) { 579 return _URL_SAFE_ALPHABET; 580 } else if ((options & ORDERED) == ORDERED) { 581 return _ORDERED_ALPHABET; 582 } else { 583 return _STANDARD_ALPHABET; 584 } 585 } // end getAlphabet 586 587 /** 588 * Returns one of the _SOMETHING_DECODABET byte arrays depending on the 589 * options specified. It's possible, 590 * though silly, to specify ORDERED and URL_SAFE in which case one of them 591 * will be picked, though there is no 592 * guarantee as to which one will be picked. 593 */ 594 private static byte[] getDecodabet(final int options) { 595 if ((options & URL_SAFE) == URL_SAFE) { 596 return _URL_SAFE_DECODABET; 597 } else if ((options & ORDERED) == ORDERED) { 598 return _ORDERED_DECODABET; 599 } else { 600 return _STANDARD_DECODABET; 601 } 602 } // end getAlphabet 603 604 /** 605 * Defeats instantiation. 606 */ 607 private Base64() { 608 } 609 610 /* ******** E N C O D I N G M E T H O D S ******** */ 611 612 /** 613 * Encodes up to the first three bytes of array <var>threeBytes</var> and 614 * returns a four-byte array in Base64 615 * notation. The actual number of significant bytes in your array is given 616 * by <var>numSigBytes</var>. The 617 * array <var>threeBytes</var> needs only be as big as 618 * <var>numSigBytes</var>. Code can reuse a byte array by 619 * passing a four-byte array as <var>b4</var>. 620 * 621 * @param b4 A reusable byte array to reduce array instantiation 622 * @param threeBytes the array to convert 623 * @param numSigBytes the number of significant bytes in your array 624 * 625 * @return four byte array in Base64 notation. 626 * 627 * @since 1.5.1 628 */ 629 private static byte[] encode3to4(final byte[] b4, final byte[] threeBytes, 630 final int numSigBytes, final int options) { 631 encode3to4(threeBytes, 0, numSigBytes, b4, 0, options); 632 return b4; 633 } // end encode3to4 634 635 /** 636 * <p> 637 * Encodes up to three bytes of the array <var>source</var> and writes the 638 * resulting four Base64 bytes to 639 * <var>destination</var>. The source and destination arrays can be 640 * manipulated anywhere along their length by 641 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method 642 * does not check to make sure your 643 * arrays are large enough to accomodate <var>srcOffset</var> + 3 for the 644 * <var>source</var> array or 645 * <var>destOffset</var> + 4 for the <var>destination</var> array. The 646 * actual number of significant bytes in 647 * your array is given by <var>numSigBytes</var>. 648 * </p> 649 * <p> 650 * This is the lowest level of the encoding methods with all possible 651 * parameters. 652 * </p> 653 * 654 * @param source the array to convert 655 * @param srcOffset the index where conversion begins 656 * @param numSigBytes the number of significant bytes in your array 657 * @param destination the array to hold the conversion 658 * @param destOffset the index where output will be put 659 * 660 * @since 1.3 661 */ 662 private static void encode3to4(final byte[] source, final int srcOffset, 663 final int numSigBytes, 664 final byte[] destination, final int destOffset, 665 final int options) { 666 667 final byte[] alphabet = getAlphabet(options); 668 669 // 1 2 3 670 // 01234567890123456789012345678901 Bit position 671 // --------000000001111111122222222 Array position from threeBytes 672 // --------| || || || | Six bit groups to index alphabet 673 // >>18 >>12 >> 6 >> 0 Right shift necessary 674 // 0x3f 0x3f 0x3f Additional AND 675 676 // Create buffer with zero-padding if there are only one or two 677 // significant bytes passed in the array. 678 // We have to shift left 24 in order to flush out the 1's that appear 679 // when Java treats a value as negative that is cast from a byte to an int. 680 final int inBuff = (numSigBytes > 0? source[srcOffset] << 24 >>> 8 : 0) | 681 (numSigBytes > 1? source[srcOffset + 1] << 24 >>> 16 : 682 0) | 683 (numSigBytes > 2? source[srcOffset + 2] << 24 >>> 24 : 684 0); 685 686 switch (numSigBytes) { 687 case 3: 688 destination[destOffset] = alphabet[inBuff >>> 18]; 689 destination[destOffset + 1] = alphabet[inBuff >>> 12 & 0x3f]; 690 destination[destOffset + 2] = alphabet[inBuff >>> 6 & 0x3f]; 691 destination[destOffset + 3] = alphabet[inBuff & 0x3f]; 692 return; 693 694 case 2: 695 destination[destOffset] = alphabet[inBuff >>> 18]; 696 destination[destOffset + 1] = alphabet[inBuff >>> 12 & 0x3f]; 697 destination[destOffset + 2] = alphabet[inBuff >>> 6 & 0x3f]; 698 destination[destOffset + 3] = EQUALS_SIGN; 699 return; 700 701 case 1: 702 destination[destOffset] = alphabet[inBuff >>> 18]; 703 destination[destOffset + 1] = alphabet[inBuff >>> 12 & 0x3f]; 704 destination[destOffset + 2] = EQUALS_SIGN; 705 destination[destOffset + 3] = EQUALS_SIGN; 706 return; 707 708 default: 709 } // end switch 710 } // end encode3to4 711 712 /** 713 * Performs Base64 encoding on the {@code raw} ByteBuffer, writing it 714 * to the {@code encoded} 715 * ByteBuffer. This is an experimental feature. Currently it does not pass 716 * along any options (such as 717 * {@link #DO_BREAK_LINES} or {@link #GZIP}. 718 * 719 * @param raw input buffer 720 * @param encoded output buffer 721 * 722 * @since 2.3 723 */ 724 public static void encode(final ByteBuffer raw, final ByteBuffer encoded) { 725 final byte[] raw3 = { 0, 0, 0 }; 726 final byte[] enc4 = { 0, 0, 0, 0 }; 727 728 while (raw.hasRemaining()) { 729 final int rem = Math.min(3, raw.remaining()); 730 raw.get(raw3, 0, rem); 731 encode3to4(enc4, raw3, rem, NO_OPTIONS); 732 encoded.put(enc4); 733 } // end input remaining 734 } 735 736 /** 737 * Performs Base64 encoding on the {@code raw} ByteBuffer, writing it 738 * to the {@code encoded} 739 * CharBuffer. This is an experimental feature. Currently it does not pass 740 * along any options (such as 741 * {@link #DO_BREAK_LINES} or {@link #GZIP}. 742 * 743 * @param raw input buffer 744 * @param encoded output buffer 745 * 746 * @since 2.3 747 */ 748 public static void encode(final ByteBuffer raw, final CharBuffer encoded) { 749 final byte[] raw3 = { 0, 0, 0 }; 750 final byte[] enc4 = { 0, 0, 0, 0 }; 751 752 while (raw.hasRemaining()) { 753 final int rem = Math.min(3, raw.remaining()); 754 raw.get(raw3, 0, rem); 755 encode3to4(enc4, raw3, rem, NO_OPTIONS); 756 for (int i = 0; i < 4; i++) { 757 encoded.put((char) (enc4[i] & 0xFF)); 758 } 759 } // end input remaining 760 } 761 762 /** 763 * Serializes an object and returns the Base64-encoded version of that 764 * serialized object. 765 * 766 * <p> 767 * As of v 2.3, if the object cannot be serialized or there is another 768 * error, the method will throw an 769 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it 770 * just returned a null value, but in 771 * retrospect that's a pretty poor way to handle it. 772 * </p> 773 * <p> 774 * The object is not GZip-compressed before being encoded. 775 * 776 * @param serializableObject The object to encode 777 * 778 * @return The Base64-encoded object 779 * 780 * @throws IOException if there is an error 781 * @throws NullPointerException if serializedObject is null 782 * @since 1.4 783 */ 784 public static String encodeObject(final Serializable serializableObject) 785 throws IOException { 786 return encodeObject(serializableObject, NO_OPTIONS); 787 } // end encodeObject 788 789 /** 790 * Serializes an object and returns the Base64-encoded version of that 791 * serialized object. 792 * 793 * <p> 794 * As of v 2.3, if the object cannot be serialized or there is another 795 * error, the method will throw an 796 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it 797 * just returned a null value, but in 798 * retrospect that's a pretty poor way to handle it. 799 * </p> 800 * <p> 801 * The object is not GZip-compressed before being encoded. 802 * <p> 803 * Example options: 804 * 805 * <pre> 806 * GZIP: gzip-compresses object before encoding it. 807 * DO_BREAK_LINES: break lines at 76 characters 808 * </pre> 809 * <p> 810 * Example: {@code encodeObject( myObj, Base64.GZIP )} or 811 * <p> 812 * Example: {@code encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES 813 * )} 814 * 815 * @param serializableObject The object to encode 816 * @param options Specified options 817 * 818 * @return The Base64-encoded object 819 * 820 * @throws IOException if there is an error 821 * @see Base64#GZIP 822 * @see Base64#DO_BREAK_LINES 823 * @since 2.0 824 */ 825 public static String encodeObject(final Serializable serializableObject, 826 final int options) throws IOException { 827 828 if (serializableObject == null) { 829 throw new IllegalArgumentException("Cannot serialize a null object."); 830 } // end if: null 831 832 // Streams 833 ByteArrayOutputStream baos = null; 834 OutputStream b64os = null; 835 GZIPOutputStream gzos = null; 836 ObjectOutputStream oos = null; 837 838 try { 839 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream 840 baos = new ByteArrayOutputStream(); 841 b64os = new InnerOutputStream(baos, ENCODE | options); 842 if ((options & GZIP) != 0) { 843 // Gzip 844 gzos = new GZIPOutputStream(b64os); 845 oos = new ObjectOutputStream(gzos); 846 } else { 847 // Not gzipped 848 oos = new ObjectOutputStream(b64os); 849 } 850 oos.writeObject(serializableObject); 851 } // end try 852 // end catch 853 finally { 854 FileUtils.close(oos); 855 FileUtils.close(gzos); 856 FileUtils.close(b64os); 857 FileUtils.close(baos); 858 } // end finally 859 860 // Return value according to relevant encoding. 861 try { 862 return new String(baos.toByteArray(), PREFERRED_ENCODING); 863 } // end try 864 catch (final UnsupportedEncodingException uue) { 865 // Fall back to some Java default 866 return new String(baos.toByteArray()); 867 } // end catch 868 869 } // end encode 870 871 /** 872 * Encodes a byte array into Base64 notation. Does not GZip-compress data. 873 * 874 * @param source The data to convert 875 * 876 * @return The data in Base64-encoded form 877 * 878 * @throws NullPointerException if source array is null 879 * @since 1.4 880 */ 881 public static String encodeBytes(final byte[] source) { 882 // Since we're not going to have the GZIP encoding turned on, 883 // we're not going to have an java.io.IOException thrown, so 884 // we should not force the user to have to catch it. 885 String encoded = null; 886 try { 887 encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); 888 } catch (final IOException ex) { 889 assert false : ex.getMessage(); 890 } // end catch 891 assert encoded != null; 892 return encoded; 893 } // end encodeBytes 894 895 /** 896 * Encodes a byte array into Base64 notation. 897 * <p> 898 * Example options: 899 * 900 * <pre> 901 * GZIP: gzip-compresses object before encoding it. 902 * DO_BREAK_LINES: break lines at 76 characters 903 * <i>Note: Technically, this makes your encoding non-compliant.</i> 904 * </pre> 905 * <p> 906 * Example: {@code encodeBytes( myData, Base64.GZIP )} or 907 * <p> 908 * Example: {@code encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES 909 * )} 910 * 911 * 912 * <p> 913 * As of v 2.3, if there is an error with the GZIP stream, the method will 914 * throw an java.io.IOException. 915 * <b>This is new to v2.3!</b> In earlier versions, it just returned a null 916 * value, but in retrospect that's a 917 * pretty poor way to handle it. 918 * </p> 919 * 920 * @param source The data to convert 921 * @param options Specified options 922 * 923 * @return The Base64-encoded data as a String 924 * 925 * @throws IOException if there is an error 926 * @throws NullPointerException if source array is null 927 * @see Base64#GZIP 928 * @see Base64#DO_BREAK_LINES 929 * @since 2.0 930 */ 931 public static String encodeBytes(final byte[] source, final int options) 932 throws IOException { 933 return encodeBytes(source, 0, source.length, options); 934 } // end encodeBytes 935 936 /** 937 * Encodes a byte array into Base64 notation. Does not GZip-compress data. 938 * 939 * <p> 940 * As of v 2.3, if there is an error, the method will throw an 941 * java.io.IOException. <b>This is new to 942 * v2.3!</b> In earlier versions, it just returned a null value, but in 943 * retrospect that's a pretty poor way to 944 * handle it. 945 * </p> 946 * 947 * @param source The data to convert 948 * @param off Offset in array where conversion should begin 949 * @param len Length of data to convert 950 * 951 * @return The Base64-encoded data as a String 952 * 953 * @throws NullPointerException if source array is null 954 * @throws IllegalArgumentException if source array, offset, or 955 * length are invalid 956 * @since 1.4 957 */ 958 public static String encodeBytes(final byte[] source, final int off, 959 final int len) { 960 // Since we're not going to have the GZIP encoding turned on, 961 // we're not going to have an java.io.IOException thrown, so 962 // we should not force the user to have to catch it. 963 String encoded = null; 964 try { 965 encoded = encodeBytes(source, off, len, NO_OPTIONS); 966 } catch (final IOException ex) { 967 assert false : ex.getMessage(); 968 } // end catch 969 assert encoded != null; 970 return encoded; 971 } // end encodeBytes 972 973 /** 974 * Encodes a byte array into Base64 notation. 975 * <p> 976 * Example options: 977 * 978 * <pre> 979 * GZIP: gzip-compresses object before encoding it. 980 * DO_BREAK_LINES: break lines at 76 characters 981 * <i>Note: Technically, this makes your encoding non-compliant.</i> 982 * </pre> 983 * <p> 984 * Example: {@code encodeBytes( myData, Base64.GZIP )} or 985 * <p> 986 * Example: {@code encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES 987 * )} 988 * 989 * 990 * <p> 991 * As of v 2.3, if there is an error with the GZIP stream, the method will 992 * throw an java.io.IOException. 993 * <b>This is new to v2.3!</b> In earlier versions, it just returned a null 994 * value, but in retrospect that's a 995 * pretty poor way to handle it. 996 * </p> 997 * 998 * @param source The data to convert 999 * @param off Offset in array where conversion should begin 1000 * @param len Length of data to convert 1001 * @param options Specified options 1002 * 1003 * @return The Base64-encoded data as a String 1004 * 1005 * @throws IOException if there is an error 1006 * @throws NullPointerException if source array is null 1007 * @throws IllegalArgumentException if source array, offset, or 1008 * length are invalid 1009 * @see Base64#GZIP 1010 * @see Base64#DO_BREAK_LINES 1011 * @since 2.0 1012 */ 1013 public static String encodeBytes(final byte[] source, final int off, 1014 final int len, final int options) 1015 throws IOException { 1016 final byte[] encoded = encodeBytesToBytes(source, off, len, options); 1017 1018 // Return value according to relevant encoding. 1019 try { 1020 return new String(encoded, PREFERRED_ENCODING); 1021 } // end try 1022 catch (final UnsupportedEncodingException uue) { 1023 return new String(encoded); 1024 } // end catch 1025 1026 } // end encodeBytes 1027 1028 /** 1029 * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead 1030 * of instantiating a String. This is 1031 * more efficient if you're working with I/O streams and have large data 1032 * sets to encode. 1033 * 1034 * @param source The data to convert 1035 * 1036 * @return The Base64-encoded data as a byte[] (of ASCII characters) 1037 * 1038 * @throws NullPointerException if source array is null 1039 * @since 2.3.1 1040 */ 1041 public static byte[] encodeBytesToBytes(final byte[] source) { 1042 byte[] encoded = null; 1043 try { 1044 encoded = encodeBytesToBytes(source, 0, source.length, NO_OPTIONS); 1045 } catch (final IOException ex) { 1046 assert false : 1047 "IOExceptions only come from GZipping, which is turned off: " + 1048 ex.getMessage(); 1049 } 1050 return encoded; 1051 } 1052 1053 /** 1054 * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte 1055 * array instead of instantiating a 1056 * String. This is more efficient if you're working with I/O streams and 1057 * have large data sets to encode. 1058 * 1059 * @param source The data to convert 1060 * @param off Offset in array where conversion should begin 1061 * @param len Length of data to convert 1062 * @param options Specified options 1063 * 1064 * @return The Base64-encoded data as a String 1065 * 1066 * @throws IOException if there is an error 1067 * @throws NullPointerException if source array is null 1068 * @throws IllegalArgumentException if source array, offset, or 1069 * length are invalid 1070 * @see Base64#GZIP 1071 * @see Base64#DO_BREAK_LINES 1072 * @since 2.3.1 1073 */ 1074 public static byte[] encodeBytesToBytes(final byte[] source, final int off, 1075 final int len, final int options) 1076 throws IOException { 1077 1078 if (source == null) { 1079 throw new IllegalArgumentException("Cannot serialize a null array."); 1080 } // end if: null 1081 1082 if (off < 0) { 1083 throw new IllegalArgumentException("Cannot have negative offset: " + off); 1084 } // end if: off < 0 1085 1086 if (len < 0) { 1087 throw new IllegalArgumentException("Cannot have length offset: " + len); 1088 } // end if: len < 0 1089 1090 if (off + len > source.length) { 1091 throw new IllegalArgumentException(String.format( 1092 "Cannot have offset of %d and length of %d with array of length %d", 1093 off, len, source.length)); 1094 } // end if: off < 0 1095 1096 // Compress? 1097 if ((options & GZIP) != 0) { 1098 ByteArrayOutputStream baos = null; 1099 GZIPOutputStream gzos = null; 1100 InnerOutputStream b64os = null; 1101 1102 try { 1103 // GZip -> Base64 -> ByteArray 1104 baos = new ByteArrayOutputStream(); 1105 b64os = new InnerOutputStream(baos, ENCODE | options); 1106 gzos = new GZIPOutputStream(b64os); 1107 1108 gzos.write(source, off, len); 1109 } // end try 1110 // end catch 1111 finally { 1112 FileUtils.close(gzos); 1113 FileUtils.close(b64os); 1114 FileUtils.close(baos); 1115 } // end finally 1116 1117 return baos.toByteArray(); 1118 } // end if: compress 1119 1120 // Else, don't compress. Better not to use streams at all then. 1121 else { 1122 final boolean breakLines = (options & DO_BREAK_LINES) != 0; 1123 // New lines 1124 // Try to determine more precisely how big the array needs to be. 1125 // If we get it right, we don't have to do an array copy, and 1126 // we save a bunch of memory. 1127 int encLen = (len / 3) * 4 + 1128 (len % 3 > 0? 4 : 0); // Bytes needed for actual encoding 1129 if (breakLines) { 1130 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters 1131 } 1132 final byte[] outBuff = new byte[encLen]; 1133 1134 int d = 0; 1135 int e = 0; 1136 final int len2 = len - 2; 1137 int lineLength = 0; 1138 for (; d < len2; d += 3, e += 4) { 1139 encode3to4(source, d + off, 3, outBuff, e, options); 1140 1141 lineLength += 4; 1142 if (breakLines && lineLength >= MAX_LINE_LENGTH) { 1143 outBuff[e + 4] = NEW_LINE; 1144 e++; 1145 lineLength = 0; 1146 } // end if: end of line 1147 } // end for: each piece of array 1148 1149 if (d < len) { 1150 encode3to4(source, d + off, len - d, outBuff, e, options); 1151 e += 4; 1152 } // end if: some padding needed 1153 1154 // Only resize array if we didn't guess it right. 1155 if (e <= outBuff.length - 1) { 1156 // If breaking lines and the last byte falls right at 1157 // the line length (76 bytes per line), there will be 1158 // one extra byte, and the array will need to be resized. 1159 // Not too bad of an estimate on array size, I'd say. 1160 final byte[] finalOut = new byte[e]; 1161 System.arraycopy(outBuff, 0, finalOut, 0, e); 1162 return finalOut; 1163 } else { 1164 return outBuff; 1165 } 1166 1167 } // end else: don't compress 1168 1169 } // end encodeBytesToBytes 1170 1171 /* ******** D E C O D I N G M E T H O D S ******** */ 1172 1173 /** 1174 * Decodes four bytes from array <var>source</var> and writes the resulting 1175 * bytes (up to three of them) to 1176 * <var>destination</var>. The source and destination arrays can be 1177 * manipulated anywhere along their length by 1178 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method 1179 * does not check to make sure your 1180 * arrays are large enough to accomodate <var>srcOffset</var> + 4 for the 1181 * <var>source</var> array or 1182 * <var>destOffset</var> + 3 for the <var>destination</var> array. This 1183 * method returns the actual number of 1184 * bytes that were converted from the Base64 encoding. 1185 * <p> 1186 * This is the lowest level of the decoding methods with all possible 1187 * parameters. 1188 * </p> 1189 * 1190 * @param source the array to convert 1191 * @param srcOffset the index where conversion begins 1192 * @param destination the array to hold the conversion 1193 * @param destOffset the index where output will be put 1194 * @param options alphabet type is pulled from this (standard, 1195 * url-safe, ordered) 1196 * 1197 * @return the number of decoded bytes converted 1198 * 1199 * @throws NullPointerException if source or destination arrays are 1200 * null 1201 * @throws IllegalArgumentException if srcOffset or destOffset are 1202 * invalid or there is not enough room in the 1203 * array. 1204 * @since 1.3 1205 */ 1206 private static int decode4to3(final byte[] source, final int srcOffset, 1207 final byte[] destination, final int destOffset, 1208 final int options) { 1209 1210 // Lots of error checking and exception throwing 1211 if (source == null) { 1212 throw new IllegalArgumentException("Source array was null."); 1213 } // end if 1214 if (destination == null) { 1215 throw new IllegalArgumentException("Destination array was null."); 1216 } // end if 1217 if (srcOffset < 0 || srcOffset + 3 >= source.length) { 1218 throw new IllegalArgumentException(String.format( 1219 "Source array with length %d cannot have offset of %d and still process four bytes.", 1220 source.length, srcOffset)); 1221 } // end if 1222 if (destOffset < 0 || destOffset + 2 >= destination.length) { 1223 throw new IllegalArgumentException(String.format( 1224 "Destination array with length %d cannot have offset of %d and still store three bytes.", 1225 destination.length, destOffset)); 1226 } // end if 1227 1228 final byte[] decodabet = getDecodabet(options); 1229 1230 // Example: Dk== 1231 if (source[srcOffset + 2] == EQUALS_SIGN) { 1232 final int outBuff = (decodabet[source[srcOffset]] & 0xFF) << 18 | 1233 (decodabet[source[srcOffset + 1]] & 0xFF) << 12; 1234 1235 destination[destOffset] = (byte) (outBuff >>> 16); 1236 return 1; 1237 } 1238 1239 // Example: DkL= 1240 else if (source[srcOffset + 3] == EQUALS_SIGN) { 1241 final int outBuff = (decodabet[source[srcOffset]] & 0xFF) << 18 | 1242 (decodabet[source[srcOffset + 1]] & 0xFF) << 12 | 1243 (decodabet[source[srcOffset + 2]] & 0xFF) << 6; 1244 1245 destination[destOffset] = (byte) (outBuff >>> 16); 1246 destination[destOffset + 1] = (byte) (outBuff >>> 8); 1247 return 2; 1248 } 1249 1250 // Example: DkLE 1251 else { 1252 final int outBuff = (decodabet[source[srcOffset]] & 0xFF) << 18 | 1253 (decodabet[source[srcOffset + 1]] & 0xFF) << 12 | 1254 (decodabet[source[srcOffset + 2]] & 0xFF) << 6 | 1255 decodabet[source[srcOffset + 3]] & 0xFF; 1256 1257 destination[destOffset] = (byte) (outBuff >> 16); 1258 destination[destOffset + 1] = (byte) (outBuff >> 8); 1259 destination[destOffset + 2] = (byte) outBuff; 1260 1261 return 3; 1262 } 1263 } // end decodeToBytes 1264 1265 /** 1266 * Low-level access to decoding ASCII characters in the form of a byte 1267 * array. <strong>Ignores GUNZIP option, 1268 * if it's set.</strong> This is not generally a recommended method, 1269 * although it is used internally as part of 1270 * the decoding process. Special case: if len = 0, an empty array is 1271 * returned. Still, if you need more speed 1272 * and reduced memory footprint (and aren't gzipping), consider this 1273 * method. 1274 * 1275 * @param source The Base64 encoded data 1276 * 1277 * @return decoded data 1278 * 1279 * @since 2.3.1 1280 */ 1281 public static byte[] decode(final byte[] source) throws IOException { 1282 final byte[] decoded; 1283 decoded = decode(source, 0, source.length, NO_OPTIONS); 1284 return decoded; 1285 } 1286 1287 /** 1288 * Low-level access to decoding ASCII characters in the form of a byte 1289 * array. <strong>Ignores GUNZIP option, 1290 * if it's set.</strong> This is not generally a recommended method, 1291 * although it is used internally as part of 1292 * the decoding process. Special case: if len = 0, an empty array is 1293 * returned. Still, if you need more speed 1294 * and reduced memory footprint (and aren't gzipping), consider this 1295 * method. 1296 * 1297 * @param source The Base64 encoded data 1298 * @param off The offset of where to begin decoding 1299 * @param len The length of characters to decode 1300 * @param options Can specify options such as alphabet type to use 1301 * 1302 * @return decoded data 1303 * 1304 * @throws IOException If bogus characters exist in source 1305 * data 1306 * @since 1.3 1307 */ 1308 public static byte[] decode(final byte[] source, final int off, final int len, 1309 final int options) throws IOException { 1310 1311 // Lots of error checking and exception throwing 1312 if (source == null) { 1313 throw new IllegalArgumentException("Cannot decode null source array."); 1314 } // end if 1315 if (off < 0 || off + len > source.length) { 1316 throw new IllegalArgumentException(String.format( 1317 "Source array with length %d cannot have offset of %d and process %d bytes.", 1318 source.length, off, len)); 1319 } // end if 1320 1321 if (len == 0) { 1322 return SingletonUtils.getSingletonByteArray(); 1323 } else if (len < 4) { 1324 throw new IllegalArgumentException( 1325 "Base64-encoded string must have at least four characters, but length specified was " + 1326 len); 1327 } // end if 1328 1329 final byte[] decodabet = getDecodabet(options); 1330 1331 final int len34 = len * 3 / 4; // Estimate on array size 1332 final byte[] outBuff = new byte[len34]; // Upper limit on size of output 1333 int outBuffPosn = 0; // Keep track of where we're writing 1334 1335 final byte[] b4 = 1336 { 0, 0, 0, 0 }; // Four byte buffer from source, eliminating white space 1337 int b4Posn = 0; // Keep track of four byte input buffer 1338 int i; // Source array counter 1339 byte sbiDecode; // Special value from decodabet 1340 1341 for (i = off; i < off + len; i++) { // Loop through source 1342 1343 sbiDecode = decodabet[source[i] & 0xFF]; 1344 1345 // White space, Equals sign, or legit Base64 character 1346 // Note the values such as -5 and -9 in the 1347 // DECODABETs at the top of the file. 1348 if (sbiDecode >= WHITE_SPACE_ENC) { 1349 if (sbiDecode >= EQUALS_SIGN_ENC) { 1350 b4[b4Posn++] = source[i]; // Save non-whitespace 1351 if (b4Posn > 3) { // Time to decode? 1352 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options); 1353 b4Posn = 0; 1354 1355 // If that was the equals sign, break out of 'for' loop 1356 if (source[i] == EQUALS_SIGN) { 1357 break; 1358 } // end if: equals sign 1359 } // end if: quartet built 1360 } // end if: equals sign or better 1361 } // end if: white space, equals sign or better 1362 else { 1363 // There's a bad input character in the Base64 stream. 1364 throw new IOException(String.format( 1365 "Bad Base64 input character decimal %d in array position %d", 1366 source[i] & 0xFF, i)); 1367 } // end else: 1368 } // each input character 1369 1370 final byte[] out = new byte[outBuffPosn]; 1371 System.arraycopy(outBuff, 0, out, 0, outBuffPosn); 1372 return out; 1373 } // end decode 1374 1375 /** 1376 * Decodes data from Base64 notation, automatically detecting 1377 * gzip-compressed data and decompressing it. 1378 * 1379 * @param s the string to decode 1380 * 1381 * @return the decoded data 1382 * 1383 * @throws IOException If there is a problem 1384 * @since 1.4 1385 */ 1386 public static byte[] decode(final String s) throws IOException { 1387 return decode(s, NO_OPTIONS); 1388 } 1389 1390 /** 1391 * Decodes data from Base64 notation, automatically detecting 1392 * gzip-compressed data and decompressing it. 1393 * 1394 * @param s the string to decode 1395 * @param options encode options such as URL_SAFE 1396 * 1397 * @return the decoded data 1398 * 1399 * @throws IOException if there is an error 1400 * @throws NullPointerException if <tt>s</tt> is null 1401 * @since 1.4 1402 */ 1403 public static byte[] decode(final String s, final int options) 1404 throws IOException { 1405 1406 if (s == null) { 1407 throw new IllegalArgumentException("Input string was null."); 1408 } // end if 1409 1410 byte[] bytes; 1411 try { 1412 bytes = s.getBytes(PREFERRED_ENCODING); 1413 } // end try 1414 catch (final UnsupportedEncodingException uee) { 1415 bytes = s.getBytes(); 1416 } // end catch 1417 // </change> 1418 1419 // Decode 1420 bytes = decode(bytes, 0, bytes.length, options); 1421 1422 // Check to see if it's gzip-compressed 1423 // GZIP Magic Two-Byte Number: 0x8b1f (35615) 1424 final boolean dontGunzip = (options & DONT_GUNZIP) != 0; 1425 if (bytes != null && bytes.length >= 4 && !dontGunzip) { 1426 1427 final int head = bytes[0] & 0xff | bytes[1] << 8 & 0xff00; 1428 if (GZIPInputStream.GZIP_MAGIC == head) { 1429 ByteArrayInputStream bais = null; 1430 GZIPInputStream gzis = null; 1431 ByteArrayOutputStream baos = null; 1432 final byte[] buffer = new byte[2048]; 1433 int length; 1434 1435 try { 1436 baos = new ByteArrayOutputStream(); 1437 bais = new ByteArrayInputStream(bytes); 1438 gzis = new GZIPInputStream(bais); 1439 1440 while ((length = gzis.read(buffer)) >= 0) { 1441 baos.write(buffer, 0, length); 1442 } // end while: reading input 1443 1444 // No error? Get new bytes. 1445 bytes = baos.toByteArray(); 1446 1447 } // end try 1448 catch (final IOException e) { 1449 SysErrLogger.FAKE_LOGGER.ignoreLog(e); 1450 // Just return originally-decoded bytes 1451 } // end catch 1452 finally { 1453 FileUtils.close(baos); 1454 FileUtils.close(gzis); 1455 FileUtils.close(bais); 1456 } // end finally 1457 1458 } // end if: gzipped 1459 } // end if: bytes.length >= 2 1460 1461 return bytes; 1462 } // end decode 1463 1464 /** 1465 * Attempts to decode Base64 data and deserialize a Java Object within. 1466 * Returns <tt>null</tt> if there was an 1467 * error. 1468 * 1469 * @param encodedObject The Base64 data to decode 1470 * 1471 * @return The decoded and deserialized object 1472 * 1473 * @throws NullPointerException if encodedObject is null 1474 * @throws IOException if there is a general error 1475 * @throws ClassNotFoundException if the decoded object is of a 1476 * class that cannot be found by the JVM 1477 * @since 1.5 1478 */ 1479 public static Object decodeToObject(final String encodedObject) 1480 throws IOException, ClassNotFoundException { 1481 return decodeToObject(encodedObject, NO_OPTIONS, null); 1482 } 1483 1484 /** 1485 * Attempts to decode Base64 data and deserialize a Java Object within. 1486 * Returns <tt>null</tt> if there was an 1487 * error. If <tt>loader</tt> is not null, it will be the class loader used 1488 * when deserializing. 1489 * 1490 * @param encodedObject The Base64 data to decode 1491 * @param options Various parameters related to decoding 1492 * @param loader Optional class loader to use in deserializing 1493 * classes. 1494 * 1495 * @return The decoded and deserialized object 1496 * 1497 * @throws NullPointerException if encodedObject is null 1498 * @throws IOException if there is a general error 1499 * @throws ClassNotFoundException if the decoded object is of a 1500 * class that cannot be found by the JVM 1501 * @since 2.3.4 1502 */ 1503 public static Object decodeToObject(final String encodedObject, 1504 final int options, 1505 final ClassLoader loader) 1506 throws IOException, ClassNotFoundException { 1507 1508 // Decode and gunzip if necessary 1509 final byte[] objBytes = decode(encodedObject, options); 1510 1511 ByteArrayInputStream bais = null; 1512 ObjectInputStream ois = null; 1513 Object obj; 1514 1515 try { 1516 bais = new ByteArrayInputStream(objBytes); 1517 1518 // If no custom class loader is provided, use Java's builtin OIS. 1519 if (loader == null) { 1520 ois = new ObjectInputStream(bais); 1521 } // end if: no loader provided 1522 1523 // Else make a customized object input stream that uses 1524 // the provided class loader. 1525 else { 1526 ois = new ObjectInputStream(bais) { 1527 @Override 1528 public final Class<?> resolveClass( 1529 final ObjectStreamClass streamClass) 1530 throws IOException, ClassNotFoundException { 1531 final Class<?> c = 1532 Class.forName(streamClass.getName(), false, loader);//NOSONAR 1533 if (c == null) { 1534 return super.resolveClass(streamClass); 1535 } else { 1536 return c; // Class loader knows of this class. 1537 } // end else: not null 1538 } // end resolveClass 1539 }; // end ois 1540 } // end else: no custom class loader 1541 1542 obj = ois.readObject(); 1543 } // end try 1544 // end catch 1545 // end catch 1546 finally { 1547 FileUtils.close(bais); 1548 FileUtils.close(ois); 1549 } // end finally 1550 1551 return obj; 1552 } // end decodeObject 1553 1554 /** 1555 * Convenience method for encoding data to a file. 1556 * 1557 * <p> 1558 * As of v 2.3, if there is a error, the method will throw an 1559 * java.io.IOException. <b>This is new to v2.3!</b> 1560 * In earlier versions, it just returned false, but in retrospect that's a 1561 * pretty poor way to handle it. 1562 * </p> 1563 * 1564 * @param dataToEncode byte array of data to encode in base64 form 1565 * @param filename Filename for saving encoded data 1566 * 1567 * @throws IOException if there is an error 1568 * @throws NullPointerException if dataToEncode is null 1569 * @since 2.1 1570 */ 1571 public static void encodeToFile(final byte[] dataToEncode, 1572 final String filename) throws IOException { 1573 1574 if (dataToEncode == null) { 1575 throw new IllegalArgumentException("Data to encode was null."); 1576 } // end iff 1577 1578 InnerOutputStream bos = null; 1579 try { 1580 bos = new InnerOutputStream(new FileOutputStream(filename), ENCODE); 1581 bos.write(dataToEncode); 1582 } // end try 1583 // end catch: java.io.IOException 1584 finally { 1585 FileUtils.close(bos); 1586 } // end finally 1587 1588 } // end encodeToFile 1589 1590 /** 1591 * Convenience method for decoding data to a file. 1592 * 1593 * <p> 1594 * As of v 2.3, if there is a error, the method will throw an 1595 * java.io.IOException. <b>This is new to v2.3!</b> 1596 * In earlier versions, it just returned false, but in retrospect that's a 1597 * pretty poor way to handle it. 1598 * </p> 1599 * 1600 * @param dataToDecode Base64-encoded data as a string 1601 * @param filename Filename for saving decoded data 1602 * 1603 * @throws IOException if there is an error 1604 * @since 2.1 1605 */ 1606 public static void decodeToFile(final String dataToDecode, 1607 final String filename) throws IOException { 1608 1609 InnerOutputStream bos = null; 1610 try { 1611 bos = new InnerOutputStream(new FileOutputStream(filename), DECODE); 1612 bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); 1613 } // end try 1614 // end catch: java.io.IOException 1615 finally { 1616 FileUtils.close(bos); 1617 } // end finally 1618 1619 } // end decodeToFile 1620 1621 /** 1622 * Convenience method for reading a base64-encoded file and decoding it. 1623 * 1624 * <p> 1625 * As of v 2.3, if there is a error, the method will throw an 1626 * java.io.IOException. <b>This is new to v2.3!</b> 1627 * In earlier versions, it just returned false, but in retrospect that's a 1628 * pretty poor way to handle it. 1629 * </p> 1630 * 1631 * @param filename Filename for reading encoded data 1632 * 1633 * @return decoded byte array 1634 * 1635 * @throws IOException if there is an error 1636 * @since 2.1 1637 */ 1638 public static byte[] decodeFromFile(final String filename) 1639 throws IOException { 1640 1641 byte[] decodedData; 1642 InnerInputStream bis = null; 1643 try { 1644 // Set up some useful variables 1645 final File file = new File(filename); 1646 final byte[] buffer; 1647 int length = 0; 1648 int numBytes; 1649 1650 // Check for size of file 1651 if (file.length() > Integer.MAX_VALUE) { 1652 throw new IOException( 1653 "File is too big for this convenience method (" + file.length() + 1654 " bytes)."); 1655 } // end if: file too big for int index 1656 buffer = new byte[(int) file.length()]; 1657 1658 // Open a stream 1659 bis = new InnerInputStream( 1660 new BufferedInputStream(new FileInputStream(file)), DECODE); 1661 1662 // Read until done 1663 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { 1664 length += numBytes; 1665 } // end while 1666 1667 // Save in a variable to return 1668 decodedData = new byte[length]; 1669 System.arraycopy(buffer, 0, decodedData, 0, length); 1670 1671 } // end try 1672 // end catch: java.io.IOException 1673 finally { 1674 FileUtils.close(bis); 1675 } // end finally 1676 1677 return decodedData; 1678 } // end decodeFromFile 1679 1680 /** 1681 * Convenience method for reading a binary file and base64-encoding it. 1682 * 1683 * <p> 1684 * As of v 2.3, if there is a error, the method will throw an 1685 * java.io.IOException. <b>This is new to v2.3!</b> 1686 * In earlier versions, it just returned false, but in retrospect that's a 1687 * pretty poor way to handle it. 1688 * </p> 1689 * 1690 * @param filename Filename for reading binary data 1691 * 1692 * @return base64-encoded string 1693 * 1694 * @throws IOException if there is an error 1695 * @since 2.1 1696 */ 1697 public static String encodeFromFile(final String filename) 1698 throws IOException { 1699 1700 String encodedData; 1701 InnerInputStream bis = null; 1702 try { 1703 // Set up some useful variables 1704 final File file = new File(filename); 1705 // Check for size of file 1706 if (file.length() > Integer.MAX_VALUE) { 1707 throw new IOException( 1708 "File is too big for this convenience method (" + file.length() + 1709 " bytes)."); 1710 } // end if: file too big for int index 1711 final byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 1712 40)]; // Need max() for math on small 1713 // files (v2.2.1); Need +1 for a 1714 // few corner cases (v2.3.5) 1715 int length = 0; 1716 int numBytes; 1717 1718 // Open a stream 1719 bis = new InnerInputStream( 1720 new BufferedInputStream(new FileInputStream(file)), ENCODE); 1721 1722 // Read until done 1723 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { 1724 length += numBytes; 1725 } // end while 1726 1727 // Save in a variable to return 1728 encodedData = new String(buffer, 0, length, PREFERRED_ENCODING); 1729 1730 } // end try 1731 // end catch: java.io.IOException 1732 finally { 1733 FileUtils.close(bis); 1734 } // end finally 1735 1736 return encodedData; 1737 } // end encodeFromFile 1738 1739 /** 1740 * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>. 1741 * 1742 * @param infile Input file 1743 * @param outfile Output file 1744 * 1745 * @throws IOException if there is an error 1746 * @since 2.2 1747 */ 1748 public static void encodeFileToFile(final String infile, final String outfile) 1749 throws IOException { 1750 1751 final String encoded = encodeFromFile(infile); 1752 OutputStream out = null; 1753 try { 1754 out = new BufferedOutputStream(new FileOutputStream(outfile)); 1755 out.write(encoded.getBytes(PREFERRED_ENCODING)); // Strict, 7-bit output. 1756 } // end try 1757 // end catch 1758 finally { 1759 FileUtils.close(out); 1760 } // end finally 1761 } // end encodeFileToFile 1762 1763 /** 1764 * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>. 1765 * 1766 * @param infile Input file 1767 * @param outfile Output file 1768 * 1769 * @throws IOException if there is an error 1770 * @since 2.2 1771 */ 1772 public static void decodeFileToFile(final String infile, final String outfile) 1773 throws IOException { 1774 1775 final byte[] decoded = decodeFromFile(infile); 1776 OutputStream out = null; 1777 try { 1778 out = new BufferedOutputStream(new FileOutputStream(outfile)); 1779 out.write(decoded); 1780 } // end try 1781 // end catch 1782 finally { 1783 FileUtils.close(out); 1784 } // end finally 1785 } // end decodeFileToFile 1786 1787 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 1788 1789 /** 1790 * A {@link InnerInputStream} will read data from another 1791 * <tt>java.io.InputStream</tt>, given in the 1792 * constructor, and encode/decode to/from Base64 notation on the fly. 1793 * 1794 * @see Base64 1795 * @since 1.3 1796 */ 1797 private static class InnerInputStream extends FilterInputStream { 1798 1799 private final boolean encode; // Encoding or decoding 1800 private int position; // Current position in the buffer 1801 private final byte[] buffer; // Small buffer holding converted data 1802 private final int bufferLength; // Length of buffer (3 or 4) 1803 private int numSigBytes; // Number of meaningful bytes in the buffer 1804 private int lineLength; 1805 private final boolean breakLines; 1806 // Break lines at less than 80 characters 1807 private final int options; // Record options used to create the stream. 1808 private final byte[] decodabet; 1809 // Local copies to avoid extra method calls 1810 1811 /** 1812 * Constructs a {@link InnerInputStream} in DECODE mode. 1813 * 1814 * @param in the <tt>java.io.InputStream</tt> from which to read 1815 * data. 1816 * 1817 * @since 1.3 1818 */ 1819 public InnerInputStream(final InputStream in) { 1820 this(in, DECODE); 1821 } // end constructor 1822 1823 /** 1824 * Constructs a {@link InnerInputStream} in either ENCODE or DECODE 1825 * mode. 1826 * <p> 1827 * Valid options: 1828 * 1829 * <pre> 1830 * ENCODE or DECODE: Encode or Decode as data is read. 1831 * DO_BREAK_LINES: break lines at 76 characters 1832 * (only meaningful when encoding)</i> 1833 * </pre> 1834 * <p> 1835 * Example: {@code new Base64.InputStream( in, Base64.DECODE )} 1836 * 1837 * @param in the <tt>java.io.InputStream</tt> from which to read 1838 * data. 1839 * @param options Specified options 1840 * 1841 * @see Base64#ENCODE 1842 * @see Base64#DECODE 1843 * @see Base64#DO_BREAK_LINES 1844 * @since 2.0 1845 */ 1846 private InnerInputStream(final InputStream in, final int options) { 1847 1848 super(in); 1849 this.options = options; // Record for later 1850 breakLines = (options & DO_BREAK_LINES) > 0; 1851 encode = (options & ENCODE) > 0; 1852 bufferLength = encode? 4 : 3; 1853 final byte[] bsize3 = { 0, 0, 0 }; 1854 final byte[] bsize4 = { 0, 0, 0, 0 }; 1855 buffer = encode? bsize4 : bsize3; 1856 position = -1; 1857 lineLength = 0; 1858 decodabet = getDecodabet(options); 1859 } // end constructor 1860 1861 /** 1862 * Reads enough of the input stream to convert to/from Base64 and 1863 * returns the next byte. 1864 * 1865 * @return next byte 1866 * 1867 * @since 1.3 1868 */ 1869 @Override 1870 public final int read() throws IOException { 1871 1872 // Do we need to get data? 1873 if (position < 0) { 1874 if (encode) { 1875 final byte[] b3 = { 0, 0, 0 }; 1876 int numBinaryBytes = 0; 1877 for (int i = 0; i < 3; i++) { 1878 final int b = in.read(); 1879 1880 // If end of stream, b is -1. 1881 if (b >= 0) { 1882 b3[i] = (byte) b; 1883 numBinaryBytes++; 1884 } else { 1885 break; // out of for loop 1886 } // end else: end of stream 1887 1888 } // end for: each needed input byte 1889 1890 if (numBinaryBytes > 0) { 1891 encode3to4(b3, 0, numBinaryBytes, buffer, 0, options); 1892 position = 0; 1893 numSigBytes = 4; 1894 } // end if: got data 1895 else { 1896 return -1; // Must be end of stream 1897 } // end else 1898 } // end if: encoding 1899 1900 // Else decoding 1901 else { 1902 final byte[] b4 = { 0, 0, 0, 0 }; 1903 int i; 1904 for (i = 0; i < 4; i++) { 1905 // Read four "meaningful" bytes: 1906 int b; 1907 do { 1908 b = in.read(); 1909 } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC); 1910 1911 if (b < 0) { 1912 break; // Reads a -1 if end of stream 1913 } // end if: end of stream 1914 1915 b4[i] = (byte) b; 1916 } // end for: each needed input byte 1917 1918 if (i == 4) { 1919 numSigBytes = decode4to3(b4, 0, buffer, 0, options); 1920 position = 0; 1921 } // end if: got four characters 1922 else if (i == 0) { 1923 return -1; 1924 } // end else if: also padded correctly 1925 else { 1926 // Must have broken out from above. 1927 throw new IOException("Improperly padded Base64 input."); 1928 } // end 1929 1930 } // end else: decode 1931 } // end else: get data 1932 1933 // Got data? 1934 if (position >= 0) { 1935 // End of relevant data? 1936 if ( /* !encode && */position >= numSigBytes) { 1937 return -1; 1938 } // end if: got data 1939 1940 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) { 1941 lineLength = 0; 1942 return '\n'; 1943 } // end if 1944 else { 1945 lineLength++; // This isn't important when decoding 1946 // but throwing an extra "if" seems 1947 // just as wasteful. 1948 1949 final int b = buffer[position++]; 1950 1951 if (position >= bufferLength) { 1952 position = -1; 1953 } // end if: end 1954 1955 return b & 0xFF; // This is how you "cast" a byte that's 1956 // intended to be unsigned. 1957 } // end else 1958 } // end if: position >= 0 1959 1960 // Else error 1961 else { 1962 throw new IOException("Error in Base64 code reading stream."); 1963 } // end else 1964 } // end read 1965 1966 /** 1967 * Calls {@link #read()} repeatedly until the end of stream is reached 1968 * or <var>len</var> bytes are read. 1969 * Returns number of bytes read into array or -1 if end of stream is 1970 * encountered. 1971 * 1972 * @param dest array to hold values 1973 * @param off offset for array 1974 * @param len max number of bytes to read into array 1975 * 1976 * @return bytes read into array or -1 if end of stream is encountered. 1977 * 1978 * @since 1.3 1979 */ 1980 @Override 1981 public final int read(final byte[] dest, final int off, final int len) 1982 throws IOException { 1983 int i; 1984 int b; 1985 for (i = 0; i < len; i++) { 1986 b = read(); 1987 1988 if (b >= 0) { 1989 dest[off + i] = (byte) b; 1990 } else if (i == 0) { 1991 return -1; 1992 } else { 1993 break; // Out of 'for' loop 1994 } // Out of 'for' loop 1995 } // end for: each byte read 1996 return i; 1997 } // end read 1998 1999 } // end inner class InputStream 2000 2001 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ 2002 2003 /** 2004 * A {@link InnerOutputStream} will write data to another 2005 * <tt>java.io.OutputStream</tt>, given in the 2006 * constructor, and encode/decode to/from Base64 notation on the fly. 2007 * 2008 * @see Base64 2009 * @since 1.3 2010 */ 2011 private static class InnerOutputStream extends FilterOutputStream { 2012 2013 private final boolean encode; 2014 private int position; 2015 private byte[] buffer; 2016 private final int bufferLength; 2017 private int lineLength; 2018 private final boolean breakLines; 2019 private final byte[] b4 = { 0, 0, 0, 0 }; // Scratch used in a few places 2020 private boolean suspendEncoding; 2021 private final int options; // Record for later 2022 private final byte[] decodabet; 2023 // Local copies to avoid extra method calls 2024 2025 /** 2026 * Constructs a {@link InnerOutputStream} in ENCODE mode. 2027 * 2028 * @param out the <tt>java.io.OutputStream</tt> to which data 2029 * will be written. 2030 * 2031 * @since 1.3 2032 */ 2033 public InnerOutputStream(final OutputStream out) { 2034 this(out, ENCODE); 2035 } // end constructor 2036 2037 /** 2038 * Constructs a {@link InnerOutputStream} in either ENCODE or DECODE 2039 * mode. 2040 * <p> 2041 * Valid options: 2042 * 2043 * <pre> 2044 * ENCODE or DECODE: Encode or Decode as data is read. 2045 * DO_BREAK_LINES: don't break lines at 76 characters 2046 * (only meaningful when encoding)</i> 2047 * </pre> 2048 * <p> 2049 * Example: {@code new Base64.OutputStream( out, Base64.ENCODE )} 2050 * 2051 * @param out the <tt>java.io.OutputStream</tt> to which data 2052 * will be written. 2053 * @param options Specified options. 2054 * 2055 * @see Base64#ENCODE 2056 * @see Base64#DECODE 2057 * @see Base64#DO_BREAK_LINES 2058 * @since 1.3 2059 */ 2060 private InnerOutputStream(final OutputStream out, final int options) { 2061 super(out); 2062 breakLines = (options & DO_BREAK_LINES) != 0; 2063 encode = (options & ENCODE) != 0; 2064 bufferLength = encode? 3 : 4; 2065 final byte[] bsize3 = { 0, 0, 0 }; 2066 final byte[] bsize4 = { 0, 0, 0, 0 }; 2067 buffer = encode? bsize3 : bsize4; 2068 position = 0; 2069 lineLength = 0; 2070 suspendEncoding = false; 2071 this.options = options; 2072 decodabet = getDecodabet(options); 2073 } // end constructor 2074 2075 /** 2076 * Writes the byte to the output stream after converting to/from Base64 2077 * notation. When encoding, bytes are 2078 * buffered three at a time before the output stream actually gets a 2079 * write() call. When decoding, bytes are 2080 * buffered four at a time. 2081 * 2082 * @param theByte the byte to write 2083 * 2084 * @since 1.3 2085 */ 2086 @Override 2087 public final void write(final int theByte) throws IOException { 2088 // Encoding suspended? 2089 if (suspendEncoding) { 2090 out.write(theByte); 2091 return; 2092 } // end if: supsended 2093 2094 // Encode? 2095 if (encode) { 2096 buffer[position++] = (byte) theByte; 2097 if (position >= bufferLength) { // Enough to encode. 2098 2099 out.write(encode3to4(b4, buffer, bufferLength, options)); 2100 2101 lineLength += 4; 2102 if (breakLines && lineLength >= MAX_LINE_LENGTH) { 2103 out.write(NEW_LINE); 2104 lineLength = 0; 2105 } // end if: end of line 2106 2107 position = 0; 2108 } // end if: enough to output 2109 } // end if: encoding 2110 2111 // Else, Decoding 2112 else { 2113 // Meaningful Base64 character? 2114 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) { 2115 buffer[position++] = (byte) theByte; 2116 if (position >= bufferLength) { // Enough to output. 2117 2118 final int len = decode4to3(buffer, 0, b4, 0, options); 2119 out.write(b4, 0, len); 2120 position = 0; 2121 } // end if: enough to output 2122 } // end if: meaningful base64 character 2123 else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) { 2124 throw new IOException("Invalid character in Base64 data."); 2125 } // end else: not white space either 2126 } // end else: decoding 2127 } // end write 2128 2129 /** 2130 * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are 2131 * written. 2132 * 2133 * @param theBytes array from which to read bytes 2134 * @param off offset for array 2135 * @param len max number of bytes to read into array 2136 * 2137 * @since 1.3 2138 */ 2139 @Override 2140 public final void write(final byte[] theBytes, final int off, final int len) 2141 throws IOException { 2142 // Encoding suspended? 2143 if (suspendEncoding) { 2144 out.write(theBytes, off, len); 2145 return; 2146 } // end if: supsended 2147 2148 for (int i = 0; i < len; i++) { 2149 write(theBytes[off + i]); 2150 } // end for: each byte written 2151 2152 } // end write 2153 2154 /** 2155 * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer 2156 * without closing the stream. 2157 * 2158 * @throws IOException if there's an error. 2159 */ 2160 public final void flushBase64() throws IOException { 2161 if (position > 0) { 2162 if (encode) { 2163 out.write(encode3to4(b4, buffer, position, options)); 2164 position = 0; 2165 } // end if: encoding 2166 else { 2167 throw new IOException("Base64 input not properly padded."); 2168 } // end else: decoding 2169 } // end if: buffer partially full 2170 2171 } // end flush 2172 2173 /** 2174 * Flushes and closes (I think, in the superclass) the stream. 2175 * 2176 * @since 1.3 2177 */ 2178 @Override 2179 public final void close() throws IOException { 2180 // 1. Ensure that pending characters are written 2181 flushBase64(); 2182 2183 // 2. Actually close the stream 2184 // Base class both flushes and closes. 2185 super.close(); 2186 2187 buffer = null; 2188 out = null; 2189 } // end close 2190 2191 /** 2192 * Suspends encoding of the stream. May be helpful if you need to embed 2193 * a piece of base64-encoded data in a 2194 * stream. 2195 * 2196 * @throws IOException if there's an error flushing 2197 * @since 1.5.1 2198 */ 2199 public final void suspendEncoding() throws IOException { 2200 flushBase64(); 2201 suspendEncoding = true; 2202 } // end suspendEncoding 2203 2204 /** 2205 * Resumes encoding of the stream. May be helpful if you need to embed a 2206 * piece of base64-encoded data in a 2207 * stream. 2208 * 2209 * @since 1.5.1 2210 */ 2211 public final void resumeEncoding() { 2212 suspendEncoding = false; 2213 } // end resumeEncoding 2214 2215 } // end inner class OutputStream 2216 2217 } // end class Base64