/*
*
* 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: 2012/11/21 Heavily refactored extension to support non-HTTP messages.
// ZAP: 2013/04/15 Issue 632: Manual Request Editor dialogue (HTTP) configurations not saved correctly
// ZAP: 2014/01/28 Issue 207: Support keyboard shortcuts
// ZAP: 2014/12/12 Issue 1449: Added help button
// ZAP: 2015/08/07 Issue 1768: Update to use a more recent default user agent
package org.parosproxy.paros.extension.manualrequest.http.impl;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.manualrequest.ManualRequestEditorDialog;
import org.parosproxy.paros.extension.manualrequest.MessageSender;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.zaproxy.zap.PersistentConnectionListener;
import org.zaproxy.zap.extension.help.ExtensionHelp;
import org.zaproxy.zap.extension.httppanel.HttpPanel;
import org.zaproxy.zap.extension.httppanel.HttpPanelRequest;
import org.zaproxy.zap.extension.httppanel.HttpPanelResponse;
import org.zaproxy.zap.extension.httppanel.Message;
import org.zaproxy.zap.view.ZapMenuItem;
public class ManualHttpRequestEditorDialog extends ManualRequestEditorDialog {
private static final long serialVersionUID = -5830450800029295419L;
private static final Logger logger = Logger.getLogger(ManualHttpRequestEditorDialog.class);
private ZapMenuItem menuItem;
private HttpPanelSender sender;
private RequestResponsePanel requestResponsePanel;
private HttpPanelRequest requestPanel;
private HttpPanelResponse responsePanel;
private JToolBar footerToolbar = null;
// footer elements
private JLabel labelTimeElapse = null;
private JLabel labelContentLength = null;
private JLabel labelTotalLength = null;
private String helpKey = null;
public ManualHttpRequestEditorDialog(boolean isSendEnabled, String configurationKey) throws HeadlessException {
this(isSendEnabled, configurationKey, null);
}
public ManualHttpRequestEditorDialog(boolean isSendEnabled, String configurationKey, String helpKey) throws HeadlessException {
super(isSendEnabled, configurationKey);
this.helpKey = helpKey;
sender = new HttpPanelSender(getRequestPanel(), getResponsePanel());
initialize();
}
@Override
protected void initialize() {
super.initialize();
// add footer status bar
getWindowPanel().add(getFooterStatusBar(), BorderLayout.SOUTH);
//setting footer status bar label and separator
getFooterStatusBar().add(getLabelTimeLapse());
getFooterStatusBar().addSeparator();
getFooterStatusBar().add(getLabelContentLength());
getFooterStatusBar().addSeparator();
getFooterStatusBar().add(getLabelTotalLength());
}
@Override
public void setVisible(boolean show) {
super.setVisible(show);
switchToTab(0);
}
@Override
public Class<? extends Message> getMessageType() {
return HttpMessage.class;
}
@Override
public Message getMessage() {
return getRequestPanel().getMessage();
}
@Override
public void setMessage(Message aMessage) {
if (aMessage == null) {
return;
}
getRequestPanel().setMessage(aMessage);
getResponsePanel().setMessage(aMessage);
setFooterStatus(null);
switchToTab(0);
}
@Override
protected MessageSender getMessageSender() {
return sender;
}
@Override
protected HttpPanelRequest getRequestPanel() {
if (requestPanel == null) {
requestPanel = new HttpPanelRequest(true, configurationKey);
requestPanel.setEnableViewSelect(true);
requestPanel.loadConfig(Model.getSingleton().getOptionsParam().getConfig());
}
return requestPanel;
}
private HttpPanelResponse getResponsePanel() {
if (responsePanel == null) {
responsePanel = new HttpPanelResponse(false, configurationKey);
responsePanel.setEnableViewSelect(true);
responsePanel.loadConfig(Model.getSingleton().getOptionsParam().getConfig());
}
return responsePanel;
}
@Override
protected Component getManualSendPanel() {
if (requestResponsePanel == null) {
requestResponsePanel = new RequestResponsePanel(configurationKey, getRequestPanel(), getResponsePanel());
if (helpKey != null) {
JButton helpButton = new JButton();
helpButton.setIcon(ExtensionHelp.HELP_ICON);
helpButton.setToolTipText(Constant.messages.getString("help.dialog.button.tooltip"));
helpButton.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
ExtensionHelp.showHelp(helpKey);
}
});
requestResponsePanel.addToolbarButton(helpButton);
}
requestResponsePanel.addEndButton(getBtnSend());
requestResponsePanel.addSeparator();
requestResponsePanel.loadConfig();
}
return requestResponsePanel;
}
@Override
protected void btnSendAction() {
send(requestPanel.getMessage());
}
@Override
protected void postSend() {
super.postSend();
switchToTab(1);
setFooterStatus((HttpMessage) getResponsePanel().getMessage());
}
/**
* Return the footer status bar object
* @return
*/
protected JToolBar getFooterStatusBar() {
if (footerToolbar == null) {
footerToolbar = new JToolBar();
footerToolbar.setEnabled(true);
footerToolbar.setFloatable(false);
footerToolbar.setRollover(true);
footerToolbar.setName("Footer Toolbar Left");
footerToolbar.setBorder(BorderFactory.createEtchedBorder());
}
return footerToolbar;
}
private void setFooterStatus(HttpMessage msg) {
if (msg != null) {
//get values
long contentLength = msg.getResponseBody().length();
long totalLength = msg.getResponseHeader().toString().length() + contentLength;
long timeLapse =msg.getTimeElapsedMillis();
// show time lapse and content length between request and response Constant.messages.getString("manReq.label.timeLapse")
getLabelTimeLapse().setText(
Constant.messages.getString("manReq.label.timeLapse") + String.valueOf(timeLapse) + " ms");
getLabelContentLength().setText(
Constant.messages.getString("manReq.label.contentLength") + String.valueOf(contentLength) + " " + Constant.messages.getString("manReq.label.totalLengthBytes"));
getLabelTotalLength().setText(
Constant.messages.getString("manReq.label.totalLength") + String.valueOf(totalLength) + " " + Constant.messages.getString("manReq.label.totalLengthBytes"));
} else {
getLabelTimeLapse().setText(Constant.messages.getString("manReq.label.timeLapse"));
getLabelContentLength().setText(Constant.messages.getString("manReq.label.contentLength"));
getLabelTotalLength().setText(Constant.messages.getString("manReq.label.totalLength"));
}
}
private void switchToTab(int i) {
if (requestResponsePanel != null) {
requestResponsePanel.switchToTab(i);
}
}
@Override
protected void saveConfig() {
requestResponsePanel.saveConfig();
}
@Override
public ZapMenuItem getMenuItem() {
if (menuItem == null) {
menuItem = new ZapMenuItem("menu.tools.manReq",
KeyStroke.getKeyStroke(KeyEvent.VK_M, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Message message = getMessage();
if (message == null) {
setDefaultMessage();
} else if (message instanceof HttpMessage && ((HttpMessage)message).getRequestHeader().isEmpty()) {
setDefaultMessage();
}
setVisible(true);
}
});
}
return menuItem;
}
@Override
public void clear() {
super.clear();
getResponsePanel().clearView();
}
@Override
public void setDefaultMessage() {
HttpMessage msg = new HttpMessage();
try {
URI uri = new URI("http://www.any_domain_name.org/path", true);
msg.setRequestHeader(
new HttpRequestHeader(HttpRequestHeader.GET, uri, HttpHeader.HTTP10,
Model.getSingleton().getOptionsParam().getConnectionParam()));
setMessage(msg);
} catch (HttpMalformedHeaderException e) {
logger.error(e.getMessage(), e);
} catch (URIException e) {
logger.error(e.getMessage(), e);
}
}
/**
* Get Label status time lapse
* @return
*/
private JLabel getLabelTimeLapse(){
if (labelTimeElapse == null){
labelTimeElapse = new JLabel("", JLabel.LEADING);
}
return labelTimeElapse;
}
/**
* Get Label status Content Length
* @return
*/
private JLabel getLabelContentLength(){
if (labelContentLength == null){
labelContentLength = new JLabel("", JLabel.LEADING);
}
return labelContentLength;
}
/**
* Get Label status Total Length
* @return
*/
private JLabel getLabelTotalLength(){
if (labelTotalLength == null){
labelTotalLength = new JLabel("", JLabel.LEADING);
}
return labelTotalLength;
}
private static final class RequestResponsePanel extends JPanel {
private static final String REQUEST_CAPTION = Constant.messages.getString("manReq.tab.request");
private static final String RESPONSE_CAPTION = Constant.messages.getString("manReq.tab.response");
private static final String TABS_VIEW_TOOL_TIP = Constant.messages.getString("manReq.display.tabs");
private static final String ABOVE_VIEW_TOOL_TIP = Constant.messages.getString("manReq.display.above");
private static final String SIDE_BY_SIDE_VIEW_TOOL_TIP = Constant.messages.getString("manReq.display.sidebyside");
private static final String SELECTEDLAYOUT_CONFIG_KEY = "selectedlayout";
private static final String HORIZONTAL_DIVIDER_LOCATION_CONFIG_KEY = "horizontalDividerLocation";
private static final String VERTICAL_DIVIDER_LOCATION_CONFIG_KEY = "verticalDividerLocation";
private static final long serialVersionUID = -3335708932021769432L;
private static final int TABS_VIEW = 0;
private static final int ABOVE_VIEW = 1;
private static final int SIDE_BY_SIDE_VIEW = 2;
private final HttpPanelRequest requestPanel;
private final HttpPanelResponse responsePanel;
private int currentView;
private JComponent currentViewPanel;
private JToggleButton currentButtonView;
private JToggleButton tabsButtonView;
private JToggleButton aboveButtonView;
private JToggleButton sideBySideButtonView;
private String configurationKey;
private int verticalDividerLocation;
private int horizontalDividerLocation;
public RequestResponsePanel(String configurationKey, HttpPanelRequest request, HttpPanelResponse response) throws IllegalArgumentException {
super(new BorderLayout());
if (request == null || response == null) {
throw new IllegalArgumentException("The request and response panels cannot be null.");
}
this.configurationKey = configurationKey;
this.requestPanel = request;
this.responsePanel = response;
this.currentView = -1;
tabsButtonView = new JToggleButton(new ImageIcon(ManualRequestEditorDialog.class.getResource("/resource/icon/layout_tabbed.png")));
tabsButtonView.setToolTipText(TABS_VIEW_TOOL_TIP);
tabsButtonView.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changeView(TABS_VIEW);
}
});
addToolbarButton(tabsButtonView);
aboveButtonView = new JToggleButton(new ImageIcon(ManualRequestEditorDialog.class.getResource("/resource/icon/layout_vertical_split.png")));
aboveButtonView.setToolTipText(ABOVE_VIEW_TOOL_TIP);
aboveButtonView.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changeView(ABOVE_VIEW);
}
});
addToolbarButton(aboveButtonView);
sideBySideButtonView = new JToggleButton(new ImageIcon(ManualRequestEditorDialog.class.getResource("/resource/icon/layout_horizontal_split.png")));
sideBySideButtonView.setToolTipText(SIDE_BY_SIDE_VIEW_TOOL_TIP);
sideBySideButtonView.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
changeView(SIDE_BY_SIDE_VIEW);
}
});
addToolbarButton(sideBySideButtonView);
}
public void loadConfig() {
verticalDividerLocation = Model.getSingleton().getOptionsParam().getConfig().getInt(configurationKey + VERTICAL_DIVIDER_LOCATION_CONFIG_KEY, -1);
horizontalDividerLocation = Model.getSingleton().getOptionsParam().getConfig().getInt(configurationKey + HORIZONTAL_DIVIDER_LOCATION_CONFIG_KEY, -1);
changeView(Model.getSingleton().getOptionsParam().getConfig().getInt(configurationKey + SELECTEDLAYOUT_CONFIG_KEY, TABS_VIEW));
requestPanel.loadConfig(Model.getSingleton().getOptionsParam().getConfig());
responsePanel.loadConfig(Model.getSingleton().getOptionsParam().getConfig());
}
public void saveConfig() {
switch(currentView) {
case ABOVE_VIEW:
verticalDividerLocation = ((JSplitPane)currentViewPanel).getDividerLocation();
break;
case SIDE_BY_SIDE_VIEW:
horizontalDividerLocation = ((JSplitPane)currentViewPanel).getDividerLocation();
break;
default:
}
Model.getSingleton().getOptionsParam().getConfig().setProperty(configurationKey + VERTICAL_DIVIDER_LOCATION_CONFIG_KEY, Integer.valueOf(verticalDividerLocation));
Model.getSingleton().getOptionsParam().getConfig().setProperty(configurationKey + HORIZONTAL_DIVIDER_LOCATION_CONFIG_KEY, Integer.valueOf(horizontalDividerLocation));
Model.getSingleton().getOptionsParam().getConfig().setProperty(configurationKey + SELECTEDLAYOUT_CONFIG_KEY, Integer.valueOf(currentView));
requestPanel.saveConfig(Model.getSingleton().getOptionsParam().getConfig());
responsePanel.saveConfig(Model.getSingleton().getOptionsParam().getConfig());
}
public void addToolbarButton(JToggleButton button) {
requestPanel.addOptions(button, HttpPanel.OptionsLocation.AFTER_COMPONENTS);
}
public void addToolbarButton(JButton button) {
requestPanel.addOptions(button, HttpPanel.OptionsLocation.AFTER_COMPONENTS);
}
public void addSeparator() {
requestPanel.addOptionsSeparator();
}
public void addEndButton(JButton button) {
requestPanel.addOptions(button, HttpPanel.OptionsLocation.END);
}
public void switchToTab(int i) {
if (currentView == TABS_VIEW) {
((JTabbedPane) currentViewPanel).setSelectedIndex(i);
}
}
public void changeView(int newView) {
if (newView != currentView) {
final int oldView = currentView;
currentView = newView;
if (oldView != -1) {
this.removeAll();
currentButtonView.setSelected(false);
switch(oldView) {
case ABOVE_VIEW:
verticalDividerLocation = ((JSplitPane)currentViewPanel).getDividerLocation();
break;
case SIDE_BY_SIDE_VIEW:
horizontalDividerLocation = ((JSplitPane)currentViewPanel).getDividerLocation();
break;
default:
}
}
switch (newView) {
case TABS_VIEW:
switchToTabsView();
break;
case ABOVE_VIEW:
switchToAboveView();
break;
case SIDE_BY_SIDE_VIEW:
switchToSideBySideView();
break;
default:
switchToTabsView();
break;
}
currentButtonView.setSelected(true);
this.add(currentViewPanel);
this.validate();
this.repaint();
}
}
private void switchToTabsView() {
currentView = TABS_VIEW;
currentButtonView = tabsButtonView;
final JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab(REQUEST_CAPTION, null, requestPanel, null);
tabbedPane.addTab(RESPONSE_CAPTION, null, responsePanel, null);
tabbedPane.setSelectedIndex(0);
currentViewPanel = tabbedPane;
}
private void switchToAboveView() {
currentView = ABOVE_VIEW;
currentButtonView = aboveButtonView;
currentViewPanel = createSplitPane(JSplitPane.VERTICAL_SPLIT);
}
private void switchToSideBySideView() {
currentView = SIDE_BY_SIDE_VIEW;
currentButtonView = sideBySideButtonView;
currentViewPanel = createSplitPane(JSplitPane.HORIZONTAL_SPLIT);
}
private JSplitPane createSplitPane(int orientation) {
final JTabbedPane tabbedPaneRequest = new JTabbedPane();
tabbedPaneRequest.addTab(REQUEST_CAPTION, null, requestPanel, null);
final JTabbedPane tabbedPaneResponse = new JTabbedPane();
tabbedPaneResponse.addTab(RESPONSE_CAPTION, null, responsePanel, null);
final JSplitPane splitPane = new JSplitPane(orientation, tabbedPaneRequest, tabbedPaneResponse);
splitPane.setDividerSize(3);
splitPane.setResizeWeight(0.5d);
splitPane.setContinuousLayout(false);
splitPane.setDoubleBuffered(true);
int dividerLocation;
if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
dividerLocation = horizontalDividerLocation;
} else {
dividerLocation = verticalDividerLocation;
}
splitPane.setDividerLocation(dividerLocation);
return splitPane;
}
}
public void addPersistentConnectionListener(PersistentConnectionListener listener) {
((HttpPanelSender) getMessageSender()).addPersistentConnectionListener(listener);
}
public void removePersistentConnectionListener(PersistentConnectionListener listener) {
((HttpPanelSender) getMessageSender()).removePersistentConnectionListener(listener);
}
}