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