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