001: /*
002: * Copyright (c) 1999, 2000
003: * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
004: * D-52064 Aachen, Germany.
005: * All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU Library General Public License as
009: * published by the Free Software Foundation; either version 2 of the
010: * License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Library General Public License for more details.
016: *
017: * You should have received a copy of the GNU Library General Public
018: * License along with this program (see the file COPYING.LIB for more
019: * details); if not, write to the Free Software Foundation, Inc.,
020: * 675 Mass Ave, Cambridge, MA 02139, USA.
021: */
022:
023: package seda.sandStorm.lib.util;
024:
025: import seda.sandStorm.api.*;
026: import seda.sandStorm.core.*;
027: import java.util.*;
028:
029: /**
030: * Encode and decode Base64 data.
031: */
032: public abstract class Base64 {
033:
034: private static final boolean DEBUG = false;
035:
036: /**
037: * Converts data into base64 encoded data.
038: *
039: * @param data Data to be encoded.
040: * @return A BufferElement with the data encoded in Base64.
041: */
042: public static BufferElement encode(BufferElement data) {
043:
044: byte input[] = data.getBytes();
045: int offset = data.getOffset();
046: int length = data.getSize();
047:
048: // Calculate length of encoded data including optional padding.
049: int encodedLength = ((length + 2) / 3) * 4;
050: byte output[] = new byte[encodedLength];
051: int outoffset = 0;
052:
053: // Now do the encoding, thus inflating every three bytes of binary
054: // data to four ASCII characters.
055: int b1, b2, b3;
056: int endPos = offset + length - 1 - 2;
057: while (offset <= endPos) {
058: b1 = input[offset++];
059: b2 = input[offset++];
060: b3 = input[offset++];
061:
062: output[outoffset++] = encodingBase64Alphabet[(b1 >>> 2) & 0x3F];
063: output[outoffset++] = encodingBase64Alphabet[((b1 << 4) & 0x30)
064: | ((b2 >>> 4) & 0xF)];
065: output[outoffset++] = encodingBase64Alphabet[((b2 << 2) & 0x3C)
066: | ((b3 >>> 6) & 0x03)];
067: output[outoffset++] = encodingBase64Alphabet[b3 & 0x3F];
068: }
069:
070: // If one or two bytes are left (because we work on blocks of three
071: // bytes), convert them too and apply padding.
072:
073: endPos += 2; // now points to the last encodable byte
074: if (offset <= endPos) {
075: b1 = input[offset++];
076: output[outoffset++] = encodingBase64Alphabet[(b1 >>> 2) & 0x3F];
077: if (offset <= endPos) {
078: b2 = input[offset++];
079: output[outoffset++] = encodingBase64Alphabet[((b1 << 4) & 0x30)
080: | ((b2 >>> 4) & 0xF)];
081: output[outoffset++] = encodingBase64Alphabet[(b2 << 2) & 0x3C];
082: output[outoffset] = '=';
083: } else {
084: output[outoffset++] = encodingBase64Alphabet[(b1 << 4) & 0x30];
085: output[outoffset++] = '=';
086: output[outoffset] = '=';
087: }
088: }
089: return new BufferElement(output);
090: }
091:
092: /**
093: * Converts Base64 encoded data into binary data.
094: *
095: * @param data Base64 encoded data.
096: * @return A BufferElement containing the decoded data.
097: */
098: public static BufferElement decode(BufferElement data) {
099:
100: byte input[] = data.getBytes();
101: int offset = data.getOffset();
102: int length = data.getSize();
103:
104: // Check if we need to squash the data
105: if ((offset != 0) || (length != input.length)) {
106: byte input2[] = new byte[length];
107: System.arraycopy(input, offset, input2, 0, length);
108: input = input2;
109: }
110:
111: // Strip out whitespace
112: StringTokenizer st = new StringTokenizer(new String(input),
113: " \t\r\n", false);
114: String newinput = new String();
115: while (st.hasMoreTokens()) {
116: newinput += st.nextToken();
117: }
118: input = newinput.getBytes();
119: offset = 0;
120: length = input.length;
121:
122: // Calculate length of decoded data including optional padding.
123: int endPos = offset + length - 1;
124: // Determine the length of data to be decoded. Optional padding has
125: // to be removed first.
126: if (DEBUG)
127: System.err.println("endPos: " + endPos);
128: while ((endPos >= 0)
129: && ((input[endPos] == '=') || (input[endPos] == '\r') || (input[endPos] == '\n'))) {
130: endPos--;
131: }
132: if (DEBUG)
133: System.err.println("endPos fixed to: " + endPos);
134:
135: int decodedLength = endPos - offset - length / 4 + 1;
136: byte output[] = new byte[decodedLength];
137: int outoffset = 0;
138:
139: // Now do the four-to-three entities/letters/bytes/whatever
140: // conversion. We chew on as many four-letter groups as we can,
141: // converting them into three byte groups.
142:
143: byte b1, b2, b3, b4;
144: // This points to the last letter in the last four-letter group
145: int stopPos = endPos - 3;
146: while (offset <= stopPos) {
147: if (DEBUG)
148: System.err.print("b1: " + input[offset] + " -> ");
149: b1 = decodingBase64Alphabet[input[offset++]];
150: if (DEBUG)
151: System.err
152: .print(b1 + "\nb2: " + input[offset] + " -> ");
153: b2 = decodingBase64Alphabet[input[offset++]];
154: if (DEBUG)
155: System.err
156: .print(b2 + "\nb3: " + input[offset] + " -> ");
157: b3 = decodingBase64Alphabet[input[offset++]];
158: if (DEBUG)
159: System.err
160: .print(b3 + "\nb4: " + input[offset] + " -> ");
161: b4 = decodingBase64Alphabet[input[offset++]];
162: if (DEBUG)
163: System.err.println(b4);
164: output[outoffset++] = (byte) (((b1 << 2) & 0xFF) | ((b2 >>> 4) & 0x03));
165: output[outoffset++] = (byte) (((b2 << 4) & 0xFF) | ((b3 >>> 2) & 0x0F));
166: output[outoffset++] = (byte) (((b3 << 6) & 0xFF) | (b4 & 0x3F));
167: }
168:
169: // If one, two or three letters from the base64 encoded data are
170: // left, convert them too.
171: // Hack Note(tm): if the length of encoded data is not a multiple
172: // of four, then padding must occur ('='). As the decoding alphabet
173: // contains zeros everywhere with the exception of valid letters,
174: // indexing into the mapping is just fine and reliefs us of the
175: // pain to check everything and make thus makes the code better.
176:
177: if (DEBUG)
178: System.err.println("offset " + offset + ", endPos "
179: + endPos + ", length " + length);
180:
181: if (offset <= endPos) {
182: if (DEBUG)
183: System.err.print("END b1: " + input[offset] + " -> ");
184: b1 = decodingBase64Alphabet[input[offset++]];
185: if (DEBUG)
186: System.err.print(b1 + "\nEND b2: " + input[offset]
187: + " -> ");
188: b2 = decodingBase64Alphabet[input[offset++]];
189: if (DEBUG)
190: System.err.println(b2);
191: output[outoffset++] = (byte) (((b1 << 2) & 0xFF) | ((b2 >>> 4) & 0x03));
192: if (offset <= endPos) {
193: b3 = decodingBase64Alphabet[input[offset]];
194: output[outoffset++] = (byte) (((b2 << 4) & 0xFF) | ((b3 >>> 2) & 0x0F));
195: }
196: }
197: return new BufferElement(output);
198: }
199:
200: /**
201: * Mapping from binary 0-63 to base64 alphabet according to RFC 2045.
202: */
203: private static final byte[] encodingBase64Alphabet = { 'A', 'B',
204: 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
205: 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
206: 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
207: 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
208: 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
209: '+', '/' };
210:
211: /**
212: * Mapping from base64 alphabet to binary 0-63.
213: */
214: private static final byte[] decodingBase64Alphabet;
215:
216: /**
217: * The class initializer is responsible to set up the mapping from the
218: * base64 alphabet (ASCII-based) to binary 0-63.
219: */
220: static {
221: decodingBase64Alphabet = new byte[256];
222: for (int i = 0; i < 64; ++i) {
223: decodingBase64Alphabet[encodingBase64Alphabet[i]] = (byte) (i & 0xff);
224: }
225: }
226:
227: }
|