1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.mail.util;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.PrintStream;
24
25 public class Base64Encoder
26 implements Encoder
27 {
28 protected final byte[] encodingTable =
29 {
30 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
31 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
32 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
33 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
34 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
35 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
36 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
37 (byte)'v',
38 (byte)'w', (byte)'x', (byte)'y', (byte)'z',
39 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
40 (byte)'7', (byte)'8', (byte)'9',
41 (byte)'+', (byte)'/'
42 };
43
44 protected byte padding = (byte)'=';
45
46
47
48
49 protected final byte[] decodingTable = new byte[256];
50
51 protected void initialiseDecodingTable()
52 {
53 for (int i = 0; i < encodingTable.length; i++)
54 {
55 decodingTable[encodingTable[i]] = (byte)i;
56 }
57 }
58
59 public Base64Encoder()
60 {
61 initialiseDecodingTable();
62 }
63
64 /**
65 * encode the input data producing a base 64 output stream.
66 *
67 * @return the number of bytes produced.
68 */
69 public int encode(
70 byte[] data,
71 int off,
72 int length,
73 OutputStream out)
74 throws IOException
75 {
76 int modulus = length % 3;
77 int dataLength = (length - modulus);
78 int a1, a2, a3;
79
80 for (int i = off; i < off + dataLength; i += 3)
81 {
82 a1 = data[i] & 0xff;
83 a2 = data[i + 1] & 0xff;
84 a3 = data[i + 2] & 0xff;
85
86 out.write(encodingTable[(a1 >>> 2) & 0x3f]);
87 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
88 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
89 out.write(encodingTable[a3 & 0x3f]);
90 }
91
92
93
94
95 int b1, b2, b3;
96 int d1, d2;
97
98 switch (modulus)
99 {
100 case 0:
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
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
465 int limit = 76 - 7 - charset.length();
466 boolean firstLine = true;
467 StringBuffer encodedString = new StringBuffer(76);
468
469 while (true) {
470
471 encode(in, encodedString, limit);
472
473 if (encodedString.length() == 0) {
474 break;
475 }
476
477
478
479 if (!firstLine) {
480 if (fold) {
481 writer.print("\r\n");
482 }
483 else {
484 writer.print(" ");
485 }
486 }
487
488
489 writer.print("=?");
490 writer.print(charset);
491 writer.print("?B?");
492
493 writer.print(encodedString.toString());
494
495 writer.print("?=");
496 writer.flush();
497
498
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
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
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 }