/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2010 psiinon@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.view;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.HeadlessException;
import java.awt.Window;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.apache.commons.httpclient.URI;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.AbstractDialog;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.SiteNode;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.model.Target;
import org.zaproxy.zap.utils.DisplayUtils;
/**
* A modal dialog for selecting a node in the SitesTree.
* @author psiinon
*
*/
public class NodeSelectDialog extends AbstractDialog {
private static final long serialVersionUID = 1L;
private JPanel jPanel = null;
private JTree treeContext = null;
private JTree treeSite = null;
private JButton selectButton = null;
private JButton cancelButton = null;
private DefaultTreeModel contextTree = null;
private DefaultTreeModel siteTree = null;
private SiteNode selectedSiteNode = null;
private Target selectedTarget = null;
private boolean allowRoot = false;
/**
* Constructs a modal {@code NodeSelectDialog} with the given parent.
*
* @param parent the {@code Window} from which the dialog is displayed or {@code null} if this dialog has no parent
* @throws HeadlessException when {@code GraphicsEnvironment.isHeadless()} returns {@code true}
*/
public NodeSelectDialog(Window parent) throws HeadlessException {
super(parent, true); // Modal, ie always on top
initialize();
}
/**
* This method initializes this
*/
private void initialize() {
this.setTitle(Constant.messages.getString("siteselect.dialog.title"));
this.setContentPane(getJPanel());
this.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowOpened(java.awt.event.WindowEvent e) {
}
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
cancelButton.doClick();
}
});
pack();
}
public SiteNode showDialog(URI uri) {
return this.showDialog(Model.getSingleton().getSession().getSiteTree().findNode(uri));
}
public SiteNode showDialog(SiteNode defaultNode) {
// Assume Contexts cant be selected
this.getTreeContext().setVisible(false);
SiteNode siteRoot = (SiteNode)Model.getSingleton().getSession().getSiteTree().getRoot();
populateNode(siteRoot, (SiteNode)this.siteTree.getRoot(), defaultNode);
if (selectedSiteNode != null) {
// Found the default node, select it
TreePath path = new TreePath(this.siteTree.getPathToRoot(selectedSiteNode));
this.getTreeSite().setExpandsSelectedPaths(true);
this.getTreeSite().setSelectionPath(path);
this.getTreeSite().scrollPathToVisible(path);
this.getTreeSite().expandPath(path);
} else {
// no default path, just expand the top level
TreePath path = new TreePath(this.siteTree.getPathToRoot((TreeNode)this.siteTree.getRoot()));
this.getTreeSite().expandPath(path);
}
this.setVisible(true);
// The dialog is modal so this wont return until the dialog visibility is unset
return selectedSiteNode;
}
public Target showDialog(Target defaultTarget) {
// Assume Contexts can be selected
this.getTreeContext().setVisible(true);
SiteNode siteRoot = (SiteNode)Model.getSingleton().getSession().getSiteTree().getRoot();
populateContexts((SiteNode)this.contextTree.getRoot());
if (defaultTarget != null) {
populateNode(siteRoot, (SiteNode)this.siteTree.getRoot(), defaultTarget.getStartNode());
} else {
populateNode(siteRoot, (SiteNode)this.siteTree.getRoot(), null);
}
if (selectedSiteNode != null) {
// Found the default node, select it
TreePath path = new TreePath(this.siteTree.getPathToRoot(selectedSiteNode));
this.getTreeSite().setExpandsSelectedPaths(true);
this.getTreeSite().setSelectionPath(path);
this.getTreeSite().scrollPathToVisible(path);
this.getTreeSite().expandPath(path);
} else {
// no default path, just expand the top level
TreePath path = new TreePath(this.siteTree.getPathToRoot((TreeNode)this.siteTree.getRoot()));
this.getTreeSite().expandPath(path);
}
this.setVisible(true);
// The dialog is modal so this wont return until the dialog visibility is unset
if (selectedSiteNode != null) {
return new Target(selectedSiteNode);
}
return selectedTarget;
}
private void populateContexts(SiteNode root) {
// Uncomment to hide contexts tree if there are no valid contexts -
// not sure if this is a good idea or not :/
//int contexts = 0;
int contextsInScope = 0;
for (Context ctx : Model.getSingleton().getSession().getContexts()) {
// TODO ignore handle protected mode?
if (ctx.getIncludeInContextRegexs().size() > 0) {
SiteNode node = new SiteNode(null, HistoryReference.TYPE_PROXIED, ctx.getName());
node.setUserObject(new Target(ctx));
root.add(node);
//contexts ++;
if (ctx.isInScope()) {
contextsInScope ++;
}
}
}
if (contextsInScope > 1) {
// Allow user to choose everything in scope
SiteNode node = new SiteNode(null, HistoryReference.TYPE_PROXIED, Constant.messages.getString("context.allInScope"));
node.setUserObject(new Target(null, null, true, true));
root.add(node);
}
//this.getTreeContext().setVisible(contexts > 0);
this.getTreeContext().expandRow(0);
}
private void populateNode(SiteNode src, SiteNode dest, SiteNode defaultNode) {
if (src.equals(defaultNode)) {
// Flag this for use later..
this.selectedSiteNode = dest;
}
for (int i=0; i < src.getChildCount(); i++) {
SiteNode child = (SiteNode) src.getChildAt(i);
SiteNode copy = new SiteNode(null, HistoryReference.TYPE_PROXIED, child.getNodeName());
copy.setUserObject(child);
dest.add(copy);
this.populateNode(child, copy, defaultNode);
}
}
private DefaultTreeModel emptyContextTree() {
return new DefaultTreeModel(new SiteNode(null, -1, Constant.messages.getString("context.list")));
}
private DefaultTreeModel emptySiteTree() {
// Make a very sparse copy of the site tree
SiteNode siteRoot = (SiteNode)Model.getSingleton().getSession().getSiteTree().getRoot();
SiteNode root = new SiteNode(null, -1, Constant.messages.getString("tab.sites"));
root.setUserObject(siteRoot);
return new DefaultTreeModel(root);
}
/**
* This method initializes jPanel
*
* @return javax.swing.JPanel
*/
private JPanel getJPanel() {
if (jPanel == null) {
GridBagConstraints gridBagConstraints15 = new GridBagConstraints();
java.awt.GridBagConstraints gridBagConstraints13 = new GridBagConstraints();
javax.swing.JLabel jLabel2 = new JLabel();
java.awt.GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
java.awt.GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
jPanel = new JPanel();
jPanel.setLayout(new GridBagLayout());
jPanel.setPreferredSize(DisplayUtils.getScaledDimension(400,400));
jPanel.setMinimumSize(DisplayUtils.getScaledDimension(400,400));
gridBagConstraints2.gridx = 1;
gridBagConstraints2.gridy = 5;
gridBagConstraints2.insets = new java.awt.Insets(2,2,2,2);
gridBagConstraints2.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints3.gridx = 2;
gridBagConstraints3.gridy = 5;
gridBagConstraints3.insets = new java.awt.Insets(2,2,2,10);
gridBagConstraints3.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraints13.gridx = 0;
gridBagConstraints13.gridy = 5;
gridBagConstraints13.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints13.weightx = 1.0D;
gridBagConstraints13.insets = new java.awt.Insets(2,10,2,5);
gridBagConstraints15.weightx = 1.0D;
gridBagConstraints15.weighty = 1.0D;
gridBagConstraints15.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints15.insets = new java.awt.Insets(2,2,2,2);
gridBagConstraints15.gridwidth = 3;
gridBagConstraints15.gridx = 0;
gridBagConstraints15.gridy = 2;
gridBagConstraints15.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints15.ipadx = 0;
gridBagConstraints15.ipady = 10;
jPanel.add(new ContextsSitesPanel(getTreeContext(), getTreeSite()), gridBagConstraints15);
jPanel.add(jLabel2, gridBagConstraints13);
jPanel.add(getCancelButton(), gridBagConstraints2);
jPanel.add(getSelectButton(), gridBagConstraints3);
}
return jPanel;
}
private JTree getTreeSite() {
if (treeSite == null) {
this.siteTree = this.emptySiteTree();
treeSite = new JTree(this.siteTree);
treeSite.setShowsRootHandles(true);
treeSite.setName("nodeSelectTree");
treeSite.setToggleClickCount(1);
treeSite.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
treeSite.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
@Override
public void valueChanged(javax.swing.event.TreeSelectionEvent e) {
SiteNode node = (SiteNode) treeSite.getLastSelectedPathComponent();
getSelectButton().setEnabled(node != null && (node.getParent() != null || allowRoot));
}
});
treeSite.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mousePressed(java.awt.event.MouseEvent e) {
}
@Override
public void mouseReleased(java.awt.event.MouseEvent e) {
mouseClicked(e);
}
@Override
public void mouseClicked(java.awt.event.MouseEvent e) {
// right mouse button action
if (treeSite.getLastSelectedPathComponent() != null) {
// They selected a site node, deselect any context
getTreeContext().clearSelection();
}
if (e.getClickCount() > 1) {
// Its a double click - close the dialog to select this node
nodeSelected();
}
}
});
treeSite.setCellRenderer(new SiteMapTreeCellRenderer(new ArrayList<SiteMapListener>()));
}
return treeSite;
}
private void nodeSelected() {
SiteNode node = (SiteNode) treeSite.getLastSelectedPathComponent();
if (node != null && (node.getParent() != null || allowRoot)) {
selectedSiteNode = (SiteNode) node.getUserObject();
selectedTarget = null;
NodeSelectDialog.this.setVisible(false);
}
}
private void contextSelected() {
SiteNode node = (SiteNode) treeContext.getLastSelectedPathComponent();
if (node != null && (node.getParent() != null || allowRoot)) {
selectedTarget = (Target) node.getUserObject();
selectedSiteNode = null;
NodeSelectDialog.this.setVisible(false);
}
}
private JTree getTreeContext() {
if (treeContext == null) {
this.contextTree = this.emptyContextTree();
treeContext = new JTree(this.contextTree);
treeContext.setShowsRootHandles(true);
treeContext.setName("nodeContextTree");
treeContext.setToggleClickCount(1);
treeContext.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
treeContext.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
@Override
public void valueChanged(javax.swing.event.TreeSelectionEvent e) {
SiteNode node = (SiteNode) treeContext.getLastSelectedPathComponent();
getSelectButton().setEnabled(node != null && (node.getParent() != null || allowRoot));
}
});
treeContext.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mousePressed(java.awt.event.MouseEvent e) {
}
@Override
public void mouseReleased(java.awt.event.MouseEvent e) {
mouseClicked(e);
}
@Override
public void mouseClicked(java.awt.event.MouseEvent e) {
// right mouse button action
if (treeContext.getLastSelectedPathComponent() != null) {
// They selected a context node, deselect any site node
getTreeSite().clearSelection();
}
if (e.getClickCount() > 1) {
// Its a double click - close the dialog to select this node
contextSelected();
}
}
});
treeContext.setCellRenderer(new ContextsTreeCellRenderer());
}
return treeContext;
}
/**
* This method initializes btnStart
*
* @return javax.swing.JButton
*/
private JButton getSelectButton() {
if (selectButton == null) {
selectButton = new JButton();
selectButton.setText(Constant.messages.getString("siteselect.button.select"));
selectButton.setEnabled(false); // Enabled when a node is selected
selectButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
if (treeSite.getLastSelectedPathComponent() != null) {
nodeSelected();
} else if (treeContext.getLastSelectedPathComponent() != null) {
contextSelected();
}
}
});
}
return selectButton;
}
/**
* This method initializes btnStop
*
* @return javax.swing.JButton
*/
private JButton getCancelButton() {
if (cancelButton == null) {
cancelButton = new JButton();
cancelButton.setText(Constant.messages.getString("all.button.cancel"));
cancelButton.setEnabled(true);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
selectedSiteNode = null;
selectedTarget = null;
NodeSelectDialog.this.setVisible(false);
//NodeSelectDialog.this.dispose();
}
});
}
return cancelButton;
}
public boolean isAllowRoot() {
return allowRoot;
}
public void setAllowRoot(boolean allowRoot) {
this.allowRoot = allowRoot;
}
}