/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2016 ZAP development team
*
* 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.brk;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.view.TabbedPanel;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.view.TabbedPanel2;
import org.zaproxy.zap.view.ZapToggleButton;
// Button notes
// BreakRequest button, if set all requests trapped
// BreakResponse button, ditto for responses
// If break point hit, Break tab gets focus and icon goes red
// Step button, only if break point hit, submits just this req/resp, breaks on next
// Continue button, only if break point hit, submits this req/resp and continues until next break point hit
// If BreakReq & Resp both selected Step and Continue buttons have same effect
//
public class BreakPanelToolbarFactory {
private ContinueButtonAction continueButtonAction;
private StepButtonAction stepButtonAction;
private DropButtonAction dropButtonAction;
private AddBreakpointButtonAction addBreakpointButtonAction;
private BreakRequestsButtonAction breakRequestsButtonAction;
private BreakResponsesButtonAction breakResponsesButtonAction;
private BreakAllButtonAction breakAllButtonAction;
private boolean cont = false;
private boolean step = false;
private boolean stepping = false;
private boolean drop = false;
private boolean isBreakRequest = false;
private boolean isBreakResponse = false;
private boolean isBreakAll = false;
private BreakPanel breakPanel = null;
private BreakpointsParam breakpointsParams;
private int mode = 0;
public BreakPanelToolbarFactory(BreakpointsParam breakpointsParams, BreakPanel breakPanel) {
super();
continueButtonAction = new ContinueButtonAction();
stepButtonAction = new StepButtonAction();
dropButtonAction = new DropButtonAction();
addBreakpointButtonAction = new AddBreakpointButtonAction();
breakRequestsButtonAction = new BreakRequestsButtonAction();
breakResponsesButtonAction = new BreakResponsesButtonAction();
breakAllButtonAction = new BreakAllButtonAction();
this.breakpointsParams = breakpointsParams;
this.breakPanel = breakPanel;
}
private void setActiveIcon(boolean active) {
if (active) {
// Have to do this before the getParent() call
breakPanel.setTabFocus();
}
if (breakPanel.getParent() instanceof TabbedPanel) {
TabbedPanel parent = (TabbedPanel) breakPanel.getParent();
if (active) {
parent.setIconAt(
parent.indexOfComponent(breakPanel),
new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/101.png"))); // Red X
} else {
parent.setIconAt(
parent.indexOfComponent(breakPanel),
new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/101grey.png"))); // Grey X
}
if (parent instanceof TabbedPanel2) {
// If possible lock the tab while it is active so it cant be closed
((TabbedPanel2)parent).setTabLocked(breakPanel, !active);
}
}
}
public void breakpointHit () {
// This could have been via a break point, so force the serialisation
resetRequestSerialization(true);
// Set the active icon and reset the continue button
this.setActiveIcon(true);
setContinue(false);
}
public boolean isBreakRequest() {
return isBreakRequest || isBreakAll;
}
public boolean isBreakResponse() {
return isBreakResponse || isBreakAll;
}
public boolean isBreakAll() {
return isBreakAll;
}
public JButton getBtnStep() {
return new JButton(stepButtonAction);
}
public JButton getBtnContinue() {
return new JButton(continueButtonAction);
}
public JButton getBtnDrop() {
return new JButton(dropButtonAction);
}
private int askForDropConfirmation() {
String title = Constant.messages.getString("brk.dialogue.confirmDropMessage.title");
String message = Constant.messages.getString("brk.dialogue.confirmDropMessage.message");
JCheckBox checkBox = new JCheckBox(Constant.messages.getString("brk.dialogue.confirmDropMessage.option.dontAskAgain"));
String confirmButtonLabel = Constant.messages.getString("brk.dialogue.confirmDropMessage.button.confirm.label");
String cancelButtonLabel = Constant.messages.getString("brk.dialogue.confirmDropMessage.button.cancel.label");
int option = JOptionPane.showOptionDialog(View.getSingleton().getMainFrame(),
new Object[] {message, " ", checkBox}, title,
JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
null, new String[] { confirmButtonLabel, cancelButtonLabel}, null);
if (checkBox.isSelected()) {
breakpointsParams.setConfirmDropMessage(false);
}
return option;
}
public JToggleButton getBtnBreakRequest() {
ZapToggleButton btnBreakRequest;
btnBreakRequest = new ZapToggleButton(breakRequestsButtonAction);
btnBreakRequest.setSelectedIcon(
new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/105r.png")));
btnBreakRequest.setSelectedToolTipText(Constant.messages.getString("brk.toolbar.button.request.unset"));
return btnBreakRequest;
}
public JToggleButton getBtnBreakResponse() {
ZapToggleButton btnBreakResponse;
btnBreakResponse = new ZapToggleButton(breakResponsesButtonAction);
btnBreakResponse.setSelectedIcon(
new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/106r.png")));
btnBreakResponse.setSelectedToolTipText(Constant.messages.getString("brk.toolbar.button.response.unset"));
return btnBreakResponse;
}
public JToggleButton getBtnBreakAll() {
ZapToggleButton btnBreakAll;
btnBreakAll = new ZapToggleButton(breakAllButtonAction);
btnBreakAll.setSelectedIcon(
new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/151.png")));
btnBreakAll.setSelectedToolTipText(Constant.messages.getString("brk.toolbar.button.all.unset"));
return btnBreakAll;
}
public JButton getBtnBreakPoint() {
return new JButton(addBreakpointButtonAction);
}
public boolean isStepping() {
return stepping;
}
private void resetRequestSerialization (boolean forceSerialize) {
if (Control.getSingleton() == null) {
// Still in setup
return;
}
// If forces or either break buttons are pressed force the proxy to submit requests and responses serially
if (forceSerialize || isBreakRequest() || isBreakResponse() || isBreakAll) {
Control.getSingleton().getProxy().setSerialize(true);
} else {
Control.getSingleton().getProxy().setSerialize(false);
}
}
public void setBreakRequest(boolean brk) {
isBreakRequest = brk;
resetRequestSerialization(false);
breakRequestsButtonAction.setSelected(isBreakRequest);
}
public void setBreakResponse(boolean brk) {
isBreakResponse = brk;
resetRequestSerialization(false);
breakResponsesButtonAction.setSelected(isBreakResponse);
}
public void setBreakAll(boolean brk) {
isBreakAll = brk;
if (!brk) {
stepping = false;
}
resetRequestSerialization(false);
breakAllButtonAction.setSelected(isBreakAll);
}
private void toggleBreakRequest() {
setBreakRequest(!isBreakRequest);
}
private void toggleBreakResponse() {
setBreakResponse(!isBreakResponse);
}
private void toggleBreakAll() {
setBreakAll(!isBreakAll);
}
public boolean isHoldMessage() {
if (step) {
// Only works one time, until its pressed again
stepping = true;
step = false;
return false;
}
if (cont) {
// They've pressed the continue button, stop stepping
stepping = false;
resetRequestSerialization(false);
return false;
}
if (drop) {
return false;
}
return true;
}
public boolean isContinue() {
return cont;
}
public void setBreakEnabled(boolean enabled) {
if (!enabled) {
this.isBreakRequest = false;
this.isBreakResponse = false;
this.isBreakAll = false;
this.setContinue(true);
}
breakRequestsButtonAction.setSelected(false);
breakRequestsButtonAction.setEnabled(enabled);
breakResponsesButtonAction.setSelected(false);
breakResponsesButtonAction.setEnabled(enabled);
breakAllButtonAction.setSelected(false);
breakAllButtonAction.setEnabled(enabled);
}
private void setButtonsAndIconState(boolean enabled) {
stepButtonAction.setEnabled(enabled);
continueButtonAction.setEnabled(enabled);
dropButtonAction.setEnabled(enabled);
if (!enabled) {
this.setActiveIcon(false);
}
}
protected void setContinue(boolean isContinue) {
this.cont = isContinue;
setButtonsAndIconState( ! isContinue);
}
protected void setStep(boolean isStep) {
step = isStep;
setButtonsAndIconState( ! isStep);
}
protected void setDrop(boolean isDrop) {
if (isDrop && breakpointsParams.isConfirmDropMessage() &&
askForDropConfirmation() != JOptionPane.OK_OPTION) {
return;
}
drop = isDrop;
setButtonsAndIconState( ! isDrop);
}
public boolean isToBeDropped() {
if (drop) {
drop = false;
return true;
}
return false;
}
public void init() {
cont = false;
step = false;
stepping = false;
drop = false;
isBreakRequest = false;
isBreakResponse = false;
isBreakAll = false;
}
public void reset() {
if (isBreakRequest()) {
toggleBreakRequest();
}
if (isBreakResponse()) {
toggleBreakResponse();
}
if (isBreakAll()) {
toggleBreakAll();
}
setContinue(true);
}
/**
* Sets the current button mode.
* <p>
* If the mode is already set no change is done, otherwise it does the following:
* <ul>
* <li>When changing from {@link BreakpointsParam#BUTTON_MODE_SIMPLE BUTTON_MODE_SIMPLE} to
* {@link BreakpointsParam#BUTTON_MODE_DUAL BUTTON_MODE_DUAL} set "break on request" and "on response" enabled and
* "break on all" disabled, if "break on all" is enabled;</li>
* <li>When changing from {@code BUTTON_MODE_DUAL} to {@code BUTTON_MODE_SIMPLE} set "break on all" enabled and "break on
* request" and "on response" disabled, if at least one of "break on request" and "on response" is enabled;</li>
* <li>If none of the "break on ..." states is enabled there's no changes in its states.</li>
* </ul>
* The enabled state of previous mode is disabled to prevent interferences between the modes.
*
* @param mode the mode to be set
* @see #isBreakAll()
* @see #isBreakRequest()
* @see #isBreakResponse()
*/
public void setButtonMode (int mode) {
if (this.mode == mode) {
return;
}
if (this.mode == BreakpointsParam.BUTTON_MODE_SIMPLE) {
if (isBreakAll) {
setBreakAll(false);
setBreakRequest(true);
setBreakResponse(true);
}
} else if (isBreakRequest || isBreakResponse) {
setBreakRequest(false);
setBreakResponse(false);
setBreakAll(true);
}
this.mode = mode;
}
private class ContinueButtonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public ContinueButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/131.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.cont"));
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
setContinue(true);
setBreakAll(false);
setBreakRequest(false);
setBreakResponse(false);
}
}
private class StepButtonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public StepButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/143.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.step"));
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
if (mode == BreakpointsParam.BUTTON_MODE_SIMPLE && ! isBreakAll) {
// In simple mode 'step' if the breakAll button is disabled then it acts like 'continue'
// so that its hopefully obvious to users when break is on or not
setContinue(true);
} else {
setStep(true);
}
}
}
private class DropButtonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public DropButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/150.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.bin"));
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
setDrop(true);
}
}
private class AddBreakpointButtonAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public AddBreakpointButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/break_add.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.brkpoint"));
}
@Override
public void actionPerformed(ActionEvent e) {
breakPanel.showNewBreakPointDialog();
}
}
private class BreakRequestsButtonAction extends SelectableAbstractAction {
private static final long serialVersionUID = 1L;
public BreakRequestsButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/105.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.request.set"));
}
@Override
public void actionPerformed(ActionEvent e) {
toggleBreakRequest();
}
}
private class BreakResponsesButtonAction extends SelectableAbstractAction {
private static final long serialVersionUID = 1L;
public BreakResponsesButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/106.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.response.set"));
}
@Override
public void actionPerformed(ActionEvent e) {
toggleBreakResponse();
}
}
private class BreakAllButtonAction extends SelectableAbstractAction {
private static final long serialVersionUID = 1L;
public BreakAllButtonAction() {
super(null, new ImageIcon(BreakPanelToolbarFactory.class.getResource("/resource/icon/16/152.png")));
putValue(Action.SHORT_DESCRIPTION, Constant.messages.getString("brk.toolbar.button.all.set"));
}
@Override
public void actionPerformed(ActionEvent e) {
toggleBreakAll();
}
}
/**
* An {@code AbstractAction} which allows to be selected.
*
* @see AbstractAction
* @see #setSelected(boolean)
*/
private static abstract class SelectableAbstractAction extends AbstractAction {
private static final long serialVersionUID = 1L;
/**
* Creates a {@code SelectableAbstractAction} with the specified {@code name} and {@code icon}.
*
* @param name the name for the action or {@code null} for no name
* @param icon the icon for the action or {@code null} for no icon
*/
public SelectableAbstractAction(String name, Icon icon) {
super(name, icon);
}
/**
* Sets whether the action is selected or not.
*
* @param selected {@code true} if the action should be selected, {@code false} otherwise
*/
public void setSelected(boolean selected) {
putValue(Action.SELECTED_KEY, Boolean.valueOf(selected));
}
}
}