0001: package org.apache.ojb.broker.util;
0002:
0003: /* Copyright 2002-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import org.apache.ojb.broker.util.logging.LoggerFactory;
0019:
0020: public class Base64 {
0021:
0022: /** Specify encoding (value is <tt>true</tt>). */
0023: public final static boolean ENCODE = true;
0024:
0025: /** Specify decoding (value is <tt>false</tt>). */
0026: public final static boolean DECODE = false;
0027:
0028: /** Maximum line length (76) of Base64 output. */
0029: private final static int MAX_LINE_LENGTH = 76;
0030:
0031: /** The equals sign (=) as a byte. */
0032: private final static byte EQUALS_SIGN = (byte) '=';
0033:
0034: /** The new line character (\n) as a byte. */
0035: private final static byte NEW_LINE = (byte) '\n';
0036:
0037: /** The 64 valid Base64 values. */
0038: private final static byte[] ALPHABET = { (byte) 'A', (byte) 'B',
0039: (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
0040: (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
0041: (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
0042: (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
0043: (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
0044: (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
0045: (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
0046: (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
0047: (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
0048: (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
0049: (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
0050: (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
0051: (byte) '+', (byte) '/' };
0052:
0053: /**
0054: * Translates a Base64 value to either its 6-bit reconstruction value
0055: * or a negative number indicating some other meaning.
0056: **/
0057: private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9,
0058: -9, -9, -9, // Decimal 0 - 8
0059: -5, -5, // Whitespace: Tab and Linefeed
0060: -9, -9, // Decimal 11 - 12
0061: -5, // Whitespace: Carriage Return
0062: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
0063: -9, -9, -9, -9, -9, // Decimal 27 - 31
0064: -5, // Whitespace: Space
0065: -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
0066: 62, // Plus sign at decimal 43
0067: -9, -9, -9, // Decimal 44 - 46
0068: 63, // Slash at decimal 47
0069: 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
0070: -9, -9, -9, // Decimal 58 - 60
0071: -1, // Equals sign at decimal 61
0072: -9, -9, -9, // Decimal 62 - 64
0073: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
0074: 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
0075: -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
0076: 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
0077: 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
0078: -9, -9, -9, -9 // Decimal 123 - 126
0079: /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
0080: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
0081: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
0082: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
0083: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
0084: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
0085: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
0086: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
0087: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
0088: -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
0089: };
0090:
0091: private final static byte BAD_ENCODING = -9; // Indicates error in encoding
0092: private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
0093: private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
0094:
0095: /** Defeats instantiation. */
0096: private Base64() {
0097: }
0098:
0099: /**
0100: * Testing. Feel free--in fact I encourage you--to throw out
0101: * this entire "main" method when you actually deploy this code.
0102: */
0103: public static void main(String[] args) {
0104: try {
0105: // Test encoding/decoding byte arrays
0106: {
0107: byte[] bytes1 = { (byte) 2, (byte) 2, (byte) 3,
0108: (byte) 0, (byte) 9 }; // My zip code
0109: byte[] bytes2 = { (byte) 99, (byte) 2, (byte) 2,
0110: (byte) 3, (byte) 0, (byte) 9 };
0111: System.out.println("Bytes 2,2,3,0,9 as Base64: "
0112: + encodeBytes(bytes1));
0113: System.out.println("Bytes 2,2,3,0,9 w/ offset: "
0114: + encodeBytes(bytes2, 1, bytes2.length - 1));
0115: byte[] dbytes = decode(encodeBytes(bytes1));
0116: System.out.print(encodeBytes(bytes1) + " decoded: ");
0117: for (int i = 0; i < dbytes.length; i++)
0118: System.out.print(dbytes[i]
0119: + (i < dbytes.length - 1 ? "," : "\n"));
0120: } // end testing byte arrays
0121:
0122: // Test Input Stream
0123: {
0124: // Read GIF stored in base64 form.
0125: java.io.FileInputStream fis = new java.io.FileInputStream(
0126: "test.gif.b64");
0127: Base64.InputStream b64is = new Base64.InputStream(fis,
0128: DECODE);
0129:
0130: byte[] bytes = new byte[0];
0131: int b = -1;
0132: while ((b = b64is.read()) >= 0) {
0133: byte[] temp = new byte[bytes.length + 1];
0134: System.arraycopy(bytes, 0, temp, 0, bytes.length);
0135: temp[bytes.length] = (byte) b;
0136: bytes = temp;
0137: } // end while: terribly inefficient way to read data
0138: b64is.close();
0139: javax.swing.ImageIcon iicon = new javax.swing.ImageIcon(
0140: bytes);
0141: javax.swing.JLabel jlabel = new javax.swing.JLabel(
0142: "Read from test.gif.b64", iicon, 0);
0143: javax.swing.JFrame jframe = new javax.swing.JFrame();
0144: jframe.getContentPane().add(jlabel);
0145: jframe.pack();
0146: jframe.show();
0147:
0148: // Write raw bytes to file
0149: java.io.FileOutputStream fos = new java.io.FileOutputStream(
0150: "test.gif_out");
0151: fos.write(bytes);
0152: fos.close();
0153:
0154: // Read raw bytes and encode
0155: fis = new java.io.FileInputStream("test.gif_out");
0156: b64is = new Base64.InputStream(fis, ENCODE);
0157: byte[] ebytes = new byte[0];
0158: b = -1;
0159: while ((b = b64is.read()) >= 0) {
0160: byte[] temp = new byte[ebytes.length + 1];
0161: System.arraycopy(ebytes, 0, temp, 0, ebytes.length);
0162: temp[ebytes.length] = (byte) b;
0163: ebytes = temp;
0164: } // end while: terribly inefficient way to read data
0165: b64is.close();
0166: String s = new String(ebytes);
0167: javax.swing.JTextArea jta = new javax.swing.JTextArea(s);
0168: javax.swing.JScrollPane jsp = new javax.swing.JScrollPane(
0169: jta);
0170: jframe = new javax.swing.JFrame();
0171: jframe.setTitle("Read from test.gif_out");
0172: jframe.getContentPane().add(jsp);
0173: jframe.pack();
0174: jframe.show();
0175:
0176: // Write encoded bytes to file
0177: fos = new java.io.FileOutputStream("test.gif.b64_out");
0178: fos.write(ebytes);
0179:
0180: // Read GIF stored in base64 form.
0181: fis = new java.io.FileInputStream("test.gif.b64_out");
0182: b64is = new Base64.InputStream(fis, DECODE);
0183: byte[] edbytes = new byte[0];
0184: b = -1;
0185: while ((b = b64is.read()) >= 0) {
0186: byte[] temp = new byte[edbytes.length + 1];
0187: System.arraycopy(edbytes, 0, temp, 0,
0188: edbytes.length);
0189: temp[edbytes.length] = (byte) b;
0190: edbytes = temp;
0191: } // end while: terribly inefficient way to read data
0192: b64is.close();
0193: iicon = new javax.swing.ImageIcon(edbytes);
0194: jlabel = new javax.swing.JLabel(
0195: "Read from test.gif.b64_out", iicon, 0);
0196: jframe = new javax.swing.JFrame();
0197: jframe.getContentPane().add(jlabel);
0198: jframe.pack();
0199: jframe.show();
0200: } // end: Test Input Stream
0201:
0202: // Test Output Stream
0203: {
0204: // Read raw bytes
0205: java.io.FileInputStream fis = new java.io.FileInputStream(
0206: "test.gif_out");
0207: byte[] rbytes = new byte[0];
0208: int b = -1;
0209: while ((b = fis.read()) >= 0) {
0210: byte[] temp = new byte[rbytes.length + 1];
0211: System.arraycopy(rbytes, 0, temp, 0, rbytes.length);
0212: temp[rbytes.length] = (byte) b;
0213: rbytes = temp;
0214: } // end while: terribly inefficient way to read data
0215: fis.close();
0216:
0217: // Write raw bytes to encoded file
0218: java.io.FileOutputStream fos = new java.io.FileOutputStream(
0219: "test.gif.b64_out2");
0220: Base64.OutputStream b64os = new Base64.OutputStream(
0221: fos, ENCODE);
0222: b64os.write(rbytes);
0223: b64os.close();
0224:
0225: // Read raw bytes that are actually encoded (but we'll ignore that)
0226: fis = new java.io.FileInputStream("test.gif.b64_out2");
0227: byte[] rebytes = new byte[0];
0228: b = -1;
0229: while ((b = fis.read()) >= 0) {
0230: byte[] temp = new byte[rebytes.length + 1];
0231: System.arraycopy(rebytes, 0, temp, 0,
0232: rebytes.length);
0233: temp[rebytes.length] = (byte) b;
0234: rebytes = temp;
0235: } // end while: terribly inefficient way to read data
0236: fis.close();
0237: String s = new String(rebytes);
0238: javax.swing.JTextArea jta = new javax.swing.JTextArea(s);
0239: javax.swing.JScrollPane jsp = new javax.swing.JScrollPane(
0240: jta);
0241: javax.swing.JFrame jframe = new javax.swing.JFrame();
0242: jframe.setTitle("Read from test.gif.b64_out2");
0243: jframe.getContentPane().add(jsp);
0244: jframe.pack();
0245: jframe.show();
0246:
0247: // Write encoded bytes to decoded raw file
0248: fos = new java.io.FileOutputStream("test.gif_out2");
0249: b64os = new Base64.OutputStream(fos, DECODE);
0250: b64os.write(rebytes);
0251: b64os.close();
0252: javax.swing.ImageIcon iicon = new javax.swing.ImageIcon(
0253: "test.gif_out2");
0254: javax.swing.JLabel jlabel = new javax.swing.JLabel(
0255: "Read from test.gif_out2", iicon, 0);
0256: jframe = new javax.swing.JFrame();
0257: jframe.getContentPane().add(jlabel);
0258: jframe.pack();
0259: jframe.show();
0260:
0261: } // end: Test Output Stream
0262:
0263: // Test wagner's files
0264: {
0265: java.io.FileInputStream fis = new java.io.FileInputStream(
0266: "D:\\temp\\testencoding.txt");
0267: Base64.InputStream b64is = new Base64.InputStream(fis,
0268: DECODE);
0269: java.io.FileOutputStream fos = new java.io.FileOutputStream(
0270: "D:\\temp\\file.zip");
0271: int b;
0272: while ((b = b64is.read()) >= 0)
0273: fos.write(b);
0274: fos.close();
0275: b64is.close();
0276:
0277: } // end test wagner's file
0278:
0279: } // end try
0280: catch (Exception e) {
0281: e.printStackTrace();
0282: }
0283: } // end main
0284:
0285: /* ******** E N C O D I N G M E T H O D S ******** */
0286:
0287: /**
0288: * Encodes the first three bytes of array <var>threeBytes</var>
0289: * and returns a four-byte array in Base64 notation.
0290: *
0291: * @param threeBytes the array to convert
0292: * @return four byte array in Base64 notation.
0293: * @since 1.3
0294: */
0295: private static byte[] encode3to4(byte[] threeBytes) {
0296: return encode3to4(threeBytes, 3);
0297: } // end encodeToBytes
0298:
0299: /**
0300: * Encodes up to the first three bytes of array <var>threeBytes</var>
0301: * and returns a four-byte array in Base64 notation.
0302: * The actual number of significant bytes in your array is
0303: * given by <var>numSigBytes</var>.
0304: * The array <var>threeBytes</var> needs only be as big as
0305: * <var>numSigBytes</var>.
0306: *
0307: * @param threeBytes the array to convert
0308: * @param numSigBytes the number of significant bytes in your array
0309: * @return four byte array in Base64 notation.
0310: * @since 1.3
0311: */
0312: private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
0313: byte[] dest = new byte[4];
0314: encode3to4(threeBytes, 0, numSigBytes, dest, 0);
0315: return dest;
0316: }
0317:
0318: /**
0319: * Encodes up to three bytes of the array <var>source</var>
0320: * and writes the resulting four Base64 bytes to <var>destination</var>.
0321: * The source and destination arrays can be manipulated
0322: * anywhere along their length by specifying
0323: * <var>srcOffset</var> and <var>destOffset</var>.
0324: * This method does not check to make sure your arrays
0325: * are large enough to accomodate <var>srcOffset</var> + 3 for
0326: * the <var>source</var> array or <var>destOffset</var> + 4 for
0327: * the <var>destination</var> array.
0328: * The actual number of significant bytes in your array is
0329: * given by <var>numSigBytes</var>.
0330: *
0331: * @param source the array to convert
0332: * @param srcOffset the index where conversion begins
0333: * @param numSigBytes the number of significant bytes in your array
0334: * @param destination the array to hold the conversion
0335: * @param destOffset the index where output will be put
0336: * @return the <var>destination</var> array
0337: * @since 1.3
0338: */
0339: private static byte[] encode3to4(byte[] source, int srcOffset,
0340: int numSigBytes, byte[] destination, int destOffset) {
0341: // 1 2 3
0342: // 01234567890123456789012345678901 Bit position
0343: // --------000000001111111122222222 Array position from threeBytes
0344: // --------| || || || | Six bit groups to index ALPHABET
0345: // >>18 >>12 >> 6 >> 0 Right shift necessary
0346: // 0x3f 0x3f 0x3f Additional AND
0347:
0348: // Create buffer with zero-padding if there are only one or two
0349: // significant bytes passed in the array.
0350: // We have to shift left 24 in order to flush out the 1's that appear
0351: // when Java treats a value as negative that is cast from a byte to an int.
0352: int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8)
0353: : 0)
0354: | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16)
0355: : 0)
0356: | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24)
0357: : 0);
0358:
0359: switch (numSigBytes) {
0360: case 3:
0361: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0362: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0363: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0364: destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
0365: return destination;
0366:
0367: case 2:
0368: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0369: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0370: destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
0371: destination[destOffset + 3] = EQUALS_SIGN;
0372: return destination;
0373:
0374: case 1:
0375: destination[destOffset] = ALPHABET[(inBuff >>> 18)];
0376: destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
0377: destination[destOffset + 2] = EQUALS_SIGN;
0378: destination[destOffset + 3] = EQUALS_SIGN;
0379: return destination;
0380:
0381: default:
0382: return destination;
0383: } // end switch
0384: } // end encode3to4
0385:
0386: /**
0387: * Serializes an object and returns the Base64-encoded
0388: * version of that serialized object. If the object
0389: * cannot be serialized or there is another error,
0390: * the method will return <tt>null</tt>.
0391: *
0392: * @param serializableObject The object to encode
0393: * @return The Base64-encoded object
0394: * @since 1.4
0395: */
0396: public static String encodeObject(
0397: java.io.Serializable serializableObject) {
0398: return encodeObject(serializableObject, true);
0399: } // end encodeObject
0400:
0401: /**
0402: * Serializes an object and returns the Base64-encoded
0403: * version of that serialized object. If the object
0404: * cannot be serialized or there is another error,
0405: * the method will return <tt>null</tt>.
0406: *
0407: * @param serializableObject The object to encode
0408: * @param breakLines Break lines at 80 characters or less.
0409: * @return The Base64-encoded object
0410: * @since 1.4
0411: */
0412: public static String encodeObject(
0413: java.io.Serializable serializableObject, boolean breakLines) {
0414: java.io.ByteArrayOutputStream baos = null;
0415: java.io.OutputStream b64os = null;
0416: java.io.ObjectOutputStream oos = null;
0417:
0418: try {
0419: baos = new java.io.ByteArrayOutputStream();
0420: b64os = new Base64.OutputStream(baos, Base64.ENCODE,
0421: breakLines);
0422: oos = new java.io.ObjectOutputStream(b64os);
0423:
0424: oos.writeObject(serializableObject);
0425: } // end try
0426: catch (java.io.IOException e) {
0427: e.printStackTrace();
0428: return null;
0429: } // end catch
0430: finally {
0431: try {
0432: oos.close();
0433: } catch (Exception e) {
0434: // ignore it
0435: }
0436: try {
0437: b64os.close();
0438: } catch (Exception e) {
0439: // ignore it
0440: }
0441: try {
0442: baos.close();
0443: } catch (Exception e) {
0444: // ignore it
0445: }
0446: } // end finally
0447:
0448: return new String(baos.toByteArray());
0449: } // end encode
0450:
0451: /**
0452: * Encodes a byte array into Base64 notation.
0453: * Equivalen to calling
0454: * <code>encodeBytes( source, 0, source.length )</code>
0455: *
0456: * @param source The data to convert
0457: * @since 1.4
0458: */
0459: public static String encodeBytes(byte[] source) {
0460: return encodeBytes(source, true);
0461: } // end encodeBytes
0462:
0463: /**
0464: * Encodes a byte array into Base64 notation.
0465: * Equivalen to calling
0466: * <code>encodeBytes( source, 0, source.length )</code>
0467: *
0468: * @param source The data to convert
0469: * @param breakLines Break lines at 80 characters or less.
0470: * @since 1.4
0471: */
0472: public static String encodeBytes(byte[] source, boolean breakLines) {
0473: return encodeBytes(source, 0, source.length, breakLines);
0474: } // end encodeBytes
0475:
0476: /**
0477: * Encodes a byte array into Base64 notation.
0478: *
0479: * @param source The data to convert
0480: * @param off Offset in array where conversion should begin
0481: * @param len Length of data to convert
0482: * @since 1.4
0483: */
0484: public static String encodeBytes(byte[] source, int off, int len) {
0485: return encodeBytes(source, off, len, true);
0486: } // end encodeBytes
0487:
0488: /**
0489: * Encodes a byte array into Base64 notation.
0490: *
0491: * @param source The data to convert
0492: * @param off Offset in array where conversion should begin
0493: * @param len Length of data to convert
0494: * @param breakLines Break lines at 80 characters or less.
0495: * @since 1.4
0496: */
0497: public static String encodeBytes(byte[] source, int off, int len,
0498: boolean breakLines) {
0499: int len43 = len * 4 / 3;
0500: byte[] outBuff = new byte[(len43) // Main 4:3
0501: + ((len % 3) > 0 ? 4 : 0) // Account for padding
0502: + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
0503: int d = 0;
0504: int e = 0;
0505: int len2 = len - 2;
0506: int lineLength = 0;
0507: for (; d < len2; d += 3, e += 4) {
0508: encode3to4(source, d + off, 3, outBuff, e);
0509:
0510: lineLength += 4;
0511: if (breakLines && lineLength == MAX_LINE_LENGTH) {
0512: outBuff[e + 4] = NEW_LINE;
0513: e++;
0514: lineLength = 0;
0515: } // end if: end of line
0516: } // en dfor: each piece of array
0517:
0518: if (d < len) {
0519: encode3to4(source, d + off, len - d, outBuff, e);
0520: e += 4;
0521: } // end if: some padding needed
0522:
0523: return new String(outBuff, 0, e);
0524: } // end encodeBytes
0525:
0526: /**
0527: * Encodes a string in Base64 notation with line breaks
0528: * after every 75 Base64 characters.
0529: *
0530: * @param s the string to encode
0531: * @return the encoded string
0532: * @since 1.3
0533: */
0534: public static String encodeString(String s) {
0535: return encodeString(s, true);
0536: } // end encodeString
0537:
0538: /**
0539: * Encodes a string in Base64 notation with line breaks
0540: * after every 75 Base64 characters.
0541: *
0542: * @param s the string to encode
0543: * @param breakLines Break lines at 80 characters or less.
0544: * @return the encoded string
0545: * @since 1.3
0546: */
0547: public static String encodeString(String s, boolean breakLines) {
0548: return encodeBytes(s.getBytes(), breakLines);
0549: } // end encodeString
0550:
0551: /* ******** D E C O D I N G M E T H O D S ******** */
0552:
0553: /**
0554: * Decodes the first four bytes of array <var>fourBytes</var>
0555: * and returns an array up to three bytes long with the
0556: * decoded values.
0557: *
0558: * @param fourBytes the array with Base64 content
0559: * @return array with decoded values
0560: * @since 1.3
0561: */
0562: private static byte[] decode4to3(byte[] fourBytes) {
0563: byte[] outBuff1 = new byte[3];
0564: int count = decode4to3(fourBytes, 0, outBuff1, 0);
0565: byte[] outBuff2 = new byte[count];
0566:
0567: System.arraycopy(outBuff1, 0, outBuff2, 0, count);
0568: return outBuff2;
0569: }
0570:
0571: /**
0572: * Decodes four bytes from array <var>source</var>
0573: * and writes the resulting bytes (up to three of them)
0574: * to <var>destination</var>.
0575: * The source and destination arrays can be manipulated
0576: * anywhere along their length by specifying
0577: * <var>srcOffset</var> and <var>destOffset</var>.
0578: * This method does not check to make sure your arrays
0579: * are large enough to accomodate <var>srcOffset</var> + 4 for
0580: * the <var>source</var> array or <var>destOffset</var> + 3 for
0581: * the <var>destination</var> array.
0582: * This method returns the actual number of bytes that
0583: * were converted from the Base64 encoding.
0584: *
0585: *
0586: * @param source the array to convert
0587: * @param srcOffset the index where conversion begins
0588: * @param destination the array to hold the conversion
0589: * @param destOffset the index where output will be put
0590: * @return the number of decoded bytes converted
0591: * @since 1.3
0592: */
0593: private static int decode4to3(byte[] source, int srcOffset,
0594: byte[] destination, int destOffset) {
0595: // Example: Dk==
0596: if (source[srcOffset + 2] == EQUALS_SIGN) {
0597: // Two ways to do the same thing. Don't know which way I like best.
0598: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0599: // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
0600: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0601: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
0602:
0603: destination[destOffset] = (byte) (outBuff >>> 16);
0604: return 1;
0605: }
0606:
0607: // Example: DkL=
0608: else if (source[srcOffset + 3] == EQUALS_SIGN) {
0609: // Two ways to do the same thing. Don't know which way I like best.
0610: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0611: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0612: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
0613: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0614: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0615: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
0616:
0617: destination[destOffset] = (byte) (outBuff >>> 16);
0618: destination[destOffset + 1] = (byte) (outBuff >>> 8);
0619: return 2;
0620: }
0621:
0622: // Example: DkLE
0623: else {
0624: try {
0625: // Two ways to do the same thing. Don't know which way I like best.
0626: //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
0627: // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
0628: // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
0629: // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
0630: int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
0631: | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
0632: | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
0633: | ((DECODABET[source[srcOffset + 3]] & 0xFF));
0634:
0635: destination[destOffset] = (byte) (outBuff >> 16);
0636: destination[destOffset + 1] = (byte) (outBuff >> 8);
0637: destination[destOffset + 2] = (byte) (outBuff);
0638:
0639: return 3;
0640: } catch (Exception e) {
0641: LoggerFactory.getDefaultLogger().error(
0642: "" + source[srcOffset] + ": "
0643: + (DECODABET[source[srcOffset]]));
0644: LoggerFactory.getDefaultLogger().error(
0645: "" + source[srcOffset + 1] + ": "
0646: + (DECODABET[source[srcOffset + 1]]));
0647: LoggerFactory.getDefaultLogger().error(
0648: "" + source[srcOffset + 2] + ": "
0649: + (DECODABET[source[srcOffset + 2]]));
0650: LoggerFactory.getDefaultLogger().error(
0651: "" + source[srcOffset + 3] + ": "
0652: + (DECODABET[source[srcOffset + 3]]));
0653: return -1;
0654: } // end catch
0655: }
0656: } // end decodeToBytes
0657:
0658: /**
0659: * Decodes data from Base64 notation.
0660: *
0661: * @param s the string to decode
0662: * @return the decoded data
0663: * @since 1.4
0664: */
0665: public static byte[] decode(String s) {
0666: byte[] bytes = s.getBytes();
0667: return decode(bytes, 0, bytes.length);
0668: } // end decode
0669:
0670: /**
0671: * Decodes data from Base64 notation and
0672: * returns it as a string.
0673: * Equivlaent to calling
0674: * <code>new String( decode( s ) )</code>
0675: *
0676: * @param s the strind to decode
0677: * @return The data as a string
0678: * @since 1.4
0679: */
0680: public static String decodeToString(String s) {
0681: return new String(decode(s));
0682: } // end decodeToString
0683:
0684: /**
0685: * Attempts to decode Base64 data and deserialize a Java
0686: * Object within. Returns <tt>null if there was an error.
0687: *
0688: * @param encodedObject The Base64 data to decode
0689: * @return The decoded and deserialized object
0690: * @since 1.4
0691: */
0692: public static Object decodeToObject(String encodedObject) {
0693: byte[] objBytes = decode(encodedObject);
0694:
0695: java.io.ByteArrayInputStream bais = null;
0696: java.io.ObjectInputStream ois = null;
0697:
0698: try {
0699: bais = new java.io.ByteArrayInputStream(objBytes);
0700: ois = new java.io.ObjectInputStream(bais);
0701:
0702: return ois.readObject();
0703: } // end try
0704: catch (java.io.IOException e) {
0705: e.printStackTrace();
0706: return null;
0707: } // end catch
0708: catch (java.lang.ClassNotFoundException e) {
0709: e.printStackTrace();
0710: return null;
0711: } // end catch
0712: finally {
0713: try {
0714: bais.close();
0715: } catch (Exception e) {
0716: // ignore it
0717: }
0718: try {
0719: ois.close();
0720: } catch (Exception e) {
0721: // ignore it
0722: }
0723: } // end finally
0724: } // end decodeObject
0725:
0726: /**
0727: * Decodes Base64 content in byte array format and returns
0728: * the decoded byte array.
0729: *
0730: * @param source The Base64 encoded data
0731: * @param off The offset of where to begin decoding
0732: * @param len The length of characters to decode
0733: * @return decoded data
0734: * @since 1.3
0735: */
0736: public static byte[] decode(byte[] source, int off, int len) {
0737: int len34 = len * 3 / 4;
0738: byte[] outBuff = new byte[len34]; // Upper limit on size of output
0739: int outBuffPosn = 0;
0740:
0741: byte[] b4 = new byte[4];
0742: int b4Posn = 0;
0743: int i = 0;
0744: byte sbiCrop = 0;
0745: byte sbiDecode = 0;
0746: for (i = 0; i < len; i++) {
0747: sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
0748: sbiDecode = DECODABET[sbiCrop];
0749:
0750: if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
0751: {
0752: if (sbiDecode >= EQUALS_SIGN_ENC) {
0753: b4[b4Posn++] = sbiCrop;
0754: if (b4Posn > 3) {
0755: outBuffPosn += decode4to3(b4, 0, outBuff,
0756: outBuffPosn);
0757: b4Posn = 0;
0758:
0759: // If that was the equals sign, break out of 'for' loop
0760: if (sbiCrop == EQUALS_SIGN)
0761: break;
0762: } // end if: quartet built
0763:
0764: } // end if: equals sign or better
0765:
0766: } // end if: white space, equals sign or better
0767: else {
0768: LoggerFactory.getDefaultLogger().error(
0769: "Bad Base64 input character at " + i + ": "
0770: + source[i] + "(decimal)");
0771: return null;
0772: } // end else:
0773: } // each input character
0774:
0775: byte[] out = new byte[outBuffPosn];
0776: System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
0777: return out;
0778: } // end decode
0779:
0780: /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
0781:
0782: /**
0783: * A {@link Base64.InputStream} will read data from another
0784: * {@link java.io.InputStream}, given in the constructor,
0785: * and encode/decode to/from Base64 notation on the fly.
0786: *
0787: * @see Base64
0788: * @see java.io.FilterInputStream
0789: * @since 1.3
0790: */
0791: public static class InputStream extends java.io.FilterInputStream {
0792: private boolean encode; // Encoding or decoding
0793: private int position; // Current position in the buffer
0794: private byte[] buffer; // Small buffer holding converted data
0795: private int bufferLength; // Length of buffer (3 or 4)
0796: private int numSigBytes; // Number of meaningful bytes in the buffer
0797: private int lineLength;
0798: private boolean breakLines; // Break lines at less than 80 characters
0799:
0800: /**
0801: * Constructs a {@link Base64.InputStream} in DECODE mode.
0802: *
0803: * @param in the {@link java.io.InputStream} from which to read data.
0804: * @since 1.3
0805: */
0806: public InputStream(java.io.InputStream in) {
0807: this (in, Base64.DECODE);
0808: } // end constructor
0809:
0810: /**
0811: * Constructs a {@link Base64.InputStream} in
0812: * either ENCODE or DECODE mode.
0813: *
0814: * @param in the {@link java.io.InputStream} from which to read data.
0815: * @param encode Conversion direction
0816: * @see Base64#ENCODE
0817: * @see Base64#DECODE
0818: * @since 1.3
0819: */
0820: public InputStream(java.io.InputStream in, boolean encode) {
0821: this (in, encode, true);
0822: } // end constructor
0823:
0824: /**
0825: * Constructs a {@link Base64.InputStream} in
0826: * either ENCODE or DECODE mode.
0827: *
0828: * @param in the {@link java.io.InputStream} from which to read data.
0829: * @param encode Conversion direction
0830: * @param breakLines Break lines at less than 80 characters.
0831: * @see Base64#ENCODE
0832: * @see Base64#DECODE
0833: * @since 1.3
0834: */
0835: public InputStream(java.io.InputStream in, boolean encode,
0836: boolean breakLines) {
0837: super (in);
0838: this .breakLines = breakLines;
0839: this .encode = encode;
0840: this .bufferLength = encode ? 4 : 3;
0841: this .buffer = new byte[bufferLength];
0842: this .position = -1;
0843: this .lineLength = 0;
0844: } // end constructor
0845:
0846: /**
0847: * Reads enough of the input stream to convert
0848: * to/from Base64 and returns the next byte.
0849: *
0850: * @return next byte
0851: * @since 1.3
0852: */
0853: public int read() throws java.io.IOException {
0854: // Do we need to get data?
0855: if (position < 0) {
0856: if (encode) {
0857: byte[] b3 = new byte[3];
0858: int numBinaryBytes = 0;
0859: for (int i = 0; i < 3; i++) {
0860: try {
0861: int b = in.read();
0862:
0863: // If end of stream, b is -1.
0864: if (b >= 0) {
0865: b3[i] = (byte) b;
0866: numBinaryBytes++;
0867: } // end if: not end of stream
0868:
0869: } // end try: read
0870: catch (java.io.IOException e) {
0871: // Only a problem if we got no data at all.
0872: if (i == 0)
0873: throw e;
0874:
0875: } // end catch
0876: } // end for: each needed input byte
0877:
0878: if (numBinaryBytes > 0) {
0879: encode3to4(b3, 0, numBinaryBytes, buffer, 0);
0880: position = 0;
0881: numSigBytes = 4;
0882: } // end if: got data
0883: else {
0884: return -1;
0885: } // end else
0886: } // end if: encoding
0887:
0888: // Else decoding
0889: else {
0890: byte[] b4 = new byte[4];
0891: int i = 0;
0892: for (i = 0; i < 4; i++) {
0893: // Read four "meaningful" bytes:
0894: int b = 0;
0895: do {
0896: b = in.read();
0897: } while (b >= 0
0898: && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC);
0899:
0900: if (b < 0)
0901: break; // Reads a -1 if end of stream
0902:
0903: b4[i] = (byte) b;
0904: } // end for: each needed input byte
0905:
0906: if (i == 4) {
0907: numSigBytes = decode4to3(b4, 0, buffer, 0);
0908: position = 0;
0909: } // end if: got four characters
0910: else if (i == 0) {
0911: return -1;
0912: } // end else if: also padded correctly
0913: else {
0914: // Must have broken out from above.
0915: throw new java.io.IOException(
0916: "Improperly padded Base64 input.");
0917: } // end
0918:
0919: } // end else: decode
0920: } // end else: get data
0921:
0922: // Got data?
0923: if (position >= 0) {
0924: // End of relevant data?
0925: if ( /*!encode &&*/position >= numSigBytes)
0926: return -1;
0927:
0928: if (encode && breakLines
0929: && lineLength >= MAX_LINE_LENGTH) {
0930: lineLength = 0;
0931: return '\n';
0932: } // end if
0933: else {
0934: lineLength++; // This isn't important when decoding
0935: // but throwing an extra "if" seems
0936: // just as wasteful.
0937:
0938: int b = buffer[position++];
0939:
0940: if (position >= bufferLength)
0941: position = -1;
0942:
0943: return b & 0xFF; // This is how you "cast" a byte that's
0944: // intended to be unsigned.
0945: } // end else
0946: } // end if: position >= 0
0947:
0948: // Else error
0949: else {
0950: // When JDK1.4 is more accepted, use an assertion here.
0951: throw new java.io.IOException(
0952: "Error in Base64 code reading stream.");
0953: } // end else
0954: } // end read
0955:
0956: /**
0957: * Calls {@link #read} repeatedly until the end of stream
0958: * is reached or <var>len</var> bytes are read.
0959: * Returns number of bytes read into array or -1 if
0960: * end of stream is encountered.
0961: *
0962: * @param dest array to hold values
0963: * @param off offset for array
0964: * @param len max number of bytes to read into array
0965: * @return bytes read into array or -1 if end of stream is encountered.
0966: * @since 1.3
0967: */
0968: public int read(byte[] dest, int off, int len)
0969: throws java.io.IOException {
0970: int i;
0971: int b;
0972: for (i = 0; i < len; i++) {
0973: b = read();
0974:
0975: //if( b < 0 && i == 0 )
0976: // return -1;
0977:
0978: if (b >= 0)
0979: dest[off + i] = (byte) b;
0980: else if (i == 0)
0981: return -1;
0982: else
0983: break; // Out of 'for' loop
0984: } // end for: each byte read
0985: return i;
0986: } // end read
0987:
0988: } // end inner class InputStream
0989:
0990: /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
0991:
0992: /**
0993: * A {@link Base64.OutputStream} will write data to another
0994: * {@link java.io.OutputStream}, given in the constructor,
0995: * and encode/decode to/from Base64 notation on the fly.
0996: *
0997: * @see Base64
0998: * @see java.io.FilterOutputStream
0999: * @since 1.3
1000: */
1001: public static class OutputStream extends java.io.FilterOutputStream {
1002: private boolean encode;
1003: private int position;
1004: private byte[] buffer;
1005: private int bufferLength;
1006: private int lineLength;
1007: private boolean breakLines;
1008:
1009: /**
1010: * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1011: *
1012: * @param out the {@link java.io.OutputStream} to which data will be written.
1013: * @since 1.3
1014: */
1015: public OutputStream(java.io.OutputStream out) {
1016: this (out, Base64.ENCODE);
1017: } // end constructor
1018:
1019: /**
1020: * Constructs a {@link Base64.OutputStream} in
1021: * either ENCODE or DECODE mode.
1022: *
1023: * @param out the {@link java.io.OutputStream} to which data will be written.
1024: * @param encode Conversion direction
1025: * @see Base64#ENCODE
1026: * @see Base64#DECODE
1027: * @since 1.3
1028: */
1029: public OutputStream(java.io.OutputStream out, boolean encode) {
1030: this (out, encode, true);
1031: } // end constructor
1032:
1033: /**
1034: * Constructs a {@link Base64.OutputStream} in
1035: * either ENCODE or DECODE mode.
1036: *
1037: * @param out the {@link java.io.OutputStream} to which data will be written.
1038: * @param encode Conversion direction
1039: * @param breakLines Break lines to be less than 80 characters.
1040: * @see Base64#ENCODE
1041: * @see Base64#DECODE
1042: * @since 1.3
1043: */
1044: public OutputStream(java.io.OutputStream out, boolean encode,
1045: boolean breakLines) {
1046: super (out);
1047: this .breakLines = breakLines;
1048: this .encode = encode;
1049: this .bufferLength = encode ? 3 : 4;
1050: this .buffer = new byte[bufferLength];
1051: this .position = 0;
1052: this .lineLength = 0;
1053: } // end constructor
1054:
1055: /**
1056: * Writes the byte to the output stream after
1057: * converting to/from Base64 notation.
1058: * When encoding, bytes are buffered three
1059: * at a time before the output stream actually
1060: * gets a write() call.
1061: * When decoding, bytes are buffered four
1062: * at a time.
1063: *
1064: * @param theByte the byte to write
1065: * @since 1.3
1066: */
1067: public void write(int theByte) throws java.io.IOException {
1068: if (encode) {
1069: buffer[position++] = (byte) theByte;
1070: if (position >= bufferLength) // Enough to encode.
1071: {
1072: out.write(Base64.encode3to4(buffer, bufferLength));
1073:
1074: lineLength += 4;
1075: if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1076: out.write(NEW_LINE);
1077: lineLength = 0;
1078: } // end if: end of line
1079:
1080: position = 0;
1081: } // end if: enough to output
1082: } // end if: encoding
1083:
1084: // Else, Decoding
1085: else {
1086: // Meaningful Base64 character?
1087: if (DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) {
1088: buffer[position++] = (byte) theByte;
1089: if (position >= bufferLength) // Enough to output.
1090: {
1091: out.write(Base64.decode4to3(buffer));
1092: position = 0;
1093: } // end if: enough to output
1094: } // end if: meaningful base64 character
1095: else if (DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) {
1096: throw new java.io.IOException(
1097: "Invalid character in Base64 data.");
1098: } // end else: not white space either
1099: } // end else: decoding
1100: } // end write
1101:
1102: /**
1103: * Calls {@link #write} repeatedly until <var>len</var>
1104: * bytes are written.
1105: *
1106: * @param theBytes array from which to read bytes
1107: * @param off offset for array
1108: * @param len max number of bytes to read into array
1109: * @since 1.3
1110: */
1111: public void write(byte[] theBytes, int off, int len)
1112: throws java.io.IOException {
1113: for (int i = 0; i < len; i++) {
1114: write(theBytes[off + i]);
1115: } // end for: each byte written
1116:
1117: } // end write
1118:
1119: /**
1120: * Appropriately pads Base64 notation when encoding
1121: * or throws an exception if Base64 input is not
1122: * properly padded when decoding.
1123: *
1124: * @since 1.3
1125: */
1126: public void flush() throws java.io.IOException {
1127: super .flush();
1128:
1129: if (position > 0) {
1130: if (encode) {
1131: out.write(Base64.encode3to4(buffer, position));
1132: } // end if: encoding
1133: else {
1134: throw new java.io.IOException(
1135: "Base64 input not properly padded.");
1136: } // end else: decoding
1137: } // end if: buffer partially full
1138:
1139: out.flush();
1140: } // end flush
1141:
1142: /**
1143: * Flushes and closes stream.
1144: *
1145: * @since 1.3
1146: */
1147: public void close() throws java.io.IOException {
1148: super .close();
1149: //this.flush();
1150:
1151: out.close();
1152:
1153: buffer = null;
1154: out = null;
1155: } // end close
1156:
1157: } // end inner class OutputStream
1158:
1159: } // end class Base64
|