0001: package org.jivesoftware.util;
0002:
0003: /**
0004: * <p>Encodes and decodes to and from Base64 notation.</p>
0005: * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
0006: *
0007: * <p>
0008: * Change Log:
0009: * </p>
0010: * <ul>
0011: * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
0012: * when using very small files (~< 40 bytes).</li>
0013: * <li>v2.2 - Added some helper methods for encoding/decoding directly from
0014: * one file to the next. Also added a main() method to support command line
0015: * encoding/decoding from one file to the next. Also added these Base64 dialects:
0016: * <ol>
0017: * <li>The default is RFC3548 format.</li>
0018: * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
0019: * URL and file name friendly format as described in Section 4 of RFC3548.
0020: * http://www.faqs.org/rfcs/rfc3548.html</li>
0021: * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
0022: * URL and file name friendly format that preserves lexical ordering as described
0023: * in http://www.faqs.org/qa/rfcc-1940.html</li>
0024: * </ol>
0025: * Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
0026: * for contributing the new Base64 dialects.
0027: * </li>
0028: *
0029: * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
0030: * some convenience methods for reading and writing to and from files.</li>
0031: * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
0032: * with other encodings (like EBCDIC).</li>
0033: * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
0034: * encoded data was a single byte.</li>
0035: * <li>v2.0 - I got rid of methods that used booleans to set options.
0036: * Now everything is more consolidated and cleaner. The code now detects
0037: * when data that's being decoded is gzip-compressed and will decompress it
0038: * automatically. Generally things are cleaner. You'll probably have to
0039: * change some method calls that you were making to support the new
0040: * options format (<tt>int</tt>s that you "OR" together).</li>
0041: * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
0042: * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
0043: * Added the ability to "suspend" encoding in the Output Stream so
0044: * you can turn on and off the encoding if you need to embed base64
0045: * data in an otherwise "normal" stream (like an XML file).</li>
0046: * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
0047: * This helps when using GZIP streams.
0048: * Added the ability to GZip-compress objects before encoding them.</li>
0049: * <li>v1.4 - Added helper methods to read/write files.</li>
0050: * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
0051: * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
0052: * where last buffer being read, if not completely full, was not returned.</li>
0053: * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
0054: * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
0055: * </ul>
0056: *
0057: * <p>
0058: * I am placing this code in the Public Domain. Do with it as you will.
0059: * This software comes with no guarantees or warranties but with
0060: * plenty of well-wishing instead!
0061: * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
0062: * periodically to check for updates or to contribute improvements.
0063: * </p>
0064: *
0065: * @author Robert Harder
0066: * @author rob@iharder.net
0067: * @version 2.2.1
0068: */
0069: public class Base64 {
0070:
0071: /* ******** P U B L I C F I E L D S ******** */
0072:
0073: /** No options specified. Value is zero. */
0074: public final static int NO_OPTIONS = 0;
0075:
0076: /** Specify encoding. */
0077: public final static int ENCODE = 1;
0078:
0079: /** Specify decoding. */
0080: public final static int DECODE = 0;
0081:
0082: /** Specify that data should be gzip-compressed. */
0083: public final static int GZIP = 2;
0084:
0085: /** Don't break lines when encoding (violates strict Base64 specification) */
0086: public final static int DONT_BREAK_LINES = 8;
0087:
0088: /**
0089: * Encode using Base64-like encoding that is URL- and Filename-safe as described
0090: * in Section 4 of RFC3548:
0091: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
0092: * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
0093: * or at the very least should not be called Base64 without also specifying that is
0094: * was encoded using the URL- and Filename-safe dialect.
0095: */
0096: public final static int URL_SAFE = 16;
0097:
0098: /**
0099: * Encode using the special "ordered" dialect of Base64 described here:
0100: * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
0101: */
0102: public final static int ORDERED = 32;
0103:
0104: /* ******** P R I V A T E F I E L D S ******** */
0105:
0106: /** Maximum line length (76) of Base64 output. */
0107: private final static int MAX_LINE_LENGTH = 76;
0108:
0109: /** The equals sign (=) as a byte. */
0110: private final static byte EQUALS_SIGN = (byte) '=';
0111:
0112: /** The new line character (\n) as a byte. */
0113: private final static byte NEW_LINE = (byte) '\n';
0114:
0115: /** Preferred encoding. */
0116: private final static String PREFERRED_ENCODING = "UTF-8";
0117:
0118: // I think I end up not using the BAD_ENCODING indicator.
0119: //private final static byte BAD_ENCODING = -9; // Indicates error in encoding
0120: private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
0121: private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
0122:
0123: /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
0124:
0125: /** The 64 valid Base64 values. */
0126: //private final static byte[] ALPHABET;
0127: /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
0128: private final static byte[] _STANDARD_ALPHABET = { (byte) 'A',
0129: (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
0130: (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
0131: (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
0132: (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
0133: (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
0134: (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
0135: (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
0136: (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
0137: (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
0138: (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
0139: (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
0140: (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
0141: (byte) '9', (byte) '+', (byte) '/' };
0142:
0143: /**
0144: * Translates a Base64 value to either its 6-bit reconstruction value
0145: * or a negative number indicating some other meaning.
0146: **/
0147: private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9,
0148: -9, -9, -9, -9, -9, // Decimal 0 - 8
0149: -5, -5, // Whitespace: Tab and Linefeed
0150: -9, -9, // Decimal 11 - 12
0151: -5, // Whitespace: Carriage Return
0152: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
0153: -9, -9, -9, -9, -9, // Decimal 27 - 31
0154: -5, // Whitespace: Space
0155: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
0156: 62, // Plus sign at decimal 43
0157: -9, -9, -9, // Decimal 44 - 46
0158: 63, // Slash at decimal 47
0159: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
0160: -9, -9, -9, // Decimal 58 - 60
0161: -1, // Equals sign at decimal 61
0162: -9, -9, -9, // Decimal 62 - 64
0163: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
0164: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
0165: -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
0166: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
0167: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
0168: -9, -9, -9, -9 // Decimal 123 - 126
0169: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
0170: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
0171: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
0172: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
0173: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
0174: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
0175: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
0176: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
0177: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
0178: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
0179: };
0180:
0181: /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
0182:
0183: /**
0184: * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
0185: * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
0186: * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
0187: */
0188: private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A',
0189: (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
0190: (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
0191: (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
0192: (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
0193: (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
0194: (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
0195: (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
0196: (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
0197: (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
0198: (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
0199: (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
0200: (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
0201: (byte) '9', (byte) '-', (byte) '_' };
0202:
0203: /**
0204: * Used in decoding URL- and Filename-safe dialects of Base64.
0205: */
0206: private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9,
0207: -9, -9, -9, -9, -9, // Decimal 0 - 8
0208: -5, -5, // Whitespace: Tab and Linefeed
0209: -9, -9, // Decimal 11 - 12
0210: -5, // Whitespace: Carriage Return
0211: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
0212: -9, -9, -9, -9, -9, // Decimal 27 - 31
0213: -5, // Whitespace: Space
0214: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
0215: -9, // Plus sign at decimal 43
0216: -9, // Decimal 44
0217: 62, // Minus sign at decimal 45
0218: -9, // Decimal 46
0219: -9, // Slash at decimal 47
0220: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
0221: -9, -9, -9, // Decimal 58 - 60
0222: -1, // Equals sign at decimal 61
0223: -9, -9, -9, // Decimal 62 - 64
0224: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
0225: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
0226: -9, -9, -9, -9, // Decimal 91 - 94
0227: 63, // Underscore at decimal 95
0228: -9, // Decimal 96
0229: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
0230: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
0231: -9, -9, -9, -9 // Decimal 123 - 126
0232: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
0233: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
0234: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
0235: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
0236: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
0237: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
0238: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
0239: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
0240: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
0241: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
0242: };
0243:
0244: /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
0245:
0246: /**
0247: * I don't get the point of this technique, but it is described here:
0248: * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
0249: */
0250: private final static byte[] _ORDERED_ALPHABET = { (byte) '-',
0251: (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
0252: (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
0253: (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
0254: (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
0255: (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
0256: (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
0257: (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
0258: (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c',
0259: (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h',
0260: (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm',
0261: (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
0262: (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w',
0263: (byte) 'x', (byte) 'y', (byte) 'z' };
0264:
0265: /**
0266: * Used in decoding the "ordered" dialect of Base64.
0267: */
0268: private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9,
0269: -9, -9, -9, -9, -9, // Decimal 0 - 8
0270: -5, -5, // Whitespace: Tab and Linefeed
0271: -9, -9, // Decimal 11 - 12
0272: -5, // Whitespace: Carriage Return
0273: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
0274: -9, -9, -9, -9, -9, // Decimal 27 - 31
0275: -5, // Whitespace: Space
0276: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
0277: -9, // Plus sign at decimal 43
0278: -9, // Decimal 44
0279: 0, // Minus sign at decimal 45
0280: -9, // Decimal 46
0281: -9, // Slash at decimal 47
0282: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
0283: -9, -9, -9, // Decimal 58 - 60
0284: -1, // Equals sign at decimal 61
0285: -9, -9, -9, // Decimal 62 - 64
0286: 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M'
0287: 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z'
0288: -9, -9, -9, -9, // Decimal 91 - 94
0289: 37, // Underscore at decimal 95
0290: -9, // Decimal 96
0291: 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm'
0292: 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z'
0293: -9, -9, -9, -9 // Decimal 123 - 126
0294: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
0295: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
0296: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
0297: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
0298: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
0299: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
0300: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
0301: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
0302: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
0303: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
0304: };
0305:
0306: /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
0307:
0308: /**
0309: * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
0310: * the options specified.
0311: * It's possible, though silly, to specify ORDERED and URLSAFE
0312: * in which case one of them will be picked, though there is
0313: * no guarantee as to which one will be picked.
0314: */
0315: private final static byte[] getAlphabet(int options) {
0316: if ((options & URL_SAFE) == URL_SAFE)
0317: return _URL_SAFE_ALPHABET;
0318: else if ((options & ORDERED) == ORDERED)
0319: return _ORDERED_ALPHABET;
0320: else
0321: return _STANDARD_ALPHABET;
0322:
0323: } // end getAlphabet
0324:
0325: /**
0326: * Returns one of the _SOMETHING_DECODABET byte arrays depending on
0327: * the options specified.
0328: * It's possible, though silly, to specify ORDERED and URL_SAFE
0329: * in which case one of them will be picked, though there is
0330: * no guarantee as to which one will be picked.
0331: */
0332: private final static byte[] getDecodabet(int options) {
0333: if ((options & URL_SAFE) == URL_SAFE)
0334: return _URL_SAFE_DECODABET;
0335: else if ((options & ORDERED) == ORDERED)
0336: return _ORDERED_DECODABET;
0337: else
0338: return _STANDARD_DECODABET;
0339:
0340: } // end getAlphabet
0341:
0342: /** Defeats instantiation. */
0343: private Base64() {
0344: }
0345:
0346: /**
0347: * Encodes or decodes two files from the command line;
0348: * <strong>feel free to delete this method (in fact you probably should)
0349: * if you're embedding this code into a larger program.</strong>
0350: */
0351: public final static void main(String[] args) {
0352: if (args.length < 3) {
0353: usage("Not enough arguments.");
0354: } // end if: args.length < 3
0355: else {
0356: String flag = args[0];
0357: String infile = args[1];
0358: String outfile = args[2];
0359: if (flag.equals("-e")) {
0360: Base64.encodeFileToFile(infile, outfile);
0361: } // end if: encode
0362: else if (flag.equals("-d")) {
0363: Base64.decodeFileToFile(infile, outfile);
0364: } // end else if: decode
0365: else {
0366: usage("Unknown flag: " + flag);
0367: } // end else
0368: } // end else
0369: } // end main
0370:
0371: /**
0372: * Prints command line usage.
0373: *
0374: * @param msg A message to include with usage info.
0375: */
0376: private final static void usage(String msg) {
0377: System.err.println(msg);
0378: System.err
0379: .println("Usage: java Base64 -e|-d inputfile outputfile");
0380: } // end usage
0381:
0382: /* ******** E N C O D I N G M E T H O D S ******** */
0383:
0384: /**
0385: * Encodes up to the first three bytes of array <var>threeBytes</var>
0386: * and returns a four-byte array in Base64 notation.
0387: * The actual number of significant bytes in your array is
0388: * given by <var>numSigBytes</var>.
0389: * The array <var>threeBytes</var> needs only be as big as
0390: * <var>numSigBytes</var>.
0391: * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
0392: *
0393: * @param b4 A reusable byte array to reduce array instantiation
0394: * @param threeBytes the array to convert
0395: * @param numSigBytes the number of significant bytes in your array
0396: * @return four byte array in Base64 notation.
0397: * @since 1.5.1
0398: */
0399: private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
0400: int numSigBytes, int options) {
0401: encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
0402: return b4;
0403: } // end encode3to4
0404:
0405: /**
0406: * <p>Encodes up to three bytes of the array <var>source</var>
0407: * and writes the resulting four Base64 bytes to <var>destination</var>.
0408: * The source and destination arrays can be manipulated
0409: * anywhere along their length by specifying
0410: * <var>srcOffset</var> and <var>destOffset</var>.
0411: * This method does not check to make sure your arrays
0412: * are large enough to accomodate <var>srcOffset</var> + 3 for
0413: * the <var>source</var> array or <var>destOffset</var> + 4 for
0414: * the <var>destination</var> array.
0415: * The actual number of significant bytes in your array is
0416: * given by <var>numSigBytes</var>.</p>
0417: * <p>This is the lowest level of the encoding methods with
0418: * all possible parameters.</p>
0419: *
0420: * @param source the array to convert
0421: * @param srcOffset the index where conversion begins
0422: * @param numSigBytes the number of significant bytes in your array
0423: * @param destination the array to hold the conversion
0424: * @param destOffset the index where output will be put
0425: * @return the <var>destination</var> array
0426: * @since 1.3
0427: */
0428: private static byte[] encode3to4(byte[] source, int srcOffset,
0429: int numSigBytes, byte[] destination, int destOffset,
0430: int options) {
0431: byte[] ALPHABET = getAlphabet(options);
0432:
0433: // 1 2 3
0434: // 01234567890123456789012345678901 Bit position
0435: // --------000000001111111122222222 Array position from threeBytes
0436: // --------| || || || | Six bit groups to index ALPHABET
0437: // >>18 >>12 >> 6 >> 0 Right shift necessary
0438: // 0x3f 0x3f 0x3f Additional AND
0439:
0440: // Create buffer with zero-padding if there are only one or two
0441: // significant bytes passed in the array.
0442: // We have to shift left 24 in order to flush out the 1's that appear
0443: // when Java treats a value as negative that is cast from a byte to an int.
0444: int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8)
0445: : 0)
0446: | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16)
0447: : 0)
0448: | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24)
0449: : 0);
0450:
0451: switch (numSigBytes) {
0452: case 3:
0453: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0454: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0455: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0456: destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
0457: return destination;
0458:
0459: case 2:
0460: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0461: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0462: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0463: destination[destOffset + 3] = EQUALS_SIGN;
0464: return destination;
0465:
0466: case 1:
0467: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0468: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0469: destination[destOffset + 2] = EQUALS_SIGN;
0470: destination[destOffset + 3] = EQUALS_SIGN;
0471: return destination;
0472:
0473: default:
0474: return destination;
0475: } // end switch
0476: } // end encode3to4
0477:
0478: /**
0479: * Serializes an object and returns the Base64-encoded
0480: * version of that serialized object. If the object
0481: * cannot be serialized or there is another error,
0482: * the method will return <tt>null</tt>.
0483: * The object is not GZip-compressed before being encoded.
0484: *
0485: * @param serializableObject The object to encode
0486: * @return The Base64-encoded object
0487: * @since 1.4
0488: */
0489: public static String encodeObject(
0490: java.io.Serializable serializableObject) {
0491: return encodeObject(serializableObject, NO_OPTIONS);
0492: } // end encodeObject
0493:
0494: /**
0495: * Serializes an object and returns the Base64-encoded
0496: * version of that serialized object. If the object
0497: * cannot be serialized or there is another error,
0498: * the method will return <tt>null</tt>.
0499: * <p>
0500: * Valid options:<pre>
0501: * GZIP: gzip-compresses object before encoding it.
0502: * DONT_BREAK_LINES: don't break lines at 76 characters
0503: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0504: * </pre>
0505: * <p>
0506: * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
0507: * <p>
0508: * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0509: *
0510: * @param serializableObject The object to encode
0511: * @param options Specified options
0512: * @return The Base64-encoded object
0513: * @see Base64#GZIP
0514: * @see Base64#DONT_BREAK_LINES
0515: * @since 2.0
0516: */
0517: public static String encodeObject(
0518: java.io.Serializable serializableObject, int options) {
0519: // Streams
0520: java.io.ByteArrayOutputStream baos = null;
0521: java.io.OutputStream b64os = null;
0522: java.io.ObjectOutputStream oos = null;
0523: java.util.zip.GZIPOutputStream gzos = null;
0524:
0525: // Isolate options
0526: int gzip = (options & GZIP);
0527: int dontBreakLines = (options & DONT_BREAK_LINES);
0528:
0529: try {
0530: // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
0531: baos = new java.io.ByteArrayOutputStream();
0532: b64os = new Base64.OutputStream(baos, ENCODE | options);
0533:
0534: // GZip?
0535: if (gzip == GZIP) {
0536: gzos = new java.util.zip.GZIPOutputStream(b64os);
0537: oos = new java.io.ObjectOutputStream(gzos);
0538: } // end if: gzip
0539: else
0540: oos = new java.io.ObjectOutputStream(b64os);
0541:
0542: oos.writeObject(serializableObject);
0543: } // end try
0544: catch (java.io.IOException e) {
0545: e.printStackTrace();
0546: return null;
0547: } // end catch
0548: finally {
0549: try {
0550: oos.close();
0551: } catch (Exception e) {
0552: }
0553: try {
0554: gzos.close();
0555: } catch (Exception e) {
0556: }
0557: try {
0558: b64os.close();
0559: } catch (Exception e) {
0560: }
0561: try {
0562: baos.close();
0563: } catch (Exception e) {
0564: }
0565: } // end finally
0566:
0567: // Return value according to relevant encoding.
0568: try {
0569: return new String(baos.toByteArray(), PREFERRED_ENCODING);
0570: } // end try
0571: catch (java.io.UnsupportedEncodingException uue) {
0572: return new String(baos.toByteArray());
0573: } // end catch
0574:
0575: } // end encode
0576:
0577: /**
0578: * Encodes a byte array into Base64 notation.
0579: * Does not GZip-compress data.
0580: *
0581: * @param source The data to convert
0582: * @since 1.4
0583: */
0584: public static String encodeBytes(byte[] source) {
0585: return encodeBytes(source, 0, source.length, NO_OPTIONS);
0586: } // end encodeBytes
0587:
0588: /**
0589: * Encodes a byte array into Base64 notation.
0590: * <p>
0591: * Valid options:<pre>
0592: * GZIP: gzip-compresses object before encoding it.
0593: * DONT_BREAK_LINES: don't break lines at 76 characters
0594: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0595: * </pre>
0596: * <p>
0597: * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
0598: * <p>
0599: * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0600: *
0601: *
0602: * @param source The data to convert
0603: * @param options Specified options
0604: * @see Base64#GZIP
0605: * @see Base64#DONT_BREAK_LINES
0606: * @since 2.0
0607: */
0608: public static String encodeBytes(byte[] source, int options) {
0609: return encodeBytes(source, 0, source.length, options);
0610: } // end encodeBytes
0611:
0612: /**
0613: * Encodes a byte array into Base64 notation.
0614: * Does not GZip-compress data.
0615: *
0616: * @param source The data to convert
0617: * @param off Offset in array where conversion should begin
0618: * @param len Length of data to convert
0619: * @since 1.4
0620: */
0621: public static String encodeBytes(byte[] source, int off, int len) {
0622: return encodeBytes(source, off, len, NO_OPTIONS);
0623: } // end encodeBytes
0624:
0625: /**
0626: * Encodes a byte array into Base64 notation.
0627: * <p>
0628: * Valid options:<pre>
0629: * GZIP: gzip-compresses object before encoding it.
0630: * DONT_BREAK_LINES: don't break lines at 76 characters
0631: * <i>Note: Technically, this makes your encoding non-compliant.</i>
0632: * </pre>
0633: * <p>
0634: * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
0635: * <p>
0636: * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
0637: *
0638: *
0639: * @param source The data to convert
0640: * @param off Offset in array where conversion should begin
0641: * @param len Length of data to convert
0642: * @param options Specified options; alphabet type is pulled from this (standard, url-safe, ordered)
0643: * @see Base64#GZIP
0644: * @see Base64#DONT_BREAK_LINES
0645: * @since 2.0
0646: */
0647: public static String encodeBytes(byte[] source, int off, int len,
0648: int options) {
0649: // Isolate options
0650: int dontBreakLines = (options & DONT_BREAK_LINES);
0651: int gzip = (options & GZIP);
0652:
0653: // Compress?
0654: if (gzip == GZIP) {
0655: java.io.ByteArrayOutputStream baos = null;
0656: java.util.zip.GZIPOutputStream gzos = null;
0657: Base64.OutputStream b64os = null;
0658:
0659: try {
0660: // GZip -> Base64 -> ByteArray
0661: baos = new java.io.ByteArrayOutputStream();
0662: b64os = new Base64.OutputStream(baos, ENCODE | options);
0663: gzos = new java.util.zip.GZIPOutputStream(b64os);
0664:
0665: gzos.write(source, off, len);
0666: gzos.close();
0667: } // end try
0668: catch (java.io.IOException e) {
0669: e.printStackTrace();
0670: return null;
0671: } // end catch
0672: finally {
0673: try {
0674: gzos.close();
0675: } catch (Exception e) {
0676: }
0677: try {
0678: b64os.close();
0679: } catch (Exception e) {
0680: }
0681: try {
0682: baos.close();
0683: } catch (Exception e) {
0684: }
0685: } // end finally
0686:
0687: // Return value according to relevant encoding.
0688: try {
0689: return new String(baos.toByteArray(),
0690: PREFERRED_ENCODING);
0691: } // end try
0692: catch (java.io.UnsupportedEncodingException uue) {
0693: return new String(baos.toByteArray());
0694: } // end catch
0695: } // end if: compress
0696:
0697: // Else, don't compress. Better not to use streams at all then.
0698: else {
0699: // Convert option to boolean in way that code likes it.
0700: boolean breakLines = dontBreakLines == 0;
0701:
0702: int len43 = len * 4 / 3;
0703: byte[] outBuff = new byte[(len43) // Main 4:3
0704: + ((len % 3) > 0 ? 4 : 0) // Account for padding
0705: + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
0706: int d = 0;
0707: int e = 0;
0708: int len2 = len - 2;
0709: int lineLength = 0;
0710: for (; d < len2; d += 3, e += 4) {
0711: encode3to4(source, d + off, 3, outBuff, e, options);
0712:
0713: lineLength += 4;
0714: if (breakLines && lineLength == MAX_LINE_LENGTH) {
0715: outBuff[e + 4] = NEW_LINE;
0716: e++;
0717: lineLength = 0;
0718: } // end if: end of line
0719: } // en dfor: each piece of array
0720:
0721: if (d < len) {
0722: encode3to4(source, d + off, len - d, outBuff, e,
0723: options);
0724: e += 4;
0725: } // end if: some padding needed
0726:
0727: // Return value according to relevant encoding.
0728: try {
0729: return new String(outBuff, 0, e, PREFERRED_ENCODING);
0730: } // end try
0731: catch (java.io.UnsupportedEncodingException uue) {
0732: return new String(outBuff, 0, e);
0733: } // end catch
0734:
0735: } // end else: don't compress
0736:
0737: } // end encodeBytes
0738:
0739: /* ******** D E C O D I N G M E T H O D S ******** */
0740:
0741: /**
0742: * Decodes four bytes from array <var>source</var>
0743: * and writes the resulting bytes (up to three of them)
0744: * to <var>destination</var>.
0745: * The source and destination arrays can be manipulated
0746: * anywhere along their length by specifying
0747: * <var>srcOffset</var> and <var>destOffset</var>.
0748: * This method does not check to make sure your arrays
0749: * are large enough to accomodate <var>srcOffset</var> + 4 for
0750: * the <var>source</var> array or <var>destOffset</var> + 3 for
0751: * the <var>destination</var> array.
0752: * This method returns the actual number of bytes that
0753: * were converted from the Base64 encoding.
0754: * <p>This is the lowest level of the decoding methods with
0755: * all possible parameters.</p>
0756: *
0757: *
0758: * @param source the array to convert
0759: * @param srcOffset the index where conversion begins
0760: * @param destination the array to hold the conversion
0761: * @param destOffset the index where output will be put
0762: * @param options alphabet type is pulled from this (standard, url-safe, ordered)
0763: * @return the number of decoded bytes converted
0764: * @since 1.3
0765: */
0766: private static int decode4to3(byte[] source, int srcOffset,
0767: byte[] destination, int destOffset, int options) {
0768: byte[] DECODABET = getDecodabet(options);
0769:
0770: // Example: Dk==
0771: if (source[srcOffset + 2] == EQUALS_SIGN) {
0772: // Two ways to do the same thing. Don't know which way I like best.
0773: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0774: // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
0775: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0776: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
0777:
0778: destination[destOffset] = (byte) (outBuff >>> 16);
0779: return 1;
0780: }
0781:
0782: // Example: DkL=
0783: else if (source[srcOffset + 3] == EQUALS_SIGN) {
0784: // Two ways to do the same thing. Don't know which way I like best.
0785: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0786: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0787: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
0788: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0789: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0790: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
0791:
0792: destination[destOffset] = (byte) (outBuff >>> 16);
0793: destination[destOffset + 1] = (byte) (outBuff >>> 8);
0794: return 2;
0795: }
0796:
0797: // Example: DkLE
0798: else {
0799: try {
0800: // Two ways to do the same thing. Don't know which way I like best.
0801: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0802: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0803: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
0804: // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
0805: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0806: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0807: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
0808: | ((DECODABET[source[srcOffset + 3]] & 0xFF));
0809:
0810: destination[destOffset] = (byte) (outBuff >> 16);
0811: destination[destOffset + 1] = (byte) (outBuff >> 8);
0812: destination[destOffset + 2] = (byte) (outBuff);
0813:
0814: return 3;
0815: } catch (Exception e) {
0816: System.out.println("" + source[srcOffset] + ": "
0817: + (DECODABET[source[srcOffset]]));
0818: System.out.println("" + source[srcOffset + 1] + ": "
0819: + (DECODABET[source[srcOffset + 1]]));
0820: System.out.println("" + source[srcOffset + 2] + ": "
0821: + (DECODABET[source[srcOffset + 2]]));
0822: System.out.println("" + source[srcOffset + 3] + ": "
0823: + (DECODABET[source[srcOffset + 3]]));
0824: return -1;
0825: } // end catch
0826: }
0827: } // end decodeToBytes
0828:
0829: /**
0830: * Very low-level access to decoding ASCII characters in
0831: * the form of a byte array. Does not support automatically
0832: * gunzipping or any other "fancy" features.
0833: *
0834: * @param source The Base64 encoded data
0835: * @param off The offset of where to begin decoding
0836: * @param len The length of characters to decode
0837: * @return decoded data
0838: * @since 1.3
0839: */
0840: public static byte[] decode(byte[] source, int off, int len,
0841: int options) {
0842: byte[] DECODABET = getDecodabet(options);
0843:
0844: int len34 = len * 3 / 4;
0845: byte[] outBuff = new byte[len34]; // Upper limit on size of output
0846: int outBuffPosn = 0;
0847:
0848: byte[] b4 = new byte[4];
0849: int b4Posn = 0;
0850: int i = 0;
0851: byte sbiCrop = 0;
0852: byte sbiDecode = 0;
0853: for (i = off; i < off + len; i++) {
0854: sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
0855: sbiDecode = DECODABET[sbiCrop];
0856:
0857: if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
0858: {
0859: if (sbiDecode >= EQUALS_SIGN_ENC) {
0860: b4[b4Posn++] = sbiCrop;
0861: if (b4Posn > 3) {
0862: outBuffPosn += decode4to3(b4, 0, outBuff,
0863: outBuffPosn, options);
0864: b4Posn = 0;
0865:
0866: // If that was the equals sign, break out of 'for' loop
0867: if (sbiCrop == EQUALS_SIGN)
0868: break;
0869: } // end if: quartet built
0870:
0871: } // end if: equals sign or better
0872:
0873: } // end if: white space, equals sign or better
0874: else {
0875: System.err.println("Bad Base64 input character at " + i
0876: + ": " + source[i] + "(decimal)");
0877: return null;
0878: } // end else:
0879: } // each input character
0880:
0881: byte[] out = new byte[outBuffPosn];
0882: System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
0883: return out;
0884: } // end decode
0885:
0886: /**
0887: * Decodes data from Base64 notation, automatically
0888: * detecting gzip-compressed data and decompressing it.
0889: *
0890: * @param s the string to decode
0891: * @return the decoded data
0892: * @since 1.4
0893: */
0894: public static byte[] decode(String s) {
0895: return decode(s, NO_OPTIONS);
0896: }
0897:
0898: /**
0899: * Decodes data from Base64 notation, automatically
0900: * detecting gzip-compressed data and decompressing it.
0901: *
0902: * @param s the string to decode
0903: * @param options encode options such as URL_SAFE
0904: * @return the decoded data
0905: * @since 1.4
0906: */
0907: public static byte[] decode(String s, int options) {
0908: byte[] bytes;
0909: try {
0910: bytes = s.getBytes(PREFERRED_ENCODING);
0911: } // end try
0912: catch (java.io.UnsupportedEncodingException uee) {
0913: bytes = s.getBytes();
0914: } // end catch
0915: //</change>
0916:
0917: // Decode
0918: bytes = decode(bytes, 0, bytes.length, options);
0919:
0920: // Check to see if it's gzip-compressed
0921: // GZIP Magic Two-Byte Number: 0x8b1f (35615)
0922: if (bytes != null && bytes.length >= 4) {
0923:
0924: int head = ((int) bytes[0] & 0xff)
0925: | ((bytes[1] << 8) & 0xff00);
0926: if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
0927: java.io.ByteArrayInputStream bais = null;
0928: java.util.zip.GZIPInputStream gzis = null;
0929: java.io.ByteArrayOutputStream baos = null;
0930: byte[] buffer = new byte[2048];
0931: int length = 0;
0932:
0933: try {
0934: baos = new java.io.ByteArrayOutputStream();
0935: bais = new java.io.ByteArrayInputStream(bytes);
0936: gzis = new java.util.zip.GZIPInputStream(bais);
0937:
0938: while ((length = gzis.read(buffer)) >= 0) {
0939: baos.write(buffer, 0, length);
0940: } // end while: reading input
0941:
0942: // No error? Get new bytes.
0943: bytes = baos.toByteArray();
0944:
0945: } // end try
0946: catch (java.io.IOException e) {
0947: // Just return originally-decoded bytes
0948: } // end catch
0949: finally {
0950: try {
0951: baos.close();
0952: } catch (Exception e) {
0953: }
0954: try {
0955: gzis.close();
0956: } catch (Exception e) {
0957: }
0958: try {
0959: bais.close();
0960: } catch (Exception e) {
0961: }
0962: } // end finally
0963:
0964: } // end if: gzipped
0965: } // end if: bytes.length >= 2
0966:
0967: return bytes;
0968: } // end decode
0969:
0970: /**
0971: * Attempts to decode Base64 data and deserialize a Java
0972: * Object within. Returns <tt>null</tt> if there was an error.
0973: *
0974: * @param encodedObject The Base64 data to decode
0975: * @return The decoded and deserialized object
0976: * @since 1.5
0977: */
0978: public static Object decodeToObject(String encodedObject) {
0979: // Decode and gunzip if necessary
0980: byte[] objBytes = decode(encodedObject);
0981:
0982: java.io.ByteArrayInputStream bais = null;
0983: java.io.ObjectInputStream ois = null;
0984: Object obj = null;
0985:
0986: try {
0987: bais = new java.io.ByteArrayInputStream(objBytes);
0988: ois = new java.io.ObjectInputStream(bais);
0989:
0990: obj = ois.readObject();
0991: } // end try
0992: catch (java.io.IOException e) {
0993: e.printStackTrace();
0994: obj = null;
0995: } // end catch
0996: catch (java.lang.ClassNotFoundException e) {
0997: e.printStackTrace();
0998: obj = null;
0999: } // end catch
1000: finally {
1001: try {
1002: bais.close();
1003: } catch (Exception e) {
1004: }
1005: try {
1006: ois.close();
1007: } catch (Exception e) {
1008: }
1009: } // end finally
1010:
1011: return obj;
1012: } // end decodeObject
1013:
1014: /**
1015: * Convenience method for encoding data to a file.
1016: *
1017: * @param dataToEncode byte array of data to encode in base64 form
1018: * @param filename Filename for saving encoded data
1019: * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1020: *
1021: * @since 2.1
1022: */
1023: public static boolean encodeToFile(byte[] dataToEncode,
1024: String filename) {
1025: boolean success = false;
1026: Base64.OutputStream bos = null;
1027: try {
1028: bos = new Base64.OutputStream(new java.io.FileOutputStream(
1029: filename), Base64.ENCODE);
1030: bos.write(dataToEncode);
1031: success = true;
1032: } // end try
1033: catch (java.io.IOException e) {
1034:
1035: success = false;
1036: } // end catch: IOException
1037: finally {
1038: try {
1039: bos.close();
1040: } catch (Exception e) {
1041: }
1042: } // end finally
1043:
1044: return success;
1045: } // end encodeToFile
1046:
1047: /**
1048: * Convenience method for decoding data to a file.
1049: *
1050: * @param dataToDecode Base64-encoded data as a string
1051: * @param filename Filename for saving decoded data
1052: * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1053: *
1054: * @since 2.1
1055: */
1056: public static boolean decodeToFile(String dataToDecode,
1057: String filename) {
1058: boolean success = false;
1059: Base64.OutputStream bos = null;
1060: try {
1061: bos = new Base64.OutputStream(new java.io.FileOutputStream(
1062: filename), Base64.DECODE);
1063: bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
1064: success = true;
1065: } // end try
1066: catch (java.io.IOException e) {
1067: success = false;
1068: } // end catch: IOException
1069: finally {
1070: try {
1071: bos.close();
1072: } catch (Exception e) {
1073: }
1074: } // end finally
1075:
1076: return success;
1077: } // end decodeToFile
1078:
1079: /**
1080: * Convenience method for reading a base64-encoded
1081: * file and decoding it.
1082: *
1083: * @param filename Filename for reading encoded data
1084: * @return decoded byte array or null if unsuccessful
1085: *
1086: * @since 2.1
1087: */
1088: public static byte[] decodeFromFile(String filename) {
1089: byte[] decodedData = null;
1090: Base64.InputStream bis = null;
1091: try {
1092: // Set up some useful variables
1093: java.io.File file = new java.io.File(filename);
1094: byte[] buffer = null;
1095: int length = 0;
1096: int numBytes = 0;
1097:
1098: // Check for size of file
1099: if (file.length() > Integer.MAX_VALUE) {
1100: System.err
1101: .println("File is too big for this convenience method ("
1102: + file.length() + " bytes).");
1103: return null;
1104: } // end if: file too big for int index
1105: buffer = new byte[(int) file.length()];
1106:
1107: // Open a stream
1108: bis = new Base64.InputStream(
1109: new java.io.BufferedInputStream(
1110: new java.io.FileInputStream(file)),
1111: Base64.DECODE);
1112:
1113: // Read until done
1114: while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
1115: length += numBytes;
1116:
1117: // Save in a variable to return
1118: decodedData = new byte[length];
1119: System.arraycopy(buffer, 0, decodedData, 0, length);
1120:
1121: } // end try
1122: catch (java.io.IOException e) {
1123: System.err.println("Error decoding from file " + filename);
1124: } // end catch: IOException
1125: finally {
1126: try {
1127: bis.close();
1128: } catch (Exception e) {
1129: }
1130: } // end finally
1131:
1132: return decodedData;
1133: } // end decodeFromFile
1134:
1135: /**
1136: * Convenience method for reading a binary file
1137: * and base64-encoding it.
1138: *
1139: * @param filename Filename for reading binary data
1140: * @return base64-encoded string or null if unsuccessful
1141: *
1142: * @since 2.1
1143: */
1144: public static String encodeFromFile(String filename) {
1145: String encodedData = null;
1146: Base64.InputStream bis = null;
1147: try {
1148: // Set up some useful variables
1149: java.io.File file = new java.io.File(filename);
1150: byte[] buffer = new byte[Math.max(
1151: (int) (file.length() * 1.4), 40)]; // Need max() for math on small files (v2.2.1)
1152: int length = 0;
1153: int numBytes = 0;
1154:
1155: // Open a stream
1156: bis = new Base64.InputStream(
1157: new java.io.BufferedInputStream(
1158: new java.io.FileInputStream(file)),
1159: Base64.ENCODE);
1160:
1161: // Read until done
1162: while ((numBytes = bis.read(buffer, length, 4096)) >= 0)
1163: length += numBytes;
1164:
1165: // Save in a variable to return
1166: encodedData = new String(buffer, 0, length,
1167: Base64.PREFERRED_ENCODING);
1168:
1169: } // end try
1170: catch (java.io.IOException e) {
1171: System.err.println("Error encoding from file " + filename);
1172: } // end catch: IOException
1173: finally {
1174: try {
1175: bis.close();
1176: } catch (Exception e) {
1177: }
1178: } // end finally
1179:
1180: return encodedData;
1181: } // end encodeFromFile
1182:
1183: /**
1184: * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1185: *
1186: * @param infile Input file
1187: * @param outfile Output file
1188: * @since 2.2
1189: */
1190: public static void encodeFileToFile(String infile, String outfile) {
1191: String encoded = Base64.encodeFromFile(infile);
1192: java.io.OutputStream out = null;
1193: try {
1194: out = new java.io.BufferedOutputStream(
1195: new java.io.FileOutputStream(outfile));
1196: out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
1197: } // end try
1198: catch (java.io.IOException ex) {
1199: ex.printStackTrace();
1200: } // end catch
1201: finally {
1202: try {
1203: out.close();
1204: } catch (Exception ex) {
1205: }
1206: } // end finally
1207: } // end encodeFileToFile
1208:
1209: /**
1210: * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1211: *
1212: * @param infile Input file
1213: * @param outfile Output file
1214: * @since 2.2
1215: */
1216: public static void decodeFileToFile(String infile, String outfile) {
1217: byte[] decoded = Base64.decodeFromFile(infile);
1218: java.io.OutputStream out = null;
1219: try {
1220: out = new java.io.BufferedOutputStream(
1221: new java.io.FileOutputStream(outfile));
1222: out.write(decoded);
1223: } // end try
1224: catch (java.io.IOException ex) {
1225: ex.printStackTrace();
1226: } // end catch
1227: finally {
1228: try {
1229: out.close();
1230: } catch (Exception ex) {
1231: }
1232: } // end finally
1233: } // end decodeFileToFile
1234:
1235: /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1236:
1237: /**
1238: * A {@link Base64.InputStream} will read data from another
1239: * <tt>java.io.InputStream</tt>, given in the constructor,
1240: * and encode/decode to/from Base64 notation on the fly.
1241: *
1242: * @see Base64
1243: * @since 1.3
1244: */
1245: public static class InputStream extends java.io.FilterInputStream {
1246: private boolean encode; // Encoding or decoding
1247: private int position; // Current position in the buffer
1248: private byte[] buffer; // Small buffer holding converted data
1249: private int bufferLength; // Length of buffer (3 or 4)
1250: private int numSigBytes; // Number of meaningful bytes in the buffer
1251: private int lineLength;
1252: private boolean breakLines; // Break lines at less than 80 characters
1253: private int options; // Record options used to create the stream.
1254: private byte[] alphabet; // Local copies to avoid extra method calls
1255: private byte[] decodabet; // Local copies to avoid extra method calls
1256:
1257: /**
1258: * Constructs a {@link Base64.InputStream} in DECODE mode.
1259: *
1260: * @param in the <tt>java.io.InputStream</tt> from which to read data.
1261: * @since 1.3
1262: */
1263: public InputStream(java.io.InputStream in) {
1264: this (in, DECODE);
1265: } // end constructor
1266:
1267: /**
1268: * Constructs a {@link Base64.InputStream} in
1269: * either ENCODE or DECODE mode.
1270: * <p>
1271: * Valid options:<pre>
1272: * ENCODE or DECODE: Encode or Decode as data is read.
1273: * DONT_BREAK_LINES: don't break lines at 76 characters
1274: * (only meaningful when encoding)
1275: * <i>Note: Technically, this makes your encoding non-compliant.</i>
1276: * </pre>
1277: * <p>
1278: * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1279: *
1280: *
1281: * @param in the <tt>java.io.InputStream</tt> from which to read data.
1282: * @param options Specified options
1283: * @see Base64#ENCODE
1284: * @see Base64#DECODE
1285: * @see Base64#DONT_BREAK_LINES
1286: * @since 2.0
1287: */
1288: public InputStream(java.io.InputStream in, int options) {
1289: super (in);
1290: this .breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1291: this .encode = (options & ENCODE) == ENCODE;
1292: this .bufferLength = encode ? 4 : 3;
1293: this .buffer = new byte[bufferLength];
1294: this .position = -1;
1295: this .lineLength = 0;
1296: this .options = options; // Record for later, mostly to determine which alphabet to use
1297: this .alphabet = getAlphabet(options);
1298: this .decodabet = getDecodabet(options);
1299: } // end constructor
1300:
1301: /**
1302: * Reads enough of the input stream to convert
1303: * to/from Base64 and returns the next byte.
1304: *
1305: * @return next byte
1306: * @since 1.3
1307: */
1308: public int read() throws java.io.IOException {
1309: // Do we need to get data?
1310: if (position < 0) {
1311: if (encode) {
1312: byte[] b3 = new byte[3];
1313: int numBinaryBytes = 0;
1314: for (int i = 0; i < 3; i++) {
1315: try {
1316: int b = in.read();
1317:
1318: // If end of stream, b is -1.
1319: if (b >= 0) {
1320: b3[i] = (byte) b;
1321: numBinaryBytes++;
1322: } // end if: not end of stream
1323:
1324: } // end try: read
1325: catch (java.io.IOException e) {
1326: // Only a problem if we got no data at all.
1327: if (i == 0)
1328: throw e;
1329:
1330: } // end catch
1331: } // end for: each needed input byte
1332:
1333: if (numBinaryBytes > 0) {
1334: encode3to4(b3, 0, numBinaryBytes, buffer, 0,
1335: options);
1336: position = 0;
1337: numSigBytes = 4;
1338: } // end if: got data
1339: else {
1340: return -1;
1341: } // end else
1342: } // end if: encoding
1343:
1344: // Else decoding
1345: else {
1346: byte[] b4 = new byte[4];
1347: int i = 0;
1348: for (i = 0; i < 4; i++) {
1349: // Read four "meaningful" bytes:
1350: int b = 0;
1351: do {
1352: b = in.read();
1353: } while (b >= 0
1354: && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1355:
1356: if (b < 0)
1357: break; // Reads a -1 if end of stream
1358:
1359: b4[i] = (byte) b;
1360: } // end for: each needed input byte
1361:
1362: if (i == 4) {
1363: numSigBytes = decode4to3(b4, 0, buffer, 0,
1364: options);
1365: position = 0;
1366: } // end if: got four characters
1367: else if (i == 0) {
1368: return -1;
1369: } // end else if: also padded correctly
1370: else {
1371: // Must have broken out from above.
1372: throw new java.io.IOException(
1373: "Improperly padded Base64 input.");
1374: } // end
1375:
1376: } // end else: decode
1377: } // end else: get data
1378:
1379: // Got data?
1380: if (position >= 0) {
1381: // End of relevant data?
1382: if ( /*!encode &&*/position >= numSigBytes)
1383: return -1;
1384:
1385: if (encode && breakLines
1386: && lineLength >= MAX_LINE_LENGTH) {
1387: lineLength = 0;
1388: return '\n';
1389: } // end if
1390: else {
1391: lineLength++; // This isn't important when decoding
1392: // but throwing an extra "if" seems
1393: // just as wasteful.
1394:
1395: int b = buffer[position++];
1396:
1397: if (position >= bufferLength)
1398: position = -1;
1399:
1400: return b & 0xFF; // This is how you "cast" a byte that's
1401: // intended to be unsigned.
1402: } // end else
1403: } // end if: position >= 0
1404:
1405: // Else error
1406: else {
1407: // When JDK1.4 is more accepted, use an assertion here.
1408: throw new java.io.IOException(
1409: "Error in Base64 code reading stream.");
1410: } // end else
1411: } // end read
1412:
1413: /**
1414: * Calls {@link #read()} repeatedly until the end of stream
1415: * is reached or <var>len</var> bytes are read.
1416: * Returns number of bytes read into array or -1 if
1417: * end of stream is encountered.
1418: *
1419: * @param dest array to hold values
1420: * @param off offset for array
1421: * @param len max number of bytes to read into array
1422: * @return bytes read into array or -1 if end of stream is encountered.
1423: * @since 1.3
1424: */
1425: public int read(byte[] dest, int off, int len)
1426: throws java.io.IOException {
1427: int i;
1428: int b;
1429: for (i = 0; i < len; i++) {
1430: b = read();
1431:
1432: //if( b < 0 && i == 0 )
1433: // return -1;
1434:
1435: if (b >= 0)
1436: dest[off + i] = (byte) b;
1437: else if (i == 0)
1438: return -1;
1439: else
1440: break; // Out of 'for' loop
1441: } // end for: each byte read
1442: return i;
1443: } // end read
1444:
1445: } // end inner class InputStream
1446:
1447: /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1448:
1449: /**
1450: * A {@link Base64.OutputStream} will write data to another
1451: * <tt>java.io.OutputStream</tt>, given in the constructor,
1452: * and encode/decode to/from Base64 notation on the fly.
1453: *
1454: * @see Base64
1455: * @since 1.3
1456: */
1457: public static class OutputStream extends java.io.FilterOutputStream {
1458: private boolean encode;
1459: private int position;
1460: private byte[] buffer;
1461: private int bufferLength;
1462: private int lineLength;
1463: private boolean breakLines;
1464: private byte[] b4; // Scratch used in a few places
1465: private boolean suspendEncoding;
1466: private int options; // Record for later
1467: private byte[] alphabet; // Local copies to avoid extra method calls
1468: private byte[] decodabet; // Local copies to avoid extra method calls
1469:
1470: /**
1471: * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1472: *
1473: * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1474: * @since 1.3
1475: */
1476: public OutputStream(java.io.OutputStream out) {
1477: this (out, ENCODE);
1478: } // end constructor
1479:
1480: /**
1481: * Constructs a {@link Base64.OutputStream} in
1482: * either ENCODE or DECODE mode.
1483: * <p>
1484: * Valid options:<pre>
1485: * ENCODE or DECODE: Encode or Decode as data is read.
1486: * DONT_BREAK_LINES: don't break lines at 76 characters
1487: * (only meaningful when encoding)
1488: * <i>Note: Technically, this makes your encoding non-compliant.</i>
1489: * </pre>
1490: * <p>
1491: * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1492: *
1493: * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1494: * @param options Specified options.
1495: * @see Base64#ENCODE
1496: * @see Base64#DECODE
1497: * @see Base64#DONT_BREAK_LINES
1498: * @since 1.3
1499: */
1500: public OutputStream(java.io.OutputStream out, int options) {
1501: super (out);
1502: this .breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1503: this .encode = (options & ENCODE) == ENCODE;
1504: this .bufferLength = encode ? 3 : 4;
1505: this .buffer = new byte[bufferLength];
1506: this .position = 0;
1507: this .lineLength = 0;
1508: this .suspendEncoding = false;
1509: this .b4 = new byte[4];
1510: this .options = options;
1511: this .alphabet = getAlphabet(options);
1512: this .decodabet = getDecodabet(options);
1513: } // end constructor
1514:
1515: /**
1516: * Writes the byte to the output stream after
1517: * converting to/from Base64 notation.
1518: * When encoding, bytes are buffered three
1519: * at a time before the output stream actually
1520: * gets a write() call.
1521: * When decoding, bytes are buffered four
1522: * at a time.
1523: *
1524: * @param theByte the byte to write
1525: * @since 1.3
1526: */
1527: public void write(int theByte) throws java.io.IOException {
1528: // Encoding suspended?
1529: if (suspendEncoding) {
1530: super .out.write(theByte);
1531: return;
1532: } // end if: supsended
1533:
1534: // Encode?
1535: if (encode) {
1536: buffer[position++] = (byte) theByte;
1537: if (position >= bufferLength) // Enough to encode.
1538: {
1539: out.write(encode3to4(b4, buffer, bufferLength,
1540: options));
1541:
1542: lineLength += 4;
1543: if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1544: out.write(NEW_LINE);
1545: lineLength = 0;
1546: } // end if: end of line
1547:
1548: position = 0;
1549: } // end if: enough to output
1550: } // end if: encoding
1551:
1552: // Else, Decoding
1553: else {
1554: // Meaningful Base64 character?
1555: if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1556: buffer[position++] = (byte) theByte;
1557: if (position >= bufferLength) // Enough to output.
1558: {
1559: int len = Base64.decode4to3(buffer, 0, b4, 0,
1560: options);
1561: out.write(b4, 0, len);
1562: //out.write( Base64.decode4to3( buffer ) );
1563: position = 0;
1564: } // end if: enough to output
1565: } // end if: meaningful base64 character
1566: else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1567: throw new java.io.IOException(
1568: "Invalid character in Base64 data.");
1569: } // end else: not white space either
1570: } // end else: decoding
1571: } // end write
1572:
1573: /**
1574: * Calls {@link #write(int)} repeatedly until <var>len</var>
1575: * bytes are written.
1576: *
1577: * @param theBytes array from which to read bytes
1578: * @param off offset for array
1579: * @param len max number of bytes to read into array
1580: * @since 1.3
1581: */
1582: public void write(byte[] theBytes, int off, int len)
1583: throws java.io.IOException {
1584: // Encoding suspended?
1585: if (suspendEncoding) {
1586: super .out.write(theBytes, off, len);
1587: return;
1588: } // end if: supsended
1589:
1590: for (int i = 0; i < len; i++) {
1591: write(theBytes[off + i]);
1592: } // end for: each byte written
1593:
1594: } // end write
1595:
1596: /**
1597: * Method added by PHIL. [Thanks, PHIL. -Rob]
1598: * This pads the buffer without closing the stream.
1599: */
1600: public void flushBase64() throws java.io.IOException {
1601: if (position > 0) {
1602: if (encode) {
1603: out
1604: .write(encode3to4(b4, buffer, position,
1605: options));
1606: position = 0;
1607: } // end if: encoding
1608: else {
1609: throw new java.io.IOException(
1610: "Base64 input not properly padded.");
1611: } // end else: decoding
1612: } // end if: buffer partially full
1613:
1614: } // end flush
1615:
1616: /**
1617: * Flushes and closes (I think, in the superclass) the stream.
1618: *
1619: * @since 1.3
1620: */
1621: public void close() throws java.io.IOException {
1622: // 1. Ensure that pending characters are written
1623: flushBase64();
1624:
1625: // 2. Actually close the stream
1626: // Base class both flushes and closes.
1627: super .close();
1628:
1629: buffer = null;
1630: out = null;
1631: } // end close
1632:
1633: /**
1634: * Suspends encoding of the stream.
1635: * May be helpful if you need to embed a piece of
1636: * base640-encoded data in a stream.
1637: *
1638: * @since 1.5.1
1639: */
1640: public void suspendEncoding() throws java.io.IOException {
1641: flushBase64();
1642: this .suspendEncoding = true;
1643: } // end suspendEncoding
1644:
1645: /**
1646: * Resumes encoding of the stream.
1647: * May be helpful if you need to embed a piece of
1648: * base640-encoded data in a stream.
1649: *
1650: * @since 1.5.1
1651: */
1652: public void resumeEncoding() {
1653: this .suspendEncoding = false;
1654: } // end resumeEncoding
1655:
1656: } // end inner class OutputStream
1657:
1658: } // end class Base64
|