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