/* * 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.view.syntaxhighlight; import java.awt.Color; import java.awt.Component; import java.util.LinkedList; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.Action; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JViewport; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Highlighter; import javax.swing.text.Highlighter.HighlightPainter; import org.apache.commons.configuration.FileConfiguration; import org.apache.log4j.Logger; import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.SyntaxConstants; import org.fife.ui.rtextarea.RTextArea; import org.fife.ui.rtextarea.RTextScrollPane; import org.parosproxy.paros.Constant; import org.parosproxy.paros.extension.ExtensionPopupMenuItem; import org.parosproxy.paros.view.View; import org.zaproxy.zap.extension.httppanel.Message; import org.zaproxy.zap.extension.httppanel.view.syntaxhighlight.menus.SyntaxMenu; import org.zaproxy.zap.extension.httppanel.view.syntaxhighlight.menus.ViewMenu; import org.zaproxy.zap.extension.search.SearchMatch; import org.zaproxy.zap.utils.FontUtils; import org.zaproxy.zap.view.HighlightSearchEntry; import org.zaproxy.zap.view.HighlighterManager; public abstract class HttpPanelSyntaxHighlightTextArea extends RSyntaxTextArea implements Observer { private static final long serialVersionUID = -9082089105656842054L; private static Logger log = Logger.getLogger(HttpPanelSyntaxHighlightTextArea.class); public static final String PLAIN_SYNTAX_LABEL = Constant.messages.getString("http.panel.view.syntaxtext.syntax.plain"); private static final String ANTI_ALIASING = "aa"; private static final String SHOW_LINE_NUMBERS = "linenumbers"; private static final String WORD_WRAP = "wordwrap"; private static final String HIGHLIGHT_CURRENT_LINE = "highlightline"; private static final String FADE_CURRENT_HIGHLIGHT_LINE = "fadehighlightline"; private static final String SHOW_WHITESPACE_CHARACTERS = "whitespaces"; private static final String SHOW_NEWLINE_CHARACTERS = "newlines"; private static final String MARK_OCCURRENCES = "markocurrences"; private static final String ROUNDED_SELECTION_EDGES = "roundedselection"; private static final String BRACKET_MATCHING = "bracketmatch"; private static final String ANIMATED_BRACKET_MATCHING = "animatedbracketmatch"; private Message message; private Vector<SyntaxStyle> syntaxStyles; private static SyntaxMenu syntaxMenu = null; private static ViewMenu viewMenu = null; private static TextAreaMenuItem cutAction = null; private static TextAreaMenuItem copyAction = null; private static TextAreaMenuItem pasteAction = null; private static TextAreaMenuItem deleteAction = null; private static TextAreaMenuItem undoAction = null; private static TextAreaMenuItem redoAction = null; private static TextAreaMenuItem selectAllAction = null; public HttpPanelSyntaxHighlightTextArea() { ((RSyntaxDocument)getDocument()).setTokenMakerFactory(getTokenMakerFactory()); setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE); syntaxStyles = new Vector<>(); addSyntaxStyle(PLAIN_SYNTAX_LABEL, SyntaxConstants.SYNTAX_STYLE_NONE); if (syntaxMenu == null) { initActions(); } setPopupMenu(null); this.message = null; setHyperlinksEnabled(false); setAntiAliasingEnabled(true); setLineWrap(true); setHighlightCurrentLine(false); setFadeCurrentLineHighlight(false); setWhitespaceVisible(false); setEOLMarkersVisible(false); setMarkOccurrences(false); setBracketMatchingEnabled(false); setAnimateBracketMatching(false); setAutoIndentEnabled(false); setCloseCurlyBraces(false); setCloseMarkupTags(false); setClearWhitespaceLinesEnabled(false); // Correct the font size this.setFont(FontUtils.getFont(this.getFont().getFontName())); initHighlighter(); } @Override protected JPopupMenu createPopupMenu() { return null; } private void initHighlighter() { HighlighterManager highlighter = HighlighterManager.getInstance(); highlighter.addObserver(this); if (message != null) { highlightAll(); } } // Highlight all search strings from HighlightManager private void highlightAll() { HighlighterManager highlighter = HighlighterManager.getInstance(); LinkedList<HighlightSearchEntry> highlights = highlighter.getHighlights(); for (HighlightSearchEntry entry: highlights) { highlightEntryParser(entry); } } // Parse the TextArea data and search the HighlightEntry strings // Highlight all found strings private void highlightEntryParser(HighlightSearchEntry entry) { String text; int lastPos = 0; text = this.getText(); Highlighter hilite = this.getHighlighter(); HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(entry.getColor()); while ( (lastPos = text.indexOf(entry.getToken(), lastPos)) > -1) { try { hilite.addHighlight(lastPos, lastPos + entry.getToken().length(), painter); lastPos += entry.getToken().length(); } catch (BadLocationException e) { log.warn("Could not highlight entry", e); } } } @Override // Apply highlights after a setText() public void setText(String s) { super.setText(s); highlightAll(); } public abstract void search(Pattern p, List<SearchMatch> matches); // highlight a specific SearchMatch in the editor public abstract void highlight(SearchMatch sm); protected void highlight(int start, int end) { Highlighter hilite = this.getHighlighter(); HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.LIGHT_GRAY); try { // DOBIN removeAllHighlights(); hilite.addHighlight(start, end, painter); this.setCaretPosition(start); } catch (BadLocationException e) { log.error(e.getMessage(), e); } } public Object highlight(int start, int end, HighlightPainter painter) { try { Object highlightReference = getHighlighter().addHighlight(start, end, painter); this.setCaretPosition(start); return highlightReference; } catch (BadLocationException e) { log.error(e.getMessage(), e); } return null; } public void removeHighlight(Object highlightReference) { getHighlighter().removeHighlight(highlightReference); } private void removeAllHighlights() { Highlighter hilite = this.getHighlighter(); hilite.removeAllHighlights(); } @Override // HighlighterManager called us // there is either // - a new highlight // - something other (added several, deleted, ...). public void update(Observable arg0, Object arg1) { if (arg1 == null) { // Re-highlight everything removeAllHighlights(); highlightAll(); } else { // Add specific highlight HighlightSearchEntry token = (HighlightSearchEntry) arg1; highlightEntryParser(token); } this.invalidate(); } public void setMessage(Message aMessage) { this.message = aMessage; } public Message getMessage() { return message; } public void loadConfiguration(String key, FileConfiguration fileConfiguration) { setAntiAliasingEnabled(fileConfiguration.getBoolean(key + ANTI_ALIASING, this.getAntiAliasingEnabled())); Component c = getParent(); if (c instanceof JViewport) { c = c.getParent(); if (c instanceof RTextScrollPane) { final RTextScrollPane scrollPane = (RTextScrollPane)c; scrollPane.setLineNumbersEnabled(fileConfiguration.getBoolean(key + SHOW_LINE_NUMBERS, scrollPane.getLineNumbersEnabled())); } } setLineWrap(fileConfiguration.getBoolean(key + WORD_WRAP, this.getLineWrap())); setHighlightCurrentLine(fileConfiguration.getBoolean(key + HIGHLIGHT_CURRENT_LINE, this.getHighlightCurrentLine())); setFadeCurrentLineHighlight(fileConfiguration.getBoolean(key + FADE_CURRENT_HIGHLIGHT_LINE, this.getFadeCurrentLineHighlight())); setWhitespaceVisible(fileConfiguration.getBoolean(key + SHOW_WHITESPACE_CHARACTERS, this.isWhitespaceVisible())); setEOLMarkersVisible(fileConfiguration.getBoolean(key + SHOW_NEWLINE_CHARACTERS, this.getEOLMarkersVisible())); setMarkOccurrences(fileConfiguration.getBoolean(key + MARK_OCCURRENCES, this.getMarkOccurrences())); setRoundedSelectionEdges(fileConfiguration.getBoolean(key + ROUNDED_SELECTION_EDGES, this.getRoundedSelectionEdges())); setBracketMatchingEnabled(fileConfiguration.getBoolean(key + BRACKET_MATCHING, this.isBracketMatchingEnabled())); setAnimateBracketMatching(fileConfiguration.getBoolean(key + ANIMATED_BRACKET_MATCHING, this.getAnimateBracketMatching())); } public void saveConfiguration(String key, FileConfiguration fileConfiguration) { fileConfiguration.setProperty(key + ANTI_ALIASING, Boolean.valueOf(this.getAntiAliasingEnabled())); Component c = getParent(); if (c instanceof JViewport) { c = c.getParent(); if (c instanceof RTextScrollPane) { final RTextScrollPane scrollPane = (RTextScrollPane)c; fileConfiguration.setProperty(key + SHOW_LINE_NUMBERS, Boolean.valueOf(scrollPane.getLineNumbersEnabled())); } } fileConfiguration.setProperty(key + WORD_WRAP, Boolean.valueOf(this.getLineWrap())); fileConfiguration.setProperty(key + HIGHLIGHT_CURRENT_LINE, Boolean.valueOf(this.getHighlightCurrentLine())); fileConfiguration.setProperty(key + FADE_CURRENT_HIGHLIGHT_LINE, Boolean.valueOf(this.getFadeCurrentLineHighlight())); fileConfiguration.setProperty(key + SHOW_WHITESPACE_CHARACTERS, Boolean.valueOf(this.isWhitespaceVisible())); fileConfiguration.setProperty(key + SHOW_NEWLINE_CHARACTERS, Boolean.valueOf(this.getEOLMarkersVisible())); fileConfiguration.setProperty(key + MARK_OCCURRENCES, Boolean.valueOf(this.getMarkOccurrences())); fileConfiguration.setProperty(key + ROUNDED_SELECTION_EDGES, Boolean.valueOf(this.getRoundedSelectionEdges())); fileConfiguration.setProperty(key + BRACKET_MATCHING, Boolean.valueOf(this.isBracketMatchingEnabled())); fileConfiguration.setProperty(key + ANIMATED_BRACKET_MATCHING, Boolean.valueOf(this.getAnimateBracketMatching())); } public Vector<SyntaxStyle> getSyntaxStyles() { return syntaxStyles; } protected void addSyntaxStyle(String label, String styleKey) { syntaxStyles.add(new SyntaxStyle(label, styleKey)); } protected abstract CustomTokenMakerFactory getTokenMakerFactory(); private static synchronized void initActions() { if (syntaxMenu == null) { syntaxMenu = new SyntaxMenu(); viewMenu = new ViewMenu(); undoAction = new TextAreaMenuItem(RTextArea.UNDO_ACTION, true, false); redoAction = new TextAreaMenuItem(RTextArea.REDO_ACTION, false, true); cutAction = new TextAreaMenuItem(RTextArea.CUT_ACTION, false, false); copyAction = new TextAreaMenuItem(RTextArea.COPY_ACTION, false, false); pasteAction = new TextAreaMenuItem(RTextArea.PASTE_ACTION, false, false); deleteAction = new TextAreaMenuItem(RTextArea.DELETE_ACTION, false, true); selectAllAction = new TextAreaMenuItem(RTextArea.SELECT_ALL_ACTION, false, false); final List<JMenuItem> mainPopupMenuItems = View.getSingleton().getPopupList(); mainPopupMenuItems.add(syntaxMenu); mainPopupMenuItems.add(viewMenu); mainPopupMenuItems.add(undoAction); mainPopupMenuItems.add(redoAction); mainPopupMenuItems.add(cutAction); mainPopupMenuItems.add(copyAction); mainPopupMenuItems.add(pasteAction); mainPopupMenuItems.add(deleteAction); mainPopupMenuItems.add(selectAllAction); } } public static class SyntaxStyle { private String label; private String styleKey; public SyntaxStyle(String label, String styleKey) { this.label = label; this.styleKey = styleKey; } public String getLabel() { return label; } public String getStyleKey() { return styleKey; } } protected static class CustomTokenMakerFactory extends AbstractTokenMakerFactory { @Override protected void initTokenMakerMap() { String pkg = "org.fife.ui.rsyntaxtextarea.modes."; putMapping(SYNTAX_STYLE_NONE, pkg + "PlainTextTokenMaker"); } } private static class TextAreaMenuItem extends ExtensionPopupMenuItem { private static final long serialVersionUID = -8369459846515841057L; private int actionId; private boolean precedeWithSeparator; private boolean succeedWithSeparator; public TextAreaMenuItem(int actionId, boolean precedeWithSeparator, boolean succeedWithSeparator) throws IllegalArgumentException { this.actionId = actionId; this.precedeWithSeparator = precedeWithSeparator; this.succeedWithSeparator = succeedWithSeparator; Action action = RTextArea.getAction(actionId); if(action == null) { throw new IllegalArgumentException("Action not found with id: " + actionId); } setAction(action); } @Override public boolean isEnableForComponent(Component invoker) { if (invoker instanceof HttpPanelSyntaxHighlightTextArea) { HttpPanelSyntaxHighlightTextArea httpPanelTextArea = (HttpPanelSyntaxHighlightTextArea)invoker; switch(actionId) { case RTextArea.CUT_ACTION: if (!httpPanelTextArea.isEditable()) { this.setEnabled(false); } break; case RTextArea.DELETE_ACTION: case RTextArea.PASTE_ACTION: this.setEnabled(httpPanelTextArea.isEditable()); break; case RTextArea.SELECT_ALL_ACTION: this.setEnabled(httpPanelTextArea.getDocument().getLength() != 0); break; } return true; } return false; } @Override public boolean precedeWithSeparator() { return precedeWithSeparator; } @Override public boolean succeedWithSeparator() { return succeedWithSeparator; } @Override public boolean isSafe() { return true; } } }