1 package org.columba.mail.gui.message.viewer; 19 20 import java.awt.BorderLayout ; 21 import java.awt.Font ; 22 import java.awt.event.MouseEvent ; 23 import java.awt.event.MouseListener ; 24 import java.io.ByteArrayInputStream ; 25 import java.io.File ; 26 import java.io.FileOutputStream ; 27 import java.io.InputStream ; 28 import java.net.MalformedURLException ; 29 import java.net.URL ; 30 import java.nio.charset.Charset ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.Observable ; 34 import java.util.Observer ; 35 import java.util.logging.Logger ; 36 import java.util.regex.Matcher ; 37 import java.util.regex.Pattern ; 38 39 import javax.swing.BorderFactory ; 40 import javax.swing.JComponent ; 41 import javax.swing.JEditorPane ; 42 import javax.swing.JOptionPane ; 43 import javax.swing.JPanel ; 44 import javax.swing.JPopupMenu ; 45 import javax.swing.SwingUtilities ; 46 import javax.swing.event.CaretEvent ; 47 import javax.swing.event.CaretListener ; 48 import javax.swing.text.AttributeSet ; 49 import javax.swing.text.BadLocationException ; 50 import javax.swing.text.Element ; 51 import javax.swing.text.html.HTML ; 52 import javax.swing.text.html.HTMLDocument ; 53 54 import org.columba.api.plugin.IExtension; 55 import org.columba.api.plugin.IExtensionHandler; 56 import org.columba.api.plugin.IExtensionHandlerKeys; 57 import org.columba.api.plugin.PluginException; 58 import org.columba.api.plugin.PluginHandlerNotFoundException; 59 import org.columba.core.charset.CharsetOwnerInterface; 60 import org.columba.core.config.Config; 61 import org.columba.core.desktop.ColumbaDesktop; 62 import org.columba.core.gui.frame.DefaultContainer; 63 import org.columba.core.gui.htmlviewer.api.IHTMLViewerPlugin; 64 import org.columba.core.gui.util.FontProperties; 65 import org.columba.core.io.StreamUtils; 66 import org.columba.core.logging.Logging; 67 import org.columba.core.plugin.PluginManager; 68 import org.columba.core.util.TempFileStore; 69 import org.columba.core.xml.XmlElement; 70 import org.columba.mail.config.MailConfig; 71 import org.columba.mail.config.OptionsItem; 72 import org.columba.mail.folder.IMailbox; 73 import org.columba.mail.gui.composer.ComposerController; 74 import org.columba.mail.gui.composer.ComposerModel; 75 import org.columba.mail.gui.frame.MailFrameMediator; 76 import org.columba.mail.gui.message.IMessageController; 77 import org.columba.mail.gui.message.action.AddToAddressbookAction; 78 import org.columba.mail.gui.message.action.ComposeMessageAction; 79 import org.columba.mail.gui.message.action.CopyLinkLocationAction; 80 import org.columba.mail.gui.message.action.OpenAction; 81 import org.columba.mail.gui.message.action.OpenWithAction; 82 import org.columba.mail.gui.message.util.ColumbaURL; 83 import org.columba.mail.parser.text.HtmlParser; 84 import org.columba.ristretto.coder.Base64DecoderInputStream; 85 import org.columba.ristretto.coder.FallbackCharsetDecoderInputStream; 86 import org.columba.ristretto.coder.QuotedPrintableDecoderInputStream; 87 import org.columba.ristretto.message.MimeHeader; 88 import org.columba.ristretto.message.MimePart; 89 import org.columba.ristretto.message.MimeTree; 90 91 96 public class TextViewer extends JPanel implements IMimePartViewer, Observer , 97 CaretListener { 98 99 100 private static final Logger LOG = Logger 101 .getLogger("org.columba.mail.gui.message.viewer"); 102 103 private static final Pattern CIDPattern = Pattern.compile("cid:([^\"]+)", 104 Pattern.CASE_INSENSITIVE); 105 106 private String css = ""; 109 110 private XmlElement smilies; 112 113 private boolean enableSmilies; 114 115 private String name; 117 118 123 124 private String body; 125 126 129 private boolean htmlMessage; 130 131 private IMessageController mediator; 132 133 private IHTMLViewerPlugin viewerPlugin; 134 135 private IMailbox folder; 136 137 private Object uid; 138 139 private boolean usingJDIC; 140 141 public TextViewer(IMessageController mediator) { 142 super(); 143 144 this.mediator = mediator; 145 146 initHTMLViewerPlugin(); 147 148 setLayout(new BorderLayout ()); 149 150 add(viewerPlugin.getContainer(), BorderLayout.CENTER); 151 152 initConfiguration(); 153 154 initStyleSheet(); 155 156 if (!usingJDIC) 157 viewerPlugin.getComponent() 158 .addMouseListener(new URLMouseListener()); 159 160 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 161 } 162 163 private void initHTMLViewerPlugin() { 164 OptionsItem optionsItem = MailConfig.getInstance().getOptionsItem(); 165 String selectedBrowser = optionsItem.getStringWithDefault( 166 OptionsItem.MESSAGEVIEWER, OptionsItem.SELECTED_BROWSER, 167 "Default"); 168 169 try { 170 viewerPlugin = createHTMLViewerPluginInstance(selectedBrowser); 171 if (viewerPlugin == null || !viewerPlugin.initialized()) { 173 JOptionPane.showMessageDialog(this, 174 "Error while trying to load html viewer"); 175 176 LOG 177 .severe("Error while trying to load html viewer -> falling back to default"); 178 179 viewerPlugin = createHTMLViewerPluginInstance("Default"); 180 } 181 } catch (Exception e) { 182 viewerPlugin = createHTMLViewerPluginInstance("Default"); 183 184 if (Logging.DEBUG) 185 e.printStackTrace(); 186 } catch (Error e) { 187 viewerPlugin = createHTMLViewerPluginInstance("Default"); 188 189 if (Logging.DEBUG) 190 e.printStackTrace(); 191 } 192 193 } 194 195 private JPopupMenu createPopupMenu(ColumbaURL url) { 196 JPopupMenu menu = new JPopupMenu (); 197 menu.add(new CopyLinkLocationAction(url)); 198 menu.addSeparator(); 199 menu.add(new OpenAction(url)); 200 menu.add(new OpenWithAction(url)); 201 menu.addSeparator(); 202 menu.add(new AddToAddressbookAction(url)); 203 menu.add(new ComposeMessageAction(url)); 204 205 return menu; 206 } 207 208 private IHTMLViewerPlugin createHTMLViewerPluginInstance(String pluginId) { 209 IHTMLViewerPlugin plugin = null; 210 try { 211 212 IExtensionHandler handler = PluginManager.getInstance() 213 .getExtensionHandler( 214 IExtensionHandlerKeys.ORG_COLUMBA_CORE_HTMLVIEWER); 215 216 IExtension extension = handler.getExtension(pluginId); 217 if (extension == null) 218 return null; 219 220 plugin = (IHTMLViewerPlugin) extension.instanciateExtension(null); 221 222 return plugin; 223 } catch (PluginHandlerNotFoundException e) { 224 LOG.severe("Error while loading viewer plugin: " + e.getMessage()); 225 if (Logging.DEBUG) 226 e.printStackTrace(); 227 } catch (PluginException e) { 228 LOG.severe("Error while loading viewer plugin: " + e.getMessage()); 229 if (Logging.DEBUG) 230 e.printStackTrace(); 231 } 232 233 return null; 234 } 235 236 239 private void initConfiguration() { 240 XmlElement gui = MailConfig.getInstance().get("options").getElement( 241 "/options/gui"); 242 XmlElement messageviewer = gui.getElement("messageviewer"); 243 244 if (messageviewer == null) { 245 messageviewer = gui.addSubElement("messageviewer"); 246 } 247 248 messageviewer.addObserver(this); 249 250 smilies = messageviewer.getElement("smilies"); 251 252 if (smilies == null) { 253 smilies = messageviewer.addSubElement("smilies"); 254 } 255 256 smilies.addObserver(this); 258 259 String enable = smilies.getAttribute("enabled", "true"); 260 261 if (enable.equals("true")) { 262 enableSmilies = true; 263 } else { 264 enableSmilies = false; 265 } 266 267 XmlElement quote = messageviewer.getElement("quote"); 268 269 if (quote == null) { 270 quote = messageviewer.addSubElement("quote"); 271 } 272 273 quote.addObserver(this); 275 276 Font font = FontProperties.getTextFont(); 278 name = font.getName(); 279 280 XmlElement options = Config.getInstance().get("options").getElement( 281 "/options"); 282 XmlElement gui1 = options.getElement("gui"); 283 XmlElement fonts = gui1.getElement("fonts"); 284 285 if (fonts == null) { 286 fonts = gui1.addSubElement("fonts"); 287 } 288 289 fonts.addObserver(this); 291 292 296 } 297 298 303 public void view(IMailbox folder, Object uid, Integer [] address, 304 MailFrameMediator mediator) throws Exception { 305 306 this.folder = folder; 307 this.uid = uid; 308 309 MimePart bodyPart = null; 310 InputStream bodyStream; 311 312 MimeTree mimePartTree = folder.getMimePartTree(uid); 313 314 bodyPart = mimePartTree.getFromAddress(address); 315 316 if (bodyPart == null) { 317 bodyStream = new ByteArrayInputStream ("<No Message-Text>" 318 .getBytes()); 319 } else { 320 htmlMessage = bodyPart.getHeader().getMimeType().getSubtype() 322 .equals("html"); 323 324 bodyStream = folder.getMimePartBodyStream(uid, bodyPart 325 .getAddress()); 326 } 327 328 bodyStream = MessageParser.decodeBodyStream(bodyPart, bodyStream); 329 330 if (!htmlMessage) { 332 Charset charset = ((CharsetOwnerInterface) mediator).getCharset(); 333 charset = MessageParser.extractCharset(charset, bodyPart); 334 335 bodyStream = new FallbackCharsetDecoderInputStream(bodyStream, 336 charset); 337 } 338 339 StringBuffer text = StreamUtils.readCharacterStream(bodyStream); 341 342 if (isHTMLStrippingEnabled()) { 344 text = new StringBuffer (HtmlParser.stripHtmlTags(text.toString(), 346 true)); 347 348 htmlMessage = false; 349 } 350 351 if (htmlMessage) { 352 body = text.toString(); 354 355 body = downloadCIDParts(body, mimePartTree); 357 358 } else { 359 361 body = MessageParser.transformTextToHTML(text.toString(), css, 362 enableSmilies); 363 364 366 } 367 368 } 369 370 private boolean isHTMLStrippingEnabled() { 371 XmlElement html = MailConfig.getInstance().getMainFrameOptionsConfig() 372 .getRoot().getElement("/options/html"); 373 374 return Boolean.valueOf(html.getAttribute("disable")).booleanValue(); 375 } 376 377 383 private void initStyleSheet() { 384 388 393 394 css = "<style type=\"text/css\">\n" + "body {font-family:\"" + name 395 + "\";} \n" + "a { color: blue; text-decoration: underline }\n" 396 + "font.quoting {color:#949494;} \n" + "</style>\n"; 397 398 } 399 400 407 public void update(Observable arg0, Object arg1) { 408 Font font = FontProperties.getTextFont(); 409 name = font.getName(); 410 411 initStyleSheet(); 412 413 remove(viewerPlugin.getContainer()); 415 416 initHTMLViewerPlugin(); 418 419 add(viewerPlugin.getContainer(), BorderLayout.CENTER); 421 } 422 423 public String getSelectedText() { 424 return viewerPlugin.getSelectedText(); 425 } 426 427 public String getText() { 428 return viewerPlugin.getText(); 429 } 430 431 public void setCaretPosition(int position) { 432 viewerPlugin.setCaretPosition(position); 433 } 434 435 public void moveCaretPosition(int position) { 436 viewerPlugin.moveCaretPosition(position); 437 } 438 439 442 public JComponent getView() { 443 return viewerPlugin.getContainer(); 444 } 445 446 449 public void updateGUI() throws Exception { 450 viewerPlugin.view(body); 451 } 452 453 456 public void caretUpdate(CaretEvent arg0) { 457 } 459 460 private String downloadCIDParts(String body, MimeTree mimeTree) { 461 Matcher matcher = CIDPattern.matcher(body); 462 463 if (!matcher.find()) { 464 return body; 465 } 466 467 StringBuffer modifiedBody = new StringBuffer (body.length()); 468 File mimePartFile; 469 List mimeParts = mimeTree.getAllLeafs(); 470 471 MimePart CIDPart = findMimePart(mimeParts, matcher.group(1)); 472 if (CIDPart != null) { 473 mimePartFile = TempFileStore.createTempFile(); 474 try { 475 downloadMimePart(CIDPart, mimePartFile); 476 477 matcher.appendReplacement(modifiedBody, mimePartFile.toURL() 478 .toString()); 479 } catch (Exception e) { 480 matcher.appendReplacement(modifiedBody, "missing"); 481 } 482 } else { 483 matcher.appendReplacement(modifiedBody, "missing"); 484 } 485 486 while (matcher.find()) { 487 CIDPart = findMimePart(mimeParts, matcher.group(1)); 488 if (CIDPart != null) { 489 mimePartFile = TempFileStore.createTempFile(); 490 try { 491 downloadMimePart(CIDPart, mimePartFile); 492 493 matcher.appendReplacement(modifiedBody, mimePartFile 494 .toURL().toString()); 495 } catch (Exception e) { 496 matcher.appendReplacement(modifiedBody, "missing"); 497 } 498 } else { 499 matcher.appendReplacement(modifiedBody, "missing"); 500 } 501 } 502 503 matcher.appendTail(modifiedBody); 504 505 return modifiedBody.toString(); 506 } 507 508 private MimePart findMimePart(List mimeParts, String findCid) { 509 MimePart result; 510 Iterator it = mimeParts.iterator(); 511 while (it.hasNext()) { 512 result = (MimePart) it.next(); 513 514 String cid = result.getHeader().getContentID(); 515 if (cid != null 516 && cid.substring(1, cid.length() - 1).equalsIgnoreCase( 517 findCid)) { 518 return result; 519 } 520 } 521 522 return null; 523 } 524 525 private void downloadMimePart(MimePart part, File destFile) 526 throws Exception { 527 MimeHeader header = part.getHeader(); 528 529 InputStream bodyStream = folder.getMimePartBodyStream(uid, part 530 .getAddress()); 531 532 int encoding = header.getContentTransferEncoding(); 533 534 switch (encoding) { 535 case MimeHeader.QUOTED_PRINTABLE: 536 bodyStream = new QuotedPrintableDecoderInputStream(bodyStream); 537 break; 538 539 case MimeHeader.BASE64: 540 bodyStream = new Base64DecoderInputStream(bodyStream); 541 break; 542 default: 543 } 544 545 FileOutputStream fileStream = new FileOutputStream (destFile); 546 StreamUtils.streamCopy(bodyStream, fileStream); 547 fileStream.close(); 548 bodyStream.close(); 549 } 550 551 554 public boolean isHtmlMessage() { 555 return htmlMessage; 556 } 557 558 protected URL extractURL(MouseEvent event) { 559 JEditorPane pane = (JEditorPane ) event.getSource(); 560 HTMLDocument doc = (HTMLDocument ) pane.getDocument(); 561 562 Element e = doc.getCharacterElement(pane.viewToModel(event.getPoint())); 563 AttributeSet a = e.getAttributes(); 564 AttributeSet anchor = (AttributeSet ) a.getAttribute(HTML.Tag.A); 565 566 if (anchor == null) { 567 return null; 568 } 569 570 URL url = null; 571 572 try { 573 url = new URL ((String ) anchor.getAttribute(HTML.Attribute.HREF)); 574 } catch (MalformedURLException mue) { 575 return null; 576 } 577 578 return url; 579 } 580 581 587 private ColumbaURL extractMailToURL(MouseEvent event) { 588 589 ColumbaURL url = new ColumbaURL(extractURL(event)); 590 if (url.getRealURL() == null) 591 return null; 592 593 if (!url.getRealURL().getProtocol().equalsIgnoreCase("mailto")) 594 return url; 595 596 JEditorPane pane = (JEditorPane ) event.getSource(); 597 HTMLDocument doc = (HTMLDocument ) pane.getDocument(); 598 599 Element e = doc.getCharacterElement(pane.viewToModel(event.getPoint())); 600 try { 601 url.setSender(doc.getText(e.getStartOffset(), (e.getEndOffset() - e 602 .getStartOffset()))); 603 } catch (BadLocationException e1) { 604 url.setSender(""); 605 } 606 607 return url; 608 } 609 610 class URLMouseListener implements MouseListener { 611 612 public void mousePressed(MouseEvent event) { 613 if (event.isPopupTrigger()) { 614 processPopup(event); 615 } 616 } 617 618 public void mouseReleased(MouseEvent event) { 619 if (event.isPopupTrigger()) { 620 processPopup(event); 621 } 622 } 623 624 public void mouseEntered(MouseEvent event) { 625 } 626 627 public void mouseExited(MouseEvent event) { 628 } 629 630 public void mouseClicked(MouseEvent event) { 631 if (!SwingUtilities.isLeftMouseButton(event)) { 632 return; 633 } 634 635 URL url = extractURL(event); 636 637 if (url == null) { 638 return; 639 } 640 641 if (url.getProtocol().equalsIgnoreCase("mailto")) { 642 ComposerController controller = new ComposerController(); 644 new DefaultContainer(controller); 645 646 ComposerModel model = new ComposerModel(); 647 model.setTo(url.getFile()); 648 649 controller.setComposerModel(model); 651 652 controller.updateComponents(true); 653 } else { 654 ColumbaDesktop.getInstance().browse(url); 655 } 656 } 657 } 658 659 protected void processPopup(MouseEvent ev) { 660 final ColumbaURL mailto = extractMailToURL(ev); 662 663 final MouseEvent event = ev; 664 SwingUtilities.invokeLater(new Runnable () { 667 668 public void run() { 669 createPopupMenu(mailto).show(event.getComponent(), 670 event.getX(), event.getY()); 671 } 672 }); 673 } 674 } | Popular Tags |