
org.parosproxy.paros.control.MenuFileControl Maven / Gradle / Ivy
/*
*
* 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 doesn't 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
// ZAP: 2017/06/01 Issue 3555: setTitle() functionality moved in order to ensure consistent
// application
// ZAP: 2017/06/20 Inform of active actions before changing the session.
// ZAP: 2017/08/31 Use helper method I18N.getString(String, Object...).
// ZAP: 2017/11/22 Do not allow to snapshot the session with active actions (Issue 3711).
// ZAP: 2017/12/15 Confirm when overwriting session file (Issue 4153).
// ZAP: 2018/01/01 Prevent the selection of the current session on save/snapshot.
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2021/05/14 Remove redundant type arguments.
// ZAP: 2022/08/05 Address warns with Java 18.
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
// ZAP: 2023/05/21 Handle duplicate context names when importing in a more userfriendly way (Issue
// 7421).
package org.parosproxy.paros.control;
import java.awt.EventQueue;
import java.io.File;
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.logging.log4j.LogManager;
import org.apache.logging.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.Context;
import org.zaproxy.zap.model.IllegalContextNameException;
import org.zaproxy.zap.utils.ZapHtmlLabel;
import org.zaproxy.zap.utils.ZapXmlConfiguration;
import org.zaproxy.zap.view.ContextExportDialog;
import org.zaproxy.zap.view.PersistSessionDialog;
import org.zaproxy.zap.view.SessionTableSelectDialog;
import org.zaproxy.zap.view.widgets.WritableFileChooser;
public class MenuFileControl implements SessionListener {
private static final Logger LOGGER = LogManager.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) {
if (!informStopActiveActions()) {
return;
}
// 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 - i.e. 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 boolean informStopActiveActions() {
String activeActions = wrapEntriesInLiTags(control.getExtensionLoader().getActiveActions());
if (!activeActions.isEmpty()) {
String message =
Constant.messages.getString("menu.file.session.activeactions", activeActions);
if (view.showConfirmDialog(new ZapHtmlLabel(message)) != JOptionPane.OK_OPTION) {
return false;
}
}
return true;
}
private static String wrapEntriesInLiTags(List entries) {
if (entries.isEmpty()) {
return "";
}
StringBuilder strBuilder = new StringBuilder(entries.size() * 15);
for (String entry : entries) {
strBuilder.append("");
strBuilder.append(entry);
strBuilder.append(" ");
}
return strBuilder.toString();
}
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) {
view.getSiteTreePanel()
.getTreeSite()
.setModel(model.getSession().getSiteTree());
} else {
view.showWarningDialog(
Constant.messages.getString("menu.file.newSession.error"));
LOGGER.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"));
LOGGER.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"));
LOGGER.error(
"error opening session file {}",
model.getSession().getFileName(),
e);
opened[0] = Boolean.FALSE;
}
view.getSiteTreePanel()
.getTreeSite()
.setModel(model.getSession().getSiteTree());
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() {
if (!informStopActiveActions()) {
return;
}
// 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(SessionFileChooser.SESSION_FILE_FILTER);
int rc = chooser.showOpenDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
try {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
model.getOptionsParam().setUserDirectory(chooser.getCurrentDirectory());
LOGGER.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) {
LOGGER.error(e.getMessage(), e);
}
}
}
private void openDbBasedSession() {
try {
List sessionList = new ArrayList<>();
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) {
LOGGER.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);
LOGGER.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
LOGGER.error("error saving session file {}", session.getFileName());
LOGGER.error(e.getMessage(), e);
}
}
public void saveAsSession() {
if (!informStopActiveActions()) {
return;
}
Session session = model.getSession();
JFileChooser chooser =
new SessionFileChooser(model.getOptionsParam().getUserDirectory(), session);
// 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);
File file = null;
int rc = chooser.showSaveDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
String fileName = createSessionFileName(file);
try {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString(
"menu.file.savingSession")); // ZAP: i18n
control.saveSession(fileName, this);
LOGGER.info("save as session file {}", session.getFileName());
waitMessageDialog.setVisible(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
private static String createSessionFileName(File file) {
String fileName = file.getAbsolutePath();
if (!fileName.endsWith(".session")) {
fileName += ".session";
}
return fileName;
}
public void saveSnapshot() {
String activeActions = wrapEntriesInLiTags(control.getExtensionLoader().getActiveActions());
if (!activeActions.isEmpty()) {
view.showMessageDialog(
Constant.messages.getString("menu.file.snapshot.activeactions", activeActions));
return;
}
Session session = model.getSession();
JFileChooser chooser =
new SessionFileChooser(model.getOptionsParam().getUserDirectory(), session);
// 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);
File file = null;
int rc = chooser.showSaveDialog(view.getMainFrame());
if (rc == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
if (file == null) {
return;
}
String fileName = createSessionFileName(file);
try {
waitMessageDialog =
view.getWaitMessageDialog(
Constant.messages.getString(
"menu.file.savingSnapshot")); // ZAP: i18n
control.snapshotSession(fileName, this);
LOGGER.info("Snapshotting: {} as {}", session.getFileName(), fileName);
waitMessageDialog.setVisible(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
public void properties() {
// ZAP: proper call of existing method
View.getSingleton().showSessionDialog(model.getSession(), null);
}
@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.
} else {
view.showWarningDialog(Constant.messages.getString("menu.file.openSession.errorFile"));
if (file != null) {
LOGGER.error("Error opening session file {}", file.getAbsolutePath(), e);
} else {
// File is null for table based sessions (i.e. non HSQLDB)
LOGGER.error(e.getMessage(), e);
}
}
if (waitMessageDialog != null) {
waitMessageDialog.setVisible(false);
waitMessageDialog = null;
}
}
@Override
public void sessionSaved(Exception e) {
if (e != null) {
view.showWarningDialog(
Constant.messages.getString("menu.file.savingSession.error")); // ZAP: i18n
LOGGER.error("error saving session file {}", model.getSession().getFileName(), e);
LOGGER.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
LOGGER.error("error saving snapshot file {}", model.getSession().getFileName(), e);
LOGGER.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;
}
ZapXmlConfiguration config = new ZapXmlConfiguration(file);
String ctxName = config.getString(Context.CONTEXT_CONFIG_NAME);
Session session = Model.getSingleton().getSession();
boolean forcedImport = true;
while (session.getContext(ctxName) != null) {
forcedImport = false;
ctxName =
(String)
JOptionPane.showInputDialog(
View.getSingleton().getMainFrame(),
Constant.messages.getString(
"context.import.duplicate.dialog.message",
ctxName),
Constant.messages.getString(
"context.import.duplicate.dialog.title"),
JOptionPane.QUESTION_MESSAGE,
null,
null,
ctxName);
}
if (forcedImport || (ctxName != null && !ctxName.isBlank())) {
config.setProperty(Context.CONTEXT_CONFIG_NAME, ctxName);
session.importContext(config);
// 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(
Constant.messages.getString("context.import.error", detailError));
} catch (Exception e1) {
LOGGER.error(e1.getMessage(), e1);
View.getSingleton()
.showWarningDialog(
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);
}
@SuppressWarnings("serial")
private static class SessionFileChooser extends WritableFileChooser {
public static final FileFilter SESSION_FILE_FILTER =
new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory()
|| file.isFile() && file.getName().endsWith(".session");
}
@Override
public String getDescription() {
return Constant.messages.getString("file.format.zap.session");
}
};
private static final long serialVersionUID = 1L;
private final Session currentSession;
public SessionFileChooser(File currentDirectory, Session currentSession) {
super(currentDirectory);
setFileFilter(SESSION_FILE_FILTER);
this.currentSession = currentSession;
}
@Override
public void approveSelection() {
File file = getSelectedFile();
if (file != null) {
File sessionFile = new File(createSessionFileName(file));
setSelectedFile(sessionFile);
if (!currentSession.isNewState()) {
File currentFile = new File(currentSession.getFileName());
if (currentFile.getAbsolutePath().equals(sessionFile.getAbsolutePath())) {
showErrorDialog(
Constant.messages.getString(
"menu.file.error.selectedCurrentSession.msg"),
Constant.messages.getString(
"menu.file.error.selectedCurrentSession.title"));
return;
}
}
}
super.approveSelection();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy