1 package org.columba.mail.folder.command; 17 18 import java.io.ByteArrayInputStream ; 19 import java.io.File ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.nio.charset.Charset ; 23 import java.text.DateFormat ; 24 import java.util.ArrayList ; 25 import java.util.Enumeration ; 26 import java.util.List ; 27 import java.util.logging.Logger ; 28 29 import javax.swing.JCheckBox ; 30 import javax.swing.JFileChooser ; 31 import javax.swing.JOptionPane ; 32 import javax.swing.filechooser.FileFilter ; 33 34 import org.columba.api.command.ICommandReference; 35 import org.columba.api.command.IWorkerStatusController; 36 import org.columba.core.command.Command; 37 import org.columba.core.command.StatusObservableImpl; 38 import org.columba.core.command.Worker; 39 import org.columba.core.config.Config; 40 import org.columba.core.gui.frame.FrameManager; 41 import org.columba.core.io.DiskIO; 42 import org.columba.core.io.StreamUtils; 43 import org.columba.core.xml.XmlElement; 44 import org.columba.mail.command.IMailFolderCommandReference; 45 import org.columba.mail.config.MailConfig; 46 import org.columba.mail.folder.IMailbox; 47 import org.columba.mail.gui.message.util.DocumentParser; 48 import org.columba.mail.gui.message.viewer.AttachmentModel; 49 import org.columba.mail.parser.text.HtmlParser; 50 import org.columba.mail.util.MailResourceLoader; 51 import org.columba.ristretto.coder.Base64DecoderInputStream; 52 import org.columba.ristretto.coder.CharsetDecoderInputStream; 53 import org.columba.ristretto.coder.EncodedWord; 54 import org.columba.ristretto.coder.QuotedPrintableDecoderInputStream; 55 import org.columba.ristretto.message.BasicHeader; 56 import org.columba.ristretto.message.Header; 57 import org.columba.ristretto.message.MimeHeader; 58 import org.columba.ristretto.message.MimePart; 59 import org.columba.ristretto.message.MimeTree; 60 import org.columba.ristretto.message.StreamableMimePart; 61 62 63 69 public class SaveMessageBodyAsCommand extends Command { 70 71 72 private static final Logger LOG = Logger.getLogger("org.columba.mail.folder.command"); 73 74 75 private static final String NL = "\n"; 76 77 79 80 private Charset charset; 81 82 private Header header; 83 84 private MimeHeader bodyHeader; 85 private InputStream bodyStream; 86 87 private List attachments; 88 89 97 public SaveMessageBodyAsCommand(ICommandReference reference, 98 Charset charset) { 99 super(reference); 100 this.charset = charset; 101 } 102 103 104 112 public void execute(IWorkerStatusController worker) 113 throws Exception { 114 IMailFolderCommandReference r = (IMailFolderCommandReference) getReference(); 115 Object [] uids = r.getUids(); IMailbox srcFolder = (IMailbox) r.getSourceFolder(); 117 118 ((StatusObservableImpl) srcFolder.getObservable()).setWorker(worker); 120 121 JFileChooser fileChooser = new JFileChooser (); 122 123 for (int j = 0; j < uids.length; j++) { 125 Object uid = uids[j]; 126 LOG.info("Saving UID=" + uid); 127 128 header = srcFolder.getAllHeaderFields(uid); 129 setupMessageBodyPart(uid, srcFolder,worker); 130 131 AttachmentModel attMod = new AttachmentModel(); 132 attMod.setCollection(srcFolder.getMimePartTree(uid)); 133 134 attachments = attMod.getDisplayedMimeParts(); 135 136 boolean ishtml = false; 138 139 if (bodyHeader.getMimeType().getSubtype().equals("html")) { 140 ishtml = true; 141 } 142 143 ExtensionFileFilter txtFilter = new ExtensionFileFilter("txt", 145 "Text (*.txt)"); 146 ExtensionFileFilter htmlFilter = new ExtensionFileFilter("html", 147 "Html (*.html)"); 148 fileChooser.resetChoosableFileFilters(); 149 fileChooser.setAcceptAllFileFilterUsed(false); 150 fileChooser.addChoosableFileFilter(txtFilter); 151 fileChooser.addChoosableFileFilter(htmlFilter); 152 153 JCheckBox inclHeaders = new JCheckBox (MailResourceLoader.getString( 155 "dialog", "saveas", "save_all_headers"), 156 getInclAllHeadersOption()); 157 fileChooser.setAccessory(inclHeaders); 158 159 String subject = EncodedWord.decode( header.get("Subject")).toString(); 161 String defaultName = getValidFilename(subject,true); 162 163 if (ishtml) { 164 fileChooser.setDialogTitle(MailResourceLoader.getString( 165 "dialog", "saveas", "save_html_message")); 166 fileChooser.setFileFilter(htmlFilter); 167 168 if (defaultName.length() > 0) { 169 fileChooser.setSelectedFile(new File (defaultName + "." 170 + htmlFilter.getExtension())); 171 } 172 } else { 173 fileChooser.setDialogTitle(MailResourceLoader.getString( 174 "dialog", "saveas", "save_text_message")); 175 fileChooser.setFileFilter(txtFilter); 176 177 if (defaultName.length() > 0) { 178 fileChooser.setSelectedFile(new File (defaultName + "." 179 + txtFilter.getExtension())); 180 } 181 } 182 183 int res = fileChooser.showSaveDialog(null); 185 186 if (res == JFileChooser.APPROVE_OPTION) { 187 File f = fileChooser.getSelectedFile(); 188 ExtensionFileFilter filter = (ExtensionFileFilter) fileChooser.getFileFilter(); 189 190 if (ExtensionFileFilter.getFileExtension(f) == null) { 192 f = new File (f.getAbsolutePath() + "." 193 + filter.getExtension()); 194 } 195 196 int confirm; 197 198 if (f.exists()) { 199 confirm = JOptionPane.showConfirmDialog(FrameManager.getInstance() 201 .getActiveFrame(), 202 MailResourceLoader.getString("dialog", "saveas", 203 "overwrite_existing_file"), 204 MailResourceLoader.getString("dialog", "saveas", 205 "file_exists"), JOptionPane.YES_NO_OPTION, 206 JOptionPane.QUESTION_MESSAGE); 207 } else { 208 confirm = JOptionPane.YES_OPTION; 209 } 210 211 if (confirm == JOptionPane.YES_OPTION) { 212 boolean incl = inclHeaders.isSelected(); 214 storeInclAllHeadersOption(incl); 215 LOG.info("Incl. all headers: " + incl); 216 217 if (filter.getExtension().equals(htmlFilter.getExtension())) { 219 saveMsgBodyAsHtml(incl, f); 220 } else { 221 saveMsgBodyAsText(incl, f); 222 } 223 } 224 } 225 } 226 227 } 229 230 241 private String getValidFilename(String subj, boolean replSpaces) { 242 if (subj == null) { 243 return ""; 244 } 245 246 StringBuffer buf = new StringBuffer (); 247 248 for (int i = 0; i < subj.length(); i++) { 249 char c = subj.charAt(i); 250 251 if ((c == '\\') || (c == '/') || (c == ':') || (c == ',') 252 || (c == '\n') || (c == '\t') || (c == '(') || (c == ')') || (c == '[') || (c == ']')) { 253 } else if ((c == ' ') && (replSpaces)) { 255 buf.append('_'); 256 } else { 257 buf.append(c); 258 } 259 } 260 261 return buf.toString(); 262 } 263 264 269 private boolean getInclAllHeadersOption() { 270 boolean defaultValue = false; 272 XmlElement options = Config.getInstance().get("options").getElement("/options"); 273 274 if (options == null) { 275 return defaultValue; 276 } 277 278 XmlElement savemsg = options.getElement("/savemsg"); 279 280 if (savemsg != null) { 281 if (savemsg.getAttribute("incl_all_headers", 282 String.valueOf(defaultValue)).equals("true")) { 283 return true; 284 } else { 285 return false; 286 } 287 } else { 288 return defaultValue; 289 } 290 } 291 292 298 private void storeInclAllHeadersOption(boolean val) { 299 XmlElement options = Config.getInstance().get("options").getElement("/options"); 300 301 if (options == null) { 302 return; 303 } 304 305 XmlElement savemsg = options.getElement("/savemsg"); 306 307 if (savemsg == null) { 308 savemsg = new XmlElement("savemsg"); 310 savemsg.addAttribute("incl_all_headers", String.valueOf(val)); 311 options.addElement(savemsg); 312 } else { 313 savemsg.addAttribute("incl_all_headers", String.valueOf(val)); 314 } 315 } 316 317 329 private void setupMessageBodyPart(Object uid, IMailbox srcFolder, 330 IWorkerStatusController worker) throws Exception { 331 XmlElement html = MailConfig.getInstance().getMainFrameOptionsConfig() 333 .getRoot().getElement("/options/html"); 334 335 MimeTree mimePartTree = srcFolder.getMimePartTree(uid); 337 338 MimePart bodyPart = null; 339 340 if (Boolean.valueOf(html.getAttribute("prefer")).booleanValue()) { 341 bodyPart = mimePartTree.getFirstTextPart("html"); 342 } else { 343 bodyPart = mimePartTree.getFirstTextPart("plain"); 344 } 345 346 if (bodyPart == null) { 347 bodyHeader = new MimeHeader(); 348 bodyStream = new ByteArrayInputStream (new byte[0]); 349 } else { 350 bodyHeader = bodyPart.getHeader(); 351 bodyStream = srcFolder.getMimePartBodyStream(uid, bodyPart.getAddress()); 352 } 353 } 354 355 363 private String getDecodedMessageBody() 364 throws IOException { 365 int encoding = bodyHeader.getContentTransferEncoding(); 366 367 switch (encoding) { 368 case MimeHeader.QUOTED_PRINTABLE: { 369 bodyStream = new QuotedPrintableDecoderInputStream(bodyStream); 370 371 break; 372 } 373 374 case MimeHeader.BASE64: { 375 bodyStream = new Base64DecoderInputStream(bodyStream); 376 377 break; 378 } 379 } 380 381 if (charset == null) { 383 try { 384 charset = Charset.forName(bodyHeader.getContentParameter("charset")); 386 } catch (Exception ex) { 387 charset = Charset.forName(System.getProperty("file.encoding")); 389 } 390 } 391 392 bodyStream = new CharsetDecoderInputStream(bodyStream, charset); 393 394 return StreamUtils.readCharacterStream(bodyStream).toString(); 395 } 396 397 413 private void saveMsgBodyAsHtml(boolean inclAllHeaders, 414 File file) throws IOException { 415 String decodedBody = getDecodedMessageBody(); 417 418 String body; 419 420 if (!bodyHeader.getMimeType().getSubtype().equals("html")) { 421 try { 422 body = HtmlParser.substituteSpecialCharacters(decodedBody); 424 425 body = HtmlParser.substituteURL(body); 427 body = HtmlParser.substituteEmailAddress(body); 428 429 body = DocumentParser.markQuotings(body); 431 } catch (Exception e) { 432 LOG.severe("Error parsing body: " + e.getMessage()); 433 body = "<em>Error parsing body!!!</em>"; 434 } 435 436 String css = getDefaultStyleSheet(); 438 body = "<html><head>" + NL + css + NL 439 + "<title>E-mail saved by Columba</title>" + NL 440 + "</head><body><p class=\"bodytext\">" + NL + body + NL 441 + "</p></body></html>"; 442 } else { 443 body = HtmlParser.validateHTMLString(decodedBody); 445 } 446 447 String [][] headers = getHeadersToSave(inclAllHeaders); 449 String msg = insertHtmlHeaderTable(body, headers); 450 451 try { 453 DiskIO.saveStringInFile(file, msg); 454 LOG.fine("Html msg saved as " + file.getAbsolutePath()); 455 } catch (IOException ioe) { 456 LOG.severe("Error saving message to file: " + ioe.getMessage()); 457 } 458 } 459 460 465 private String getDefaultStyleSheet() { 466 XmlElement textFont = Config.getInstance().get("options").getElement("/options/gui/textfont"); 468 String name = textFont.getAttribute("name"); 469 String size = textFont.getAttribute("size"); 470 471 String css = "<style type=\"text/css\"><!-- .bodytext {font-family:\"" 473 + name + "\"; font-size:\"" + size + "pt; \"}" 474 + ".quoting {color:#949494;}; --></style>"; 475 476 return css; 477 } 478 479 489 private String insertHtmlHeaderTable(String body, String [][] headers) { 490 StringBuffer buf = new StringBuffer (); 492 String csskey = "border: 1px solid black; font-size: 8pt; font-weight: bold;"; 493 String cssval = "border: 1px solid black; font-size: 8pt;"; 494 buf.append( 495 "<table style=\"background-color: #dddddd;\" cellspacing=\"0\">"); 496 buf.append(NL); 497 498 for (int i = 0; i < headers[0].length; i++) { 499 String val = headers[1][i]; 501 502 try { 503 val = HtmlParser.substituteSpecialCharactersInHeaderfields(val); 504 val = HtmlParser.substituteURL(val); 505 val = HtmlParser.substituteEmailAddress(val); 506 } catch (Exception e) { 507 LOG.severe("Error parsing header value: " + e.getMessage()); 508 } 509 510 buf.append("<tr><td style=\"" + csskey + "\">"); 511 buf.append(headers[0][i]); 512 buf.append("</td>"); 513 buf.append("<td style=\"" + cssval + "\">"); 514 buf.append(val); 515 buf.append("</td></tr>"); 516 buf.append(NL); 517 } 518 519 buf.append("</table>"); 520 buf.append("<br>" + NL); 521 522 String headertbl = buf.toString(); 523 524 int pos = body.toLowerCase().indexOf("<body"); 526 pos = body.indexOf(">", pos); 527 pos++; 528 529 String msg = body.substring(0, pos) + headertbl + body.substring(pos); 530 531 return msg; 532 } 533 534 544 private void saveMsgBodyAsText(boolean inclAllHeaders, 545 File file) throws IOException { 546 String decodedBody = getDecodedMessageBody(); 549 550 String body; 551 552 if (bodyHeader.getMimeType().getSubtype().equals("html")) { 553 body = HtmlParser.htmlToText(decodedBody); 557 } else { 558 body = decodedBody; 560 } 561 562 String [][] headers = getHeadersToSave(inclAllHeaders); 564 StringBuffer buf = new StringBuffer (); 565 566 for (int i = 0; i < headers[0].length; i++) { 567 buf.append(headers[0][i]); 568 buf.append(": "); 569 buf.append(headers[1][i]); 570 buf.append(NL); 571 } 572 573 buf.append(NL); 574 575 String msg = buf.toString() + body; 577 578 DiskIO.saveStringInFile(file, msg); 580 LOG.fine("Text msg saved as " + file.getAbsolutePath()); 581 } 582 583 593 private String [][] getHeadersToSave(boolean inclAll) { 594 List keyList = new ArrayList (); 595 List valueList = new ArrayList (); 596 BasicHeader basicHeader = new BasicHeader(header); 597 598 String from = basicHeader.getFrom().toString(); 599 String to = (basicHeader.getTo()[0]).toString(); 600 601 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, 602 DateFormat.MEDIUM); 603 String date = df.format(basicHeader.getDate()); 604 605 String subject = basicHeader.getSubject(); 606 607 Enumeration keys = header.getKeys(); 609 610 while (keys.hasMoreElements()) { 611 String key = (String ) keys.nextElement(); 612 613 if (key.equals("From")) { 614 } else if (key.equals("To")) { 615 } else if (key.equals("Subject")) { 616 } else if (key.equals("Date")) { 617 } else if (key.startsWith("Content-")) { 619 } else if (key.equals("Mime-Version") 621 || key.equals("MIME-Version")) { 622 } else if (key.startsWith("columba")) { 624 } else { 626 if (inclAll) { 627 keyList.add(key); 629 valueList.add((String ) header.get(key)); 630 } 631 } 632 } 633 634 keyList.add(MailResourceLoader.getString("header", "from")); 636 valueList.add(from); 637 keyList.add(MailResourceLoader.getString("header", "to")); 638 valueList.add(to); 639 keyList.add(MailResourceLoader.getString("header", "date")); 640 valueList.add(date); 641 keyList.add(MailResourceLoader.getString("header", "subject")); 642 valueList.add(subject); 643 644 for (int i = 0; i < attachments.size(); i++) { 645 String name = ((StreamableMimePart) attachments.get(i)).getHeader() 646 .getFileName(); 647 648 if (name != null) { 649 keyList.add(MailResourceLoader.getString("header", "attachment")); 650 valueList.add(name); 651 } 652 } 653 654 String [][] headerArray = new String [2][]; 656 headerArray[0] = new String [keyList.size()]; 657 headerArray[1] = new String [keyList.size()]; 658 659 for (int i = 0; i < keyList.size(); i++) { 660 headerArray[0][i] = (String ) keyList.get(i); 661 headerArray[1][i] = (String ) valueList.get(i); 662 } 663 664 return headerArray; 665 } 666 } 667 668 669 673 class ExtensionFileFilter extends FileFilter { 674 675 private String extension = "txt"; 676 677 678 private String description = "Text files (*.txt)"; 679 680 681 public ExtensionFileFilter(String extension, String description) { 682 super(); 683 this.extension = extension; 684 this.description = description; 685 } 686 687 688 public boolean accept(File f) { 689 if (f.isDirectory()) { 690 return true; 691 } 692 693 String ext = getFileExtension(f); 695 696 if ((ext != null) && (this.extension.toLowerCase().equals(ext))) { 697 return true; 698 } else { 699 return false; 700 } 701 } 702 703 709 public static String getFileExtension(File f) { 710 String ext = null; 711 String s = f.getName(); 712 int i = s.lastIndexOf('.'); 713 714 if ((i > 0) && (i < (s.length() - 1))) { 715 ext = s.substring(i + 1).toLowerCase(); 716 } 717 718 return ext; 719 } 720 721 722 public String getDescription() { 723 return this.description; 724 } 725 726 727 public String getExtension() { 728 return this.extension; 729 } 730 } 731 | Popular Tags |