0001: /*
0002: * Copyright (C) 2001-2007 Stephen Ostermiller
0003: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
0004: *
0005: * This program is free software; you can redistribute it and/or modify
0006: * it under the terms of the GNU General Public License as published by
0007: * the Free Software Foundation; either version 2 of the License, or
0008: * (at your option) any later version.
0009: *
0010: * This program is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0013: * GNU General Public License for more details.
0014: *
0015: * See COPYING.TXT for details.
0016: */
0017: package com.Ostermiller.util;
0018:
0019: import java.io.*;
0020: import java.text.MessageFormat;
0021: import java.util.ResourceBundle;
0022: import java.util.Locale;
0023:
0024: /**
0025: * Implements Base64 encoding and decoding as defined by RFC 2045: "Multi-purpose Internet
0026: * Mail Extensions (MIME) Part One: Format of Internet Message Bodies" page 23.
0027: * More information about this class is available from <a target="_top" href=
0028: * "http://ostermiller.org/utils/Base64.html">ostermiller.org</a>.
0029: *
0030: * <blockquote>
0031: * <p>The Base64 Content-Transfer-Encoding is designed to represent
0032: * arbitrary sequences of octets in a form that need not be humanly
0033: * readable. The encoding and decoding algorithms are simple, but the
0034: * encoded data are consistently only about 33 percent larger than the
0035: * unencoded data. This encoding is virtually identical to the one used
0036: * in Privacy Enhanced Mail (PEM) applications, as defined in RFC 1421.</p>
0037: *
0038: * <p>A 65-character subset of US-ASCII is used, enabling 6 bits to be
0039: * represented per printable character. (The extra 65th character, "=",
0040: * is used to signify a special processing function.)</p>
0041: *
0042: * <p>NOTE: This subset has the important property that it is represented
0043: * identically in all versions of ISO 646, including US-ASCII, and all
0044: * characters in the subset are also represented identically in all
0045: * versions of EBCDIC. Other popular encodings, such as the encoding
0046: * used by the uuencode utility, MacIntosh binhex 4.0 [RFC-1741], and
0047: * the base85 encoding specified as part of Level 2 PostScript, do no
0048: * share these properties, and thus do not fulfill the portability
0049: * requirements a binary transport encoding for mail must meet.</p>
0050: *
0051: * <p>The encoding process represents 24-bit groups of input bits as output
0052: * strings of 4 encoded characters. Proceeding from left to right, a
0053: * 24-bit input group is formed by concatenating 3 8bit input groups.
0054: * These 24 bits are then treated as 4 concatenated 6-bit groups, each
0055: * of which is translated into a single digit in the base64 alphabet.
0056: * When encoding a bit stream via the base64 encoding, the bit stream
0057: * must be presumed to be ordered with the most-significant-bit first.
0058: * That is, the first bit in the stream will be the high-order bit in
0059: * the first 8bit byte, and the eighth bit will be the low-order bit in
0060: * the first 8bit byte, and so on.</p>
0061: *
0062: * <p>Each 6-bit group is used as an index into an array of 64 printable
0063: * characters. The character referenced by the index is placed in the
0064: * output string. These characters, identified in Table 1, below, are
0065: * selected so as to be universally representable, and the set excludes
0066: * characters with particular significance to SMTP (e.g., ".", CR, LF)
0067: * and to the multi-part boundary delimiters defined in RFC 2046 (e.g.,
0068: * "-").</p>
0069: * <pre>
0070: * Table 1: The Base64 Alphabet
0071: *
0072: * Value Encoding Value Encoding Value Encoding Value Encoding
0073: * 0 A 17 R 34 i 51 z
0074: * 1 B 18 S 35 j 52 0
0075: * 2 C 19 T 36 k 53 1
0076: * 3 D 20 U 37 l 54 2
0077: * 4 E 21 V 38 m 55 3
0078: * 5 F 22 W 39 n 56 4
0079: * 6 G 23 X 40 o 57 5
0080: * 7 H 24 Y 41 p 58 6
0081: * 8 I 25 Z 42 q 59 7
0082: * 9 J 26 a 43 r 60 8
0083: * 10 K 27 b 44 s 61 9
0084: * 11 L 28 c 45 t 62 +
0085: * 12 M 29 d 46 u 63 /
0086: * 13 N 30 e 47 v
0087: * 14 O 31 f 48 w (pad) =
0088: * 15 P 32 g 49 x
0089: * 16 Q 33 h 50 y
0090: * </pre>
0091: * <p>The encoded output stream must be represented in lines of no more
0092: * than 76 characters each. All line breaks or other characters no
0093: * found in Table 1 must be ignored by decoding software. In base64
0094: * data, characters other than those in Table 1, line breaks, and other
0095: * white space probably indicate a transmission error, about which a
0096: * warning message or even a message rejection might be appropriate
0097: * under some circumstances.</p>
0098: *
0099: * <p>Special processing is performed if fewer than 24 bits are available
0100: * at the end of the data being encoded. A full encoding quantum is
0101: * always completed at the end of a body. When fewer than 24 input bits
0102: * are available in an input group, zero bits are added (on the right)
0103: * to form an integral number of 6-bit groups. Padding at the end of
0104: * the data is performed using the "=" character. Since all base64
0105: * input is an integral number of octets, only the following cases can
0106: * arise: (1) the final quantum of encoding input is an integral
0107: * multiple of 24 bits; here, the final unit of encoded output will be
0108: * an integral multiple of 4 characters with no "=" padding, (2) the
0109: * final quantum of encoding input is exactly 8 bits; here, the final
0110: * unit of encoded output will be two characters followed by two "="
0111: * padding characters, or (3) the final quantum of encoding input is
0112: * exactly 16 bits; here, the final unit of encoded output will be three
0113: * characters followed by one "=" padding character.</p>
0114: *
0115: * <p>Because it is used only for padding at the end of the data, the
0116: * occurrence of any "=" characters may be taken as evidence that the
0117: * end of the data has been reached (without truncation in transit). No
0118: * such assurance is possible, however, when the number of octets
0119: * transmitted was a multiple of three and no "=" characters are
0120: * present.</p>
0121: *
0122: * <p>Any characters outside of the base64 alphabet are to be ignored in
0123: * base64-encoded data.</p>
0124: *
0125: * <p>Care must be taken to use the proper octets for line breaks if base64
0126: * encoding is applied directly to text material that has not been
0127: * converted to canonical form. In particular, text line breaks must be
0128: * converted into CRLF sequences prior to base64 encoding. The
0129: * important thing to note is that this may be done directly by the
0130: * encoder rather than in a prior canonicalization step in some
0131: * implementations.</p>
0132: *
0133: * <p>NOTE: There is no need to worry about quoting potential boundary
0134: * delimiters within base64-encoded bodies within multi-part entities
0135: * because no hyphen characters are used in the base64 encoding.</p>
0136: * </blockquote>
0137: *
0138: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
0139: * @since ostermillerutils 1.00.00
0140: */
0141: public class Base64 {
0142:
0143: /**
0144: * Symbol that represents the end of an input stream
0145: *
0146: * @since ostermillerutils 1.00.00
0147: */
0148: private static final int END_OF_INPUT = -1;
0149:
0150: /**
0151: * A character that is not a valid base 64 character.
0152: *
0153: * @since ostermillerutils 1.00.00
0154: */
0155: private static final int NON_BASE_64 = -1;
0156:
0157: /**
0158: * A character that is not a valid base 64 character.
0159: *
0160: * @since ostermillerutils 1.00.00
0161: */
0162: private static final int NON_BASE_64_WHITESPACE = -2;
0163:
0164: /**
0165: * A character that is not a valid base 64 character.
0166: *
0167: * @since ostermillerutils 1.00.00
0168: */
0169: private static final int NON_BASE_64_PADDING = -3;
0170:
0171: /**
0172: * This class need not be instantiated, all methods are static.
0173: *
0174: * @since ostermillerutils 1.00.00
0175: */
0176: private Base64() {
0177: // should not be called
0178: }
0179:
0180: /**
0181: * Table of the sixty-four characters that are used as
0182: * the Base64 alphabet: [a-z0-9A-Z+/]
0183: *
0184: * @since ostermillerutils 1.00.00
0185: */
0186: protected static final byte[] base64Chars = { 'A', 'B', 'C', 'D',
0187: 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
0188: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
0189: 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
0190: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
0191: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', };
0192:
0193: /**
0194: * Reverse lookup table for the Base64 alphabet.
0195: * reversebase64Chars[byte] gives n for the nth Base64
0196: * character or negative if a character is not a Base64 character.
0197: *
0198: * @since ostermillerutils 1.00.00
0199: */
0200: protected static final byte[] reverseBase64Chars = new byte[0x100];
0201: static {
0202: // Fill in NON_BASE_64 for all characters to start with
0203: for (int i = 0; i < reverseBase64Chars.length; i++) {
0204: reverseBase64Chars[i] = NON_BASE_64;
0205: }
0206: // For characters that are base64Chars, adjust
0207: // the reverse lookup table.
0208: for (byte i = 0; i < base64Chars.length; i++) {
0209: reverseBase64Chars[base64Chars[i]] = i;
0210: }
0211: reverseBase64Chars[' '] = NON_BASE_64_WHITESPACE;
0212: reverseBase64Chars['\n'] = NON_BASE_64_WHITESPACE;
0213: reverseBase64Chars['\r'] = NON_BASE_64_WHITESPACE;
0214: reverseBase64Chars['\t'] = NON_BASE_64_WHITESPACE;
0215: reverseBase64Chars['\f'] = NON_BASE_64_WHITESPACE;
0216: reverseBase64Chars['='] = NON_BASE_64_PADDING;
0217: }
0218:
0219: /**
0220: * Version number of this program
0221: *
0222: * @since ostermillerutils 1.00.00
0223: */
0224: public static final String version = "1.2";
0225:
0226: /**
0227: * Locale specific strings displayed to the user.
0228: *
0229: * @since ostermillerutils 1.00.00
0230: */
0231: protected static ResourceBundle labels = ResourceBundle.getBundle(
0232: "com.Ostermiller.util.Base64", Locale.getDefault());
0233:
0234: private static final int ACTION_GUESS = 0;
0235: private static final int ACTION_ENCODE = 1;
0236: private static final int ACTION_DECODE = 2;
0237:
0238: private static final int ARGUMENT_GUESS = 0;
0239: private static final int ARGUMENT_STRING = 1;
0240: private static final int ARGUMENT_FILE = 2;
0241:
0242: private enum Base64CmdLnOption {
0243: /** --help */
0244: HELP(new CmdLnOption(labels.getString("help.option"))
0245: .setDescription(labels.getString("help.message"))),
0246: /** --version */
0247: VERSION(new CmdLnOption(labels.getString("version.option"))
0248: .setDescription(labels.getString("version.message"))),
0249: /** --about */
0250: ABOUT(new CmdLnOption(labels.getString("about.option"))
0251: .setDescription(labels.getString("about.message"))),
0252: /** --guess */
0253: GUESS(new CmdLnOption(labels.getString("guess.option"), 'g')
0254: .setDescription(labels.getString("g.message") + " ("
0255: + labels.getString("default") + ")")),
0256: /** --encode */
0257: ENCODE(new CmdLnOption(labels.getString("encode.option"), 'e')
0258: .setDescription(labels.getString("e.message"))),
0259: /** --lines */
0260: LINES(new CmdLnOption(labels.getString("lines.option"), 'l')
0261: .setDescription(labels.getString("l.message") + " ("
0262: + labels.getString("default") + ")")),
0263: /** --nolines */
0264: NOLINES(new CmdLnOption(labels.getString("nolines.option"))
0265: .setDescription(labels.getString("nolines.message"))),
0266: /** --decode */
0267: DECODE(new CmdLnOption(labels.getString("decode.option"), 'd')
0268: .setDescription(labels.getString("d.message"))),
0269: /** --decodeall */
0270: DECODEALL(new CmdLnOption(labels.getString("decodeall.option"),
0271: 'a').setDescription(labels.getString("a.message"))),
0272: /** --decodegood */
0273: DECODEGOOD(new CmdLnOption(labels
0274: .getString("decodegood.option")).setDescription(labels
0275: .getString("decodegood.message")
0276: + " (" + labels.getString("default") + ")")),
0277: /** --ext */
0278: EXT(new CmdLnOption(labels.getString("ext.option"), 'x')
0279: .setOptionalArgument().setDescription(
0280: labels.getString("x.message"))),
0281: /** --force */
0282: FORCE(new CmdLnOption(labels.getString("force.option"), 'f')
0283: .setDescription(labels.getString("f.message"))),
0284: /** --noforce */
0285: NOFORCE(new CmdLnOption(labels.getString("noforce.option"))
0286: .setDescription(labels.getString("noforce.message")
0287: + " (" + labels.getString("default") + ")")),
0288: /** --verbose */
0289: VERBOSE(
0290: new CmdLnOption(labels.getString("verbose.option"), 'v')
0291: .setDescription(labels.getString("v.message")
0292: + " (" + labels.getString("default")
0293: + ")")),
0294: /** --quiet */
0295: QUIET(new CmdLnOption(labels.getString("quiet.option"), 'q')
0296: .setDescription(labels.getString("q.message"))),
0297: /** --reallyquiet */
0298: REALLYQUIET(new CmdLnOption(labels
0299: .getString("reallyquiet.option"), 'Q')
0300: .setDescription(labels.getString("Q.message"))),
0301: /** --file */
0302: FILE(new CmdLnOption(labels.getString("file.option"))
0303: .setDescription(labels.getString("file.message"))),
0304: /** --string */
0305: STRING(new CmdLnOption(labels.getString("string.option"))
0306: .setDescription(labels.getString("string.message"))),
0307: /** --endline */
0308: ENDLINE(
0309: new CmdLnOption(labels.getString("newline.option"), 'n')
0310: .setDescription(labels
0311: .getString("newline.message"))),
0312: /** --noendline */
0313: NOENDLINE(new CmdLnOption(labels.getString("nonewline.option"))
0314: .setDescription(labels.getString("nonewline.message")));
0315:
0316: private CmdLnOption option;
0317:
0318: private Base64CmdLnOption(CmdLnOption option) {
0319: option.setUserObject(this );
0320: this .option = option;
0321: }
0322:
0323: private CmdLnOption getCmdLineOption() {
0324: return option;
0325: }
0326: }
0327:
0328: /**
0329: * Converts the line ending on files, or standard input.
0330: * Run with --help argument for more information.
0331: *
0332: * @param args Command line arguments.
0333: *
0334: * @since ostermillerutils 1.00.00
0335: */
0336: public static void main(String[] args) {
0337: CmdLn commandLine = new CmdLn(args).setDescription(labels
0338: .getString("base64")
0339: + labels.getString("purpose.message"));
0340: for (Base64CmdLnOption option : Base64CmdLnOption.values()) {
0341: commandLine.addOption(option.getCmdLineOption());
0342: }
0343: int action = ACTION_GUESS;
0344: String extension = "base64";
0345: boolean force = false;
0346: boolean printMessages = true;
0347: boolean printErrors = true;
0348: boolean forceDecode = false;
0349: boolean lineBreaks = true;
0350: int argumentType = ARGUMENT_GUESS;
0351: boolean decodeEndLine = false;
0352: for (CmdLnResult result : commandLine.getResults()) {
0353: switch ((Base64CmdLnOption) result.getOption()
0354: .getUserObject()) {
0355: case HELP: {
0356: // print out the help message
0357: commandLine.printHelp();
0358: System.exit(0);
0359: }
0360: break;
0361: case VERSION: {
0362: // print out the version message
0363: System.out.println(MessageFormat.format(labels
0364: .getString("version"),
0365: (Object[]) new String[] { version }));
0366: System.exit(0);
0367: }
0368: break;
0369: case ABOUT: {
0370: System.out
0371: .println(labels.getString("base64")
0372: + " -- "
0373: + labels.getString("purpose.message")
0374: + "\n"
0375: + MessageFormat
0376: .format(
0377: labels
0378: .getString("copyright"),
0379: (Object[]) new String[] {
0380: "2001-2007",
0381: "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)" })
0382: + "\n\n" + labels.getString("license"));
0383: System.exit(0);
0384: }
0385: break;
0386: case DECODE: {
0387: action = ACTION_DECODE;
0388: }
0389: break;
0390: case DECODEALL: {
0391: forceDecode = true;
0392: }
0393: break;
0394: case DECODEGOOD: {
0395: forceDecode = false;
0396: }
0397: break;
0398: case ENCODE: {
0399: action = ACTION_ENCODE;
0400: }
0401: break;
0402: case LINES: {
0403: lineBreaks = true;
0404: }
0405: break;
0406: case NOLINES: {
0407: lineBreaks = false;
0408: }
0409: break;
0410: case GUESS: {
0411: action = ACTION_GUESS;
0412: }
0413: break;
0414: case EXT: {
0415: extension = result.getArgument();
0416: if (extension == null)
0417: extension = "";
0418: }
0419: break;
0420: case FORCE: {
0421: force = true;
0422: }
0423: break;
0424: case NOFORCE: {
0425: force = false;
0426: }
0427: break;
0428: case VERBOSE: {
0429: printMessages = true;
0430: printErrors = true;
0431: }
0432: break;
0433: case QUIET: {
0434: printMessages = false;
0435: printErrors = true;
0436: }
0437: break;
0438: case REALLYQUIET: {
0439: printMessages = false;
0440: printErrors = false;
0441: }
0442: break;
0443: case FILE: {
0444: argumentType = ARGUMENT_FILE;
0445: }
0446: break;
0447: case STRING: {
0448: argumentType = ARGUMENT_STRING;
0449: }
0450: break;
0451: case ENDLINE: {
0452: decodeEndLine = true;
0453: }
0454: break;
0455: case NOENDLINE: {
0456: decodeEndLine = false;
0457: }
0458: break;
0459: }
0460: }
0461:
0462: int exitCond = 0;
0463: boolean done = false;
0464: for (String argument : commandLine.getNonOptionArguments()) {
0465: done = true;
0466: File source = new File(argument);
0467: if (argumentType == ARGUMENT_STRING
0468: || (argumentType == ARGUMENT_GUESS && !source
0469: .exists())) {
0470: try {
0471: int fileAction = action;
0472: if (fileAction == ACTION_GUESS) {
0473: if (isBase64(argument)) {
0474: fileAction = ACTION_DECODE;
0475: } else {
0476: fileAction = ACTION_ENCODE;
0477: }
0478: }
0479: if (fileAction == ACTION_ENCODE) {
0480: if (printMessages) {
0481: System.out.println(labels
0482: .getString("encodingarg"));
0483: }
0484: encode(new ByteArrayInputStream(argument
0485: .getBytes()), System.out, lineBreaks);
0486: } else {
0487: if (printMessages) {
0488: System.out.println(labels
0489: .getString("decodingarg"));
0490: }
0491: decode(new ByteArrayInputStream(argument
0492: .getBytes()), System.out, !forceDecode);
0493: if (decodeEndLine)
0494: System.out.println();
0495: }
0496: } catch (Base64DecodingException x) {
0497: if (printErrors) {
0498: System.err
0499: .println(argument
0500: + ": "
0501: + x.getMessage()
0502: + " "
0503: + labels
0504: .getString("unexpectedcharforce"));
0505: }
0506: exitCond = 1;
0507: } catch (IOException x) {
0508: if (printErrors) {
0509: System.err.println(argument + ": "
0510: + x.getMessage());
0511: }
0512: exitCond = 1;
0513: }
0514: } else if (!source.exists()) {
0515: if (printErrors) {
0516: System.err.println(MessageFormat.format(labels
0517: .getString("doesnotexist"),
0518: (Object[]) new String[] { argument }));
0519: }
0520: exitCond = 1;
0521: } else if (!source.canRead()) {
0522: if (printErrors) {
0523: System.err.println(MessageFormat.format(labels
0524: .getString("cantread"),
0525: (Object[]) new String[] { argument }));
0526: }
0527: exitCond = 1;
0528: } else {
0529: try {
0530: int fileAction = action;
0531: if (fileAction == ACTION_GUESS) {
0532: if (isBase64(source)) {
0533: fileAction = ACTION_DECODE;
0534: } else {
0535: fileAction = ACTION_ENCODE;
0536: }
0537: }
0538: String outName = argument;
0539: if (extension.length() > 0) {
0540: if (fileAction == ACTION_ENCODE) {
0541: outName = argument + "." + extension;
0542: } else {
0543: if (argument.endsWith("." + extension)) {
0544: outName = argument
0545: .substring(
0546: 0,
0547: argument.length()
0548: - (extension
0549: .length() + 1));
0550: }
0551: }
0552: }
0553: File outFile = new File(outName);
0554: if (!force && outFile.exists()) {
0555: if (printErrors) {
0556: System.err
0557: .println(MessageFormat
0558: .format(
0559: labels
0560: .getString("overwrite"),
0561: (Object[]) new String[] { outName }));
0562: }
0563: exitCond = 1;
0564: } else if (!(outFile.exists() || outFile
0565: .createNewFile())
0566: || !outFile.canWrite()) {
0567: if (printErrors) {
0568: System.err
0569: .println(MessageFormat
0570: .format(
0571: labels
0572: .getString("cantwrite"),
0573: (Object[]) new String[] { outName }));
0574: }
0575: exitCond = 1;
0576: } else {
0577: if (fileAction == ACTION_ENCODE) {
0578: if (printMessages) {
0579: System.out
0580: .println(MessageFormat
0581: .format(
0582: labels
0583: .getString("encoding"),
0584: (Object[]) new String[] {
0585: argument,
0586: outName }));
0587: }
0588: encode(source, outFile, lineBreaks);
0589: } else {
0590: if (printMessages) {
0591: System.out
0592: .println(MessageFormat
0593: .format(
0594: labels
0595: .getString("decoding"),
0596: (Object[]) new String[] {
0597: argument,
0598: outName }));
0599: }
0600: decode(source, outFile, !forceDecode);
0601: }
0602: }
0603: } catch (Base64DecodingException x) {
0604: if (printErrors) {
0605: System.err
0606: .println(argument
0607: + ": "
0608: + x.getMessage()
0609: + " "
0610: + labels
0611: .getString("unexpectedcharforce"));
0612: }
0613: exitCond = 1;
0614: } catch (IOException x) {
0615: if (printErrors) {
0616: System.err.println(argument + ": "
0617: + x.getMessage());
0618: }
0619: exitCond = 1;
0620: }
0621: }
0622: }
0623: if (!done) {
0624: try {
0625: if (action == ACTION_GUESS) {
0626: if (printErrors) {
0627: System.err.println(labels
0628: .getString("cantguess"));
0629: }
0630: exitCond = 1;
0631: } else if (action == ACTION_ENCODE) {
0632: encode(new BufferedInputStream(System.in),
0633: new BufferedOutputStream(System.out),
0634: lineBreaks);
0635: } else {
0636: decode(new BufferedInputStream(System.in),
0637: new BufferedOutputStream(System.out),
0638: !forceDecode);
0639: if (decodeEndLine)
0640: System.out.println();
0641: }
0642: } catch (Base64DecodingException x) {
0643: if (printErrors) {
0644: System.err.println(x.getMessage() + " "
0645: + labels.getString("unexpectedcharforce"));
0646: }
0647: exitCond = 1;
0648: } catch (IOException x) {
0649: if (printErrors) {
0650: System.err.println(x.getMessage());
0651: }
0652: exitCond = 1;
0653: }
0654: }
0655: System.exit(exitCond);
0656: }
0657:
0658: /**
0659: * Encode a String in Base64.
0660: * The String is converted to and from bytes according to the platform's
0661: * default character encoding.
0662: * No line breaks or other white space are inserted into the encoded data.
0663: *
0664: * @param string The data to encode.
0665: * @return An encoded String.
0666: *
0667: * @since ostermillerutils 1.00.00
0668: */
0669: public static String encode(String string) {
0670: return new String(encode(string.getBytes()));
0671: }
0672:
0673: /**
0674: * Encode a String in Base64.
0675: * No line breaks or other white space are inserted into the encoded data.
0676: *
0677: * @param string The data to encode.
0678: * @param enc Character encoding to use when converting to and from bytes.
0679: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
0680: * @return An encoded String.
0681: *
0682: * @since ostermillerutils 1.00.00
0683: */
0684: public static String encode(String string, String enc)
0685: throws UnsupportedEncodingException {
0686: return new String(encode(string.getBytes(enc)), enc);
0687: }
0688:
0689: /**
0690: * Encode bytes in Base64.
0691: * No line breaks or other white space are inserted into the encoded data.
0692: *
0693: * @param bytes The data to encode.
0694: * @return String with Base64 encoded data.
0695: *
0696: * @since ostermillerutils 1.04.00
0697: */
0698: public static String encodeToString(byte[] bytes) {
0699: return encodeToString(bytes, false);
0700: }
0701:
0702: /**
0703: * Encode bytes in Base64.
0704: *
0705: * @param bytes The data to encode.
0706: * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
0707: * @return String with Base64 encoded data.
0708: *
0709: * @since ostermillerutils 1.04.00
0710: */
0711: public static String encodeToString(byte[] bytes, boolean lineBreaks) {
0712: try {
0713: return new String(encode(bytes, lineBreaks), "ASCII");
0714: } catch (UnsupportedEncodingException iex) {
0715: // ASCII should be supported
0716: throw new RuntimeException(iex);
0717: }
0718: }
0719:
0720: /**
0721: * Encode bytes in Base64.
0722: * No line breaks or other white space are inserted into the encoded data.
0723: *
0724: * @param bytes The data to encode.
0725: * @return Encoded bytes.
0726: *
0727: * @since ostermillerutils 1.00.00
0728: */
0729: public static byte[] encode(byte[] bytes) {
0730: return encode(bytes, false);
0731: }
0732:
0733: /**
0734: * Encode bytes in Base64.
0735: *
0736: * @param bytes The data to encode.
0737: * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
0738: * @return Encoded bytes.
0739: *
0740: * @since ostermillerutils 1.04.00
0741: */
0742: public static byte[] encode(byte[] bytes, boolean lineBreaks) {
0743: ByteArrayInputStream in = new ByteArrayInputStream(bytes);
0744: // calculate the length of the resulting output.
0745: // in general it will be 4/3 the size of the input
0746: // but the input length must be divisible by three.
0747: // If it isn't the next largest size that is divisible
0748: // by three is used.
0749: int mod;
0750: int length = bytes.length;
0751: if ((mod = length % 3) != 0) {
0752: length += 3 - mod;
0753: }
0754: length = length * 4 / 3;
0755: ByteArrayOutputStream out = new ByteArrayOutputStream(length);
0756: try {
0757: encode(in, out, lineBreaks);
0758: } catch (IOException x) {
0759: // This can't happen.
0760: // The input and output streams were constructed
0761: // on memory structures that don't actually use IO.
0762: throw new RuntimeException(x);
0763: }
0764: return out.toByteArray();
0765: }
0766:
0767: /**
0768: * Encode this file in Base64.
0769: * Line breaks will be inserted every 76 characters.
0770: *
0771: * @param fIn File to be encoded (will be overwritten).
0772: * @throws IOException if an input or output error occurs.
0773: *
0774: * @since ostermillerutils 1.00.00
0775: */
0776: public static void encode(File fIn) throws IOException {
0777: encode(fIn, fIn, true);
0778: }
0779:
0780: /**
0781: * Encode this file in Base64.
0782: *
0783: * @param fIn File to be encoded (will be overwritten).
0784: * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
0785: * @throws IOException if an input or output error occurs.
0786: *
0787: * @since ostermillerutils 1.00.00
0788: */
0789: public static void encode(File fIn, boolean lineBreaks)
0790: throws IOException {
0791: encode(fIn, fIn, lineBreaks);
0792: }
0793:
0794: /**
0795: * Encode this file in Base64.
0796: * Line breaks will be inserted every 76 characters.
0797: *
0798: * @param fIn File to be encoded.
0799: * @param fOut File to which the results should be written (may be the same as fIn).
0800: * @throws IOException if an input or output error occurs.
0801: *
0802: * @since ostermillerutils 1.00.00
0803: */
0804: public static void encode(File fIn, File fOut) throws IOException {
0805: encode(fIn, fOut, true);
0806: }
0807:
0808: /**
0809: * Encode this file in Base64.
0810: *
0811: * @param fIn File to be encoded.
0812: * @param fOut File to which the results should be written (may be the same as fIn).
0813: * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
0814: * @throws IOException if an input or output error occurs.
0815: *
0816: * @since ostermillerutils 1.00.00
0817: */
0818: public static void encode(File fIn, File fOut, boolean lineBreaks)
0819: throws IOException {
0820: File temp = null;
0821: InputStream in = null;
0822: OutputStream out = null;
0823: try {
0824: in = new BufferedInputStream(new FileInputStream(fIn));
0825: temp = File.createTempFile("Base64", null, null);
0826: out = new BufferedOutputStream(new FileOutputStream(temp));
0827: encode(in, out, lineBreaks);
0828: in.close();
0829: in = null;
0830: out.flush();
0831: out.close();
0832: out = null;
0833: FileHelper.move(temp, fOut, true);
0834: } finally {
0835: if (in != null) {
0836: in.close();
0837: in = null;
0838: }
0839: if (out != null) {
0840: out.flush();
0841: out.close();
0842: out = null;
0843: }
0844: }
0845: }
0846:
0847: /**
0848: * Encode data from the InputStream to the OutputStream in Base64.
0849: * Line breaks are inserted every 76 characters in the output.
0850: *
0851: * @param in Stream from which to read data that needs to be encoded.
0852: * @param out Stream to which to write encoded data.
0853: * @throws IOException if there is a problem reading or writing.
0854: *
0855: * @since ostermillerutils 1.00.00
0856: */
0857: public static void encode(InputStream in, OutputStream out)
0858: throws IOException {
0859: encode(in, out, true);
0860: }
0861:
0862: /**
0863: * Encode data from the InputStream to the OutputStream in Base64.
0864: *
0865: * @param in Stream from which to read data that needs to be encoded.
0866: * @param out Stream to which to write encoded data.
0867: * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
0868: * @throws IOException if there is a problem reading or writing.
0869: *
0870: * @since ostermillerutils 1.00.00
0871: */
0872: public static void encode(InputStream in, OutputStream out,
0873: boolean lineBreaks) throws IOException {
0874: // Base64 encoding converts three bytes of input to
0875: // four bytes of output
0876: int[] inBuffer = new int[3];
0877: int lineCount = 0;
0878:
0879: boolean done = false;
0880: while (!done && (inBuffer[0] = in.read()) != END_OF_INPUT) {
0881: // Fill the buffer
0882: inBuffer[1] = in.read();
0883: inBuffer[2] = in.read();
0884:
0885: // Calculate the out Buffer
0886: // The first byte of our in buffer will always be valid
0887: // but we must check to make sure the other two bytes
0888: // are not END_OF_INPUT before using them.
0889: // The basic idea is that the three bytes get split into
0890: // four bytes along these lines:
0891: // [AAAAAABB] [BBBBCCCC] [CCDDDDDD]
0892: // [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD]
0893: // bytes are considered to be zero when absent.
0894: // the four bytes are then mapped to common ASCII symbols
0895:
0896: // A's: first six bits of first byte
0897: out.write(base64Chars[inBuffer[0] >> 2]);
0898: if (inBuffer[1] != END_OF_INPUT) {
0899: // B's: last two bits of first byte, first four bits of second byte
0900: out.write(base64Chars[((inBuffer[0] << 4) & 0x30)
0901: | (inBuffer[1] >> 4)]);
0902: if (inBuffer[2] != END_OF_INPUT) {
0903: // C's: last four bits of second byte, first two bits of third byte
0904: out.write(base64Chars[((inBuffer[1] << 2) & 0x3c)
0905: | (inBuffer[2] >> 6)]);
0906: // D's: last six bits of third byte
0907: out.write(base64Chars[inBuffer[2] & 0x3F]);
0908: } else {
0909: // C's: last four bits of second byte
0910: out.write(base64Chars[((inBuffer[1] << 2) & 0x3c)]);
0911: // an equals sign for a character that is not a Base64 character
0912: out.write('=');
0913: done = true;
0914: }
0915: } else {
0916: // B's: last two bits of first byte
0917: out.write(base64Chars[((inBuffer[0] << 4) & 0x30)]);
0918: // an equal signs for characters that is not a Base64 characters
0919: out.write('=');
0920: out.write('=');
0921: done = true;
0922: }
0923: lineCount += 4;
0924: if (lineBreaks && lineCount >= 76) {
0925: out.write('\n');
0926: lineCount = 0;
0927: }
0928: }
0929: if (lineBreaks && lineCount >= 1) {
0930: out.write('\n');
0931: lineCount = 0;
0932: }
0933: out.flush();
0934: }
0935:
0936: /**
0937: * Decode a Base64 encoded String.
0938: * Characters that are not part of the Base64 alphabet are ignored
0939: * in the input.
0940: * The String is converted to and from bytes according to the platform's
0941: * default character encoding.
0942: *
0943: * @param string The data to decode.
0944: * @return A decoded String.
0945: *
0946: * @since ostermillerutils 1.00.00
0947: */
0948: public static String decode(String string) {
0949: return new String(decode(string.getBytes()));
0950: }
0951:
0952: /**
0953: * Decode a Base64 encoded String.
0954: * Characters that are not part of the Base64 alphabet are ignored
0955: * in the input.
0956: *
0957: * @param string The data to decode.
0958: * @param enc Character encoding to use when converting to and from bytes.
0959: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
0960: * @return A decoded String.
0961: *
0962: * @since ostermillerutils 1.00.00
0963: */
0964: public static String decode(String string, String enc)
0965: throws UnsupportedEncodingException {
0966: return new String(decode(string.getBytes(enc)), enc);
0967: }
0968:
0969: /**
0970: * Decode a Base64 encoded String.
0971: * Characters that are not part of the Base64 alphabet are ignored
0972: * in the input.
0973: *
0974: * @param string The data to decode.
0975: * @param encIn Character encoding to use when converting input to bytes (should not matter because Base64 data is designed to survive most character encodings)
0976: * @param encOut Character encoding to use when converting decoded bytes to output.
0977: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
0978: * @return A decoded String.
0979: *
0980: * @since ostermillerutils 1.00.00
0981: */
0982: public static String decode(String string, String encIn,
0983: String encOut) throws UnsupportedEncodingException {
0984: return new String(decode(string.getBytes(encIn)), encOut);
0985: }
0986:
0987: /**
0988: * Decode a Base64 encoded String.
0989: * Characters that are not part of the Base64 alphabet are ignored
0990: * in the input.
0991: * The String is converted to and from bytes according to the platform's
0992: * default character encoding.
0993: *
0994: * @param string The data to decode.
0995: * @return A decoded String.
0996: *
0997: * @since ostermillerutils 1.02.16
0998: */
0999: public static String decodeToString(String string) {
1000: return new String(decode(string.getBytes()));
1001: }
1002:
1003: /**
1004: * Decode a Base64 encoded String.
1005: * Characters that are not part of the Base64 alphabet are ignored
1006: * in the input.
1007: *
1008: * @param string The data to decode.
1009: * @param enc Character encoding to use when converting to and from bytes.
1010: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1011: * @return A decoded String.
1012: *
1013: * @since ostermillerutils 1.02.16
1014: */
1015: public static String decodeToString(String string, String enc)
1016: throws UnsupportedEncodingException {
1017: return new String(decode(string.getBytes(enc)), enc);
1018: }
1019:
1020: /**
1021: * Decode a Base64 encoded String.
1022: * Characters that are not part of the Base64 alphabet are ignored
1023: * in the input.
1024: *
1025: * @param string The data to decode.
1026: * @param encIn Character encoding to use when converting input to bytes (should not matter because Base64 data is designed to survive most character encodings)
1027: * @param encOut Character encoding to use when converting decoded bytes to output.
1028: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1029: * @return A decoded String.
1030: *
1031: * @since ostermillerutils 1.02.16
1032: */
1033: public static String decodeToString(String string, String encIn,
1034: String encOut) throws UnsupportedEncodingException {
1035: return new String(decode(string.getBytes(encIn)), encOut);
1036: }
1037:
1038: /**
1039: * Decode a Base64 encoded String to an OutputStream.
1040: * Characters that are not part of the Base64 alphabet are ignored
1041: * in the input.
1042: * The String is converted from bytes according to the platform's
1043: * default character encoding.
1044: *
1045: * @param string The data to decode.
1046: * @param out Stream to which to write decoded data.
1047: * @throws IOException if an IO error occurs.
1048: *
1049: * @since ostermillerutils 1.02.16
1050: */
1051: public static void decodeToStream(String string, OutputStream out)
1052: throws IOException {
1053: decode(new ByteArrayInputStream(string.getBytes()), out);
1054: }
1055:
1056: /**
1057: * Decode a Base64 encoded String to an OutputStream.
1058: * Characters that are not part of the Base64 alphabet are ignored
1059: * in the input.
1060: *
1061: * @param string The data to decode.
1062: * @param enc Character encoding to use when converting to and from bytes.
1063: * @param out Stream to which to write decoded data.
1064: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1065: * @throws IOException if an IO error occurs.
1066: *
1067: * @since ostermillerutils 1.02.16
1068: */
1069: public static void decodeToStream(String string, String enc,
1070: OutputStream out) throws UnsupportedEncodingException,
1071: IOException {
1072: decode(new ByteArrayInputStream(string.getBytes(enc)), out);
1073: }
1074:
1075: /**
1076: * Decode a Base64 encoded String.
1077: * Characters that are not part of the Base64 alphabet are ignored
1078: * in the input.
1079: * The String is converted from bytes according to the platform's
1080: * default character encoding.
1081: *
1082: * @param string The data to decode.
1083: * @return decoded data.
1084: *
1085: * @since ostermillerutils 1.02.16
1086: */
1087: public static byte[] decodeToBytes(String string) {
1088: return decode(string.getBytes());
1089: }
1090:
1091: /**
1092: * Decode a Base64 encoded String.
1093: * Characters that are not part of the Base64 alphabet are ignored
1094: * in the input.
1095: *
1096: * @param string The data to decode.
1097: * @param enc Character encoding to use when converting from bytes.
1098: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1099: * @return decoded data.
1100: *
1101: * @since ostermillerutils 1.02.16
1102: */
1103: public static byte[] decodeToBytes(String string, String enc)
1104: throws UnsupportedEncodingException {
1105: return decode(string.getBytes(enc));
1106: }
1107:
1108: /**
1109: * Decode Base64 encoded bytes.
1110: * Characters that are not part of the Base64 alphabet are ignored
1111: * in the input.
1112: * The String is converted to bytes according to the platform's
1113: * default character encoding.
1114: *
1115: * @param bytes The data to decode.
1116: * @return A decoded String.
1117: *
1118: * @since ostermillerutils 1.02.16
1119: */
1120: public static String decodeToString(byte[] bytes) {
1121: return new String(decode(bytes));
1122: }
1123:
1124: /**
1125: * Decode Base64 encoded bytes.
1126: * Characters that are not part of the Base64 alphabet are ignored
1127: * in the input.
1128: *
1129: * @param bytes The data to decode.
1130: * @param enc Character encoding to use when converting to and from bytes.
1131: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1132: * @return A decoded String.
1133: *
1134: * @since ostermillerutils 1.02.16
1135: */
1136: public static String decodeToString(byte[] bytes, String enc)
1137: throws UnsupportedEncodingException {
1138: return new String(decode(bytes), enc);
1139: }
1140:
1141: /**
1142: * Decode Base64 encoded bytes.
1143: * Characters that are not part of the Base64 alphabet are ignored
1144: * in the input.
1145: *
1146: * @param bytes The data to decode.
1147: * @return Decoded bytes.
1148: *
1149: * @since ostermillerutils 1.02.16
1150: */
1151: public static byte[] decodeToBytes(byte[] bytes) {
1152: return decode(bytes);
1153: }
1154:
1155: /**
1156: * Decode Base64 encoded bytes.
1157: * Characters that are not part of the Base64 alphabet are ignored
1158: * in the input.
1159: *
1160: * @param bytes The data to decode.
1161: * @return Decoded bytes.
1162: *
1163: * @since ostermillerutils 1.00.00
1164: */
1165: public static byte[] decode(byte[] bytes) {
1166: ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1167: // calculate the length of the resulting output.
1168: // in general it will be at most 3/4 the size of the input
1169: // but the input length must be divisible by four.
1170: // If it isn't the next largest size that is divisible
1171: // by four is used.
1172: int mod;
1173: int length = bytes.length;
1174: if ((mod = length % 4) != 0) {
1175: length += 4 - mod;
1176: }
1177: length = length * 3 / 4;
1178: ByteArrayOutputStream out = new ByteArrayOutputStream(length);
1179: try {
1180: decode(in, out, false);
1181: } catch (IOException x) {
1182: // This can't happen.
1183: // The input and output streams were constructed
1184: // on memory structures that don't actually use IO.
1185: throw new RuntimeException(x);
1186: }
1187: return out.toByteArray();
1188: }
1189:
1190: /**
1191: * Decode Base64 encoded bytes to the an OutputStream.
1192: * Characters that are not part of the Base64 alphabet are ignored
1193: * in the input.
1194: *
1195: * @param bytes The data to decode.
1196: * @param out Stream to which to write decoded data.
1197: * @throws IOException if an IO error occurs.
1198: *
1199: * @since ostermillerutils 1.00.00
1200: */
1201: public static void decode(byte[] bytes, OutputStream out)
1202: throws IOException {
1203: ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1204: decode(in, out, false);
1205: }
1206:
1207: /**
1208: * Decode Base64 encoded bytes to the an OutputStream.
1209: * Characters that are not part of the Base64 alphabet are ignored
1210: * in the input.
1211: *
1212: * @param bytes The data to decode.
1213: * @param out Stream to which to write decoded data.
1214: * @throws IOException if an IO error occurs.
1215: *
1216: * @since ostermillerutils 1.02.16
1217: */
1218: public static void decodeToStream(byte[] bytes, OutputStream out)
1219: throws IOException {
1220: ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1221: decode(in, out, false);
1222: }
1223:
1224: /**
1225: * Decode Base64 encoded data from one file to the other.
1226: * Characters in the Base64 alphabet, white space and equals sign are
1227: * expected to be in url encoded data. The presence of other characters
1228: * could be a sign that the data is corrupted.
1229: *
1230: * @param fIn File to be decoded (will be overwritten).
1231: * @throws IOException if an IO error occurs.
1232: * @throws Base64DecodingException if unexpected data is encountered.
1233: *
1234: * @since ostermillerutils 1.00.00
1235: */
1236: public static void decode(File fIn) throws IOException {
1237: decode(fIn, fIn, true);
1238: }
1239:
1240: /**
1241: * Decode Base64 encoded data from one file to the other.
1242: * Characters in the Base64 alphabet, white space and equals sign are
1243: * expected to be in url encoded data. The presence of other characters
1244: * could be a sign that the data is corrupted.
1245: *
1246: * @param fIn File to be decoded (will be overwritten).
1247: * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1248: * @throws IOException if an IO error occurs.
1249: * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1250: *
1251: * @since ostermillerutils 1.00.00
1252: */
1253: public static void decode(File fIn, boolean throwExceptions)
1254: throws IOException {
1255: decode(fIn, fIn, throwExceptions);
1256: }
1257:
1258: /**
1259: * Decode Base64 encoded data from one file to the other.
1260: * Characters in the Base64 alphabet, white space and equals sign are
1261: * expected to be in url encoded data. The presence of other characters
1262: * could be a sign that the data is corrupted.
1263: *
1264: * @param fIn File to be decoded.
1265: * @param fOut File to which the results should be written (may be the same as fIn).
1266: * @throws IOException if an IO error occurs.
1267: * @throws Base64DecodingException if unexpected data is encountered.
1268: *
1269: * @since ostermillerutils 1.00.00
1270: */
1271: public static void decode(File fIn, File fOut) throws IOException {
1272: decode(fIn, fOut, true);
1273: }
1274:
1275: /**
1276: * Decode Base64 encoded data from one file to the other.
1277: * Characters in the Base64 alphabet, white space and equals sign are
1278: * expected to be in url encoded data. The presence of other characters
1279: * could be a sign that the data is corrupted.
1280: *
1281: * @param fIn File to be decoded.
1282: * @param fOut File to which the results should be written (may be the same as fIn).
1283: * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1284: * @throws IOException if an IO error occurs.
1285: * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1286: *
1287: * @since ostermillerutils 1.00.00
1288: */
1289: public static void decode(File fIn, File fOut,
1290: boolean throwExceptions) throws IOException {
1291: File temp = null;
1292: InputStream in = null;
1293: OutputStream out = null;
1294: try {
1295: in = new BufferedInputStream(new FileInputStream(fIn));
1296: temp = File.createTempFile("Base64", null, null);
1297: out = new BufferedOutputStream(new FileOutputStream(temp));
1298: decode(in, out, throwExceptions);
1299: in.close();
1300: in = null;
1301: out.flush();
1302: out.close();
1303: out = null;
1304: FileHelper.move(temp, fOut, true);
1305: } finally {
1306: if (in != null) {
1307: try {
1308: in.close();
1309: } catch (IOException ignore) {
1310: if (throwExceptions)
1311: throw ignore;
1312: }
1313: in = null;
1314: }
1315: if (out != null) {
1316: try {
1317: out.flush();
1318: out.close();
1319: } catch (IOException ignore) {
1320: if (throwExceptions)
1321: throw ignore;
1322: }
1323: out = null;
1324: }
1325: }
1326: }
1327:
1328: /**
1329: * Reads the next (decoded) Base64 character from the input stream.
1330: * Non Base64 characters are skipped.
1331: *
1332: * @param in Stream from which bytes are read.
1333: * @param throwExceptions Throw an exception if an unexpected character is encountered.
1334: * @return the next Base64 character from the stream or -1 if there are no more Base64 characters on the stream.
1335: * @throws IOException if an IO Error occurs.
1336: * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1337: *
1338: * @since ostermillerutils 1.00.00
1339: */
1340: private static final int readBase64(InputStream in,
1341: boolean throwExceptions) throws IOException {
1342: int read;
1343: int numPadding = 0;
1344: do {
1345: read = in.read();
1346: if (read == END_OF_INPUT)
1347: return END_OF_INPUT;
1348: read = reverseBase64Chars[(byte) read];
1349: if (throwExceptions
1350: && (read == NON_BASE_64 || (numPadding > 0 && read > NON_BASE_64))) {
1351: throw new Base64DecodingException(MessageFormat.format(
1352: labels.getString("unexpectedchar"),
1353: (Object[]) new String[] { "'" + (char) read
1354: + "' (0x" + Integer.toHexString(read)
1355: + ")" }), (char) read);
1356: }
1357: if (read == NON_BASE_64_PADDING) {
1358: numPadding++;
1359: }
1360: } while (read <= NON_BASE_64);
1361: return read;
1362: }
1363:
1364: /**
1365: * Decode Base64 encoded data from the InputStream to a byte array.
1366: * Characters that are not part of the Base64 alphabet are ignored
1367: * in the input.
1368: *
1369: * @param in Stream from which to read data that needs to be decoded.
1370: * @return decoded data.
1371: * @throws IOException if an IO error occurs.
1372: *
1373: * @since ostermillerutils 1.00.00
1374: */
1375: public static byte[] decodeToBytes(InputStream in)
1376: throws IOException {
1377: ByteArrayOutputStream out = new ByteArrayOutputStream();
1378: decode(in, out, false);
1379: return out.toByteArray();
1380: }
1381:
1382: /**
1383: * Decode Base64 encoded data from the InputStream to a String.
1384: * Characters that are not part of the Base64 alphabet are ignored
1385: * in the input.
1386: * Bytes are converted to characters in the output String according to the platform's
1387: * default character encoding.
1388: *
1389: * @param in Stream from which to read data that needs to be decoded.
1390: * @return decoded data.
1391: * @throws IOException if an IO error occurs.
1392: *
1393: * @since ostermillerutils 1.02.16
1394: */
1395: public static String decodeToString(InputStream in)
1396: throws IOException {
1397: return new String(decodeToBytes(in));
1398: }
1399:
1400: /**
1401: * Decode Base64 encoded data from the InputStream to a String.
1402: * Characters that are not part of the Base64 alphabet are ignored
1403: * in the input.
1404: *
1405: * @param in Stream from which to read data that needs to be decoded.
1406: * @param enc Character encoding to use when converting bytes to characters.
1407: * @return decoded data.
1408: * @throws IOException if an IO error occurs.Throws:
1409: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1410: *
1411: * @since ostermillerutils 1.02.16
1412: */
1413: public static String decodeToString(InputStream in, String enc)
1414: throws IOException {
1415: return new String(decodeToBytes(in), enc);
1416: }
1417:
1418: /**
1419: * Decode Base64 encoded data from the InputStream to the OutputStream.
1420: * Characters in the Base64 alphabet, white space and equals sign are
1421: * expected to be in url encoded data. The presence of other characters
1422: * could be a sign that the data is corrupted.
1423: *
1424: * @param in Stream from which to read data that needs to be decoded.
1425: * @param out Stream to which to write decoded data.
1426: * @throws IOException if an IO error occurs.
1427: * @throws Base64DecodingException if unexpected data is encountered.
1428: *
1429: * @since ostermillerutils 1.00.00
1430: */
1431: public static void decode(InputStream in, OutputStream out)
1432: throws IOException {
1433: decode(in, out, true);
1434: }
1435:
1436: /**
1437: * Decode Base64 encoded data from the InputStream to the OutputStream.
1438: * Characters in the Base64 alphabet, white space and equals sign are
1439: * expected to be in url encoded data. The presence of other characters
1440: * could be a sign that the data is corrupted.
1441: *
1442: * @param in Stream from which to read data that needs to be decoded.
1443: * @param out Stream to which to write decoded data.
1444: * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1445: * @throws IOException if an IO error occurs.
1446: * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1447: *
1448: * @since ostermillerutils 1.00.00
1449: */
1450: public static void decode(InputStream in, OutputStream out,
1451: boolean throwExceptions) throws IOException {
1452: // Base64 decoding converts four bytes of input to three bytes of output
1453: int[] inBuffer = new int[4];
1454:
1455: // read bytes unmapping them from their ASCII encoding in the process
1456: // we must read at least two bytes to be able to output anything
1457: boolean done = false;
1458: while (!done
1459: && (inBuffer[0] = readBase64(in, throwExceptions)) != END_OF_INPUT
1460: && (inBuffer[1] = readBase64(in, throwExceptions)) != END_OF_INPUT) {
1461: // Fill the buffer
1462: inBuffer[2] = readBase64(in, throwExceptions);
1463: inBuffer[3] = readBase64(in, throwExceptions);
1464:
1465: // Calculate the output
1466: // The first two bytes of our in buffer will always be valid
1467: // but we must check to make sure the other two bytes
1468: // are not END_OF_INPUT before using them.
1469: // The basic idea is that the four bytes will get reconstituted
1470: // into three bytes along these lines:
1471: // [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD]
1472: // [AAAAAABB] [BBBBCCCC] [CCDDDDDD]
1473: // bytes are considered to be zero when absent.
1474:
1475: // six A and two B
1476: out.write(inBuffer[0] << 2 | inBuffer[1] >> 4);
1477: if (inBuffer[2] != END_OF_INPUT) {
1478: // four B and four C
1479: out.write(inBuffer[1] << 4 | inBuffer[2] >> 2);
1480: if (inBuffer[3] != END_OF_INPUT) {
1481: // two C and six D
1482: out.write(inBuffer[2] << 6 | inBuffer[3]);
1483: } else {
1484: done = true;
1485: }
1486: } else {
1487: done = true;
1488: }
1489: }
1490: out.flush();
1491: }
1492:
1493: /**
1494: * Determines if the byte array is in base64 format.
1495: * <p>
1496: * Data will be considered to be in base64 format if it contains
1497: * only base64 characters and whitespace with equals sign padding
1498: * on the end so that the number of base64 characters is divisible
1499: * by four.
1500: * <p>
1501: * It is possible for data to be in base64 format but for it to not
1502: * meet these stringent requirements. It is also possible for data
1503: * to meet these requirements even though decoding it would not make
1504: * any sense. This method should be used as a guide but it is not
1505: * authoritative because of the possibility of these false positives
1506: * and false negatives.
1507: * <p>
1508: * Additionally, extra data such as headers or footers may throw
1509: * this method off the scent and cause it to return false.
1510: *
1511: * @param bytes data that could be in base64 format.
1512: * @return true iff the array appears to be in base64 format
1513: *
1514: * @since ostermillerutils 1.00.00
1515: */
1516: public static boolean isBase64(byte[] bytes) {
1517: try {
1518: return isBase64(new ByteArrayInputStream(bytes));
1519: } catch (IOException x) {
1520: // This can't happen.
1521: // The input and output streams were constructed
1522: // on memory structures that don't actually use IO.
1523: return false;
1524: }
1525: }
1526:
1527: /**
1528: * Determines if the String is in base64 format.
1529: * The String is converted to and from bytes according to the platform's
1530: * default character encoding.
1531: * <p>
1532: * Data will be considered to be in base64 format if it contains
1533: * only base64 characters and whitespace with equals sign padding
1534: * on the end so that the number of base64 characters is divisible
1535: * by four.
1536: * <p>
1537: * It is possible for data to be in base64 format but for it to not
1538: * meet these stringent requirements. It is also possible for data
1539: * to meet these requirements even though decoding it would not make
1540: * any sense. This method should be used as a guide but it is not
1541: * authoritative because of the possibility of these false positives
1542: * and false negatives.
1543: * <p>
1544: * Additionally, extra data such as headers or footers may throw
1545: * this method off the scent and cause it to return false.
1546: *
1547: * @param string String that may be in base64 format.
1548: * @return Best guess as to whether the data is in base64 format.
1549: *
1550: * @since ostermillerutils 1.00.00
1551: */
1552: public static boolean isBase64(String string) {
1553: return isBase64(string.getBytes());
1554: }
1555:
1556: /**
1557: * Determines if the String is in base64 format.
1558: * <p>
1559: * Data will be considered to be in base64 format if it contains
1560: * only base64 characters and whitespace with equals sign padding
1561: * on the end so that the number of base64 characters is divisible
1562: * by four.
1563: * <p>
1564: * It is possible for data to be in base64 format but for it to not
1565: * meet these stringent requirements. It is also possible for data
1566: * to meet these requirements even though decoding it would not make
1567: * any sense. This method should be used as a guide but it is not
1568: * authoritative because of the possibility of these false positives
1569: * and false negatives.
1570: * <p>
1571: * Additionally, extra data such as headers or footers may throw
1572: * this method off the scent and cause it to return false.
1573: *
1574: * @param string String that may be in base64 format.
1575: * @param enc Character encoding to use when converting to bytes.
1576: * @return Best guess as to whether the data is in base64 format.
1577: * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1578: */
1579: public static boolean isBase64(String string, String enc)
1580: throws UnsupportedEncodingException {
1581: return isBase64(string.getBytes(enc));
1582: }
1583:
1584: /**
1585: * Determines if the File is in base64 format.
1586: * <p>
1587: * Data will be considered to be in base64 format if it contains
1588: * only base64 characters and whitespace with equals sign padding
1589: * on the end so that the number of base64 characters is divisible
1590: * by four.
1591: * <p>
1592: * It is possible for data to be in base64 format but for it to not
1593: * meet these stringent requirements. It is also possible for data
1594: * to meet these requirements even though decoding it would not make
1595: * any sense. This method should be used as a guide but it is not
1596: * authoritative because of the possibility of these false positives
1597: * and false negatives.
1598: * <p>
1599: * Additionally, extra data such as headers or footers may throw
1600: * this method off the scent and cause it to return false.
1601: *
1602: * @param fIn File that may be in base64 format.
1603: * @return Best guess as to whether the data is in base64 format.
1604: * @throws IOException if an IO error occurs.
1605: *
1606: * @since ostermillerutils 1.00.00
1607: */
1608: public static boolean isBase64(File fIn) throws IOException {
1609: return isBase64(new BufferedInputStream(
1610: new FileInputStream(fIn)));
1611: }
1612:
1613: /**
1614: * Reads data from the stream and determines if it is
1615: * in base64 format.
1616: * <p>
1617: * Data will be considered to be in base64 format if it contains
1618: * only base64 characters and whitespace with equals sign padding
1619: * on the end so that the number of base64 characters is divisible
1620: * by four.
1621: * <p>
1622: * It is possible for data to be in base64 format but for it to not
1623: * meet these stringent requirements. It is also possible for data
1624: * to meet these requirements even though decoding it would not make
1625: * any sense. This method should be used as a guide but it is not
1626: * authoritative because of the possibility of these false positives
1627: * and false negatives.
1628: * <p>
1629: * Additionally, extra data such as headers or footers may throw
1630: * this method off the scent and cause it to return false.
1631: *
1632: * @param in Stream from which to read data to be tested.
1633: * @return Best guess as to whether the data is in base64 format.
1634: * @throws IOException if an IO error occurs.
1635: *
1636: * @since ostermillerutils 1.00.00
1637: */
1638: public static boolean isBase64(InputStream in) throws IOException {
1639: long numBase64Chars = 0;
1640: int numPadding = 0;
1641: int read;
1642:
1643: while ((read = in.read()) != -1) {
1644: read = reverseBase64Chars[read];
1645: if (read == NON_BASE_64) {
1646: return false;
1647: } else if (read == NON_BASE_64_WHITESPACE) {
1648: // ignore white space
1649: } else if (read == NON_BASE_64_PADDING) {
1650: numPadding++;
1651: numBase64Chars++;
1652: } else if (numPadding > 0) {
1653: return false;
1654: } else {
1655: numBase64Chars++;
1656: }
1657: }
1658: if (numBase64Chars == 0)
1659: return false;
1660: if (numBase64Chars % 4 != 0)
1661: return false;
1662: return true;
1663: }
1664: }
|