001 /**
002 *
003 * Copyright 2003-2006 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.mail.util;
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.io.OutputStream;
023 import java.io.PrintStream;
024
025 public class Base64Encoder
026 implements Encoder
027 {
028 protected final byte[] encodingTable =
029 {
030 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
031 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
032 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
033 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
034 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
035 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
036 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
037 (byte)'v',
038 (byte)'w', (byte)'x', (byte)'y', (byte)'z',
039 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
040 (byte)'7', (byte)'8', (byte)'9',
041 (byte)'+', (byte)'/'
042 };
043
044 protected byte padding = (byte)'=';
045
046 /*
047 * set up the decoding table.
048 */
049 protected final byte[] decodingTable = new byte[256];
050
051 protected void initialiseDecodingTable()
052 {
053 for (int i = 0; i < encodingTable.length; i++)
054 {
055 decodingTable[encodingTable[i]] = (byte)i;
056 }
057 }
058
059 public Base64Encoder()
060 {
061 initialiseDecodingTable();
062 }
063
064 /**
065 * encode the input data producing a base 64 output stream.
066 *
067 * @return the number of bytes produced.
068 */
069 public int encode(
070 byte[] data,
071 int off,
072 int length,
073 OutputStream out)
074 throws IOException
075 {
076 int modulus = length % 3;
077 int dataLength = (length - modulus);
078 int a1, a2, a3;
079
080 for (int i = off; i < off + dataLength; i += 3)
081 {
082 a1 = data[i] & 0xff;
083 a2 = data[i + 1] & 0xff;
084 a3 = data[i + 2] & 0xff;
085
086 out.write(encodingTable[(a1 >>> 2) & 0x3f]);
087 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
088 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
089 out.write(encodingTable[a3 & 0x3f]);
090 }
091
092 /*
093 * process the tail end.
094 */
095 int b1, b2, b3;
096 int d1, d2;
097
098 switch (modulus)
099 {
100 case 0: /* nothing left to do */
101 break;
102 case 1:
103 d1 = data[off + dataLength] & 0xff;
104 b1 = (d1 >>> 2) & 0x3f;
105 b2 = (d1 << 4) & 0x3f;
106
107 out.write(encodingTable[b1]);
108 out.write(encodingTable[b2]);
109 out.write(padding);
110 out.write(padding);
111 break;
112 case 2:
113 d1 = data[off + dataLength] & 0xff;
114 d2 = data[off + dataLength + 1] & 0xff;
115
116 b1 = (d1 >>> 2) & 0x3f;
117 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
118 b3 = (d2 << 2) & 0x3f;
119
120 out.write(encodingTable[b1]);
121 out.write(encodingTable[b2]);
122 out.write(encodingTable[b3]);
123 out.write(padding);
124 break;
125 }
126
127 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
128 }
129
130 private boolean ignore(
131 char c)
132 {
133 return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
134 }
135
136 /**
137 * decode the base 64 encoded byte data writing it to the given output stream,
138 * whitespace characters will be ignored.
139 *
140 * @return the number of bytes produced.
141 */
142 public int decode(
143 byte[] data,
144 int off,
145 int length,
146 OutputStream out)
147 throws IOException
148 {
149 byte[] bytes;
150 byte b1, b2, b3, b4;
151 int outLen = 0;
152
153 int end = off + length;
154
155 while (end > 0)
156 {
157 if (!ignore((char)data[end - 1]))
158 {
159 break;
160 }
161
162 end--;
163 }
164
165 int i = off;
166 int finish = end - 4;
167
168 while (i < finish)
169 {
170 while ((i < finish) && ignore((char)data[i]))
171 {
172 i++;
173 }
174
175 b1 = decodingTable[data[i++]];
176
177 while ((i < finish) && ignore((char)data[i]))
178 {
179 i++;
180 }
181
182 b2 = decodingTable[data[i++]];
183
184 while ((i < finish) && ignore((char)data[i]))
185 {
186 i++;
187 }
188
189 b3 = decodingTable[data[i++]];
190
191 while ((i < finish) && ignore((char)data[i]))
192 {
193 i++;
194 }
195
196 b4 = decodingTable[data[i++]];
197
198 out.write((b1 << 2) | (b2 >> 4));
199 out.write((b2 << 4) | (b3 >> 2));
200 out.write((b3 << 6) | b4);
201
202 outLen += 3;
203 }
204
205 if (data[end - 2] == padding)
206 {
207 b1 = decodingTable[data[end - 4]];
208 b2 = decodingTable[data[end - 3]];
209
210 out.write((b1 << 2) | (b2 >> 4));
211
212 outLen += 1;
213 }
214 else if (data[end - 1] == padding)
215 {
216 b1 = decodingTable[data[end - 4]];
217 b2 = decodingTable[data[end - 3]];
218 b3 = decodingTable[data[end - 2]];
219
220 out.write((b1 << 2) | (b2 >> 4));
221 out.write((b2 << 4) | (b3 >> 2));
222
223 outLen += 2;
224 }
225 else
226 {
227 b1 = decodingTable[data[end - 4]];
228 b2 = decodingTable[data[end - 3]];
229 b3 = decodingTable[data[end - 2]];
230 b4 = decodingTable[data[end - 1]];
231
232 out.write((b1 << 2) | (b2 >> 4));
233 out.write((b2 << 4) | (b3 >> 2));
234 out.write((b3 << 6) | b4);
235
236 outLen += 3;
237 }
238
239 return outLen;
240 }
241
242 /**
243 * decode the base 64 encoded String data writing it to the given output stream,
244 * whitespace characters will be ignored.
245 *
246 * @return the number of bytes produced.
247 */
248 public int decode(
249 String data,
250 OutputStream out)
251 throws IOException
252 {
253 byte[] bytes;
254 byte b1, b2, b3, b4;
255 int length = 0;
256
257 int end = data.length();
258
259 while (end > 0)
260 {
261 if (!ignore(data.charAt(end - 1)))
262 {
263 break;
264 }
265
266 end--;
267 }
268
269 int i = 0;
270 int finish = end - 4;
271
272 while (i < finish)
273 {
274 while ((i < finish) && ignore(data.charAt(i)))
275 {
276 i++;
277 }
278
279 b1 = decodingTable[data.charAt(i++)];
280
281 while ((i < finish) && ignore(data.charAt(i)))
282 {
283 i++;
284 }
285 b2 = decodingTable[data.charAt(i++)];
286
287 while ((i < finish) && ignore(data.charAt(i)))
288 {
289 i++;
290 }
291 b3 = decodingTable[data.charAt(i++)];
292
293 while ((i < finish) && ignore(data.charAt(i)))
294 {
295 i++;
296 }
297 b4 = decodingTable[data.charAt(i++)];
298
299 out.write((b1 << 2) | (b2 >> 4));
300 out.write((b2 << 4) | (b3 >> 2));
301 out.write((b3 << 6) | b4);
302
303 length += 3;
304 }
305
306 if (data.charAt(end - 2) == padding)
307 {
308 b1 = decodingTable[data.charAt(end - 4)];
309 b2 = decodingTable[data.charAt(end - 3)];
310
311 out.write((b1 << 2) | (b2 >> 4));
312
313 length += 1;
314 }
315 else if (data.charAt(end - 1) == padding)
316 {
317 b1 = decodingTable[data.charAt(end - 4)];
318 b2 = decodingTable[data.charAt(end - 3)];
319 b3 = decodingTable[data.charAt(end - 2)];
320
321 out.write((b1 << 2) | (b2 >> 4));
322 out.write((b2 << 4) | (b3 >> 2));
323
324 length += 2;
325 }
326 else
327 {
328 b1 = decodingTable[data.charAt(end - 4)];
329 b2 = decodingTable[data.charAt(end - 3)];
330 b3 = decodingTable[data.charAt(end - 2)];
331 b4 = decodingTable[data.charAt(end - 1)];
332
333 out.write((b1 << 2) | (b2 >> 4));
334 out.write((b2 << 4) | (b3 >> 2));
335 out.write((b3 << 6) | b4);
336
337 length += 3;
338 }
339
340 return length;
341 }
342
343 /**
344 * decode the base 64 encoded byte data writing it to the provided byte array buffer.
345 *
346 * @return the number of bytes produced.
347 */
348 public int decode(byte[] data, int off, int length, byte[] out) throws IOException
349 {
350 byte[] bytes;
351 byte b1, b2, b3, b4;
352 int outLen = 0;
353
354 int end = off + length;
355
356 while (end > 0)
357 {
358 if (!ignore((char)data[end - 1]))
359 {
360 break;
361 }
362
363 end--;
364 }
365
366 int i = off;
367 int finish = end - 4;
368
369 while (i < finish)
370 {
371 while ((i < finish) && ignore((char)data[i]))
372 {
373 i++;
374 }
375
376 b1 = decodingTable[data[i++]];
377
378 while ((i < finish) && ignore((char)data[i]))
379 {
380 i++;
381 }
382
383 b2 = decodingTable[data[i++]];
384
385 while ((i < finish) && ignore((char)data[i]))
386 {
387 i++;
388 }
389
390 b3 = decodingTable[data[i++]];
391
392 while ((i < finish) && ignore((char)data[i]))
393 {
394 i++;
395 }
396
397 b4 = decodingTable[data[i++]];
398
399 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
400 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
401 out[outLen++] = (byte)((b3 << 6) | b4);
402 }
403
404 if (data[end - 2] == padding)
405 {
406 b1 = decodingTable[data[end - 4]];
407 b2 = decodingTable[data[end - 3]];
408
409 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
410 }
411 else if (data[end - 1] == padding)
412 {
413 b1 = decodingTable[data[end - 4]];
414 b2 = decodingTable[data[end - 3]];
415 b3 = decodingTable[data[end - 2]];
416
417 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
418 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
419 }
420 else
421 {
422 b1 = decodingTable[data[end - 4]];
423 b2 = decodingTable[data[end - 3]];
424 b3 = decodingTable[data[end - 2]];
425 b4 = decodingTable[data[end - 1]];
426
427 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
428 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
429 out[outLen++] = (byte)((b3 << 6) | b4);
430 }
431
432 return outLen;
433 }
434
435 /**
436 * Test if a character is a valid Base64 encoding character. This
437 * must be either a valid digit or the padding character ("=").
438 *
439 * @param ch The test character.
440 *
441 * @return true if this is valid in Base64 encoded data, false otherwise.
442 */
443 public boolean isValidBase64(int ch) {
444 // 'A' has the value 0 in the decoding table, so we need a special one for that
445 return ch == padding || ch == 'A' || decodingTable[ch] != 0;
446 }
447
448
449 /**
450 * Perform RFC-2047 word encoding using Base64 data encoding.
451 *
452 * @param in The source for the encoded data.
453 * @param charset The charset tag to be added to each encoded data section.
454 * @param out The output stream where the encoded data is to be written.
455 * @param fold Controls whether separate sections of encoded data are separated by
456 * linebreaks or whitespace.
457 *
458 * @exception IOException
459 */
460 public void encodeWord(InputStream in, String charset, OutputStream out, boolean fold) throws IOException
461 {
462 PrintStream writer = new PrintStream(out);
463
464 // encoded words are restricted to 76 bytes, including the control adornments.
465 int limit = 76 - 7 - charset.length();
466 boolean firstLine = true;
467 StringBuffer encodedString = new StringBuffer(76);
468
469 while (true) {
470 // encode the next segment.
471 encode(in, encodedString, limit);
472 // if we're out of data, nothing will be encoded.
473 if (encodedString.length() == 0) {
474 break;
475 }
476
477 // if we have more than one segment, we need to insert separators. Depending on whether folding
478 // was requested, this is either a blank or a linebreak.
479 if (!firstLine) {
480 if (fold) {
481 writer.print("\r\n");
482 }
483 else {
484 writer.print(" ");
485 }
486 }
487
488 // add the encoded word header
489 writer.print("=?");
490 writer.print(charset);
491 writer.print("?B?");
492 // the data
493 writer.print(encodedString.toString());
494 // and the word terminator.
495 writer.print("?=");
496 writer.flush();
497
498 // reset our string buffer for the next segment.
499 encodedString.setLength(0);
500 }
501 }
502
503 /**
504 * encode the input data producing a base 64 output stream.
505 *
506 * @return the number of bytes produced.
507 */
508 public void encode(InputStream in, StringBuffer out, int limit) throws IOException
509 {
510 int count = limit / 4;
511 byte [] inBuffer = new byte[3];
512
513 while (count-- > 0) {
514
515 int readCount = in.read(inBuffer);
516 // did we get a full triplet? that's an easy encoding.
517 if (readCount == 3) {
518 int a1 = inBuffer[0] & 0xff;
519 int a2 = inBuffer[1] & 0xff;
520 int a3 = inBuffer[2] & 0xff;
521
522 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
523 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
524 out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
525 out.append((char)encodingTable[a3 & 0x3f]);
526
527 }
528 else if (readCount <= 0) {
529 // eof condition, don'e entirely.
530 return;
531 }
532 else if (readCount == 1) {
533 int a1 = inBuffer[0] & 0xff;
534 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
535 out.append((char)encodingTable[(a1 << 4) & 0x3f]);
536 out.append((char)padding);
537 out.append((char)padding);
538 return;
539 }
540 else if (readCount == 2) {
541 int a1 = inBuffer[0] & 0xff;
542 int a2 = inBuffer[1] & 0xff;
543
544 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
545 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
546 out.append((char)encodingTable[(a2 << 2) & 0x3f]);
547 out.append((char)padding);
548 return;
549 }
550 }
551 }
552 }