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 /** 028 * @version $Rev: 467553 $ $Date: 2006-10-25 06:01:51 +0200 (Mi, 25. Okt 2006) $ 029 */ 030 public class Base64Encoder 031 implements Encoder 032 { 033 protected final byte[] encodingTable = 034 { 035 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 036 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 037 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 038 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 039 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 040 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 041 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 042 (byte)'v', 043 (byte)'w', (byte)'x', (byte)'y', (byte)'z', 044 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', 045 (byte)'7', (byte)'8', (byte)'9', 046 (byte)'+', (byte)'/' 047 }; 048 049 protected byte padding = (byte)'='; 050 051 /* 052 * set up the decoding table. 053 */ 054 protected final byte[] decodingTable = new byte[256]; 055 056 protected void initialiseDecodingTable() 057 { 058 for (int i = 0; i < encodingTable.length; i++) 059 { 060 decodingTable[encodingTable[i]] = (byte)i; 061 } 062 } 063 064 public Base64Encoder() 065 { 066 initialiseDecodingTable(); 067 } 068 069 /** 070 * encode the input data producing a base 64 output stream. 071 * 072 * @return the number of bytes produced. 073 */ 074 public int encode( 075 byte[] data, 076 int off, 077 int length, 078 OutputStream out) 079 throws IOException 080 { 081 int modulus = length % 3; 082 int dataLength = (length - modulus); 083 int a1, a2, a3; 084 085 for (int i = off; i < off + dataLength; i += 3) 086 { 087 a1 = data[i] & 0xff; 088 a2 = data[i + 1] & 0xff; 089 a3 = data[i + 2] & 0xff; 090 091 out.write(encodingTable[(a1 >>> 2) & 0x3f]); 092 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 093 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); 094 out.write(encodingTable[a3 & 0x3f]); 095 } 096 097 /* 098 * process the tail end. 099 */ 100 int b1, b2, b3; 101 int d1, d2; 102 103 switch (modulus) 104 { 105 case 0: /* nothing left to do */ 106 break; 107 case 1: 108 d1 = data[off + dataLength] & 0xff; 109 b1 = (d1 >>> 2) & 0x3f; 110 b2 = (d1 << 4) & 0x3f; 111 112 out.write(encodingTable[b1]); 113 out.write(encodingTable[b2]); 114 out.write(padding); 115 out.write(padding); 116 break; 117 case 2: 118 d1 = data[off + dataLength] & 0xff; 119 d2 = data[off + dataLength + 1] & 0xff; 120 121 b1 = (d1 >>> 2) & 0x3f; 122 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; 123 b3 = (d2 << 2) & 0x3f; 124 125 out.write(encodingTable[b1]); 126 out.write(encodingTable[b2]); 127 out.write(encodingTable[b3]); 128 out.write(padding); 129 break; 130 } 131 132 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); 133 } 134 135 private boolean ignore( 136 char c) 137 { 138 return (c == '\n' || c =='\r' || c == '\t' || c == ' '); 139 } 140 141 /** 142 * decode the base 64 encoded byte data writing it to the given output stream, 143 * whitespace characters will be ignored. 144 * 145 * @return the number of bytes produced. 146 */ 147 public int decode( 148 byte[] data, 149 int off, 150 int length, 151 OutputStream out) 152 throws IOException 153 { 154 byte[] bytes; 155 byte b1, b2, b3, b4; 156 int outLen = 0; 157 158 int end = off + length; 159 160 while (end > 0) 161 { 162 if (!ignore((char)data[end - 1])) 163 { 164 break; 165 } 166 167 end--; 168 } 169 170 int i = off; 171 int finish = end - 4; 172 173 while (i < finish) 174 { 175 while ((i < finish) && ignore((char)data[i])) 176 { 177 i++; 178 } 179 180 b1 = decodingTable[data[i++]]; 181 182 while ((i < finish) && ignore((char)data[i])) 183 { 184 i++; 185 } 186 187 b2 = decodingTable[data[i++]]; 188 189 while ((i < finish) && ignore((char)data[i])) 190 { 191 i++; 192 } 193 194 b3 = decodingTable[data[i++]]; 195 196 while ((i < finish) && ignore((char)data[i])) 197 { 198 i++; 199 } 200 201 b4 = decodingTable[data[i++]]; 202 203 out.write((b1 << 2) | (b2 >> 4)); 204 out.write((b2 << 4) | (b3 >> 2)); 205 out.write((b3 << 6) | b4); 206 207 outLen += 3; 208 } 209 210 if (data[end - 2] == padding) 211 { 212 b1 = decodingTable[data[end - 4]]; 213 b2 = decodingTable[data[end - 3]]; 214 215 out.write((b1 << 2) | (b2 >> 4)); 216 217 outLen += 1; 218 } 219 else if (data[end - 1] == padding) 220 { 221 b1 = decodingTable[data[end - 4]]; 222 b2 = decodingTable[data[end - 3]]; 223 b3 = decodingTable[data[end - 2]]; 224 225 out.write((b1 << 2) | (b2 >> 4)); 226 out.write((b2 << 4) | (b3 >> 2)); 227 228 outLen += 2; 229 } 230 else 231 { 232 b1 = decodingTable[data[end - 4]]; 233 b2 = decodingTable[data[end - 3]]; 234 b3 = decodingTable[data[end - 2]]; 235 b4 = decodingTable[data[end - 1]]; 236 237 out.write((b1 << 2) | (b2 >> 4)); 238 out.write((b2 << 4) | (b3 >> 2)); 239 out.write((b3 << 6) | b4); 240 241 outLen += 3; 242 } 243 244 return outLen; 245 } 246 247 /** 248 * decode the base 64 encoded String data writing it to the given output stream, 249 * whitespace characters will be ignored. 250 * 251 * @return the number of bytes produced. 252 */ 253 public int decode( 254 String data, 255 OutputStream out) 256 throws IOException 257 { 258 byte[] bytes; 259 byte b1, b2, b3, b4; 260 int length = 0; 261 262 int end = data.length(); 263 264 while (end > 0) 265 { 266 if (!ignore(data.charAt(end - 1))) 267 { 268 break; 269 } 270 271 end--; 272 } 273 274 int i = 0; 275 int finish = end - 4; 276 277 while (i < finish) 278 { 279 while ((i < finish) && ignore(data.charAt(i))) 280 { 281 i++; 282 } 283 284 b1 = decodingTable[data.charAt(i++)]; 285 286 while ((i < finish) && ignore(data.charAt(i))) 287 { 288 i++; 289 } 290 b2 = decodingTable[data.charAt(i++)]; 291 292 while ((i < finish) && ignore(data.charAt(i))) 293 { 294 i++; 295 } 296 b3 = decodingTable[data.charAt(i++)]; 297 298 while ((i < finish) && ignore(data.charAt(i))) 299 { 300 i++; 301 } 302 b4 = decodingTable[data.charAt(i++)]; 303 304 out.write((b1 << 2) | (b2 >> 4)); 305 out.write((b2 << 4) | (b3 >> 2)); 306 out.write((b3 << 6) | b4); 307 308 length += 3; 309 } 310 311 if (data.charAt(end - 2) == padding) 312 { 313 b1 = decodingTable[data.charAt(end - 4)]; 314 b2 = decodingTable[data.charAt(end - 3)]; 315 316 out.write((b1 << 2) | (b2 >> 4)); 317 318 length += 1; 319 } 320 else if (data.charAt(end - 1) == padding) 321 { 322 b1 = decodingTable[data.charAt(end - 4)]; 323 b2 = decodingTable[data.charAt(end - 3)]; 324 b3 = decodingTable[data.charAt(end - 2)]; 325 326 out.write((b1 << 2) | (b2 >> 4)); 327 out.write((b2 << 4) | (b3 >> 2)); 328 329 length += 2; 330 } 331 else 332 { 333 b1 = decodingTable[data.charAt(end - 4)]; 334 b2 = decodingTable[data.charAt(end - 3)]; 335 b3 = decodingTable[data.charAt(end - 2)]; 336 b4 = decodingTable[data.charAt(end - 1)]; 337 338 out.write((b1 << 2) | (b2 >> 4)); 339 out.write((b2 << 4) | (b3 >> 2)); 340 out.write((b3 << 6) | b4); 341 342 length += 3; 343 } 344 345 return length; 346 } 347 348 /** 349 * decode the base 64 encoded byte data writing it to the provided byte array buffer. 350 * 351 * @return the number of bytes produced. 352 */ 353 public int decode(byte[] data, int off, int length, byte[] out) throws IOException 354 { 355 byte[] bytes; 356 byte b1, b2, b3, b4; 357 int outLen = 0; 358 359 int end = off + length; 360 361 while (end > 0) 362 { 363 if (!ignore((char)data[end - 1])) 364 { 365 break; 366 } 367 368 end--; 369 } 370 371 int i = off; 372 int finish = end - 4; 373 374 while (i < finish) 375 { 376 while ((i < finish) && ignore((char)data[i])) 377 { 378 i++; 379 } 380 381 b1 = decodingTable[data[i++]]; 382 383 while ((i < finish) && ignore((char)data[i])) 384 { 385 i++; 386 } 387 388 b2 = decodingTable[data[i++]]; 389 390 while ((i < finish) && ignore((char)data[i])) 391 { 392 i++; 393 } 394 395 b3 = decodingTable[data[i++]]; 396 397 while ((i < finish) && ignore((char)data[i])) 398 { 399 i++; 400 } 401 402 b4 = decodingTable[data[i++]]; 403 404 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); 405 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); 406 out[outLen++] = (byte)((b3 << 6) | b4); 407 } 408 409 if (data[end - 2] == padding) 410 { 411 b1 = decodingTable[data[end - 4]]; 412 b2 = decodingTable[data[end - 3]]; 413 414 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); 415 } 416 else if (data[end - 1] == padding) 417 { 418 b1 = decodingTable[data[end - 4]]; 419 b2 = decodingTable[data[end - 3]]; 420 b3 = decodingTable[data[end - 2]]; 421 422 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); 423 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); 424 } 425 else 426 { 427 b1 = decodingTable[data[end - 4]]; 428 b2 = decodingTable[data[end - 3]]; 429 b3 = decodingTable[data[end - 2]]; 430 b4 = decodingTable[data[end - 1]]; 431 432 out[outLen++] = (byte)((b1 << 2) | (b2 >> 4)); 433 out[outLen++] = (byte)((b2 << 4) | (b3 >> 2)); 434 out[outLen++] = (byte)((b3 << 6) | b4); 435 } 436 437 return outLen; 438 } 439 440 /** 441 * Test if a character is a valid Base64 encoding character. This 442 * must be either a valid digit or the padding character ("="). 443 * 444 * @param ch The test character. 445 * 446 * @return true if this is valid in Base64 encoded data, false otherwise. 447 */ 448 public boolean isValidBase64(int ch) { 449 // 'A' has the value 0 in the decoding table, so we need a special one for that 450 return ch == padding || ch == 'A' || decodingTable[ch] != 0; 451 } 452 453 454 /** 455 * Perform RFC-2047 word encoding using Base64 data encoding. 456 * 457 * @param in The source for the encoded data. 458 * @param charset The charset tag to be added to each encoded data section. 459 * @param out The output stream where the encoded data is to be written. 460 * @param fold Controls whether separate sections of encoded data are separated by 461 * linebreaks or whitespace. 462 * 463 * @exception IOException 464 */ 465 public void encodeWord(InputStream in, String charset, OutputStream out, boolean fold) throws IOException 466 { 467 PrintStream writer = new PrintStream(out); 468 469 // encoded words are restricted to 76 bytes, including the control adornments. 470 int limit = 76 - 7 - charset.length(); 471 boolean firstLine = true; 472 StringBuffer encodedString = new StringBuffer(76); 473 474 while (true) { 475 // encode the next segment. 476 encode(in, encodedString, limit); 477 // if we're out of data, nothing will be encoded. 478 if (encodedString.length() == 0) { 479 break; 480 } 481 482 // if we have more than one segment, we need to insert separators. Depending on whether folding 483 // was requested, this is either a blank or a linebreak. 484 if (!firstLine) { 485 if (fold) { 486 writer.print("\r\n"); 487 } 488 else { 489 writer.print(" "); 490 } 491 } 492 493 // add the encoded word header 494 writer.print("=?"); 495 writer.print(charset); 496 writer.print("?B?"); 497 // the data 498 writer.print(encodedString.toString()); 499 // and the word terminator. 500 writer.print("?="); 501 writer.flush(); 502 503 // reset our string buffer for the next segment. 504 encodedString.setLength(0); 505 } 506 } 507 508 /** 509 * encode the input data producing a base 64 output stream. 510 * 511 * @return the number of bytes produced. 512 */ 513 public void encode(InputStream in, StringBuffer out, int limit) throws IOException 514 { 515 int count = limit / 4; 516 byte [] inBuffer = new byte[3]; 517 518 while (count-- > 0) { 519 520 int readCount = in.read(inBuffer); 521 // did we get a full triplet? that's an easy encoding. 522 if (readCount == 3) { 523 int a1 = inBuffer[0] & 0xff; 524 int a2 = inBuffer[1] & 0xff; 525 int a3 = inBuffer[2] & 0xff; 526 527 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); 528 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 529 out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); 530 out.append((char)encodingTable[a3 & 0x3f]); 531 532 } 533 else if (readCount <= 0) { 534 // eof condition, don'e entirely. 535 return; 536 } 537 else if (readCount == 1) { 538 int a1 = inBuffer[0] & 0xff; 539 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); 540 out.append((char)encodingTable[(a1 << 4) & 0x3f]); 541 out.append((char)padding); 542 out.append((char)padding); 543 return; 544 } 545 else if (readCount == 2) { 546 int a1 = inBuffer[0] & 0xff; 547 int a2 = inBuffer[1] & 0xff; 548 549 out.append((char)encodingTable[(a1 >>> 2) & 0x3f]); 550 out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 551 out.append((char)encodingTable[(a2 << 2) & 0x3f]); 552 out.append((char)padding); 553 return; 554 } 555 } 556 } 557 }