1 21 22 package org.columba.mail; 23 24 import java.io.BufferedReader ; 25 import java.io.File ; 26 import java.io.FileReader ; 27 import java.io.IOException ; 28 import java.io.StringReader ; 29 import java.text.SimpleDateFormat ; 30 import java.util.Date ; 31 import java.util.StringTokenizer ; 32 import java.util.logging.Logger ; 33 34 import org.columba.api.command.IWorkerStatusController; 35 import org.columba.mail.folder.IMailbox; 36 import org.columba.mail.folder.mailboximport.AbstractMailboxImporter; 37 import org.columba.ristretto.coder.EncodedWord; 38 39 45 public class EudoraMailImportFilter extends AbstractMailboxImporter { 46 47 48 private static final Logger LOG = Logger.getLogger("org.columba.mail"); 49 50 51 private static final String TIME_ZONE = (new SimpleDateFormat ("Z")) 52 .format(new Date ()); 53 54 58 private static final String DEFAULT_CHARSET = System 59 .getProperty("file.encoding"); 60 61 public EudoraMailImportFilter() { 62 super(); 63 } 64 65 73 public EudoraMailImportFilter(IMailbox destinationFolder, File [] sourceFiles) { 74 super(destinationFolder, sourceFiles); 75 } 76 77 82 public int getType() { 83 return TYPE_FILE; 84 } 85 86 public String getDescription() { 87 return "Eudora Mail Pro 4.0\n"; 88 } 89 90 102 public void importMailboxFile(File file, IWorkerStatusController worker, 103 IMailbox destFolder) throws Exception { 104 LOG.fine("Starting to import Eudora mbox file: " 105 + file.getAbsolutePath()); 106 107 StringBuffer strbuf = new StringBuffer (); 108 BufferedReader in = new BufferedReader (new FileReader (file)); 109 String str; 110 int msgCounter = 0; String replacementDate = null; 114 while ((str = in.readLine()) != null) { 116 if (worker.cancelled()) { 118 return; 119 } 120 121 if (!str.startsWith("From ???@???") || (str.length() == 0)) { 124 strbuf.append(str + "\n"); 125 } else { 127 if (strbuf.length() != 0) { 129 if (convertAndSaveMessage(strbuf.toString(), 130 replacementDate, worker, destFolder)) { 131 msgCounter++; 132 } 133 } 134 if (str.length() >= 13) 136 replacementDate = str.substring(13); else 140 replacementDate = ""; 141 strbuf = new StringBuffer (); 142 } 143 144 } 145 146 if (strbuf.length() > 0) { 148 if (convertAndSaveMessage(strbuf.toString(), replacementDate, 149 worker, destFolder)) { 150 msgCounter++; 151 } 152 } 153 154 LOG.fine("Import of Eudora mbox file completed. " + msgCounter 155 + " messages imported"); 156 in.close(); 157 158 } 160 171 private String getNewDateHeader(String dateStr, String timeZone) { 172 StringTokenizer tok = new StringTokenizer (dateStr); 173 try { 174 String dow = tok.nextToken(); String mon = tok.nextToken(); String day = tok.nextToken(); String time = tok.nextToken(); String year = tok.nextToken(); 180 StringBuffer dateHeader = new StringBuffer (); 181 dateHeader.append("Date: "); 182 dateHeader.append(dow + ","); 183 dateHeader.append(" " + day + " " + mon + " " + year); 184 dateHeader.append(" " + time); 185 if ((!(timeZone == null)) && (!timeZone.equals(""))) { 186 dateHeader.append(" " + timeZone); 187 } 188 189 return dateHeader.toString(); 190 } catch (java.util.NoSuchElementException e) { 191 193 LOG.severe("Not enough tokens in \"" + dateStr 199 + "\" to create Date: header. Returning null"); 200 return null; 201 } 202 } 204 221 private boolean convertAndSaveMessage(String msg, String replacementDate, 222 IWorkerStatusController worker, IMailbox destFolder) { 223 String [] divided = divideMessage(msg); 225 if (divided == null) { 226 LOG.severe("Error splitting message into headers and body"); 227 return false; 228 } 229 String headers = divided[0]; 230 String body = divided[1]; 231 232 HeaderTokenizer tokenizer = new HeaderTokenizer(headers); 234 EncodedWord decoder = new EncodedWord(); 235 StringBuffer headerBuf = new StringBuffer (); 236 boolean dateFound = false; 237 boolean contentTypeFound = false; 238 String line = tokenizer.nextLine(); 239 while (line != null) { 240 if (line.indexOf(':') != -1) { 241 String key = line.substring(0, line.indexOf(':')); 243 String header = EncodedWord.decode( 244 (CharSequence ) line.substring(line.indexOf(':') + 1) 245 .trim()).toString(); 246 247 if (key.equalsIgnoreCase("Date")) { 249 dateFound = true; } else if (key.equalsIgnoreCase("Content-Type")) { 253 contentTypeFound = true; 254 255 262 ContentType conType = new ContentType(header); if ((conType.getType().equalsIgnoreCase("multipart")) 265 && (body.indexOf("--" + conType.getBoundary()) == -1)) { 266 if (conType.getSubType() 268 .equalsIgnoreCase("alternative")) { 269 header = guessBodyContentType(body); 271 LOG 272 .fine("Content-Type: multipart/alternative replaced with " 273 + header); 274 } else { 275 283 String [] split = createAttachmentListFromAttachmentConverted(body); 284 if ((split == null) || (split.length == 1)) { 285 header = guessBodyContentType(body); 288 LOG.fine("Content-Type: multipart/" 289 + conType.getSubType() 290 + " replaced by " + header); 291 } else { 292 header = "multipart/mixed;\n\tboundary=" 293 + conType.getBoundary(); 294 body = createBodyFromParts(split, conType 295 .getBoundary()); 296 LOG 297 .fine("Content-Type: multipart/mixed. Boundaries added to msg body"); 298 } 299 } 300 } 301 } else if (key.equalsIgnoreCase("X-Attachments")) { 302 309 if (header.length() > 0) { 310 String [] split = createAttachmentListFromHeader(body, 312 header); 313 314 if ((split == null) || (split.length == 1)) { 315 headerBuf.append("MIME-Version: 1.0\n"); key = "Content-Type"; header = guessBodyContentType(body); 323 contentTypeFound = true; 324 LOG 325 .fine("X-Attachments header replaced by Content-Type: " 326 + header); 327 } else { 328 String unique = getUniqueBoundary(body); 331 headerBuf.append("MIME-Version: 1.0\n"); key = "Content-Type"; header = "multipart/mixed;\n\tboundary=" + unique; 337 contentTypeFound = true; body = createBodyFromParts(split, unique); 340 LOG 341 .fine("X-Attachments header replaced by Content-Type: multipart/mixed"); 342 } 343 } 344 } 345 346 headerBuf.append(key + ": " + header + "\n"); 348 349 } 350 line = tokenizer.nextLine(); 351 } 353 357 if (!dateFound) { 358 LOG.fine("Date header missing - constructing new one"); 359 String dateHeader = getNewDateHeader(replacementDate, TIME_ZONE); 360 if (dateHeader != null) 361 headerBuf.append(dateHeader + "\n"); } 364 365 370 if (!contentTypeFound) { 371 LOG.fine("Content-Type header missing - constructing new one"); 372 String contHeader = "Content-Type: " + guessBodyContentType(body); 373 headerBuf.append("MIME-Version: 1.0\n"); 374 headerBuf.append(contHeader + "\n"); 375 } 376 377 return saveMessage(headerBuf.toString(), body, worker, destFolder); 379 380 } 382 396 private boolean saveMessage(String headers, String body, 397 IWorkerStatusController worker, IMailbox destFolder) { 398 StringBuffer buf = new StringBuffer (); 399 buf.append(headers); 401 buf.append("\n"); 402 buf.append(body); 403 404 try { 406 saveMessage(buf.toString(), worker, destFolder); 409 return true; 410 } catch (Exception e) { 411 LOG.severe("Error saving converted message: " + e.getMessage()); 413 return false; 414 } 415 } 417 428 private String createBodyFromParts(String [] parts, String boundary) { 429 StringBuffer newBody = new StringBuffer (); 431 for (int i = 0; i < parts.length; i++) { 432 newBody.append("--" + boundary + "\n"); 433 newBody.append(parts[i]); 434 } 435 newBody.append("--" + boundary + "--\n"); 436 return newBody.toString(); 437 } 438 439 448 private String getUniqueBoundary(String body) { 449 String boundary = "BOUNDARY"; int i = 0; 451 boolean found = true; 452 while (found) { 453 if (body.indexOf("--" + boundary) == -1) 454 found = false; 455 else { 456 boundary = boundary + String.valueOf(i); 457 i++; 458 } 459 } 460 return boundary; 462 } 463 464 473 private String [] divideMessage(String input) { 474 String [] output = new String [2]; 475 int emptyLinePos; 476 477 if (input.length() == 0) 478 return null; 479 480 if (input.charAt(0) == '\n') { 481 output[0] = new String (); 482 output[1] = input; 483 return output; 484 } 485 486 emptyLinePos = input.indexOf("\n\n"); 487 488 if (input.indexOf("\n\n") != -1) { 489 output[0] = input.substring(0, emptyLinePos + 1); 490 output[1] = input.substring(emptyLinePos + 2); 491 } else { 492 output[0] = input; 493 output[1] = new String (); 494 } 495 496 return output; 497 } 499 508 private String guessBodyContentType(String body) { 509 boolean isHTML = false; 511 if (((body.indexOf("<x-html>") != -1) || (body.indexOf("<X-HTML>") != -1)) 512 && ((body.indexOf("</x-html>") != -1) || (body 513 .indexOf("</X-HTML>") != -1))) 514 isHTML = true; 515 else if (((body.indexOf("<HTML>") != -1) || (body.indexOf("<html>") != -1)) 516 && ((body.indexOf("</HTML>") != -1) || (body.indexOf("</html>") != -1))) 517 isHTML = true; 518 519 if (!isHTML) { 520 if ((DEFAULT_CHARSET != null) && (DEFAULT_CHARSET.length() > 0)) 521 return "text/plain; charset=" + DEFAULT_CHARSET; 522 else 523 return "text/plain"; 524 } else { 525 532 BufferedReader reader = new BufferedReader (new StringReader (body)); 533 String line = null; 534 try { 535 line = reader.readLine(); 536 } catch (IOException e) { 537 LOG 539 .severe("Error while looking for charset: " 540 + e.getMessage()); 541 } 542 String charset = null; 543 while (line != null) { 544 line = line.trim(); 545 if ((line.length() > 5) 546 && (line.substring(0, 5).equalsIgnoreCase("<meta"))) { 547 String lcLine = line.toLowerCase(); if ((lcLine.indexOf("http-equiv=content-type") != -1) 550 || (lcLine.indexOf("http-equiv=\"content-type\"") != -1)) { 551 int pos = lcLine.indexOf("charset"); 553 if (pos != -1) { 554 pos = lcLine.indexOf('=', pos); 556 pos++; 557 while (lcLine.charAt(pos) == ' ') { 558 pos++; 559 } int end = lcLine.length(); 565 int tmp = lcLine.indexOf('>', pos); 566 if (tmp != -1) 567 end = tmp; 568 tmp = lcLine.indexOf('"', pos); 569 if ((tmp != -1) && (tmp < end)) 570 end = tmp; 571 tmp = lcLine.indexOf(' ', pos); 572 if ((tmp != -1) && (tmp < end)) 573 end = tmp; 574 charset = line.substring(pos, end); 577 break; } 579 } 580 } 581 try { 582 line = reader.readLine(); 583 } catch (IOException e) { 584 LOG.severe("Error while looking for charset: " 588 + e.getMessage()); 589 line = null; } 591 } 593 if ((charset != null) && (charset.length() > 0)) 594 return "text/html; charset=" + charset; 595 else { 596 if ((DEFAULT_CHARSET != null) && (DEFAULT_CHARSET.length() > 0)) 597 return "text/html; charset=" + DEFAULT_CHARSET; 598 else 599 return "text/html"; 600 } 601 } 602 603 } 605 618 private String [] createAttachmentListFromHeader(String body, 619 String xAttachments) { 620 StringTokenizer tok = new StringTokenizer (xAttachments, ";"); 621 StringBuffer attachBuf = new StringBuffer (); 622 while (tok.hasMoreTokens()) { 623 String name = tok.nextToken().trim(); 625 attachBuf.append("<a HREF=\"file://" + name + "\">" + name 626 + "</a><br>\n"); 627 } 628 629 if (attachBuf.length() == 0) { 630 String [] retVal = new String [1]; 633 retVal[0] = body; return retVal; 635 } else { 636 attachBuf 639 .insert(0, 640 "<html><head><title>Attachment list</title></head><body><p>\n"); 641 attachBuf.append("</p></body></html>\n"); 642 String charset; 644 if ((DEFAULT_CHARSET != null) && (DEFAULT_CHARSET.length() > 0)) 645 charset = "; charset=" + DEFAULT_CHARSET; 646 else 647 charset = ""; 648 attachBuf.insert(0, "Content-Type: text/html" + charset 649 + "; name=\"attachmentlist.html\"\n\n"); 650 StringBuffer bodyBuf = new StringBuffer (); 652 String header = "Content-Type: " 653 + guessBodyContentType(bodyBuf.toString()); 654 bodyBuf.append(header + "\n\n"); 655 bodyBuf.append(body); 656 String [] retVal = new String [2]; 658 retVal[0] = bodyBuf.toString(); 659 retVal[1] = attachBuf.toString(); 660 return retVal; 661 } 662 } 664 674 private String [] createAttachmentListFromAttachmentConverted(String body) { 675 StringBuffer bodyBuf = new StringBuffer (); 676 StringBuffer attachBuf = new StringBuffer (); 677 BufferedReader reader = new BufferedReader (new StringReader (body)); 678 try { 679 String line = reader.readLine(); 680 while (line != null) { 681 if (line.startsWith("Attachment Converted:")) { 682 String name = line.substring(line.indexOf(':') + 1).trim(); 684 if (name.startsWith("\"")) 685 name = name.substring(1, name.length() - 1); 686 attachBuf.append("<a HREF=\"file://" + name + "\">" + name 687 + "</a><br>\n"); 688 } else { 689 bodyBuf.append(line + "\n"); 691 } 692 line = reader.readLine(); 693 } 694 695 if (attachBuf.length() > 0) { 696 698 attachBuf 700 .insert(0, 701 "<html><head><title>Attachment list</title></head><body><p>\n"); 702 attachBuf.append("</p></body></html>\n"); 703 String charset; 705 if ((DEFAULT_CHARSET != null) && (DEFAULT_CHARSET.length() > 0)) 706 charset = "; charset=" + DEFAULT_CHARSET; 707 else 708 charset = ""; 709 attachBuf.insert(0, "Content-Type: text/html" + charset 710 + "; name=\"attachmentlist.html\"\n\n"); 711 712 String header = "Content-Type: " 714 + guessBodyContentType(bodyBuf.toString()); 715 bodyBuf.insert(0, header + "\n\n"); 716 717 String [] retVal = new String [2]; 718 retVal[0] = bodyBuf.toString(); 719 retVal[1] = attachBuf.toString(); 720 return retVal; 721 } else { 722 String [] retVal = new String [1]; 724 retVal[0] = body; return retVal; 726 } 727 } catch (IOException e) { 728 LOG.severe("Error parsing body for attachments: " + e.getMessage()); 730 return null; 731 } 732 } 734 } | Popular Tags |