package de.usd.cstchef.operations; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.border.MatteBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import burp.Logger; import de.usd.cstchef.view.ui.FormatTextField; import de.usd.cstchef.view.ui.VariableTextArea; import de.usd.cstchef.view.ui.VariableTextField; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.EOFException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashMap; import java.util.Map; public abstract class Operation extends JPanel { private static Color defaultBgColor = new Color(223, 240, 216); private static Color defaultFontColor = new Color(70, 136, 71); private static Color disabledBgColor = new Color(223, 223, 223); private static Color disabledFontColor = new Color(153, 153, 153); private static Color breakBgColor = new Color(242, 222, 222); private static Color breakFontColor = new Color(185, 74, 72); private static Color errorBgColor = new Color(255, 121, 128); private static Color errorFontColor = new Color(185, 74, 72); private static ImageIcon breakIcon = new ImageIcon(Operation.class.getResource("/stop.png")); private static ImageIcon breakIconActive = new ImageIcon(Operation.class.getResource("/stop_active.png")); private static ImageIcon disableIcon = new ImageIcon(Operation.class.getResource("/disable.png")); private static ImageIcon removeIcon = new ImageIcon(Operation.class.getResource("/remove.png")); private static ImageIcon helpIcon = new ImageIcon(Operation.class.getResource("/help.png")); private NotifyChangeListener notifyChangeListener; private boolean breakpoint = false; private boolean disabled = false; private boolean error = false; private ChangeListener changeListener; private JTextArea errorArea; private Box contentBox; private Map<String, Component> uiElements; private int operationSkip = 0; private int laneSkip = 0; public Operation() { super(); this.uiElements = new HashMap<>(); this.setLayout(new BorderLayout()); this.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); // set border Border margin = BorderFactory.createEmptyBorder(10, 10, 10, 10); MatteBorder lineBorder = new MatteBorder(0, 0, 1, 0, Color.BLACK); CompoundBorder border = new CompoundBorder(lineBorder, margin); this.setBorder(border); // add header JPanel header = new JPanel(); header.setBackground(new Color(0, 0, 0, 0)); // transparent BoxLayout layout = new BoxLayout(header, BoxLayout.X_AXIS); header.setLayout(layout); header.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); OperationInfos opInfos = this.getClass().getAnnotation(OperationInfos.class); JLabel titleLbl = new JLabel(opInfos.name()); Font f = titleLbl.getFont(); titleLbl.setFont(f.deriveFont(f.getStyle() | Font.BOLD)); JButton disableBtn = createIconButton(Operation.disableIcon); disableBtn.setToolTipText("Disable"); JButton breakpointBtn = createIconButton(Operation.breakIcon); breakpointBtn.setToolTipText("Breakpoint"); JButton removeBtn = createIconButton(Operation.removeIcon); removeBtn.setToolTipText("Remove"); JButton helpBtn = createIconButton(Operation.helpIcon); helpBtn.setToolTipText(opInfos.description()); disableBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { disabled = !isDisabled(); refreshColors(); validate(); repaint(); notifyChange(); } }); breakpointBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { breakpoint = !isBreakpoint(); ImageIcon newIcon = isBreakpoint() ? Operation.breakIconActive : Operation.breakIcon; breakpointBtn.setIcon(newIcon); refreshColors(); validate(); repaint(); notifyChange(); } }); JPanel me = this; removeBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Container parent = getParent(); onRemove(); parent.remove(me); parent.validate(); parent.repaint(); notifyChange(); } }); header.add(titleLbl); header.add(Box.createHorizontalStrut(6)); header.add(helpBtn); header.add(Box.createHorizontalGlue()); header.add(disableBtn); header.add(Box.createHorizontalStrut(3)); header.add(breakpointBtn); header.add(Box.createHorizontalStrut(3)); header.add(removeBtn); this.add(header, BorderLayout.NORTH); errorArea = new JTextArea(); errorArea.setEditable(false); errorArea.setLineWrap(true); errorArea.setWrapStyleWord(true); errorArea.setBackground(new Color(0, 0, 0, 0)); errorArea.setFont(f.deriveFont(f.getStyle() | Font.BOLD)); errorArea.setFocusable(false); errorArea.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); this.add(errorArea, BorderLayout.SOUTH); contentBox = Box.createVerticalBox(); this.add(contentBox, BorderLayout.CENTER); this.createUI(); this.refreshColors(); } public Map<String, Object> getState() { Map<String, Object> properties = new HashMap<>(); for (String key : this.uiElements.keySet()) { properties.put(key, getUiValues(this.uiElements.get(key))); } return properties; } private Object getUiValues(Component comp) { Object result = null; if (comp instanceof VariableTextArea) { result = ((VariableTextArea) comp).getRawText(); } else if (comp instanceof VariableTextField) { result = ((VariableTextField) comp).getRawText(); } else if (comp instanceof JTextField) { result = ((JTextField) comp).getText(); } else if (comp instanceof JSpinner) { result = ((JSpinner) comp).getValue(); } else if (comp instanceof JComboBox) { result = ((JComboBox<?>) comp).getSelectedItem(); if( result != null ) result = result.toString(); } else if (comp instanceof JCheckBox) { result = ((JCheckBox) comp).isSelected(); } else if (comp instanceof FormatTextField) { result = ((FormatTextField) comp).getValues(); } return result; } public void load(Map<String, Object> parameters) { for (String key : this.uiElements.keySet()) { Object value = parameters.get(key); this.setUiValue(this.uiElements.get(key), value); } } private void setUiValue(Component comp, Object value) { if (comp == null) { return; } if (comp instanceof JTextField) { ((JTextField) comp).setText((String) value); } else if (comp instanceof JSpinner) { ((JSpinner) comp).setValue(value); } else if (comp instanceof JComboBox) { ((JComboBox<?>) comp).setSelectedItem(value); } else if (comp instanceof VariableTextArea) { ((VariableTextArea) comp).setText((String) value); } else if (comp instanceof JCheckBox) { ((JCheckBox) comp).setSelected((boolean) value); } else if (comp instanceof FormatTextField) { ((FormatTextField) comp).setValues((Map<String, String>) value); } } private JButton createIconButton(ImageIcon icon) { JButton btn = new JButton(); btn.setBorder(BorderFactory.createEmptyBorder()); btn.setIcon(icon); btn.setContentAreaFilled(false); btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); btn.setAlignmentX(Component.RIGHT_ALIGNMENT); return btn; } private void refreshColors() { Color bgColor; Color fontColor; if (this.isDisabled()) { bgColor = Operation.disabledBgColor; fontColor = Operation.disabledFontColor; } else if (this.isError()) { bgColor = Operation.errorBgColor; fontColor = Operation.errorFontColor; } else if (this.isBreakpoint()) { bgColor = Operation.breakBgColor; fontColor = Operation.breakFontColor; } else { bgColor = Operation.defaultBgColor; fontColor = Operation.defaultFontColor; } this.setBackground(bgColor); this.changeFontColor(this, fontColor); } private void changeFontColor(Container container, Color color) { for (Component comp : container.getComponents()) { if (comp instanceof JLabel || comp.equals(errorArea)) { comp.setForeground(color); } else if (comp instanceof Container) { changeFontColor((Container) comp, color); } } } protected void addUIElement(String caption, Component comp) { this.addUIElement(caption, comp, true); } protected void addUIElement(String caption, Component comp, boolean notifyChange) { comp.setCursor(Cursor.getDefaultCursor()); Box box = Box.createHorizontalBox(); box.setAlignmentX(Component.LEFT_ALIGNMENT); if (comp instanceof JCheckBox) { comp.setBackground(new Color(0, 0, 0, 0)); } JLabel lbl = new JLabel(caption); box.add(lbl); box.add(Box.createHorizontalStrut(10)); box.add(comp); this.contentBox.add(box); this.contentBox.add(Box.createVerticalStrut(10)); this.uiElements.put(caption, comp); if (notifyChange) { if (notifyChangeListener == null) { notifyChangeListener = new NotifyChangeListener(); } if (comp instanceof JTextField) { ((JTextField) comp).getDocument().addDocumentListener(notifyChangeListener); } else if (comp instanceof JSpinner) { ((JSpinner) comp).addChangeListener(notifyChangeListener); } else if (comp instanceof JComboBox) { ((JComboBox<?>) comp).addActionListener(notifyChangeListener); } else if (comp instanceof VariableTextArea) { ((VariableTextArea) comp).addDocumentListener(notifyChangeListener); } else if (comp instanceof JCheckBox) { ((JCheckBox) comp).addActionListener(notifyChangeListener); } else if (comp instanceof FormatTextField) { ((FormatTextField) comp).addDocumentListener(notifyChangeListener); } else { Logger.getInstance().err("could not add a default change listener for " + comp.getClass()); } } } @Override public Dimension getPreferredSize() { Dimension dim = super.getPreferredSize(); int width = this.getParent().getWidth(); dim.setSize(width, dim.height); return dim; } public byte[] performOperation(byte[] input) { try { byte[] result = this.perform(input); this.setErrorMessage(null); return result; } catch (EOFException e) { this.setErrorMessage(new EOFException("End of file")); return new byte[0]; } catch (Throwable e) { this.setErrorMessage(e); return new byte[0]; } } public void setErrorMessage(Throwable e) { boolean error = e != null; String msg = error ? e.getMessage() : ""; String text; if (msg == null) { text = e.getClass().getName(); } else { text = error ? (msg.isEmpty() ? e.toString() : msg) : ""; } this.errorArea.setText(text); this.setError(error); this.refreshColors(); this.validate(); this.repaint(); } public void removeChangeListener() { this.changeListener = null; } public void setChangeListener(ChangeListener listener) { this.changeListener = listener; } protected void notifyChange() { if (this.changeListener != null) { this.changeListener.stateChanged(new ChangeEvent(this)); } } public boolean isBreakpoint() { return breakpoint; } public void setBreakpoint(boolean breakpoint) { this.breakpoint = breakpoint; } public boolean isDisabled() { return disabled; } public void setDisabled(boolean disabled) { this.disabled = disabled; } public boolean isError() { return error; } public void setError(boolean error) { this.error = error; } public void setOperationSkip(int count) { if( count < 0 ) count = 0; this.operationSkip = count; } public int getOperationSkip() { return this.operationSkip; } public void setLaneSkip(int count) { if( count < 0 ) count = 0; this.laneSkip = count; } public int getLaneSkip() { return this.laneSkip; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface OperationInfos { public String name() default "Unnamed plugin!"; public String description() default "Change this description!"; public OperationCategory category() default OperationCategory.MISC; } protected abstract byte[] perform(byte[] input) throws Exception; public void createUI() { } public void onRemove() { } private class NotifyChangeListener implements DocumentListener, ActionListener, ChangeListener { @Override public void changedUpdate(DocumentEvent e) { notifyChange(); } @Override public void insertUpdate(DocumentEvent e) { notifyChange(); } @Override public void removeUpdate(DocumentEvent e) { notifyChange(); } @Override public void actionPerformed(ActionEvent e) { notifyChange(); } @Override public void stateChanged(ChangeEvent e) { notifyChange(); } } }