/*
* 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.EventQueue;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.KeyStroke;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control.Mode;
import org.parosproxy.paros.extension.ExtensionAdaptor;
import org.parosproxy.paros.extension.ExtensionHook;
import org.parosproxy.paros.extension.SessionChangedListener;
import org.parosproxy.paros.model.Session;
import org.zaproxy.zap.extension.help.ExtensionHelp;
import org.zaproxy.zap.view.ZapMenuItem;
public class ExtensionSearch extends ExtensionAdaptor {
private static final Logger LOGGER = Logger.getLogger(ExtensionSearch.class);
public static final String NAME = "ExtensionSearch2";
public enum Type {All, URL, Request, Response, Header, Custom};
private static final Logger logger = Logger.getLogger(ExtensionSearch.class);
private SearchParam searchParam;
private OptionsSearchPanel optionsPanel;
private SearchPanel searchPanel = null;
private ZapMenuItem menuSearch = null;
private ZapMenuItem menuNext = null;
private ZapMenuItem menuPrev = null;
private SearchThread searchThread = null;
private boolean searchJustInScope = false;
private Map<String, HttpSearcher> customSearchers = new HashMap<>();
public ExtensionSearch() {
super(NAME);
this.setOrder(20);
}
@Override
public void hook(ExtensionHook extensionHook) {
super.hook(extensionHook);
extensionHook.addOptionsParamSet(getSearchParam());
if (getView() != null) {
extensionHook.addSessionListener(new ViewSessionChangedListener());
extensionHook.getHookView().addOptionPanel(getOptionsPanel());
extensionHook.getHookView().addStatusPanel(getSearchPanel());
extensionHook.getHookMenu().addEditMenuItem(getMenuSearch());
extensionHook.getHookMenu().addEditMenuItem(getMenuNext());
extensionHook.getHookMenu().addEditMenuItem(getMenuPrev());
ExtensionHelp.enableHelpKey(getSearchPanel(), "ui.tabs.search");
}
extensionHook.addApiImplementor(new SearchAPI(this));
}
SearchParam getSearchParam() {
if (searchParam == null) {
searchParam = new SearchParam();
}
return searchParam;
}
private OptionsSearchPanel getOptionsPanel() {
if (optionsPanel == null) {
optionsPanel = new OptionsSearchPanel();
}
return optionsPanel;
}
private SearchPanel getSearchPanel() {
if (searchPanel == null) {
searchPanel = new SearchPanel(getView());
searchPanel.setExtension(this);
}
return searchPanel;
}
public void addCustomHttpSearcher(HttpSearcher searcher) {
if (searcher == null) {
return;
}
final String searcherName = searcher.getName();
if (customSearchers.containsKey(searcherName)) {
LOGGER.warn("Attempting to add an HTTP searcher with the same name: " + searcherName);
return;
}
customSearchers.put(searcherName, searcher);
if (getView() != null) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
searchPanel.addCustomSearcher(searcherName);
}
});
}
}
public void removeCustomHttpSearcher(HttpSearcher searcher) {
final String searcherName = searcher.getName();
if (customSearchers.remove(searcherName) != null && getView() != null) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
searchPanel.removeCustomSearcher(searcherName);
}
});
}
}
public void search(String filter, Type reqType) {
this.search(filter, reqType, false, false);
}
public void search(String filter, Type reqType, boolean setToolbar, boolean inverse){
this.search(filter, reqType, null, setToolbar, inverse);
}
public void search(String filter, Type reqType, String customSearcherName, boolean setToolbar, boolean inverse){
this.searchPanel.resetSearchResults();
this.search(filter, this.searchPanel, reqType, customSearcherName, setToolbar, inverse,
null, -1, -1, true, getSearchParam().getMaximumSearchResultsGUI());
}
public void search(String filter, SearchListenner listenner, Type reqType, boolean setToolbar, boolean inverse){
this.search(filter, listenner, reqType, setToolbar, inverse, null, -1, -1);
}
public void search(String filter, SearchListenner listenner, Type reqType, boolean setToolbar, boolean inverse,
String baseUrl, int start, int count){
search(filter, listenner, reqType, setToolbar, inverse, baseUrl, start, count, true);
}
public void search(String filter, SearchListenner listenner, Type reqType, boolean setToolbar, boolean inverse,
String baseUrl, int start, int count, boolean searchAllOccurrences){
search(filter, listenner, reqType, setToolbar, inverse, baseUrl, start, count, searchAllOccurrences, -1);
}
public void search(String filter, SearchListenner listenner, Type reqType, boolean setToolbar, boolean inverse,
String baseUrl, int start, int count, boolean searchAllOccurrences, int maxOccurrences){
search(filter, listenner, reqType, null, setToolbar, inverse, baseUrl, start, count, searchAllOccurrences, maxOccurrences);
}
public void search(String filter, SearchListenner listenner, Type reqType, String customSearcherName, boolean setToolbar,
boolean inverse, String baseUrl, int start, int count, boolean searchAllOccurrences, int maxOccurrences){
if (setToolbar) {
this.getSearchPanel().searchFocus();
this.getSearchPanel().getRegExField().setText(filter);
this.getSearchPanel().setSearchType(reqType);
}
synchronized (this) {
if (searchThread != null && searchThread.isAlive()) {
searchThread.stopSearch();
while (searchThread.isAlive()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
}
}
searchThread = new SearchThread(filter, reqType, customSearcherName, listenner, inverse, searchJustInScope, baseUrl,
start, count, searchAllOccurrences, maxOccurrences);
searchThread.setCustomSearchers(customSearchers);
searchThread.start();
}
}
private ZapMenuItem getMenuSearch() {
if (menuSearch == null) {
menuSearch = new ZapMenuItem("menu.edit.search",
KeyStroke.getKeyStroke(KeyEvent.VK_H, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
menuSearch.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
searchPanel.searchFocus();
}
});
}
return menuSearch;
}
private ZapMenuItem getMenuNext() {
if (menuNext == null) {
menuNext = new ZapMenuItem("menu.edit.next",
KeyStroke.getKeyStroke(KeyEvent.VK_G, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
menuNext.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
searchPanel.highlightNextResult();
}
});
}
return menuNext;
}
private ZapMenuItem getMenuPrev() {
if (menuPrev == null) {
menuPrev = new ZapMenuItem("menu.edit.previous");
menuPrev.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
searchPanel.highlightPrevResult();
}
});
}
return menuPrev;
}
@Override
public String getAuthor() {
return Constant.ZAP_TEAM;
}
@Override
public String getDescription() {
return Constant.messages.getString("search.desc");
}
@Override
public URL getURL() {
try {
return new URL(Constant.ZAP_HOMEPAGE);
} catch (MalformedURLException e) {
return null;
}
}
public void setSearchJustInScope(boolean searchJustInScope) {
this.searchJustInScope = searchJustInScope;
}
/**
* No database tables used, so all supported
*/
@Override
public boolean supportsDb(String type) {
return true;
}
/**
* A {@code SessionChangedListener} for view/UI related functionalities.
*/
private class ViewSessionChangedListener implements SessionChangedListener {
@Override
public void sessionAboutToChange(Session session) {
// Nothing to do.
}
@Override
public void sessionScopeChanged(Session session) {
// Nothing to do.
}
@Override
public void sessionChanged(final Session session) {
if (EventQueue.isDispatchThread()) {
getSearchPanel().resetSearchResults();
return;
}
try {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
sessionChanged(session);
}
});
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
@Override
public void sessionModeChanged(Mode mode) {
// Nothing to do.
}
}
}