001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.apache.geronimo.util.encoders;
020
021 import java.io.IOException;
022 import java.io.OutputStream;
023
024 public class Base64Encoder
025 implements Encoder
026 {
027 protected final byte[] encodingTable =
028 {
029 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
030 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
031 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
032 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
033 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
034 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
035 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
036 (byte)'v',
037 (byte)'w', (byte)'x', (byte)'y', (byte)'z',
038 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
039 (byte)'7', (byte)'8', (byte)'9',
040 (byte)'+', (byte)'/'
041 };
042
043 protected byte padding = (byte)'=';
044
045 /*
046 * set up the decoding table.
047 */
048 protected final byte[] decodingTable = new byte[128];
049
050 protected void initialiseDecodingTable()
051 {
052 for (int i = 0; i < encodingTable.length; i++)
053 {
054 decodingTable[encodingTable[i]] = (byte)i;
055 }
056 }
057
058 public Base64Encoder()
059 {
060 initialiseDecodingTable();
061 }
062
063 /**
064 * encode the input data producing a base 64 output stream.
065 *
066 * @return the number of bytes produced.
067 */
068 public int encode(
069 byte[] data,
070 int off,
071 int length,
072 OutputStream out)
073 throws IOException
074 {
075 int modulus = length % 3;
076 int dataLength = (length - modulus);
077 int a1, a2, a3;
078
079 for (int i = off; i < off + dataLength; i += 3)
080 {
081 a1 = data[i] & 0xff;
082 a2 = data[i + 1] & 0xff;
083 a3 = data[i + 2] & 0xff;
084
085 out.write(encodingTable[(a1 >>> 2) & 0x3f]);
086 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
087 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
088 out.write(encodingTable[a3 & 0x3f]);
089 }
090
091 /*
092 * process the tail end.
093 */
094 int b1, b2, b3;
095 int d1, d2;
096
097 switch (modulus)
098 {
099 case 0: /* nothing left to do */
100 break;
101 case 1:
102 d1 = data[off + dataLength] & 0xff;
103 b1 = (d1 >>> 2) & 0x3f;
104 b2 = (d1 << 4) & 0x3f;
105
106 out.write(encodingTable[b1]);
107 out.write(encodingTable[b2]);
108 out.write(padding);
109 out.write(padding);
110 break;
111 case 2:
112 d1 = data[off + dataLength] & 0xff;
113 d2 = data[off + dataLength + 1] & 0xff;
114
115 b1 = (d1 >>> 2) & 0x3f;
116 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
117 b3 = (d2 << 2) & 0x3f;
118
119 out.write(encodingTable[b1]);
120 out.write(encodingTable[b2]);
121 out.write(encodingTable[b3]);
122 out.write(padding);
123 break;
124 }
125
126 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
127 }
128
129 private boolean ignore(
130 char c)
131 {
132 return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
133 }
134
135 /**
136 * decode the base 64 encoded byte data writing it to the given output stream,
137 * whitespace characters will be ignored.
138 *
139 * @return the number of bytes produced.
140 */
141 public int decode(
142 byte[] data,
143 int off,
144 int length,
145 OutputStream out)
146 throws IOException
147 {
148 byte[] bytes;
149 byte b1, b2, b3, b4;
150 int outLen = 0;
151
152 int end = off + length;
153
154 while (end > 0)
155 {
156 if (!ignore((char)data[end - 1]))
157 {
158 break;
159 }
160
161 end--;
162 }
163
164 int i = off;
165 int finish = end - 4;
166
167 while (i < finish)
168 {
169 while ((i < finish) && ignore((char)data[i]))
170 {
171 i++;
172 }
173
174 b1 = decodingTable[data[i++]];
175
176 while ((i < finish) && ignore((char)data[i]))
177 {
178 i++;
179 }
180
181 b2 = decodingTable[data[i++]];
182
183 while ((i < finish) && ignore((char)data[i]))
184 {
185 i++;
186 }
187
188 b3 = decodingTable[data[i++]];
189
190 while ((i < finish) && ignore((char)data[i]))
191 {
192 i++;
193 }
194
195 b4 = decodingTable[data[i++]];
196
197 out.write((b1 << 2) | (b2 >> 4));
198 out.write((b2 << 4) | (b3 >> 2));
199 out.write((b3 << 6) | b4);
200
201 outLen += 3;
202 }
203
204 if (data[end - 2] == padding)
205 {
206 b1 = decodingTable[data[end - 4]];
207 b2 = decodingTable[data[end - 3]];
208
209 out.write((b1 << 2) | (b2 >> 4));
210
211 outLen += 1;
212 }
213 else if (data[end - 1] == padding)
214 {
215 b1 = decodingTable[data[end - 4]];
216 b2 = decodingTable[data[end - 3]];
217 b3 = decodingTable[data[end - 2]];
218
219 out.write((b1 << 2) | (b2 >> 4));
220 out.write((b2 << 4) | (b3 >> 2));
221
222 outLen += 2;
223 }
224 else
225 {
226 b1 = decodingTable[data[end - 4]];
227 b2 = decodingTable[data[end - 3]];
228 b3 = decodingTable[data[end - 2]];
229 b4 = decodingTable[data[end - 1]];
230
231 out.write((b1 << 2) | (b2 >> 4));
232 out.write((b2 << 4) | (b3 >> 2));
233 out.write((b3 << 6) | b4);
234
235 outLen += 3;
236 }
237
238 return outLen;
239 }
240
241 /**
242 * decode the base 64 encoded String data writing it to the given output stream,
243 * whitespace characters will be ignored.
244 *
245 * @return the number of bytes produced.
246 */
247 public int decode(
248 String data,
249 OutputStream out)
250 throws IOException
251 {
252 byte[] bytes;
253 byte b1, b2, b3, b4;
254 int length = 0;
255
256 int end = data.length();
257
258 while (end > 0)
259 {
260 if (!ignore(data.charAt(end - 1)))
261 {
262 break;
263 }
264
265 end--;
266 }
267
268 int i = 0;
269 int finish = end - 4;
270
271 while (i < finish)
272 {
273 while ((i < finish) && ignore(data.charAt(i)))
274 {
275 i++;
276 }
277
278 b1 = decodingTable[data.charAt(i++)];
279
280 while ((i < finish) && ignore(data.charAt(i)))
281 {
282 i++;
283 }
284 b2 = decodingTable[data.charAt(i++)];
285
286 while ((i < finish) && ignore(data.charAt(i)))
287 {
288 i++;
289 }
290 b3 = decodingTable[data.charAt(i++)];
291
292 while ((i < finish) && ignore(data.charAt(i)))
293 {
294 i++;
295 }
296 b4 = decodingTable[data.charAt(i++)];
297
298 out.write((b1 << 2) | (b2 >> 4));
299 out.write((b2 << 4) | (b3 >> 2));
300 out.write((b3 << 6) | b4);
301
302 length += 3;
303 }
304
305 if (data.charAt(end - 2) == padding)
306 {
307 b1 = decodingTable[data.charAt(end - 4)];
308 b2 = decodingTable[data.charAt(end - 3)];
309
310 out.write((b1 << 2) | (b2 >> 4));
311
312 length += 1;
313 }
314 else if (data.charAt(end - 1) == padding)
315 {
316 b1 = decodingTable[data.charAt(end - 4)];
317 b2 = decodingTable[data.charAt(end - 3)];
318 b3 = decodingTable[data.charAt(end - 2)];
319
320 out.write((b1 << 2) | (b2 >> 4));
321 out.write((b2 << 4) | (b3 >> 2));
322
323 length += 2;
324 }
325 else
326 {
327 b1 = decodingTable[data.charAt(end - 4)];
328 b2 = decodingTable[data.charAt(end - 3)];
329 b3 = decodingTable[data.charAt(end - 2)];
330 b4 = decodingTable[data.charAt(end - 1)];
331
332 out.write((b1 << 2) | (b2 >> 4));
333 out.write((b2 << 4) | (b3 >> 2));
334 out.write((b3 << 6) | b4);
335
336 length += 3;
337 }
338
339 return length;
340 }
341 }