/* * * Paros and its related class files. * * Paros is an HTTP/HTTPS proxy for assessing web application security. * Copyright (C) 2003-2004 Chinotec Technologies Company * * This program is free software; you can redistribute it and/or * modify it under the terms of the Clarified Artistic License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Clarified Artistic License for more details. * * You should have received a copy of the Clarified Artistic License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // ZAP: 2011/05/15 Improved error logging // ZAP: 2012/02/18 Rationalised session handling // ZAP: 2012/04/23 Added @Override annotation to all appropriate methods. // ZAP: 2012/06/11 Changed to call the method Control.shutdown(boolean) with the // parameter set as true. // ZAP: 2012/06/19 Changed the method sessionOpened(File,Exception) to not call // the method ExtensionLoader.sessionChangedAllPlugin, now it's done in the // class Control. // ZAP: 2012/07/02 Changed to use the new database compact option in the method // exit(). // ZAP: 2012/07/23 Removed parameter from View.getSessionDialog call. // ZAP: 2012/12/06 Issue 428: Moved exit code to control to support the marketplace // ZAP: 2013/01/25 Removed the "(non-Javadoc)" comments. // ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments // ZAP: 2013/03/03 Issue 547: Deprecate unused classes and methods // ZAP: 2013/04/16 Issue 638: Persist and snapshot sessions instead of saving them // ZAP: 2013/08/05 Proper call for starting Session Properties dialog // ZAP: 2013/08/28 Issue 695: Sites tree doesnt clear on new session created by API // ZAP: 2014/05/20 Issue 1191: Cmdline session params have no effect // ZAP: 2014/12/22 Issue 1476: Display contexts in the Sites tree // ZAP: 2015/01/29 Issue 1489: Version number in window title // ZAP: 2015/02/05 Issue 1524: New Persist Session dialog // ZAP: 2015/04/02 Issue 321: Support multiple databases // ZAP: 2015/12/14 Log exception and internationalise error message // ZAP: 2016/10/26 Issue 1952: Do not allow Contexts with same name // ZAP: 2017/02/25 Issue 2618: Let the user select the name for snapshots package org.parosproxy.paros.control; import java.awt.EventQueue; import java.io.File; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.db.Database; import org.parosproxy.paros.db.DatabaseException; import org.parosproxy.paros.db.RecordSession; import org.parosproxy.paros.extension.option.DatabaseParam; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.model.Session; import org.parosproxy.paros.model.SessionListener; import org.parosproxy.paros.view.View; import org.parosproxy.paros.view.WaitMessageDialog; import org.zaproxy.zap.model.IllegalContextNameException; import org.zaproxy.zap.view.ContextExportDialog; import org.zaproxy.zap.view.PersistSessionDialog; import org.zaproxy.zap.view.SessionTableSelectDialog; public class MenuFileControl implements SessionListener { private static Logger log = Logger.getLogger(MenuFileControl.class); private View view = null; private Model model = null; private Control control = null; private WaitMessageDialog waitMessageDialog = null; private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); public MenuFileControl(Model model, View view, Control control) { this.view = view; this.model = model; this.control = control; } public void exit() { control.exit(false, null); } public void newSession(boolean isPromptNewSession) throws ClassNotFoundException, Exception { if (isPromptNewSession) { // ZAP: i18n if (model.getSession().isNewState()) { if (view.showConfirmDialog(Constant.messages.getString("menu.file.discardSession")) != JOptionPane.OK_OPTION) { return; } control.discardSession(); } else if (view.showConfirmDialog(Constant.messages.getString("menu.file.closeSession")) != JOptionPane.OK_OPTION) { return; } } int newSessionOption = model.getOptionsParam().getDatabaseParam().getNewSessionOption(); if (model.getOptionsParam().getDatabaseParam().isNewSessionPrompt()) { PersistSessionDialog psd = new PersistSessionDialog(View.getSingleton().getMainFrame()); // Set up the default option - ie the same one the user chose last time switch (newSessionOption) { case DatabaseParam.NEW_SESSION_TIMESTAMPED: psd.setTimestampChosen(); break; case DatabaseParam.NEW_SESSION_USER_SPECIFIED: psd.setPersistChosen(); break; case DatabaseParam.NEW_SESSION_TEMPORARY: psd.setTemporaryChosen(); break; default: break; } psd.setVisible(true); if (psd.isTimestampChosen()) { newSessionOption = DatabaseParam.NEW_SESSION_TIMESTAMPED; } else if (psd.isPersistChosen()) { newSessionOption = DatabaseParam.NEW_SESSION_USER_SPECIFIED; } else { newSessionOption = DatabaseParam.NEW_SESSION_TEMPORARY; } // Save for next time model.getOptionsParam().getDatabaseParam().setNewSessionOption(newSessionOption); model.getOptionsParam().getDatabaseParam().setNewSessionPrompt(!psd.isDontAskAgain()); } switch (newSessionOption) { case DatabaseParam.NEW_SESSION_TIMESTAMPED: String filename = getTimestampFilename(); if (filename != null) { this.newSession(filename); } else { control.newSession(); } break; case DatabaseParam.NEW_SESSION_USER_SPECIFIED: control.newSession(); this.saveAsSession(); break; default: control.newSession(); break; } } private String getTimestampFilename() { File dir = new File(Constant.getZapHome(), "sessions"); if (! dir.exists()) { if (! dir.mkdirs()) { return null; } } String timestamp = dateFormat.format(new Date()); File tmpFile = new File(dir, timestamp + ".session"); return tmpFile.getAbsolutePath(); } public boolean newSession(String fileName) { final Object[] created = { Boolean.TRUE }; waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.newSession.wait.dialogue")); control.newSession(fileName, new SessionListener() { @Override public void sessionSnapshot(Exception e) { } @Override public void sessionSaved(final Exception e) { if (EventQueue.isDispatchThread()) { if (e == null) { setTitle(); view.getSiteTreePanel().getTreeSite().setModel(model.getSession().getSiteTree()); } else { view.showWarningDialog(Constant.messages.getString("menu.file.newSession.error")); log.error("Error creating session file " + model.getSession().getFileName(), e); created[0] = Boolean.FALSE; } if (waitMessageDialog != null) { waitMessageDialog.setVisible(false); waitMessageDialog = null; } } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { sessionSaved(e); } }); } } @Override public void sessionOpened(File file, Exception e) { } }); waitMessageDialog.setVisible(true); return created[0] == Boolean.TRUE; } public boolean openSession(String session) { final Object[] opened = { Boolean.TRUE }; File sessionFile = new File(session); waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.loadSession")); log.info("opening session file " + sessionFile.getAbsolutePath()); control.openSession(sessionFile, new SessionListener() { @Override public void sessionSnapshot(Exception e) { } @Override public void sessionSaved(Exception e) { } @Override public void sessionOpened(final File file, final Exception e) { if (EventQueue.isDispatchThread()) { if (e != null) { view.showWarningDialog(Constant.messages.getString("menu.file.openSession.error")); log.error("error opening session file " + model.getSession().getFileName(), e); opened[0] = Boolean.FALSE; } view.getSiteTreePanel().getTreeSite().setModel(model.getSession().getSiteTree()); setTitle(); if (waitMessageDialog != null) { waitMessageDialog.setVisible(false); waitMessageDialog = null; } } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { sessionOpened(file, e); } }); } } }); waitMessageDialog.setVisible(true); return opened[0] == Boolean.TRUE; } public void openSession() { // TODO extract into db specific classes?? if (Database.DB_TYPE_HSQLDB.equals(model.getDb().getType())) { this.openFileBasedSession(); } else { this.openDbBasedSession(); } } private void openFileBasedSession () { JFileChooser chooser = new JFileChooser(model.getOptionsParam().getUserDirectory()); chooser.setFileHidingEnabled(false); // By default ZAP on linux puts timestamped sessions under a 'dot' directory File file = null; chooser.setFileFilter(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else if (file.isFile() && file.getName().endsWith(".session")) { return true; } return false; } @Override public String getDescription() { // ZAP: Rebrand return Constant.messages.getString("file.format.zap.session"); } }); int rc = chooser.showOpenDialog(view.getMainFrame()); if(rc == JFileChooser.APPROVE_OPTION) { try { file = chooser.getSelectedFile(); if (file == null) { return; } model.getOptionsParam().setUserDirectory(chooser.getCurrentDirectory()); log.info("opening session file " + file.getAbsolutePath()); waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.loadSession")); control.openSession(file, this); waitMessageDialog.setVisible(true); } catch (Exception e) { log.error(e.getMessage(), e); } } } private void openDbBasedSession() { try { List<String> sessionList = new ArrayList<String>(); for (RecordSession rs : model.getDb().getTableSession().listSessions()) { sessionList.add(""+rs.getSessionId()); } SessionTableSelectDialog ssd = new SessionTableSelectDialog(View.getSingleton().getMainFrame(), sessionList); ssd.setVisible(true); if (ssd.getSelectedSession() != null) { waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.loadSession")); control.openSession(ssd.getSelectedSession(), this); waitMessageDialog.setVisible(true); } } catch (DatabaseException e) { log.error(e.getMessage(), e); } } public void saveSession() { Session session = model.getSession(); if (session.isNewState()) { view.showWarningDialog("Please use Save As..."); return; } try { waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.savingSession")); // ZAP: i18n control.saveSession(session.getFileName(), this); log.info("saving session file " + session.getFileName()); // ZAP: If the save is quick the dialog can already be null here if (waitMessageDialog != null) { waitMessageDialog.setVisible(true); } } catch (Exception e) { view.showWarningDialog(Constant.messages.getString("menu.file.savingSession.error")); // ZAP: i18n log.error("error saving session file " + session.getFileName()); log.error(e.getMessage(), e); } } public void saveAsSession() { Session session = model.getSession(); JFileChooser chooser = new JFileChooser(model.getOptionsParam().getUserDirectory()); // ZAP: set session name as file name proposal File fileproposal = new File(session.getSessionName()); if (session.getFileName() != null && session.getFileName().trim().length() > 0) { // if there is already a file name, use it fileproposal = new File(session.getFileName()); } chooser.setSelectedFile(fileproposal); chooser.setFileFilter(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else if (file.isFile() && file.getName().endsWith(".session")) { return true; } return false; } @Override public String getDescription() { // ZAP: Rebrand return Constant.messages.getString("file.format.zap.session"); } }); File file = null; int rc = chooser.showSaveDialog(view.getMainFrame()); if(rc == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); if (file == null) { return; } model.getOptionsParam().setUserDirectory(chooser.getCurrentDirectory()); String fileName = file.getAbsolutePath(); if (!fileName.endsWith(".session")) { fileName += ".session"; } try { waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.savingSession")); // ZAP: i18n control.saveSession(fileName, this); log.info("save as session file " + session.getFileName()); waitMessageDialog.setVisible(true); } catch (Exception e) { log.error(e.getMessage(), e); } } } public void saveSnapshot() { Session session = model.getSession(); JFileChooser chooser = new JFileChooser(model.getOptionsParam().getUserDirectory()); // ZAP: set session name as file name proposal File fileproposal = new File(session.getSessionName()); if (session.getFileName() != null && session.getFileName().trim().length() > 0) { String proposedFileName; // if there is already a file name, use it and add a timestamp proposedFileName = StringUtils.removeEnd(session.getFileName(), ".session"); proposedFileName += "-" + dateFormat.format(new Date()) + ".session"; fileproposal = new File(proposedFileName); } chooser.setSelectedFile(fileproposal); chooser.setFileFilter(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else if (file.isFile() && file.getName().endsWith(".session")) { return true; } return false; } @Override public String getDescription() { return Constant.messages.getString("file.format.zap.session"); } }); File file = null; int rc = chooser.showSaveDialog(view.getMainFrame()); if(rc == JFileChooser.APPROVE_OPTION) { file = chooser.getSelectedFile(); if (file == null) { return; } model.getOptionsParam().setUserDirectory(chooser.getCurrentDirectory()); String fileName = file.getAbsolutePath(); if (!fileName.endsWith(".session")) { fileName += ".session"; } try { waitMessageDialog = view.getWaitMessageDialog(Constant.messages.getString("menu.file.savingSnapshot")); // ZAP: i18n control.snapshotSession(fileName, this); log.info("Snapshotting: " + session.getFileName() + " as " + fileName); waitMessageDialog.setVisible(true); } catch (Exception e) { log.error(e.getMessage(), e); } } } private void setTitle() { StringBuilder strBuilder = new StringBuilder(model.getSession().getSessionName()); if (!model.getSession().isNewState()) { File file = new File(model.getSession().getFileName()); strBuilder.append(" - ").append(file.getName().replaceAll(".session\\z", "")); } view.getMainFrame().setTitle(strBuilder.toString()); } public void properties() { // ZAP: proper call of existing method View.getSingleton().showSessionDialog(model.getSession(), null); // ZAP: Set the title consistently setTitle(); // view.getMainFrame().setTitle(Constant.PROGRAM_NAME + " " + Constant.PROGRAM_VERSION + " - " + model.getSession().getSessionName()); } @Override public void sessionOpened(File file, Exception e) { if (e == null) { // ZAP: Removed the statement that called the method // ExtensionLoader.sessionChangedAllPlugin, now it's done in the // class Control. // ZAP: Set the title consistently setTitle(); //view.getMainFrame().setTitle(file.getName().replaceAll(".session\\z","") + " - " + Constant.PROGRAM_NAME); } else { view.showWarningDialog(Constant.messages.getString("menu.file.openSession.errorFile")); if (file != null) { log.error("Error opening session file " + file.getAbsolutePath(), e); } else { // File is null for table based sessions (ie non HSQLDB) log.error(e.getMessage(), e); } } if (waitMessageDialog != null) { waitMessageDialog.setVisible(false); waitMessageDialog = null; } } @Override public void sessionSaved(Exception e) { if (e == null) { // ZAP: Set the title consistently setTitle(); //File file = new File(model.getSession().getFileName()); //view.getMainFrame().setTitle(file.getName().replaceAll(".session\\z","") + " - " + Constant.PROGRAM_NAME); } else { view.showWarningDialog(Constant.messages.getString("menu.file.savingSession.error")); // ZAP: i18n log.error("error saving session file " + model.getSession().getFileName(), e); log.error(e.getMessage(), e); } if (waitMessageDialog != null) { waitMessageDialog.setVisible(false); waitMessageDialog = null; } } @Override public void sessionSnapshot(Exception e) { if (e != null) { view.showWarningDialog(Constant.messages.getString("menu.file.snapshotSession.error")); // ZAP: i18n log.error("error saving snapshot file " + model.getSession().getFileName(), e); log.error(e.getMessage(), e); } if (waitMessageDialog != null) { waitMessageDialog.setVisible(false); waitMessageDialog = null; } } /** * Prompt the user to export a context */ public void importContext() { JFileChooser chooser = new JFileChooser(Constant.getContextsDir()); File file = null; chooser.setFileFilter(new FileFilter() { @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } else if (file.isFile() && file.getName().endsWith(".context")) { return true; } return false; } @Override public String getDescription() { return Constant.messages.getString("file.format.zap.context"); } }); int rc = chooser.showOpenDialog(View.getSingleton().getMainFrame()); if(rc == JFileChooser.APPROVE_OPTION) { try { file = chooser.getSelectedFile(); if (file == null || ! file.exists()) { return; } // Import the context Model.getSingleton().getSession().importContext(file); // Show the dialog View.getSingleton().showSessionDialog( Model.getSingleton().getSession(), Constant.messages.getString("context.list"), true); } catch (IllegalContextNameException e) { String detailError; if (e.getReason() == IllegalContextNameException.Reason.EMPTY_NAME) { detailError = Constant.messages.getString("context.error.name.empty"); } else if (e.getReason() == IllegalContextNameException.Reason.DUPLICATED_NAME) { detailError = Constant.messages.getString("context.error.name.duplicated"); } else { detailError = Constant.messages.getString("context.error.name.unknown"); } View.getSingleton().showWarningDialog( MessageFormat.format(Constant.messages.getString("context.import.error"), detailError)); } catch (Exception e1) { log.debug(e1.getMessage(), e1); View.getSingleton().showWarningDialog(MessageFormat.format( Constant.messages.getString("context.import.error"), e1.getMessage())); } } } /** * Prompt the user to export a context */ public void exportContext() { ContextExportDialog exportDialog = new ContextExportDialog(View.getSingleton().getMainFrame()); exportDialog.setVisible(true); } }