/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * 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.httppanel.component; import java.awt.CardLayout; import java.awt.Component; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.ListCellRenderer; import javax.swing.MutableComboBoxModel; import org.apache.commons.configuration.FileConfiguration; import org.apache.log4j.Logger; import org.zaproxy.zap.extension.httppanel.HttpPanel; import org.zaproxy.zap.extension.httppanel.Message; import org.zaproxy.zap.extension.httppanel.view.HttpPanelDefaultViewSelector; import org.zaproxy.zap.extension.httppanel.view.HttpPanelView; import org.zaproxy.zap.extension.search.SearchMatch; import org.zaproxy.zap.extension.search.SearchableHttpPanelView; import org.zaproxy.zap.model.MessageLocation; import org.zaproxy.zap.utils.SortedComboBoxModel; import org.zaproxy.zap.view.messagelocation.MessageLocationHighlight; import org.zaproxy.zap.view.messagelocation.MessageLocationHighlighter; public class HttpPanelComponentViewsManager implements ItemListener, MessageLocationHighlighter { private static final Logger logger = Logger.getLogger(HttpPanelComponentViewsManager.class); private static final String VIEWS_KEY = "views"; private static final String DEFAULT_VIEW_KEY = "defaultview"; private static DefaultViewSelectorComparator defaultViewSelectorComparator; private Message message; private JPanel panelViews; private JComboBox<ViewItem> comboBoxSelectView; private MutableComboBoxModel<ViewItem> comboBoxModel; private HttpPanelView currentView; private List<ViewItem> enabledViews; private Map<String, ViewItem> viewItems; private Map<String, HttpPanelView> views; private List<HttpPanelDefaultViewSelector> defaultViewsSelectors; private String savedSelectedViewName; private String configurationKey; private String viewsConfigurationKey; private boolean isEditable; private Object changingComboBoxLocker; private boolean changingComboBox; private HttpPanel owner; public HttpPanelComponentViewsManager(String configurationKey) { enabledViews = new ArrayList<>(); viewItems = new HashMap<>(); views = new HashMap<>(); defaultViewsSelectors = new ArrayList<>(); isEditable = false; this.configurationKey = configurationKey; this.viewsConfigurationKey = ""; changingComboBoxLocker = new Object(); changingComboBox = false; savedSelectedViewName = null; comboBoxModel = new SortedComboBoxModel<>(); comboBoxSelectView = new JComboBox<>(comboBoxModel); comboBoxSelectView.addItemListener(this); panelViews = new JPanel(new CardLayout()); } public HttpPanelComponentViewsManager(String configurationKey, String label) { this(configurationKey); comboBoxSelectView.setRenderer(new CustomDelegateListCellRenderer(comboBoxSelectView, label)); } public HttpPanelComponentViewsManager(HttpPanel owner, String configurationKey) { this(configurationKey); this.owner = owner; } public HttpPanelComponentViewsManager(HttpPanel owner, String configurationKey, String label) { this(configurationKey, label); this.owner = owner; } public JComponent getSelectableViewsComponent() { return comboBoxSelectView; } public JPanel getViewsPanel() { return panelViews; } public void setSelected(boolean selected) { if (currentView != null) { currentView.setSelected(selected); } } private void switchView(final String name) { if (this.currentView != null && this.currentView.getCaptionName().equals(name)) { currentView.setSelected(true); if (owner != null) { owner.fireMessageViewChangedEvent(currentView, currentView); } return; } HttpPanelView view = views.get(name); if (view == null) { logger.info("No view found with name: " + name); return; } HttpPanelView previousView = currentView; if (this.currentView != null) { this.currentView.setSelected(false); this.currentView.getModel().clear(); } this.currentView = view; comboBoxModel.setSelectedItem(viewItems.get(name)); this.currentView.getModel().setMessage(message); ((CardLayout) panelViews.getLayout()).show(panelViews, name); this.currentView.setSelected(true); if (owner != null) { owner.fireMessageViewChangedEvent(previousView, currentView); } } public void setMessage(Message aMessage) { this.message = aMessage; enableViews(); String defaultViewName = getDefaultEnabledViewName(); if (defaultViewName != null) { if (defaultViewName.equals(currentView.getName())) { currentView.getModel().setMessage(message); } else { switchView(defaultViewName); } } else if (!enabledViews.contains(viewItems.get(currentView.getName()))) { switchView(enabledViews.get(0).getConfigName()); } else { currentView.getModel().setMessage(message); } } private void enableViews() { Iterator<Entry<String, HttpPanelView>> it = views.entrySet().iterator(); while (it.hasNext()) { HttpPanelView view = it.next().getValue(); ViewItem viewItem = viewItems.get(view.getName()); if (!view.isEnabled(message)) { if (enabledViews.contains(viewItem)) { disableView(viewItem); } } else if (!enabledViews.contains(viewItem)) { enableView(viewItem); } } } private String getDefaultEnabledViewName() { String defaultViewName = null; Iterator<HttpPanelDefaultViewSelector> itD = defaultViewsSelectors.iterator(); while (itD.hasNext()) { HttpPanelDefaultViewSelector defaultView = itD.next(); if (defaultView.matchToDefaultView(message)) { if (enabledViews.contains(viewItems.get(defaultView.getViewName()))) { defaultViewName = defaultView.getViewName(); break; } } } return defaultViewName; } @Override public void itemStateChanged(ItemEvent e) { synchronized (changingComboBoxLocker) { if (changingComboBox) { return; } } if (e.getStateChange() == ItemEvent.SELECTED) { if (currentView == null) { return; } ViewItem item = (ViewItem) comboBoxModel.getSelectedItem(); if (item == null || item.getConfigName().equals(currentView.getName())) { return; } save(); switchView(item.getConfigName()); } } public void save() { if (message == null || currentView == null) { return; } if (isEditable) { if (currentView.hasChanged()) { currentView.save(); } } } public void addView(HttpPanelView view) { final String targetViewName = view.getTargetViewName(); if (!"".equals(targetViewName) && views.containsKey(targetViewName)) { removeView(targetViewName); } final String viewConfigName = view.getName(); views.put(viewConfigName, view); ViewItem viewItem = new ViewItem(viewConfigName, view.getCaptionName(), view.getPosition()); viewItems.put(viewConfigName, viewItem); panelViews.add(view.getPane(), viewConfigName); view.setEditable(isEditable); view.setParentConfigurationKey(viewsConfigurationKey); if (view.isEnabled(message)) { enableView(viewItem); boolean switchView = false; if (currentView == null) { switchView = true; } else if (savedSelectedViewName != null) { if (savedSelectedViewName.equals(viewConfigName)) { switchView = true; } else if (!savedSelectedViewName.equals(currentView.getName()) && currentView.getPosition() > view.getPosition()) { switchView = true; } } else if (currentView.getPosition() > view.getPosition()) { switchView = true; } if (switchView) { switchView(viewConfigName); } } } private void enableView(ViewItem viewItem) { enabledViews.add(viewItem); Collections.sort(enabledViews); synchronized (changingComboBoxLocker) { changingComboBox = true; comboBoxModel.addElement(viewItem); changingComboBox = false; } } private void disableView(ViewItem viewItem) { enabledViews.remove(viewItem); synchronized (changingComboBoxLocker) { changingComboBox = true; comboBoxModel.removeElement(viewItem); changingComboBox = false; } } public void addView(HttpPanelView view, FileConfiguration fileConfiguration) { addView(view); view.loadConfiguration(fileConfiguration); } public void removeView(String viewName) { HttpPanelView view = views.get(viewName); if (view == null) { return ; } views.remove(viewName); panelViews.remove(view.getPane()); ViewItem viewItem = viewItems.get(viewName); if (enabledViews.contains(viewItem)) { disableView(viewItem); } viewItems.remove(view.getName()); if (viewName.equals(currentView.getName())) { if (enabledViews.size() > 0) { switchView(enabledViews.get(0).getConfigName()); } else { currentView = null; } } } public void clearView() { if (currentView != null) { currentView.getModel().clear(); setMessage(null); } } public void clearView(boolean enableViewSelect) { clearView(); setEnableViewSelect(enableViewSelect); } public void setEnableViewSelect(boolean enableViewSelect) { comboBoxSelectView.setEnabled(enableViewSelect); } public void addDefaultViewSelector(HttpPanelDefaultViewSelector defaultViewSelector) { defaultViewsSelectors.add(defaultViewSelector); Collections.sort(defaultViewsSelectors, getDefaultViewSelectorComparator()); } public void removeDefaultViewSelector(String defaultViewSelectorName) { Iterator<HttpPanelDefaultViewSelector> itD = defaultViewsSelectors.iterator(); while (itD.hasNext()) { HttpPanelDefaultViewSelector defaultView = itD.next(); if (defaultView.getName().equals(defaultViewSelectorName)) { defaultViewsSelectors.remove(defaultView); break; } } } private static Comparator<HttpPanelDefaultViewSelector> getDefaultViewSelectorComparator() { if (defaultViewSelectorComparator == null) { createDefaultViewSelectorComparator(); } return defaultViewSelectorComparator; } private static synchronized void createDefaultViewSelectorComparator() { if (defaultViewSelectorComparator == null) { defaultViewSelectorComparator = new DefaultViewSelectorComparator(); } } public void setConfigurationKey(String parentKey) { configurationKey = parentKey + configurationKey + "."; viewsConfigurationKey = configurationKey + VIEWS_KEY + "."; Iterator<HttpPanelView> it = views.values().iterator(); while (it.hasNext()) { it.next().setParentConfigurationKey(viewsConfigurationKey); } } public void loadConfig(FileConfiguration fileConfiguration) { savedSelectedViewName = fileConfiguration.getString(configurationKey + DEFAULT_VIEW_KEY); Iterator<HttpPanelView> it = views.values().iterator(); while (it.hasNext()) { it.next().loadConfiguration(fileConfiguration); } } public void saveConfig(FileConfiguration fileConfiguration) { if (currentView != null) { fileConfiguration.setProperty(configurationKey + DEFAULT_VIEW_KEY, currentView.getName()); } Iterator<HttpPanelView> it = views.values().iterator(); while (it.hasNext()) { it.next().saveConfiguration(fileConfiguration); } } public void setEditable(boolean editable) { if (isEditable != editable) { isEditable = editable; Iterator<HttpPanelView> it = views.values().iterator(); while (it.hasNext()) { it.next().setEditable(editable); } } } public void highlight(SearchMatch sm) { if (currentView instanceof SearchableHttpPanelView) { ((SearchableHttpPanelView)currentView).highlight(sm); } else { SearchableHttpPanelView searchableView = findSearchableView(); if (currentView != null) { switchView(((HttpPanelView)searchableView).getName()); searchableView.highlight(sm); } } } public void search(Pattern p, List<SearchMatch> matches) { if (currentView instanceof SearchableHttpPanelView) { ((SearchableHttpPanelView)currentView).search(p, matches); } else { SearchableHttpPanelView searchableView = findSearchableView(); if (searchableView != null) { searchableView.search(p, matches); } } } private SearchableHttpPanelView findSearchableView() { SearchableHttpPanelView searchableView = null; Iterator<HttpPanelView> it = views.values().iterator(); while (it.hasNext()) { HttpPanelView view = it.next(); if (view.isEnabled(message)) { if (view instanceof SearchableHttpPanelView) { searchableView = (SearchableHttpPanelView)view; break; } } } return searchableView; } private static final class ViewItem implements Comparable<ViewItem> { private final String configName; private String name; private final int position; public ViewItem(String configName, String name, int position) { this.configName = configName; this.name = name; this.position = position; } public String getConfigName() { return configName; } @Override public int compareTo(ViewItem o) { if (position < o.position) { return -1; } else if (position > o.position) { return 1; } return 0; } @Override public int hashCode() { return 31 * configName.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ViewItem other = (ViewItem) obj; if (!configName.equals(other.configName)) { return false; } return true; } @Override public String toString() { return name; } } private static final class CustomDelegateListCellRenderer implements ListCellRenderer<ViewItem> { private ListCellRenderer<? super ViewItem> delegateRenderer; private JComboBox<ViewItem> comboBox; private String label; private ViewItem viewItem; public CustomDelegateListCellRenderer(JComboBox<ViewItem> aComboBox, String label) { this.delegateRenderer = aComboBox.getRenderer(); this.comboBox = aComboBox; this.label = label; this.viewItem = new ViewItem("", "", -1); this.comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { delegateRenderer = new JComboBox<ViewItem>().getRenderer(); } }); } @Override public Component getListCellRendererComponent(JList<? extends ViewItem> list, ViewItem value, int index, boolean isSelected, boolean cellHasFocus) { if (index != -1) { return delegateRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); } viewItem.name = label + value.name; return delegateRenderer.getListCellRendererComponent(list, viewItem, index, isSelected, cellHasFocus); } } private static final class DefaultViewSelectorComparator implements Comparator<HttpPanelDefaultViewSelector>, Serializable { private static final long serialVersionUID = -1380844848294384189L; @Override public int compare(HttpPanelDefaultViewSelector o1, HttpPanelDefaultViewSelector o2) { final int order1 = o1.getOrder(); final int order2 = o2.getOrder(); if (order1 < order2) { return -1; } else if (order1 > order2) { return 1; } return 0; } } @Override public boolean supports(MessageLocation location) { for (ViewItem item : enabledViews) { HttpPanelView view = views.get(item.getConfigName()); if (view instanceof MessageLocationHighlighter) { MessageLocationHighlighter highlighter = (MessageLocationHighlighter) view; if (highlighter.supports(location)) { return true; } } } return false; } @Override public boolean supports(Class<? extends MessageLocation> classLocation) { for (ViewItem item : enabledViews) { HttpPanelView view = views.get(item.getConfigName()); if (view instanceof MessageLocationHighlighter) { MessageLocationHighlighter highlighter = (MessageLocationHighlighter) view; if (highlighter.supports(classLocation)) { return true; } } } return false; } @Override public MessageLocationHighlight highlight(MessageLocation location) { if (currentView instanceof MessageLocationHighlighter) { MessageLocationHighlighter highlighter = (MessageLocationHighlighter) currentView; return highlighter.highlight(location); } return null; } @Override public MessageLocationHighlight highlight(MessageLocation location, MessageLocationHighlight highlight) { if (currentView instanceof MessageLocationHighlighter) { MessageLocationHighlighter highlighter = (MessageLocationHighlighter) currentView; return highlighter.highlight(location, highlight); } return null; } @Override public void removeHighlight(MessageLocation location, MessageLocationHighlight highlightReference) { if (currentView instanceof MessageLocationHighlighter) { MessageLocationHighlighter highlighter = (MessageLocationHighlighter) currentView; highlighter.removeHighlight(location, highlightReference); } } public HttpPanelView setSelectedView(String viewName) { for (ViewItem item : enabledViews) { if (viewName.equals(item.getConfigName())) { switchView(viewName); return currentView; } } return null; } }