Java tutorial
package de.tbuchloh.kiskis.gui; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Desktop; import java.awt.FlowLayout; import java.awt.event.AWTEventListener; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToolBar; import javax.swing.SwingUtilities; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.tbuchloh.kiskis.gui.common.MessageBox; import de.tbuchloh.kiskis.gui.common.UIConstants; import de.tbuchloh.kiskis.gui.dialogs.FileDialog; import de.tbuchloh.kiskis.gui.dialogs.ImportDialog; import de.tbuchloh.kiskis.gui.dialogs.MessageBoxErrorHandler; import de.tbuchloh.kiskis.gui.dialogs.OptionsDialog; import de.tbuchloh.kiskis.gui.dialogs.PasswordDialog; import de.tbuchloh.kiskis.gui.dialogs.PasswordDialog.Buttons; import de.tbuchloh.kiskis.gui.dialogs.TemplateOverviewDialog; import de.tbuchloh.kiskis.gui.feedback.FeedbackException; import de.tbuchloh.kiskis.gui.feedback.SubmitBug; import de.tbuchloh.kiskis.gui.feedback.SubmitFeatureRequest; import de.tbuchloh.kiskis.gui.systray.IMainFrame; import de.tbuchloh.kiskis.model.Password; import de.tbuchloh.kiskis.model.SecuredElement; import de.tbuchloh.kiskis.model.StandardDocumentFactory; import de.tbuchloh.kiskis.model.TPMDocument; import de.tbuchloh.kiskis.model.validation.DictionaryPasswordValidator; import de.tbuchloh.kiskis.model.validation.EmptyPasswordValidator; import de.tbuchloh.kiskis.model.validation.WeakPasswordValidator; import de.tbuchloh.kiskis.persistence.FileFormats; import de.tbuchloh.kiskis.persistence.ICryptoContext; import de.tbuchloh.kiskis.persistence.PasswordCryptoContext; import de.tbuchloh.kiskis.persistence.PasswordProxy; import de.tbuchloh.kiskis.persistence.PersistenceException; import de.tbuchloh.kiskis.persistence.PersistenceManager; import de.tbuchloh.kiskis.persistence.exporter.CSVExporter; import de.tbuchloh.kiskis.persistence.exporter.ExportException; import de.tbuchloh.kiskis.persistence.exporter.IExporter; import de.tbuchloh.kiskis.persistence.exporter.XSLExporter; import de.tbuchloh.kiskis.util.BuildProperties; import de.tbuchloh.kiskis.util.FileTools; import de.tbuchloh.kiskis.util.KisKisRuntimeException; import de.tbuchloh.kiskis.util.Settings; import de.tbuchloh.util.StopWatch; import de.tbuchloh.util.TimerTaskCallback; import de.tbuchloh.util.crypto.CryptoException; import de.tbuchloh.util.crypto.DigestFactory; import de.tbuchloh.util.crypto.InvalidCryptoInstallationException; import de.tbuchloh.util.crypto.SymmetricAlgo; import de.tbuchloh.util.crypto.TripleDES; import de.tbuchloh.util.localization.Messages; import de.tbuchloh.util.swing.DescriptiveItem; import de.tbuchloh.util.swing.ListSelectionDlg; import de.tbuchloh.util.swing.TextMessageBox; import de.tbuchloh.util.swing.Toolkit; import de.tbuchloh.util.swing.actions.ActionCallback; import de.tbuchloh.util.swing.dialogs.AboutDialog; import de.tbuchloh.util.swing.dialogs.HTMLViewDialog; import de.tbuchloh.util.swing.dialogs.MessagePane; import de.tbuchloh.util.swing.dialogs.TipOfTheDayDialog; import de.tbuchloh.util.swing.widgets.LRUFilesMenu; /** * <b>MainFramePanel</b>: the main component for the frame. * * @author gandalf * @version $Id: MainFrame.java,v 1.43 2007/04/27 11:03:14 tbuchloh Exp $ */ public final class MainFramePanel extends JPanel implements ContentChangedListener { /** * This task tries to evict sensitive information out of memory. Allocates as much memory as possible and writes 1 * into each byte. * * @author Tobias Buchloh (gandalf) * @version $Id: $ * @since 18.02.2012 */ private static final class CleanupMemoryTimerTask extends TimerTask { private static final Object LOCK = new Object(); @Override public void run() { synchronized (LOCK) { final StopWatch stopWatch = new StopWatch(); LOG.info("cleanupMemory entering ..."); final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); final long used = heapMemoryUsage.getUsed(); final long committed = heapMemoryUsage.getCommitted(); final long dummySize = committed - used; LOG.info(String.format("writing dummy objects. dummySize=%1$s, memoryUsage=%2$s ...", dummySize, heapMemoryUsage)); try { // we want to fill memory up to the end, but don't want to get an overrun final int chunkSize = 1024; final List<byte[]> chunks = new LinkedList<byte[]>(); while (heapMemoryUsage.getUsed() + 2 * chunkSize < heapMemoryUsage.getCommitted() * 0.95) { final byte[] chunk = new byte[chunkSize]; for (int i = 0; i < chunk.length; ++i) { chunk[i] = 0; } // System.out.println(memoryMXBean.getHeapMemoryUsage()); chunks.add(chunk); heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); } } catch (final OutOfMemoryError e) { LOG.warn("OutOfMemoryError: " + e); } finally { LOG.info("Running gc ..."); memoryMXBean.gc(); memoryMXBean.gc(); } LOG.info(String.format("cleanupMemory leaving ... time=%1$s, memoryUsage=%2$s", stopWatch.getDuration(), memoryMXBean.getHeapMemoryUsage())); } } } private final class AutoLockTimer implements AWTEventListener { private final AtomicBoolean _isActive; private TimerTaskCallback _last; public AutoLockTimer() { java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); _isActive = new AtomicBoolean(true); } /** * Overridden! * * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent) */ @Override public void eventDispatched(final AWTEvent event) { reschedule(); } protected void onLock() { LOG.debug("locking screen"); //$NON-NLS-1$ if (!_pwdBuffer.isValidPassword(new char[0])) { final JButton button = new JButton(M.createAction(this, "onUnlock")); //$NON-NLS-1$ _isActive.set(false); _parent.setMainView(Toolkit.centerWidget(button)); _parent.getJMenuBar().setVisible(false); if (_parent.getSystemTray().isValid()) { _parent.setVisible(false); _parent.onClosing(); } LOG.debug("screen locked ..."); //$NON-NLS-1$ } else { LOG.debug("screen not locked ..."); //$NON-NLS-1$ } } protected void onUnlock() { LOG.debug("try unlock"); //$NON-NLS-1$ final PasswordDialog dlg = new PasswordDialog(_parent, false); Toolkit.centerWindow(_parent, dlg); dlg.setVisible(true); if (dlg.getButton() == Buttons.APPROVED_BUTTON && _pwdBuffer.isValidPassword(dlg.getPassword())) { LOG.debug("unlock successful!"); //$NON-NLS-1$ _parent.setMainView(MainFramePanel.this); _parent.getJMenuBar().setVisible(true); _isActive.set(true); reschedule(); } } private synchronized void reschedule() { if (_last != null) { _last.cancel(); _last = null; } if (!isLocked() && Settings.getLockAfterMin() > 0) { _last = new TimerTaskCallback(this, "onLock"); //$NON-NLS-1$ _timer.schedule(_last, Settings.getLockAfterMin() * 60 * 1000); } } /** * @return true if the screen is locked currently */ public boolean isLocked() { return !_isActive.get(); } } private final class AutoSaveTimer { private TimerTask _autoSaveTask; public AutoSaveTimer() { _autoSaveTask = createTask(); scheduleTask(); } private TimerTaskCallback createTask() { return new TimerTaskCallback(this, "save"); //$NON-NLS-1$ } protected void save() { LOG.debug("Auto-save triggered!"); // if autolock is active we need a buffered password if (Settings.isAutoSaveEnabled() && !(_autoLock.isLocked() && !_pwdBuffer.hasBufferedPwd())) { if (MainFramePanel.this.saveDocument()) { final String msg = M.getString("AutoSaveTimer.showStatus"); MainFrame.showStatus(msg); } } else { LOG.debug("Auto-save skipped"); } scheduleTask(); LOG.debug("Auto-save done"); } private void scheduleTask() { _autoSaveTask.cancel(); final long autoSaveDelay = Settings.getAutoSaveDelay(); if (Settings.isAutoSaveEnabled() && autoSaveDelay > 0) { LOG.debug("reschedule auto save feature in " + autoSaveDelay); //$NON-NLS-1$ _autoSaveTask = createTask(); _timer.schedule(_autoSaveTask, autoSaveDelay); } } } private final class EditHandler { private final Action _copyAction = M.createAction(this, "copyNode"); //$NON-NLS-1$ private final Action _cutAction = M.createAction(this, "onCutNode"); //$NON-NLS-1$ private final Action _deleteAction = M.createAction(this, "onDeleteNode"); //$NON-NLS-1$ private final Action _editTemplatesAction = M.createAction(this, "onEditTemplates"); //$NON-NLS-1$ private final Action _newGroupNodeAction = M.createAction(this, "onNewGroupNode"); //$NON-NLS-1$ private final Action _newNodeAction = M.createAction(this, "onNewNode"); //$NON-NLS-1$ private final Action _pasteAction = M.createAction(this, "onPasteNode"); //$NON-NLS-1$ private final Action _renameAction = M.createAction(this, "onRenameNode"); //$NON-NLS-1$ private final Action _searchAction = M.createAction(this, "onSearchNode"); //$NON-NLS-1$ protected void copyNode() { _mainView.copyNode(); } /** * @return the associated menu. */ public JMenu getMenu() { final JMenu fileMenu = new JMenu(M.getString("edit_menu")); //$NON-NLS-1$ fileMenu.setMnemonic(M.getChar("edit_menu_mne")); //$NON-NLS-1$ final Action[] actions = { _editTemplatesAction, _renameAction, null, _newGroupNodeAction, _newNodeAction, _deleteAction, null, _copyAction, _cutAction, _pasteAction, null, _searchAction, null, _optionsAction }; addActions(fileMenu, actions); return fileMenu; } /** * @return the associated toolbar. */ public JToolBar getToolBar() { final JToolBar bar = new JToolBar(); bar.setFloatable(false); final Action[] actions = { _newGroupNodeAction, _newNodeAction, _deleteAction, null, _searchAction }; addActions(bar, actions); return bar; } protected void onCutNode() { _mainView.cutNode(); contentChanged(true); } protected void onDeleteNode() { _mainView.deleteNode(); contentChanged(true); } protected void onEditTemplates() { final TemplateOverviewDialog dlg = new TemplateOverviewDialog(MainFrame.getInstance(), getDoc()); dlg.setVisible(true); } protected void onNewGroupNode() { _mainView.createNewGroupNode(); contentChanged(true); } protected void onNewNode() { _mainView.createNewNode(); contentChanged(true); } protected void onPasteNode() { _mainView.pasteNode(); contentChanged(true); } protected void onRenameNode() { _mainView.renameNode(); contentChanged(true); } protected void onSearchNode() { _mainView.findNode(); } } private final class HelpHandler { protected void onCheckUpdates() { MainFrame.getInstance().checkUpdates(true); } protected void onOpenManual() { final File localFile = new File("doc/manual/index.html"); if (localFile.exists()) { // prefer local installation try { Desktop.getDesktop().open(localFile); } catch (final IOException e) { LOG.debug("Cannot use Desktop.open for file=" + localFile, e); final HTMLViewDialog dlg = new HTMLViewDialog(MainFrame.getInstance(), M.getLabel("onOpenManual").replaceAll("&", ""), false); try { dlg.setDocument(localFile.toURL()); dlg.setVisible(true); } catch (final Exception e1) { MessageBox.showException(e1); } } } else { final String website = BuildProperties.getWebsite(); try { try { Desktop.getDesktop().browse(new URI(website)); } catch (final IOException e) { LOG.debug(e, e); MessageBox.showErrorMessage(e.getMessage()); } } catch (final URISyntaxException e) { throw new KisKisRuntimeException("Website URL could not be parsed. website=" + website, e); } } } protected void showAbout() { try { final AboutDialog dlg = AboutDialog.create(_parent, M.getString("AboutDialog.title"), //$NON-NLS-1$ BuildProperties.getBuildProperties(), MainFrame.class.getResource(LICENSE), MainFrame.class.getResource(README), MainFrame.class.getResource(CHANGELOG)); dlg.setSystemInfo(); Toolkit.resizeToDesktop(dlg, 0.5f); Toolkit.centerWindow(MainFramePanel.this, dlg); dlg.setVisible(true); } catch (final IOException e) { throw new KisKisRuntimeException("Could not open AboutDialog", e); } } /** * @return */ public JMenu getMenu() { final JMenu fileMenu = new JMenu(M.getString("help_menu")); //$NON-NLS-1$ fileMenu.setMnemonic(M.getChar("help_menu_mne")); //$NON-NLS-1$ final Action[] helpMenuActions = { M.createAction(this, "onOpenManual"), M.createAction(this, "onShowTips"), null, M.createAction(this, "onCheckUpdates"), M.createAction(this, "onShowSelftest"), null, M.createAction(this, "onSubmitBug"), M.createAction(this, "onSubmitFeatureRequest"), null, M.createAction(this, "showAbout") }; addActions(fileMenu, helpMenuActions); return fileMenu; } protected void onShowTips() { showTipOfTheDay(true); } protected void onShowSelftest() { showSelftest(); } protected void onSubmitBug() { try { new SubmitBug().openMail(null); } catch (final FeedbackException e1) { MessageBox.showErrorTextMessageBox(e1.getMessage()); } } protected void onSubmitFeatureRequest() { try { new SubmitFeatureRequest().openMail(null); } catch (final FeedbackException e1) { MessageBox.showErrorTextMessageBox(e1.getMessage()); } } } private final class ExportHandler { private final Action _exportCSV; private final Action _exportHTML; private final Action _exportUserDefinedXSL; private final Action _exportXML; public ExportHandler() { _exportCSV = M.createAction(this, "onExportCSV"); _exportHTML = M.createAction(this, "onExportHTML"); _exportXML = M.createAction(this, "onExportXML"); _exportUserDefinedXSL = M.createAction(this, "onExportUserDefinedXSL"); } protected void export(final IExporter exporter) { final String key = "ExportHandler.warn_msg"; MessagePane.showWarningMessage(MainFramePanel.this, M.getString(key), key); try { final File f = getFile(M.getString("ExportHandler.choose_outputfile_title")); if (f != null) { exporter.export(_doc, f); MainFrame.showStatus(M.format("ExportHandler.finished", // FileTools.getShortAbsoluteFilename(f))); } } catch (final ExportException e) { LOG.debug(e, e); MessageBox.showErrorMessage(e.getMessage()); } } private File getFile(final String title) { final JFileChooser fc = new JFileChooser(Settings.getLastFile()); fc.setDialogTitle(title); if (fc.showSaveDialog(MainFramePanel.this) == JFileChooser.APPROVE_OPTION) { final File selectedFile = fc.getSelectedFile(); return selectedFile; } return null; } public JMenu getMenu() { final JMenu menu = new JMenu(M.getString("ExportHandler.menu_label")); //$NON-NLS-1$ menu.setMnemonic(M.getChar("ExportHandler.menu_mne")); //$NON-NLS-1$ final Action[] actions = { _exportCSV, _exportHTML, _exportXML, _exportUserDefinedXSL }; addActions(menu, actions); menu.setIcon(loadIcon(M.getString("ExportHandler.menu_icon"))); return menu; } protected void onExportCSV() { export(new CSVExporter()); } protected void onExportHTML() { export(new XSLExporter(ClassLoader.getSystemResource("export/kiskis-html.xsl"))); } protected void onExportUserDefinedXSL() { final File xsl = getFile(M.getString("ExportHandler.choose_stylesheet_title")); if (xsl != null) { try { export(new XSLExporter(xsl.toURL())); } catch (final MalformedURLException e) { throw new Error(e); } } } protected void onExportXML() { export(new XSLExporter(ClassLoader.getSystemResource("transform/kiskis-id.xsl"))); } } private final class PasswordBuffer implements PasswordProxy { private Password _bufferedPwd; private Password _givenPwd;; private TimerTask _disposeTask; private byte[] _pwdHash; public PasswordBuffer() { _disposeTask = createTask(); reset(); scheduleTask(); } /** * @return true if a password is buffered */ public synchronized boolean hasBufferedPwd() { return _bufferedPwd != null; } private boolean checkPassword(final char[] pwd) { final EmptyPasswordValidator epv = new EmptyPasswordValidator(); if (!epv.validatePassword(pwd)) { // confirm null-password return MessageBox.showConfirmDialog(MainFrame.getInstance(), // M.getString("no_pwd_warning")); } if (Settings.isCheckingMasterPassword()) { final WeakPasswordValidator wpv = new WeakPasswordValidator(); if (!wpv.validatePassword(pwd)) { final BigDecimal variations = wpv.getVariationCnt(); return MessageBox.showConfirmDialog(MainFrame.getInstance(), // M.format("weak_pwd_warning", variations)); } final DictionaryPasswordValidator dpv = new DictionaryPasswordValidator(); if (!dpv.validatePassword(pwd)) { return MessageBox.showConfirmDialog(MainFrame.getInstance(), // M.getString("dictionary_pwd_warning")); } } return true; } private TimerTaskCallback createTask() { return new TimerTaskCallback(this, "reset"); //$NON-NLS-1$ } private synchronized char[] doAskForPassword(File file, final boolean confirm) { LOG.debug("retrieve password!"); //$NON-NLS-1$ if (confirm) { final String key = "MainFramePanel.info_msg"; MessagePane.showInfoMessage(MainFrame.getInstance(), M.getString(key), key); } final PasswordDialog pdlg = new PasswordDialog(MainFrame.getInstance(), confirm); final String filename = FileTools.getShortAbsoluteFilename(file); pdlg.setMessage(M.format("enter_password_message", filename)); pdlg.setMessageTooltip(file.getAbsolutePath()); pdlg.setVisible(true); if (pdlg.getButton() == Buttons.APPROVED_BUTTON) { final char[] pwd = pdlg.getPassword(); if (!confirm || checkPassword(pwd)) { _pwdHash = hash(pwd); _givenPwd = new Password(pwd); if (Settings.isBufferingPwd()) { LOG.debug("Buffering entered password!"); //$NON-NLS-1$ _bufferedPwd = new Password(pwd); scheduleTask(); } return pwd; } } return null; } /** * Overridden! * * @see de.tbuchloh.kiskis.persistence.PasswordProxy#getPassword() */ @Override public synchronized char[] getPassword(File file) { return getPassword(file, false); } @Override public synchronized char[] getPassword(File file, final boolean confirm) { // TODO 09.11.2010, gandalf: too complex code if (hasGivenPassword()) { return _givenPwd.getPwd(); } else if (hasBufferedPwd()) { return _bufferedPwd.getPwd(); } else { return doAskForPassword(file, confirm); } } public boolean hasGivenPassword() { return _givenPwd != null; } private byte[] hash(final char[] input) { return DigestFactory.digest(DigestFactory.MD5, input); } public synchronized void forgetGivenPassword() { if (LOG.isDebugEnabled()) { LOG.debug("Forgetting given password"); } _givenPwd = null; } /** * @param password * the password to compare with * @return true, if the hashes are equal. */ public boolean isValidPassword(final char[] password) { return Arrays.equals(_pwdHash, hash(password)); } public synchronized void reset() { LOG.debug("dispose password buffer!"); //$NON-NLS-1$ _bufferedPwd = null; _givenPwd = null; } /** * reset hash and password buffer. */ public synchronized void resetAll() { reset(); resetHash(); } private synchronized void resetHash() { _pwdHash = hash(new char[0]); } private synchronized void scheduleTask() { _disposeTask.cancel(); if (Settings.getPwdDisposeDelay() > 0) { LOG.debug("reschedule password disposal feature"); //$NON-NLS-1$ _disposeTask = createTask(); _timer.schedule(_disposeTask, Settings.getPwdDisposeDelay()); } } } final class ReportHandler { private final Action _expiredNodesAction = M.createAction(this, "onExpiredNodes"); //$NON-NLS-1$ private final Action _lvNodesAction = M.createAction(this, "onLvNodes"); //$NON-NLS-1$ private final Action _mruNodesAction = M.createAction(this, "onMRUNodes"); //$NON-NLS-1$ public JMenu getMenu() { final JMenu menu = new JMenu(M.getString("REPORT_MENU_TITLE")); //$NON-NLS-1$ menu.setMnemonic(M.getChar("REPORT_MENU_MNE")); //$NON-NLS-1$ menu.add(_mruNodesAction); menu.add(_lvNodesAction); menu.add(_expiredNodesAction); return menu; } public JToolBar getToolBar() { final JToolBar bar = new JToolBar(); bar.setFloatable(false); bar.add(_mruNodesAction); bar.add(_lvNodesAction); return bar; } protected void onExpiredNodes() { final List<SecuredElement> l = getDoc().getExpiredElements(); final List<DescriptiveItem> items = new ArrayList<DescriptiveItem>(); for (final SecuredElement s : l) { final String name = M.format("EXPIRED_ITEM", s.getName(), UIConstants.SHORT_DATE.format(s.getPwd().getExpires().getTime())); final DescriptiveItem item = new DescriptiveItem(name, s); items.add(item); } selectAndShow(items, M.getString("onExpiredNodes.label")); //$NON-NLS-1$ } protected void onLvNodes() { final List<SecuredElement> l = getDoc().getLastViewedElements(10); final List<DescriptiveItem> items = new ArrayList<DescriptiveItem>(); for (final SecuredElement s : l) { final String name = M.format("LV_ITEM", s.getName(), UIConstants.LONG_DATE.format(s.getLastViewedDate().getTime())); final DescriptiveItem item = new DescriptiveItem(name, s); items.add(item); } selectAndShow(items, M.getString("onLvNodes.label")); //$NON-NLS-1$ } protected void onMRUNodes() { final List<SecuredElement> l = getDoc().getMostRecentlyViewedElements(10); final List<DescriptiveItem> items = new ArrayList<DescriptiveItem>(); for (final SecuredElement s : l) { final String name = M.format("MRV_ITEM", s.getName(), String.valueOf(s.getViewCounter())); final DescriptiveItem item = new DescriptiveItem(name, s); items.add(item); } selectAndShow(items, M.getString("onMRUNodes.label")); //$NON-NLS-1$ } } /** * @author Tobias Buchloh (gandalf) * @since 21.10.2010 */ private class ViewHandler { private JCheckBoxMenuItem _showingArchivedItemsBox; /** * @return the menu */ public JMenu getMenu() { final JMenu fileMenu = new JMenu(M.getString("view_menu")); //$NON-NLS-1$ fileMenu.setMnemonic(M.getChar("view_menu_mne")); //$NON-NLS-1$ _showingArchivedItemsBox = new JCheckBoxMenuItem(// M.createAction(this, "onShowingArchivedItems")); _showingArchivedItemsBox.setSelected(Settings.isShowingArchivedItems()); fileMenu.add(_showingArchivedItemsBox); return fileMenu; } /** * Hook method */ protected void onShowingArchivedItems() { Settings.setShowingArchivedItems(_showingArchivedItemsBox.isSelected()); _mainView.refreshView(); } } private static final String CHANGELOG = "CHANGELOG"; //$NON-NLS-1$ private static final String LICENSE = "LICENSE"; //$NON-NLS-1$ protected static final Log LOG = LogFactory.getLog(MainFramePanel.class); public static final Messages M = MainFrame.M; private static final MessageFormat MSG_FILE_NOT_FOUND = new MessageFormat(M.getString("MSG_FILE_NOT_FOUND")); //$NON-NLS-1$ private static final String README = "README"; //$NON-NLS-1$ private static final long serialVersionUID = 1L; private static final String TITLE = M.getString("window_title"); //$NON-NLS-1$ protected static void addActions(final JMenu fileMenu, final Action[] actions) { for (int i = 0; i < actions.length; ++i) { if (actions[i] == null) { fileMenu.addSeparator(); } else { fileMenu.add(actions[i]); } } } /** * Displays the selftest result */ public void showSelftest() { final String result = PersistenceManager.checkCryptography(); TextMessageBox.showInformation(_parent, result); } protected static void addActions(final JToolBar fileMenu, final Action[] actions) { for (int i = 0; i < actions.length; ++i) { if (actions[i] == null) { fileMenu.addSeparator(); } else { fileMenu.add(actions[i]); } } } public static Icon loadIcon(final String filename) { final URL url = MainFrame.class.getResource(filename); return new ImageIcon(url); } private final AutoLockTimer _autoLock; private final AutoSaveTimer _autoSave; protected Action _changeMasterPwdAction = M.createAction(this, "onChangeMasterPwd"); //$NON-NLS-1$ private final Action _closeDocAction = M.createAction(this, "closeDoc"); //$NON-NLS-1$ private ICryptoContext _cryptoContext; private TPMDocument _doc; private final EditHandler _editHandler; private final ExportHandler _exportHandler; private final Action _importAction = M.createAction(this, "onImport"); //$NON-NLS-1$ private final Action _lockScreenAction = M.createAction(this, IMainFrame.LOCK_SCREEN); private final LRUFilesMenu _lruFileMenu; protected MainView _mainView; private boolean _modified; private final Action _newFileAction = M.createAction(this, "newDocument"); //$NON-NLS-1$ private final Action _openFileAction = M.createAction(this, IMainFrame.OPEN_DOCUMENT); private final Action _optionsAction = M.createAction(this, "onShowOptions"); //$NON-NLS-1$ protected final MainFrame _parent; protected PasswordBuffer _pwdBuffer; private final Action _quitAction = M.createAction(this, IMainFrame.QUIT); private final ReportHandler _reportHandler; private final Action _saveAction = M.createAction(this, "saveDocument"); //$NON-NLS-1$ private final Action _saveAsAction = M.createAction(this, "saveDocumentAs"); //$NON-NLS-1$ protected Timer _timer; private JToolBar _toolbar; private final ViewHandler _viewHandler; private final HelpHandler _helpHandler; public MainFramePanel(final MainFrame parent) { super(); setLayout(new BorderLayout()); _parent = parent; _timer = new Timer(); _pwdBuffer = new PasswordBuffer(); _autoSave = new AutoSaveTimer(); _autoLock = new AutoLockTimer(); _reportHandler = new ReportHandler(); _editHandler = new EditHandler(); _viewHandler = new ViewHandler(); _exportHandler = new ExportHandler(); _helpHandler = new HelpHandler(); _lruFileMenu = new LRUFilesMenu(5); _lruFileMenu.addListener(new LRUFilesMenu.FileSelectionListener() { @Override public void selected(final File file) { loadFile(file); } }); init(); initDocument(); initToolBar(); _lruFileMenu.loadLRUFiles(Settings.getPreferences()); } protected void closeDoc() { if (confirmCloseDoc()) { initDocument(); } } public synchronized boolean confirmCloseDoc() { if (_modified) { return MessageBox.showConfirmDialog(_parent, M.format("save_on_close_msg", getDoc())); } // has not been modified => OK return true; } @Override public void contentChanged(final boolean changed) { LOG.debug("Content changed called!"); _modified = changed; updateTitle(); _mainView.updateTree(); } private ICryptoContext createCryptoContext(final File file) { SymmetricAlgo algo = getCryptoAlgorithm(); if (file.getName().endsWith("3des")) { algo = new TripleDES(); } return new PasswordCryptoContext(algo, _pwdBuffer, file); } private JMenu createFileMenu() { final JMenu fileMenu = new JMenu(M.getString("file_menu")); //$NON-NLS-1$ fileMenu.setMnemonic(M.getChar("file_menu_mne")); //$NON-NLS-1$ final Action[] first = { _newFileAction, _openFileAction, _saveAction, _saveAsAction, _closeDocAction, null, _changeMasterPwdAction, _importAction }; addActions(fileMenu, first); fileMenu.add(_exportHandler.getMenu()); final Action[] second = { null, _lockScreenAction, null, _quitAction }; addActions(fileMenu, second); fileMenu.addSeparator(); _lruFileMenu.addToMenu(fileMenu); _changeMasterPwdAction.setEnabled(Settings.isBufferingPwd()); return fileMenu; } /** * @param ctx * the {@link ICryptoContext} */ protected synchronized void doLoad(final ICryptoContext ctx) { final File selectedFile = ctx.getFile(); if (selectedFile.exists()) { _lruFileMenu.newFile(selectedFile); Settings.setLastFile(selectedFile); try { setDoc(PersistenceManager.load(ctx, new MessageBoxErrorHandler())); _cryptoContext = ctx; updateTitle(); _mainView.setRootNode(getDoc()); } catch (final PersistenceException e) { LOG.debug(e, e); _pwdBuffer.resetAll(); MessageBox.showErrorMessage(e.getMessage()); } catch (final CryptoException e) { LOG.debug(e, e); _pwdBuffer.resetAll(); MessageBox.showErrorMessage(e.getMessage()); } catch (final InvalidCryptoInstallationException e) { LOG.debug(e, e); _pwdBuffer.resetAll(); MessageBox.showMessageDialog(this, e.getMessage()); } finally { _pwdBuffer.forgetGivenPassword(); cleanupMemory(); } } else { JOptionPane.showMessageDialog(MainFramePanel.this, MSG_FILE_NOT_FOUND.format(new Object[] { selectedFile })); } } /** * Trys to evict sensitive information out of memory */ private void cleanupMemory() { _timer.schedule(new CleanupMemoryTimerTask(), 5000); } /** * @param ctx * the {@link ICryptoContext} */ protected void doSave(final ICryptoContext ctx) { final File newFile = ctx.getFile(); // we need to ask for the password final char[] pwd = _pwdBuffer.getPassword(newFile, true); synchronized (this) { if (pwd != null) { try { // OK, password was given so let's continue PersistenceManager.saveAs(getDoc(), ctx, true); cleanupMemory(); _lruFileMenu.newFile(newFile); contentChanged(false); final String succMsg = M.format("doSave.successful", newFile.getName()); MainFrame.showStatus(succMsg); if (PersistenceManager.checkMimeType(newFile) != FileFormats.PGP_FILE) { final String statusMsg = M.format("doSave.notEncrypted", newFile.getName()); MessagePane.showWarningMessage(this, statusMsg); } } catch (final PersistenceException e) { LOG.debug(e, e); MessageBox.showErrorMessage(e.getMessage()); } catch (final CryptoException e) { LOG.debug(e, e); MessageBox.showErrorMessage(e.getMessage()); } catch (final InvalidCryptoInstallationException e) { LOG.debug(e, e); MessageBox.showMessageDialog(this, e.getMessage()); } finally { _pwdBuffer.forgetGivenPassword(); } } } } /** * @see MainFrame#getAction(String) */ public ActionCallback getAction(final String key) { return (ActionCallback) M.createAction(this, key); } private ICryptoContext getContext() { if (_cryptoContext == null) { throw new NullPointerException(); } return _cryptoContext; } private SymmetricAlgo getCryptoAlgorithm() { try { final String algoClassName = Settings.getCryptoEngineClass(); final Class<?> algoClass = Class.forName(algoClassName); return (SymmetricAlgo) algoClass.newInstance(); } catch (final Exception e) { throw new RuntimeException(e); } } /** * @return Returns the doc. */ protected TPMDocument getDoc() { return _doc; } private void saveFileInBackground(final ICryptoContext ctx) { final Runnable run = new Runnable() { @Override public void run() { doSave(ctx); } }; SwingUtilities.invokeLater(run); } protected List<Action> getPopupActions() { return Arrays.asList(_newFileAction, _openFileAction, _saveAction, _saveAsAction, _closeDocAction, null, _lockScreenAction, null, _quitAction); } private void init() { _mainView = new MainView(_editHandler.getMenu().getPopupMenu()); this.add(_mainView, BorderLayout.CENTER); } private synchronized void initDocument() { _pwdBuffer.resetAll(); final File file = new File(""); _cryptoContext = createCryptoContext(file); _modified = false; setDoc(StandardDocumentFactory.createStandardDocument(file)); updateTitle(); _mainView.setRootNode(getDoc()); } protected JMenuBar initMenu() { final JMenuBar mb = new JMenuBar(); final JMenu menu = createFileMenu(); mb.add(menu); mb.add(_editHandler.getMenu()); mb.add(_viewHandler.getMenu()); mb.add(_reportHandler.getMenu()); mb.add(_helpHandler.getMenu()); return mb; } private void initToolBar() { final JPanel bars = new JPanel(new FlowLayout(FlowLayout.LEFT)); _toolbar = new JToolBar(); _toolbar.setFloatable(false); final Action[] actions = { _newFileAction, _openFileAction, _saveAction, _saveAsAction, _closeDocAction, null, _lockScreenAction, null, _quitAction }; addActions(_toolbar, actions); _toolbar.addSeparator(); bars.add(_toolbar); bars.add(_editHandler.getToolBar()); bars.add(_reportHandler.getToolBar()); this.add(bars, BorderLayout.NORTH); } /** * @param ctx * is the data-file to load. */ public void loadFile(final File file) { final ICryptoContext ctx = createCryptoContext(file); _pwdBuffer.reset(); loadFileInBackground(ctx); } private void loadFileInBackground(final ICryptoContext ctx) { final Runnable run = new Runnable() { @Override public void run() { doLoad(ctx); } }; SwingUtilities.invokeLater(run); } public void loadLastFile() { final List files = LRUFilesMenu.getLRUFiles(Settings.getPreferences(), 1); if (!files.isEmpty()) { loadFile((File) files.get(0)); } } void newDocument() { if (confirmCloseDoc()) { initDocument(); } } protected void onChangeMasterPwd() { _pwdBuffer.resetAll(); contentChanged(true); saveDocument(); } protected void onImport() { final ImportDialog dlg = new ImportDialog(MainFrame.getInstance(), _doc); dlg.setVisible(true); if (_modified) { _mainView.setRootNode(_doc); contentChanged(true); } } protected void onLockScreen() { _autoLock.onLock(); } protected void onQuit() { if (confirmCloseDoc()) { _parent.exit(); } } protected void onShowOptions() { final OptionsDialog dlg = new OptionsDialog(_parent); dlg.setVisible(true); } void openDocument() { if (confirmCloseDoc()) { final ICryptoContext ctx = selectFile(false); if (ctx != null) { loadFileInBackground(ctx); } } } public void exit() { _lruFileMenu.saveLRUFiles(Settings.getPreferences()); } public synchronized boolean saveDocument() { if (_modified) { final File file = getDoc().getFile(); if (file.exists()) { saveFileInBackground(getContext()); } else { saveDocumentAs(); } return !_modified; } return false; } public synchronized void saveDocumentAs() { final ICryptoContext ctx = selectFile(true); if (ctx != null) { _pwdBuffer.reset(); _cryptoContext = ctx; saveFileInBackground(ctx); } } protected void selectAndShow(final List<DescriptiveItem> nodes, final String title) { final ListSelectionDlg dlg = new ListSelectionDlg(MainFrame.getInstance(), title.replaceAll("&", "")); Toolkit.resizeToDesktop(dlg, 0.3f); Toolkit.centerWindow(this, dlg); dlg.setListData(nodes); dlg.setVisible(true); final DescriptiveItem el = (DescriptiveItem) dlg.getSelectedValue(); if (el != null) { _mainView.showNode((SecuredElement) el.getValue()); } } private ICryptoContext selectFile(final boolean isSaveDialog) { final FileDialog dlg = new FileDialog((JFrame) SwingUtilities.getWindowAncestor(this)); dlg.setSelectedFile(new File(Settings.getLastFile())); dlg.setSaveDialog(isSaveDialog); dlg.setVisible(true); final File selectedFile = dlg.getSelectedFile(); if (selectedFile == null) { return null; } return new PasswordCryptoContext(dlg.getSelectedAlgo(), _pwdBuffer, selectedFile); } /** * @param doc * The doc to set. */ protected void setDoc(final TPMDocument doc) { _doc = doc; } protected void updateTitle() { final Object[] p = new Object[] { getDoc().getFile().getName(), _modified ? M.getString("modified_char") //$NON-NLS-1$ : M.getString("not_modified_char") }; //$NON-NLS-1$ _parent.setTitle(MessageFormat.format(TITLE, p)); } /** * @param force * force to show the dialog */ public void showTipOfTheDay(boolean force) { final TipOfTheDayDialog dlg = new TipOfTheDayDialog(_parent, // getClass().getResource("tip-of-the-day.xml")); dlg.showTips(force); } }