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 }