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 }