/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ package net.ontopia.net; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import net.ontopia.utils.OntopiaRuntimeException; /** * INTERNAL: This class contains methods for encoding base64 * streams. Base64 encoding is described in section 6.8 of RFC 2045. */ public class Base64Decoder { private static char[] map = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; private static int[] invmap; private static final int BUFFER_SIZE = 1024 ; /** * INTERNAL: Decode string and return result as a string. */ public static String decode(String string) throws IOException { ByteArrayOutputStream ostream = new ByteArrayOutputStream(); decode(string, ostream); return ostream.toString("ISO-8859-1"); } /** * INTERNAL: Decode string and write result to output stream. */ public static void decode(String string, OutputStream ostream) throws IOException { decode(new ByteArrayInputStream(string.getBytes("ISO-8859-1")), ostream); } /** * INTERNAL: Decodes the characters in input to the output byte * array, returning the number of bytes written. If the output array * is too short, that's just too bad. */ public static int decode(char[] input, int offset, int length, byte[] output) { if (invmap == null) { invmap = new int[256]; for (int ix = 0; ix < invmap.length; ix++) invmap[ix] = -1; for (int ix = 0; ix < map.length; ix++) invmap[(byte) map[ix]] = ix; } byte[] octets = new byte[4]; int octetCount = 0; int outpos = 0; for (int ix = 0; ix < length; ix++) if (input[ix] == '=') break; else if (invmap[input[ix]] != -1) { octets[octetCount++] = (byte) invmap[input[ix]]; if (octetCount == 4) { output[outpos++] = (byte) ((octets[0] << 2) | ((octets[1] & 0x30) >> 4)); output[outpos++] = (byte) (((octets[1] & 0x0F) << 4) | ((octets[2] & 0x3C) >> 2)); output[outpos++] = (byte) (((octets[2] & 0x03) << 6) | (octets[3] & 0x3F)); octetCount = 0; } } if (octetCount > 1) output[outpos++] = (byte) ((octets[0] << 2) | ((octets[1] & 0x30) >> 4)); if (octetCount > 2) output[outpos++] = (byte) (((octets[1] & 0x0F) << 4) | ((octets[2] & 0x3C) >> 2)); return outpos; } /** * INTERNAL: Decode input stream and write result to output stream. */ public static void decode(InputStream istream, OutputStream ostream) throws IOException { byte buffer[] = new byte[BUFFER_SIZE] ; byte chunk[] = new byte[4] ; int got = -1 ; int ready = 0 ; fill: while ((got = istream.read(buffer)) > 0) { int skiped = 0 ; while ( skiped < got ) { // Check for un-understood characters: while ( ready < 4 ) { if ( skiped >= got ) continue fill ; int ch = check (buffer[skiped++]) ; if ( ch >= 0 ) chunk[ready++] = (byte) ch ; } if ( chunk[2] == 65 ) { ostream.write(get1(chunk, 0)); return ; } else if ( chunk[3] == 65 ) { ostream.write(get1(chunk, 0)) ; ostream.write(get2(chunk, 0)) ; return ; } else { ostream.write(get1(chunk, 0)) ; ostream.write(get2(chunk, 0)) ; ostream.write(get3(chunk, 0)) ; } ready = 0 ; } } if (ready != 0) throw new OntopiaRuntimeException ("Invalid length.") ; ostream.flush() ; } private static final int get1 (byte buf[], int off) { return ((buf[off] & 0x3f) << 2) | ((buf[off+1] & 0x30) >>> 4) ; } private static final int get2 (byte buf[], int off) { return ((buf[off+1] & 0x0f) << 4) | ((buf[off+2] &0x3c) >>> 2) ; } private static final int get3 (byte buf[], int off) { return ((buf[off+2] & 0x03) << 6) | (buf[off+3] & 0x3f) ; } private static final int check (int ch) { if ((ch >= 'A') && (ch <= 'Z')) { return ch - 'A' ; } else if ((ch >= 'a') && (ch <= 'z')) { return ch - 'a' + 26 ; } else if ((ch >= '0') && (ch <= '9')) { return ch - '0' + 52 ; } else { switch (ch) { case '=': return 65 ; case '+': return 62 ; case '/': return 63 ; default: return -1 ; } } } }