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