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