/*
* 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.extension.search;
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.util.regex.Pattern;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.extension.AbstractPanel;
import org.parosproxy.paros.extension.ViewDelegate;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.extension.httppanel.HttpPanelRequest;
import org.zaproxy.zap.extension.httppanel.HttpPanelResponse;
import org.zaproxy.zap.utils.DisplayUtils;
import org.zaproxy.zap.utils.ZapTextField;
import org.zaproxy.zap.view.ZapToggleButton;
public class SearchPanel extends AbstractPanel implements SearchListenner {
private static final long serialVersionUID = 1L;
/**
* @deprecated (2.3.0) Replaced by {@link #HTTP_MESSAGE_CONTAINER_NAME}.
*/
@Deprecated
public static final String PANEL_NAME = "search";
/**
* The name of the search results HTTP messages container.
*
* @see org.zaproxy.zap.view.messagecontainer.http.HttpMessageContainer
*/
public static final String HTTP_MESSAGE_CONTAINER_NAME = "SearchHttpMessageContainer";
private ExtensionSearch extension;
private javax.swing.JPanel panelCommand = null;
private javax.swing.JToolBar panelToolbar = null;
private JScrollPane jScrollPane = null;
private ZapToggleButton scopeButton = null;
private ZapTextField regEx = null;
private JButton btnSearch = null;
private JComboBox<SearchOption> searchType = null;
private JButton btnNext = null;
private JButton btnPrev = null;
private JCheckBox chkInverse = null;
private JLabel numberOfMatchesLabel;
private JButton optionsButton;
private MessageFormat numberOfMatchesFormat;
private SearchResultsTable resultsTable;
private SearchResultsTableModel resultsModel;
private final ViewDelegate view;
/**
* @deprecated (2.5.0) Use {@link #SearchPanel(ViewDelegate)} instead.
*/
@Deprecated
public SearchPanel() {
this(View.getSingleton());
initialize();
}
public SearchPanel(ViewDelegate view) {
super();
this.view = view;
initialize();
}
public ExtensionSearch getExtension() {
return extension;
}
public void setExtension(ExtensionSearch extension) {
this.extension = extension;
}
/**
* This method initializes this
*/
private void initialize() {
resultsModel = new SearchResultsTableModel();
resultsTable = new SearchResultsTable(resultsModel);
resultsTable.setName(HTTP_MESSAGE_CONTAINER_NAME);
resultsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent evt) {
if (!evt.getValueIsAdjusting()) {
SearchResult searchResult = resultsTable.getSelectedSearchResult();
if (searchResult == null) {
return;
}
displayMessage(resultsTable.getSelectedSearchResult());
// Get the focus back so that the arrow keys work
resultsTable.requestFocusInWindow();
}
}
});
this.setLayout(new CardLayout());
//this.setSize(474, 251);
this.setName(Constant.messages.getString("search.panel.title"));
this.setIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/16/049.png"))); // 'magnifying glass' icon
this.add(getPanelCommand(), getPanelCommand().getName());
this.setShowByDefault(true);
}
/**
* This method initializes panelCommand
*
* @return javax.swing.JPanel
*/
private javax.swing.JPanel getPanelCommand() {
if (panelCommand == null) {
panelCommand = new javax.swing.JPanel();
panelCommand.setLayout(new java.awt.GridBagLayout());
panelCommand.setName("Search Panel");
GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
gridBagConstraints1.gridx = 0;
gridBagConstraints1.gridy = 0;
gridBagConstraints1.weightx = 1.0D;
gridBagConstraints1.insets = new java.awt.Insets(2,2,2,2);
gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints1.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints2.gridx = 0;
gridBagConstraints2.gridy = 1;
gridBagConstraints2.weightx = 1.0;
gridBagConstraints2.weighty = 1.0;
gridBagConstraints2.insets = new java.awt.Insets(0,0,0,0);
gridBagConstraints2.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints2.anchor = java.awt.GridBagConstraints.NORTHWEST;
panelCommand.add(this.getPanelToolbar(), gridBagConstraints1);
panelCommand.add(getJScrollPane(), gridBagConstraints2);
}
return panelCommand;
}
private GridBagConstraints newGBC (int gridx) {
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = gridx;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new java.awt.Insets(0,0,0,0);
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
return gridBagConstraints;
}
private javax.swing.JToolBar getPanelToolbar() {
if (panelToolbar == null) {
panelToolbar = new javax.swing.JToolBar();
panelToolbar.setLayout(new java.awt.GridBagLayout());
panelToolbar.setEnabled(true);
panelToolbar.setFloatable(false);
panelToolbar.setRollover(true);
panelToolbar.setPreferredSize(new java.awt.Dimension(800,30));
panelToolbar.setName("Search Toolbar");
GridBagConstraints gridBagConstraintsX = new GridBagConstraints();
gridBagConstraintsX.gridx = 20;
gridBagConstraintsX.gridy = 0;
gridBagConstraintsX.weightx = 1.0;
gridBagConstraintsX.weighty = 1.0;
gridBagConstraintsX.insets = new java.awt.Insets(0,0,0,0);
gridBagConstraintsX.anchor = java.awt.GridBagConstraints.EAST;
gridBagConstraintsX.fill = java.awt.GridBagConstraints.HORIZONTAL;
GridBagConstraints optionsGridBag = new GridBagConstraints();
optionsGridBag.gridx = gridBagConstraintsX.gridx +1;
optionsGridBag.gridy = 0;
optionsGridBag.insets = new java.awt.Insets(0, 0, 0, 0);
optionsGridBag.anchor = java.awt.GridBagConstraints.EAST;
JLabel t1 = new JLabel();
JLabel inverseTooltip = new JLabel(Constant.messages.getString("search.toolbar.label.inverse"));
inverseTooltip.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.inverse"));
panelToolbar.add(getScopeButton(), newGBC(0));
panelToolbar.add(getRegExField(), newGBC(1));
panelToolbar.add(getSearchType(), newGBC(2));
panelToolbar.add(inverseTooltip, newGBC(3));
panelToolbar.add(getChkInverse(), newGBC(4));
panelToolbar.add(getBtnSearch(), newGBC(5));
panelToolbar.add(getBtnNext(), newGBC(6));
panelToolbar.add(getBtnPrev(), newGBC(7));
panelToolbar.add(new JToolBar.Separator(), newGBC(8));
panelToolbar.add(getNumberOfMatchesLabel(), newGBC(9));
panelToolbar.add(t1, gridBagConstraintsX);
panelToolbar.add(getOptionsButton(), optionsGridBag);
}
return panelToolbar;
}
private JToggleButton getScopeButton() {
if (scopeButton == null) {
scopeButton = new ZapToggleButton();
scopeButton.setIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/fugue/target-grey.png")));
scopeButton.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.scope.unselected"));
scopeButton.setSelectedIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/fugue/target.png")));
scopeButton.setSelectedToolTipText(Constant.messages.getString("search.toolbar.tooltip.scope.selected"));
DisplayUtils.scaleIcon(scopeButton);
scopeButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
extension.setSearchJustInScope(scopeButton.isSelected());
}
});
}
return scopeButton;
}
private JCheckBox getChkInverse () {
if (chkInverse == null) {
chkInverse = new JCheckBox();
chkInverse.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.inverse"));
}
return chkInverse;
}
private JButton getBtnSearch() {
if (btnSearch == null) {
btnSearch = new JButton();
btnSearch.setText(Constant.messages.getString("search.toolbar.label.search"));
btnSearch.setIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/16/049.png"))); // 'magnifying glass' icon
btnSearch.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.search"));
DisplayUtils.scaleIcon(btnSearch);
btnSearch.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
doSearch();
}
});
}
return btnSearch;
}
private JButton getBtnNext() {
if (btnNext == null) {
btnNext = new JButton();
btnNext.setText(Constant.messages.getString("search.toolbar.label.next"));
btnNext.setIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/16/107.png"))); // 'arrow down' icon
btnNext.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.next"));
DisplayUtils.scaleIcon(btnNext);
btnNext.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
highlightNextResult();
}
});
}
return btnNext;
}
private JButton getBtnPrev() {
if (btnPrev == null) {
btnPrev = new JButton();
btnPrev.setText(Constant.messages.getString("search.toolbar.label.previous"));
btnPrev.setIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/16/108.png"))); // 'arrow up' icon
btnPrev.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.previous"));
DisplayUtils.scaleIcon(btnPrev);
btnPrev.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
highlightPrevResult();
}
});
}
return btnPrev;
}
protected ZapTextField getRegExField () {
if (regEx == null) {
regEx = new ZapTextField();
regEx.setHorizontalAlignment(ZapTextField.LEFT);
regEx.setAlignmentX(0.0F);
regEx.setPreferredSize(DisplayUtils.getScaledDimension(250,25));
regEx.setText("");
regEx.setToolTipText(Constant.messages.getString("search.toolbar.tooltip.regex"));
regEx.setMinimumSize(DisplayUtils.getScaledDimension(250,25));
regEx.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
doSearch();
}
});
}
return regEx;
}
private JLabel getNumberOfMatchesLabel() {
if (numberOfMatchesLabel == null) {
numberOfMatchesLabel = new JLabel();
numberOfMatchesFormat = new MessageFormat(Constant.messages.getString("search.toolbar.label.number.of.matches"));
setNumberOfMatches(0);
}
return numberOfMatchesLabel;
}
private void setNumberOfMatches(int numberOfMatches) {
numberOfMatchesLabel.setText(numberOfMatchesFormat.format(new Object[] { Integer.valueOf(numberOfMatches) }));
}
private JButton getOptionsButton() {
if (optionsButton == null) {
optionsButton = new JButton();
optionsButton.setToolTipText(Constant.messages.getString("search.toolbar.button.options"));
optionsButton.setIcon(DisplayUtils.getScaledIcon(new ImageIcon(SearchPanel.class.getResource("/resource/icon/16/041.png"))));
optionsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Control.getSingleton()
.getMenuToolsControl()
.options(Constant.messages.getString("search.optionspanel.name"));
}
});
}
return optionsButton;
}
private JScrollPane getJScrollPane() {
if (jScrollPane == null) {
jScrollPane = new JScrollPane();
jScrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jScrollPane.setViewportView(resultsTable);
}
return jScrollPane;
}
public void resetSearchResults() {
setNumberOfMatches(0);
resultsModel.clear();
}
@Override
public void addSearchResult(final SearchResult str) {
if (EventQueue.isDispatchThread()) {
resultsModel.addSearchResult(str);
setNumberOfMatches(resultsModel.getRowCount());
if (resultsModel.getRowCount() == 1) {
highlightFirstResult();
}
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
addSearchResult(str);
}
});
}
}
/**
* @deprecated (2.5.0) No longer used/needed.
*/
@Deprecated
@SuppressWarnings("javadoc")
public void setDisplayPanel(HttpPanelRequest requestPanel, HttpPanelResponse responsePanel) {
}
private void doSearch() {
Pattern pattern;
try {
pattern = Pattern.compile(regEx.getText());
} catch (IllegalArgumentException e) {
regEx.requestFocusInWindow();
View.getSingleton()
.showWarningDialog(Constant.messages.getString("search.toolbar.error.invalid.regex"));
return;
}
if (pattern.matcher("").find()) {
int option = JOptionPane.showOptionDialog(
View.getSingleton().getMainFrame(),
Constant.messages.getString("search.toolbar.warn.regex.match.empty.string.text"),
Constant.messages.getString("search.toolbar.warn.regex.match.empty.string.title"),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
new String[] {
Constant.messages.getString("search.toolbar.warn.regex.match.empty.string.button.search"),
Constant.messages.getString("search.toolbar.warn.regex.match.empty.string.button.cancel") },
null);
if (option != JOptionPane.OK_OPTION) {
regEx.requestFocusInWindow();
return;
}
}
SearchOption option = (SearchOption) getSearchType().getSelectedItem();
ExtensionSearch.Type type = option.getType();
String customSearcherName = null;
if (ExtensionSearch.Type.Custom == type) {
customSearcherName = option.getName();
}
setNumberOfMatches(0);
extension.search(regEx.getText(), type, customSearcherName, false, chkInverse.isSelected());
// Select first result
if (resultsTable.getModel().getRowCount() > 0) {
resultsTable.getSelectionModel().setSelectionInterval(0, 0);
resultsTable.requestFocus();
}
}
protected void setSearchType(ExtensionSearch.Type type) {
if (ExtensionSearch.Type.Custom == type) {
// Not supported
return;
}
for (int i = 0; i < getSearchType().getItemCount(); i++) {
SearchOption option = getSearchType().getItemAt(i);
if (option.getType() == type) {
getSearchType().setSelectedIndex(i);
break;
}
}
}
private void displayMessage(SearchResult sr) {
HttpMessage msg = sr.getMessage();
view.displayMessage(msg);
if (msg == null) {
return;
}
highlightFirstResult(sr);
}
private void highlightMatch (SearchMatch sm) {
if (sm == null) {
return;
}
switch (sm.getLocation()) {
case REQUEST_HEAD:
view.getRequestPanel().highlightHeader(sm);
view.getRequestPanel().setTabFocus();
view.getRequestPanel().requestFocus();
break;
case REQUEST_BODY:
view.getRequestPanel().highlightBody(sm);
view.getRequestPanel().setTabFocus();
view.getRequestPanel().requestFocus();
break;
case RESPONSE_HEAD:
view.getResponsePanel().highlightHeader(sm);
view.getResponsePanel().setTabFocus();
view.getResponsePanel().requestFocus();
break;
case RESPONSE_BODY:
view.getResponsePanel().highlightBody(sm);
view.getResponsePanel().setTabFocus();
view.getResponsePanel().requestFocus();
break;
}
}
private void highlightFirstResult (SearchResult sr) {
highlightMatch(sr.getFirstMatch(view.getRequestPanel(), view.getResponsePanel()));
}
protected void highlightFirstResult() {
if (resultsTable.getModel().getRowCount() > 0) {
resultsTable.getSelectionModel().setSelectionInterval(0, 0);
resultsTable.scrollRowToVisible(0);
resultsTable.requestFocus();
}
}
protected void highlightNextResult () {
if (resultsTable.getSelectedSearchResult() == null) {
this.highlightFirstResult();
return;
}
SearchResult sr = resultsTable.getSelectedSearchResult();
SearchMatch sm = sr.getNextMatch();
if (sm != null) {
highlightMatch(sm);
} else {
// Next record
if (resultsTable.getSelectedRow() < resultsTable.getModel().getRowCount()-1) {
resultsTable.getSelectionModel().setSelectionInterval(resultsTable.getSelectedRow() + 1, resultsTable.getSelectedRow() + 1);
resultsTable.scrollRowToVisible(resultsTable.getSelectedRow());
} else {
this.highlightFirstResult();
}
}
}
private void highlightLastResult (SearchResult sr) {
highlightMatch(sr.getLastMatch(view.getRequestPanel(), view.getResponsePanel()));
}
protected void highlightPrevResult () {
if (resultsTable.getSelectedSearchResult() == null) {
this.highlightFirstResult();
return;
}
SearchResult sr = resultsTable.getSelectedSearchResult();
SearchMatch sm = sr.getPrevMatch();
if (sm != null) {
highlightMatch(sm);
} else {
// Previous record
if (resultsTable.getSelectedRow() > 0) {
resultsTable.getSelectionModel().setSelectionInterval(resultsTable.getSelectedRow() - 1, resultsTable.getSelectedRow() - 1);
} else {
resultsTable.getSelectionModel().setSelectionInterval(resultsTable.getModel().getRowCount()-1, resultsTable.getRowCount() - 1);
}
resultsTable.scrollRowToVisible(resultsTable.getSelectedRow());
highlightLastResult(resultsTable.getSelectedSearchResult());
}
}
private JComboBox<SearchOption> getSearchType () {
if (searchType == null) {
searchType = new JComboBox<>();
searchType.addItem(new SearchOption(
Constant.messages.getString("search.toolbar.label.type.all"),
ExtensionSearch.Type.All));
searchType.addItem(new SearchOption(
Constant.messages.getString("search.toolbar.label.type.url"),
ExtensionSearch.Type.URL));
searchType.addItem(new SearchOption(
Constant.messages.getString("search.toolbar.label.type.request"),
ExtensionSearch.Type.Request));
searchType.addItem(new SearchOption(
Constant.messages.getString("search.toolbar.label.type.response"),
ExtensionSearch.Type.Response));
searchType.addItem(new SearchOption(
Constant.messages.getString("search.toolbar.label.type.header"),
ExtensionSearch.Type.Header));
}
return searchType;
}
protected void addCustomSearcher(String name) {
getSearchType().addItem(new SearchOption(name, ExtensionSearch.Type.Custom));
}
protected void removeCustomSearcher(String name) {
for (int i = 0; i < getSearchType().getItemCount(); i++) {
SearchOption option = getSearchType().getItemAt(i);
if (option.getType() == ExtensionSearch.Type.Custom && name.equals(option.getName())) {
getSearchType().removeItemAt(i);
break;
}
}
}
public void searchFocus() {
this.setTabFocus();
getRegExField().requestFocus();
}
@Override
public void searchComplete() {
// Ignore
}
private static class SearchOption {
private final String name;
private final ExtensionSearch.Type type;
public SearchOption(String name, ExtensionSearch.Type type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public ExtensionSearch.Type getType() {
return type;
}
@Override
public String toString() {
return name;
}
}
}