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 = 76 - 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 }
503 }
504
505 /**
506 * encode the input data producing a base 64 output stream.
507 *
508 * @return the number of bytes produced.
509 */
510 public void encode(InputStream in, StringBuffer out, int limit) throws IOException
511 {
512 int count = limit / 4;
513 byte [] inBuffer = new byte[3];
514
515 while (count-- > 0) {
516
517 int readCount = in.read(inBuffer);
518 // did we get a full triplet? that's an easy encoding.
519 if (readCount == 3) {
520 int a1 = inBuffer[0] & 0xff;
521 int a2 = inBuffer[1] & 0xff;
522 int a3 = inBuffer[2] & 0xff;
523
524 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
525 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
526 out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
527 out.append((char)encodingTable[a3 & 0x3f]);
528
529 }
530 else if (readCount <= 0) {
531 // eof condition, don'e entirely.
532 return;
533 }
534 else if (readCount == 1) {
535 int a1 = inBuffer[0] & 0xff;
536 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
537 out.append((char)encodingTable[(a1 << 4) & 0x3f]);
538 out.append((char)padding);
539 out.append((char)padding);
540 return;
541 }
542 else if (readCount == 2) {
543 int a1 = inBuffer[0] & 0xff;
544 int a2 = inBuffer[1] & 0xff;
545
546 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
547 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
548 out.append((char)encodingTable[(a2 << 2) & 0x3f]);
549 out.append((char)padding);
550 return;
551 }
552 }
553 }
554 }