/* ** $Id: QuickViewer.java,v 1.10 2001/05/07 12:37:22 kunugi Exp $ ** ** Copyright (c) 2000-2001 Jeff Gay ** on behalf of ICEMail.org ** Copyright (c) 1998-2000 by Timothy Gerard Endres ** ** This program is free software. ** ** You may redistribute it and/or modify it under the terms of the GNU ** General Public License as published by the Free Software Foundation. ** Version 2 of the license should be included with this distribution in ** the file LICENSE, as well as License.html. If the license is not ** included with this distribution, you may find a copy at the FSF web ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the ** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA. ** ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR ** REDISTRIBUTION OF THIS SOFTWARE. */ package org.icemail.mail; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FileDialog; import java.awt.Frame; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.URL; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Enumeration; import java.util.Date; import javax.mail.Address; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.internet.ContentType; import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeMessage; import javax.mail.internet.ParseException; import javax.activation.CommandInfo; import javax.activation.DataHandler; import javax.activation.MimeType; import javax.activation.MimeTypeParseException; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextPane; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.undo.UndoManager; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.StyledDocument; import javax.swing.border.EmptyBorder; import org.icemail.Package; import org.icemail.smime.SMIMELibrary; import org.icemail.smime.MissingCertificateException; import org.icemail.smime.PasswordCancelException; import org.icemail.util.AWTUtilities; import org.icemail.util.ComponentFactory; import org.icemail.util.UserProperties; /** * QuickViewer allows the contents of a message to be viewed. * The message contents are determined, displaying the headers and body in * separate panes. If there are attachments (multi-part messages) then an * additional pane is added which contains a button for each part. * * FIX ME; at this time, QuickViewer is closely tied to MainPanel due to * the interworkings of buttons and actions. These need to be separate in * future versions. */ public class QuickViewer extends JPanel implements ActionDistributor { private static final int Debug_ = Package.DEBUG ? Package.getLevel( "QuickViewer" ) : 0; private static ImageIcon AttUnknownIcon = null; private boolean showFullHeaders_ = false; private boolean parseSMimeMessages_ = true; private int maxBodyPartSize_ = (48 * 1024); private JPanel attachPane_ = null; private JEditorPane editorPane_ = null; private JPanel theAttachPane_ = null; private UndoManager undo_ = null; private Message outerMessage_ = null; private Part message_ = null; private JScrollPane editorScroller_ = null; private JScrollPane attachScroller_ = null; private JSplitPane bodySplit_ = null; private int bodyDivLoc_ = 5; private Component body_ = null; private Part popupAttachPart_ = null; private JPopupMenu popupMenu_ = null; private JTextArea hdrTextArea_ = null; private JScrollPane hdrScroller_ = null; private JSplitPane hdrSplit_ = null; private Action signedAction_ = null; private Action envelopedAction_ = null; private IActionListener actionListener_ = new IActionListener(); static { try { Image iconImg = AWTUtilities.getImageResource( "/org/icemail/mail/images/attunknown.gif" ); QuickViewer.AttUnknownIcon = new ImageIcon( iconImg ); } catch ( Exception ex ) { System.err.println( "QuickViewer.static: Error " + "loading icon: " + ex.getMessage() ); } } public QuickViewer() { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer()" ); } parseSMimeMessages_ = ( ICEMail.getSMIMELibrary() != null ); setLayout( new BorderLayout() ); int row = 0; hdrTextArea_ = new JTextArea( "" ); hdrTextArea_.setBorder( new EmptyBorder( 1, 3, 1, 3 ) ); hdrTextArea_.setEditable( false ); hdrScroller_ = new JScrollPane(); hdrScroller_.getViewport().add( hdrTextArea_ ); hdrScroller_.setMinimumSize( new Dimension(50, 15) ); hdrScroller_.setPreferredSize( new Dimension(600, 78) ); editorScroller_ = new JScrollPane(); editorScroller_.getViewport().add( new JTextArea() ); editorScroller_.setMinimumSize( new Dimension(50, 75) ); editorScroller_.setPreferredSize( new Dimension(400, 400) ); theAttachPane_ = new JPanel(); theAttachPane_.setLayout( new BoxLayout( theAttachPane_, BoxLayout.Y_AXIS ) ); attachScroller_ = new JScrollPane(); attachScroller_.getViewport().add( theAttachPane_ ); attachScroller_.setMinimumSize( new Dimension(5, 50) ); bodySplit_ = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, editorScroller_, attachScroller_ ); bodySplit_.setMinimumSize( new Dimension(50, 50) ); bodySplit_.setContinuousLayout( true ); bodySplit_.setDividerSize( 3 ); bodySplit_.setDividerLocation( bodyDivLoc_ ); hdrSplit_ = new JSplitPane( JSplitPane.VERTICAL_SPLIT, hdrScroller_, editorScroller_ ); hdrSplit_.setMinimumSize( new Dimension(50, 50) ); hdrSplit_.setContinuousLayout( true ); hdrSplit_.setDividerSize( 3 ); add( hdrSplit_, BorderLayout.CENTER ); popupMenu_ = ComponentFactory.getPopupMenuAndItems( ICEMail.getBundle(), "QuickViewer.attachment", actionListener_ ); maxBodyPartSize_ = UserProperties.getProperty( "maxBodyPartSize", (128 * 1024) ); Image img = null; try { img = AWTUtilities.getImageResource( "/org/icemail/mail/images/signed.gif" ); } catch ( IOException ex ) { img = null; } signedAction_ = new AbstractAction() { public void actionPerformed( ActionEvent event ) { SwingUtilities.invokeLater( new Runnable() { public void run() { showSigners(); } } ); } }; signedAction_.setEnabled( false ); signedAction_.putValue( Action.SMALL_ICON, new ImageIcon( img ) ); try { img = AWTUtilities.getImageResource( "/org/icemail/mail/images/enveloped.gif" ); } catch ( IOException ex ) { img = null; } envelopedAction_ = new AbstractAction() { public void actionPerformed( ActionEvent event ) { SwingUtilities.invokeLater( new Runnable() { public void run() { showEnvelope(); } } ); } }; envelopedAction_.setEnabled( false ); envelopedAction_.putValue( Action.SMALL_ICON, new ImageIcon( img ) ); setMinimumSize( new Dimension( 50, 50 ) ); setPreferredSize( new Dimension( 600, 250 ) ); } //............................................................ // Accessors public Action getSignedAction() { return signedAction_; } public Action getEnvelopedAction() { return envelopedAction_; } public String getTextSelection() { String result = ""; if ( editorPane_ != null ) { result = editorPane_.getSelectedText(); } return result; } //............................................................ // Mutators public void setFullHeaders( boolean full ) { showFullHeaders_ = full; getHeaderComponent(); hdrScroller_.revalidate(); } public void setParseSMimeMessages( boolean parse ) { parseSMimeMessages_ = parse; setMessage( outerMessage_ ); } /** * sets the current message to be displayed in the viewer */ public void setMessage( Message msg ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.setMessage(m)" ); } // The outerMessage ONLY differs from the message when the // message being displayed is signed or encrypted. In this // case, the outerMessage is the PKCS SMIME message, while // message is the inner "wrapped MIME" message outerMessage_ = msg; message_ = outerMessage_; int divLoc = 50; int hdrDivLoc = hdrSplit_.getDividerLocation(); try { if ( outerMessage_ != null ) { Folder f = outerMessage_.getFolder(); MailUtilities.setFolderOpenAndReady( f, Folder.READ_WRITE ); } } catch ( MessagingException ex ) { // UNDONE what do upon failure??? } boolean wasSplit = attachPane_ != null; if ( wasSplit ) { bodyDivLoc_ = bodySplit_.getDividerLocation(); } undo_ = new UndoManager(); editorPane_ = null; attachPane_ = null; getHeaderComponent(); if ( parseSMimeMessages_ && outerMessage_ instanceof MimeMessage ) { ICEMail.getSMIMELibrary().clearState(); try { if ( ICEMail.getSMIMELibrary().isSMimeMessage( outerMessage_ ) ) { message_ = ICEMail.getSMIMELibrary().getContentMessage( ICEMail.getDefaultSession(), outerMessage_ ); } } catch ( MissingCertificateException ex ) { ComponentFactory.showDialog( ICEMail.getBundle(), "QuickViewer.SMimeError", 0, JOptionPane.WARNING_MESSAGE, null ); message_ = outerMessage_; } catch ( PasswordCancelException ex ) { ComponentFactory.showDialog( ICEMail.getBundle(), "QuickViewer.SMimeError", 0, JOptionPane.WARNING_MESSAGE, null ); message_ = outerMessage_; } catch ( MessagingException ex ) { message_ = outerMessage_; } signedAction_.setEnabled( ICEMail.getSMIMELibrary().isSigned( message_ ) ); envelopedAction_.setEnabled( ICEMail.getSMIMELibrary().isEnveloped( message_ ) ); } else { signedAction_.setEnabled( false ); envelopedAction_.setEnabled( false ); } parseMessagePart( message_ ); if ( editorPane_ == null ) { editorPane_ = new JTextPane( new DefaultStyledDocument() ); } editorScroller_.getViewport().removeAll(); editorScroller_.getViewport().add( editorPane_ ); editorScroller_.revalidate(); if ( attachPane_ == null ) { body_ = editorScroller_; } else { bodySplit_.setLeftComponent( editorScroller_ ); bodySplit_.setRightComponent( attachScroller_ ); bodySplit_.invalidate(); bodySplit_.validate(); body_ = bodySplit_; } editorPane_.setCaretPosition( 0 ); editorPane_.moveCaretPosition( 0 ); hdrScroller_.getViewport().removeAll(); hdrScroller_.getViewport().add( hdrTextArea_ ); hdrScroller_.revalidate(); hdrSplit_.setBottomComponent( body_ ); hdrSplit_.setDividerLocation( hdrDivLoc ); hdrSplit_.invalidate(); hdrSplit_.validate(); if ( attachPane_ != null ) { bodySplit_.setDividerLocation( bodyDivLoc_ ); attachPane_.invalidate(); attachPane_.validate(); attachScroller_.revalidate(); } } //............................................................ /** * Distribute events from other sources to action listeners and distributors. * * @param event the event to distribute */ public void distribute( ActionEvent event ) { actionListener_.actionPerformed( event ); } //............................................................ // UNDONE Currently only showing certificates of first signer... // Need a better, more versatile dialog... public void showSigners() { ICEMail.getSMIMELibrary().showSigners( message_ ); } public void showEnvelope() { ICEMail.getSMIMELibrary().showEnvelope( message_ ); } //............................................................ public void loadLayoutProperties() { hdrSplit_.setDividerLocation( Configuration.getDividerLocation( Configuration.P_MSG_VIEWER_HDR_DIVIDER, 75 ) ); bodyDivLoc_ = Configuration.getDividerLocation( Configuration.P_MSG_VIEWER_BODY_DIVIDER, 75 ); } public void saveLayoutProperties() { Configuration.saveDividerLocation( Configuration.P_MSG_VIEWER_HDR_DIVIDER, hdrSplit_.getDividerLocation() ); Configuration.saveDividerLocation( Configuration.P_MSG_VIEWER_BODY_DIVIDER, bodyDivLoc_ ); } //............................................................ /** * Set the header text area from the contents of the current message. * Either the standard headers or all headers are decoded from the current message * into a string, which is then used to reset the contents of the header text area. */ private void getHeaderComponent() { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.getHeaderComponent()" ); } // clear previous headers if current message is null if ( outerMessage_ == null ) { hdrTextArea_.setText( null ); // removes all contents return; } // pull off the basic headers if present, i.e. date, addresses, subject, etc StringBuffer xtext = new StringBuffer(); Address[] xaddresses = null; try { xaddresses = outerMessage_.getFrom(); if ( xaddresses != null && xaddresses.length > 0 ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.From" ) ); xtext.append( MessageUtilities.decodeAddresses( xaddresses ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } try { xtext.append( ICEMail.getBundle().getString( "QuickViewer.Subject" ) ); xtext.append( outerMessage_.getSubject() ); xtext.append( '\n' ); } catch ( MessagingException xex ) { } try { Date xdate = outerMessage_.getSentDate(); if ( xdate != null ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.Date" ) ); xtext.append( ICEMail.getDateFormat().format( xdate ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } try { xaddresses = outerMessage_.getRecipients( Message.RecipientType.TO ); if ( xaddresses != null && xaddresses.length > 0 ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.To" ) ); xtext.append( MessageUtilities.decodeAddresses( xaddresses ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } try { xaddresses = outerMessage_.getRecipients( Message.RecipientType.CC ); if ( xaddresses != null && xaddresses.length > 0 ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.Cc" ) ); xtext.append( MessageUtilities.decodeAddresses( xaddresses ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } try { xaddresses = outerMessage_.getRecipients( Message.RecipientType.BCC ); if ( xaddresses != null && xaddresses.length > 0 ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.Bcc" ) ); xtext.append( MessageUtilities.decodeAddresses( xaddresses ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } try { xaddresses = outerMessage_.getReplyTo(); if ( xaddresses != null && xaddresses.length > 0 ) { xtext.append( ICEMail.getBundle().getString( "QuickViewer.Reply" ) ); xtext.append( MessageUtilities.decodeAddresses( xaddresses ) ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } // now the extra headers if desired if ( showFullHeaders_ ) { // We use getHeadersWithFrom() to pick up the 'From ' header // line if there is one. try { InternetHeaders xheaders = MessageUtilities.getHeadersWithFrom( outerMessage_ ); xheaders.removeHeader( "date" ); xheaders.removeHeader( "from" ); xheaders.removeHeader( "to" ); xheaders.removeHeader( "cc" ); xheaders.removeHeader( "bcc" ); xheaders.removeHeader( "reply-to" ); xheaders.removeHeader( "subject" ); Enumeration xenum = xheaders.getAllHeaderLines(); while ( xenum.hasMoreElements() ) { xtext.append( xenum.nextElement() ); xtext.append( '\n' ); } } catch ( MessagingException xex ) { } } if ( xtext.charAt( xtext.length() - 1 ) == '\n' ) { xtext.setLength( xtext.length() - 1 ); } hdrTextArea_.setText( xtext.toString() ); hdrTextArea_.setCaretPosition( 0 ); } private void establishEditorPane( Part bodyPart ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.establishEditorPane(p)" ); } if ( editorPane_ != null ) { addAttachment( bodyPart ); } else { // UNDONE - if content type we can't edit, attach... JEditorPane pane = null; EditorKit editor = null; Document doc = null; try { ContentType xct = MessageUtilities.getContentType( bodyPart ); if ( xct.match( "text/html" ) ) { pane = new JEditorPane(); pane.setContentType( "text/html" ); pane.setEditable( false ); editor = pane.getEditorKit(); doc = editor.createDefaultDocument(); pane.addHyperlinkListener( new IHyperlinkListener() ); } else if ( xct.match( "text/rtf" ) ) { pane = new JEditorPane(); pane.setContentType( "text/rtf" ); editor = pane.getEditorKit(); doc = editor.createDefaultDocument(); } else if ( xct.match( "text/*" ) ) { doc = new DefaultStyledDocument(); pane = new JTextPane( (StyledDocument)doc ); editor = pane.getEditorKit(); } if ( pane != null && doc != null ) { BufferedReader xreader = MessageUtilities.getTextReader( bodyPart ); editor.read( xreader, doc, 0 ); xreader.close(); /* ** NOTE This code is great, IF the html pane is adequate. ** The current version (JDK 1.1.6), is quite lame, and ** if the html is bad, it hangs the program. Thus, we ** may turn this on in a later more robust release. ** // Check to see if this looks like some valid HTML. // If so, we will compensate for the poor mailer. // UNDONE This should be controllable via a property. // if ( pane instanceof JTextPane ) { StringWriter contWrt = new StringWriter(); int len = doc.getLength(); if ( len > 6 ) len = 6; editor.write( contWrt, doc, 0, len ); if ( contWrt.toString(). equalsIgnoreCase( "" ) ) { len = doc.getLength(); contWrt = new StringWriter(); editor.write( contWrt, doc, 0, len ); String contStr = pane = new JEditorPane(); pane.setContentType( "text/html" ); editor = pane.getEditorKit(); doc = editor.createDefaultDocument(); StringReader contRdr = new StringReader( contWrt.toString() ); editor.read( contRdr, doc, 0 ); } } ** */ pane.setDocument( doc ); } //undo_ = new UndoManager(); doc.addUndoableEditListener( undo_ ); editorPane_ = pane; } catch ( IOException ex ) { ex.printStackTrace( System.err ); editorPane_ = null; } catch ( BadLocationException ex ) { ex.printStackTrace( System.err ); editorPane_ = null; } catch ( MessagingException ex ) { ex.printStackTrace( System.err ); editorPane_ = null; } } } private void parseMessagePart( Part bodyPart ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.parseMessagePart(p)" ); } if ( bodyPart == null ) { return; } //------------ // now get a content viewer for the main type... //------------ try { int xpartSize = bodyPart.getSize(); ContentType xpartType = MessageUtilities.getContentType( bodyPart ); if ( Package.DEBUG && Debug_ > 5 ) { System.out.println("----------------------------------------"); System.out.println( "Content-Type: " + xpartType.toString() ); System.out.println( "Size: " + xpartSize ); System.out.println( "Class: " + bodyPart.getClass().toString() ); } if ( xpartType.match( "multipart/*" ) ) { // ----------------------------------------------------- // ---------- M U L T I P A R T // ----------------------------------------------------- Multipart mPart = (Multipart)MessageUtilities.getPartContent( bodyPart ); int partCount = mPart.getCount(); for ( int i = 0, row = 0 ; i < partCount ; i++ ) { parseMessagePart( mPart.getBodyPart(i) ); } } else if ( xpartType.match( "message/*" ) ) { // ----------------------------------------------------- // ---------- M E S S A G E // ----------------------------------------------------- try { Part part = (Message)MessageUtilities.getPartContent( bodyPart ); parseMessagePart( part ); } catch ( java.lang.SecurityException ex ) { try { MimeMessage msg = new MimeMessage( ICEMail.getDefaultSession(), bodyPart.getInputStream() ); parseMessagePart( msg ); } catch ( IOException ex1 ) { ex1.printStackTrace(); } catch ( MessagingException ex1 ) { ex1.printStackTrace(); } } } else if ( xpartSize > maxBodyPartSize_ ) { // ----------------------------------------------------- // ---------- T O O L A R G E // ----------------------------------------------------- // UNDONE - if text, grab first 16K and display... addAttachment( bodyPart ); } else if ( xpartType.match( "text/*" ) ) { // ----------------------------------------------------- // ---------- T E X T / * // ----------------------------------------------------- establishEditorPane( bodyPart ); } else { // ----------------------------------------------------- // ---------- O T H E R // ----------------------------------------------------- addAttachment( bodyPart ); } } catch ( Exception ex ) { if ( editorPane_ != null ) { // UNDONE Figure out how to implement this... // addAttachment( ex ); ex.printStackTrace( System.err ); } else { JEditorPane pane = new JTextPane( new DefaultStyledDocument() ); ByteArrayOutputStream xbaos = new ByteArrayOutputStream(); ex.printStackTrace( new PrintWriter( xbaos ) ); pane.setText( "INTERNAL ERROR processing message part.\n\n" + xbaos ); editorPane_ = pane; } } } private void addAttachment( Part bodyPart ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.addAttachment(p)" ); } boolean isHTML = false; boolean isWord = false; ImageIcon icon = QuickViewer.AttUnknownIcon; int partSize = -1; String name = "Untitled"; ContentType xct = new ContentType(); try { partSize = bodyPart.getSize(); name = MessageUtilities.getFileName( bodyPart ); xct = MessageUtilities.getContentType( bodyPart ); if ( name == null || name.length() < 1 ) { name = bodyPart.getDescription(); if ( name == null ) { name = "Untitled"; } else if ( name.length() < 1 ) { name = "Untitled"; } else if ( name.length() > 48 ) { name = name.substring( 0, 48 ); } } icon = getMailcapIcon( bodyPart.getDataHandler() ); } catch ( ParseException ex ) { ex.printStackTrace( System.err ); } catch ( MessagingException ex ) { ex.printStackTrace( System.err ); } String btnName = name + " " + partSize + " bytes"; if ( attachPane_ == null ) { attachPane_ = theAttachPane_; theAttachPane_.removeAll(); } if ( Package.DEBUG && Debug_ > 0 ) { System.out.println( "QuickViewer.addAttachment(p): n-" + name ); } JButton btn = new JButton( btnName, icon ); Insets mgn = btn.getMargin(); mgn.left = 2; mgn.right = 6; btn.setMargin(mgn); String tip = xct.getBaseType(); if ( xct.getParameter( "charset" ) != null ) { tip = tip + "; " + xct.getParameter( "charset" ); } btn.setToolTipText( tip ); btn.addActionListener( new ViewAttachementAction( bodyPart ) ); btn.addMouseListener( new IMouseAdapter( btn, popupMenu_, bodyPart ) ); attachPane_.add( btn ); } private void viewPartProperties( Part part ) { JDialog dlg = new PropertiesDialog( null, part, false ); dlg.show(); } private void viewMessagePart( Part part ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.viewMessagePart(p)" ); } String action = ICEMail.getBundle().getString( "QuickViewer.viewing" ); if ( ! okLargeAttachment( part, action ) ) return; MainFrame.getInstance().setWaitCursor(); try { DataHandler dh = part.getDataHandler(); Object viewer = getMailcapViewer( dh ); if ( viewer != null ) { if ( viewer instanceof Component ) { String title = MessageUtilities.getFileName( part ); if ( title == null || title.length() < 1 ) title = "Attachment"; ComponentFrame frame = new ComponentFrame( (Component)viewer, title, dh.getDataSource() ); frame.show(); } } } catch ( MessagingException ex ) { ex.printStackTrace( System.err ); } MainFrame.getInstance().resetCursor(); } private void viewMessagePartAs( Part part ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.viewMessagePartAs(p)" ); } String action = ICEMail.getBundle().getString( "QuickViewer.viewing" ); if ( ! okLargeAttachment( part, action ) ) return; try { DataHandler dh = part.getDataHandler(); ViewAsDialog dlg = new ViewAsDialog( (Frame) getTopLevelAncestor(), part ); dlg.show(); String viewAsType = dlg.getViewAsType(); if ( viewAsType != null ) { ViewAsDataSource newSource = new ViewAsDataSource( viewAsType, dh ); DataHandler newDh = new DataHandler( newSource ); Object viewer = getMailcapViewer( newDh ); if ( viewer != null ) { if ( viewer instanceof Component ) { ComponentFrame frame = new ComponentFrame( (Component)viewer, "Attachment", newSource ); frame.show(); } } } } catch ( MessagingException ex ) { ex.printStackTrace( System.err ); } catch ( MimeTypeParseException ex ) { ex.printStackTrace( System.err ); } } private Object getMailcapViewer( DataHandler dh ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.getMailcapViewer(d): " + dh ); } Object bean = null; String xtext = null; CommandInfo ci = dh.getCommand( "view" ); if ( Package.DEBUG && Debug_ > 0 ) { System.out.println( "QuickViewer.getMailcapViewer(d): CommandInfo - " + ci ); } if ( ci == null ) { String contentType = dh.getContentType(); try { MimeType mime = new MimeType( contentType ); contentType = mime.getBaseType(); } catch ( MimeTypeParseException ex ) { } Object[] xargs = new Object[1]; xargs[0] = contentType; xtext = ICEMail.getBundle().getFormatString( "QuickViewer.noviewcommand", xargs ); } else { try { bean = dh.getBean( ci ); } catch ( Exception ex ) { if ( Package.DEBUG && Debug_ > 0 ) { System.out.println( "QuickViewer.getMailcapViewer(d): getBean(ci) failed - " + ex ); } bean = null; } if ( bean == null ) { Object[] xargs = new Object[2]; xargs[0] = ci.getCommandName(); xargs[1] = ci.getCommandClass(); xtext = ICEMail.getBundle().getFormatString( "QuickViewer.noviewer", xargs ); } } if ( bean == null ) { bean = new JTextArea( xtext ); } return bean; } private ImageIcon getMailcapIcon( DataHandler dh ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.getMailcapIcon(d): " + dh ); } ImageIcon result = QuickViewer.AttUnknownIcon; CommandInfo ci = dh.getCommand( "icon" ); if ( ci != null ) { result = (ImageIcon) dh.getBean( ci ); } return result; } private void saveMessagePartToFile( Part part ) { if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "QuickViewer.saveMessagePartToFile(p)" ); } String action = ICEMail.getBundle().getString( "QuickViewer.saving" ); if ( ! okLargeAttachment( part, action ) ) return; try { String fileName = MessageUtilities.getFileName( part ); FileDialog dialog = new FileDialog( (Frame)getTopLevelAncestor(), ICEMail.getBundle().getString( "QuickViewer.SaveAttachment" ), FileDialog.SAVE ); dialog.setFile( fileName ); dialog.show(); fileName = dialog.getFile(); String dirName = dialog.getDirectory(); if ( fileName != null && dirName != null ) { MainFrame.getInstance().setWaitCursor(); InputStream is = null; FileOutputStream os = null; File outputFile = new File( dirName, fileName ); try { is = part.getInputStream(); os = new FileOutputStream( outputFile ); byte[] buf = new byte[ 16 * 1024 ]; for ( ; ; ) { int bytes = is.read( buf ); if ( bytes < 0 ) break; if ( bytes > 0 ) os.write( buf, 0, bytes ); } is.close(); os.close(); } catch ( IOException ex ) { ex.printStackTrace( System.err ); try { if ( is != null ) is.close(); if ( os != null ) os.close(); } catch ( IOException iox ) { } Object[] xargs = new Object[2]; xargs[0] = new String( "IOException" ); xargs[1] = ex.getMessage(); ComponentFactory.showDialog( ICEMail.getBundle(), "QuickViewer.SaveError", 0, JOptionPane.ERROR_MESSAGE, xargs ); } MainFrame.getInstance().resetCursor(); } } catch ( MessagingException ex ) { Object[] xargs = new Object[2]; xargs[0] = new String( "MessagingException" ); xargs[1] = ex.getMessage(); ComponentFactory.showDialog( ICEMail.getBundle(), "QuickViewer.SaveError", 0, JOptionPane.ERROR_MESSAGE, xargs ); } } private boolean okLargeAttachment( Part part, String action ) { boolean result = true; try { int partSize = part.getSize(); if ( partSize > (2048 * 1024) ) { double megs = ((double)partSize) / ( 1024.0 * 1024.0 ); DecimalFormat fmt = new DecimalFormat( "#,##0.00" ); Object[] xargs = new Object[2]; xargs[0] = fmt.format( megs ); xargs[1] = action; int option = ComponentFactory.showDialog( ICEMail.getBundle(), "QuickViewer.LargeAttach", 0, JOptionPane.QUESTION_MESSAGE, xargs ); if ( option == JOptionPane.YES_OPTION ) result = true; else result = false; } } catch ( MessagingException ex ) { } return result; } //............................................................ // I N N E R C L A S S E S //............................................................ private class IActionListener implements ActionListener { /** * Invoked when an action occurs. *

* Implementation of java.awt.event.ActionListener.actionPerformed() * * @param event the action event that occured */ public void actionPerformed( ActionEvent event ) { String command = event.getActionCommand(); if ( Package.DEBUG && Package.isTraceable( "QuickViewer" ) ) { System.out.println( "IActionListener.actionPerformed(ae): " + command ); } if ( command.startsWith( "POPUP:" ) ) { if ( command.equals( "POPUP:SAVE" ) ) { saveMessagePartToFile( popupAttachPart_ ); } else if ( command.equals( "POPUP:VIEW" ) ) { viewMessagePart( popupAttachPart_ ); } else if ( command.equals( "POPUP:VIEWAS" ) ) { viewMessagePartAs( popupAttachPart_ ); } else if ( command.equals( "POPUP:PROPS" ) ) { viewPartProperties( popupAttachPart_ ); } } else if ( command.equals( "CUT" ) ) { if ( editorPane_ != null ) { editorPane_.cut(); } } else if ( command.equals( "COPY" ) ) { if ( editorPane_ != null ) { editorPane_.copy(); } } else if ( command.equals( "PASTE" ) ) { if ( editorPane_ != null ) { editorPane_.paste(); } } else if ( command.equals( "UNDO" ) ) { if ( undo_ != null && undo_.canUndoOrRedo() ) { undo_.undoOrRedo(); } } } } //............................................................ private class SaveAttachementAction implements ActionListener { Part i_part_; public SaveAttachementAction( Part part ) { i_part_ = part; } public void actionPerformed( ActionEvent e ) { saveMessagePartToFile( i_part_ ); } } //............................................................ private class ViewAttachementAction implements ActionListener { Part i_part_; public ViewAttachementAction( Part part ) { i_part_ = part; } public void actionPerformed( ActionEvent e ) { viewMessagePart( i_part_ ); } } //............................................................ private class IMouseAdapter extends MouseAdapter { private Component i_parent_ = null; private JPopupMenu i_popupmenu_ = null; private Part i_part_ = null; private boolean i_isPopupTrigger_ = false; public IMouseAdapter( Component parent, JPopupMenu menu, Part bodyPart ) { super(); i_parent_ = parent; i_popupmenu_ = menu; i_part_ = bodyPart; } public void mousePressed( MouseEvent event ) { i_isPopupTrigger_ = false; if ( event.isPopupTrigger() ) { i_isPopupTrigger_ = true; popupAttachPart_ = i_part_; i_popupmenu_.show( i_parent_, event.getX(), event.getY() ); } } public void mouseReleased( MouseEvent event ) { if ( i_isPopupTrigger_ ) return; if ( event.isPopupTrigger() ) { i_isPopupTrigger_ = true; popupAttachPart_ = i_part_; i_popupmenu_.show( i_parent_, event.getX(), event.getY() ); } } public void mouseClicked( MouseEvent event ) { if ( i_isPopupTrigger_ ) { i_isPopupTrigger_ = false; } } } //............................................................ private class IHyperlinkListener implements HyperlinkListener { public void hyperlinkUpdate( HyperlinkEvent event ) { HyperlinkEvent.EventType type = event.getEventType(); Object source = event.getSource(); URL url = event.getURL(); if ( type == HyperlinkEvent.EventType.ACTIVATED ) { MainFrame.getInstance().setWaitCursor(); HotJavaMailBrowser.setBrowserDocumentURL( url ); MainFrame.getInstance().resetCursor(); } else if ( type == HyperlinkEvent.EventType.ENTERED ) { // This and EXITED do not work (why?)! When they // do, we will probably want to toggle the cursor. } else if ( type == HyperlinkEvent.EventType.EXITED ) { } } } }