View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2020.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss about it
6    * every time possible with every body.
7    *
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   *******************************************************************************/
11  package com.jsql.view.swing.panel;
12  
13  import com.jsql.model.InjectionModel;
14  import com.jsql.util.LogLevelUtil;
15  import com.jsql.view.swing.console.JTextPaneAppender;
16  import com.jsql.view.swing.console.JavaConsoleAdapter;
17  import com.jsql.view.swing.console.SimpleConsoleAdapter;
18  import com.jsql.view.swing.panel.consoles.NetworkTable;
19  import com.jsql.view.swing.panel.consoles.TabbedPaneNetworkTab;
20  import com.jsql.view.swing.panel.split.SplitHorizontalTopBottom;
21  import com.jsql.view.swing.scrollpane.JScrollIndicator;
22  import com.jsql.view.swing.scrollpane.LightScrollPane;
23  import com.jsql.view.swing.splitpane.JSplitPaneWithZeroSizeDivider;
24  import com.jsql.view.swing.tab.TabConsoles;
25  import com.jsql.view.swing.text.JPopupTextArea;
26  import com.jsql.view.swing.text.JTextAreaPlaceholderConsole;
27  import com.jsql.view.swing.text.JToolTipI18n;
28  import com.jsql.view.swing.ui.CustomMetalTabbedPaneUI;
29  import com.jsql.view.swing.util.I18nViewUtil;
30  import com.jsql.view.swing.util.MediatorHelper;
31  import com.jsql.view.swing.util.UiUtil;
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.logging.log4j.LogManager;
34  import org.apache.logging.log4j.Logger;
35  
36  import javax.swing.*;
37  import javax.swing.plaf.basic.BasicArrowButton;
38  import javax.swing.table.DefaultTableModel;
39  import java.awt.*;
40  import java.awt.event.AdjustmentEvent;
41  import java.awt.event.AdjustmentListener;
42  import java.awt.event.MouseAdapter;
43  import java.awt.event.MouseEvent;
44  import java.util.prefs.Preferences;
45  
46  /**
47   * A panel with different consoles displayed on the bottom.
48   */
49  public class PanelConsoles extends JPanel {
50      
51      /**
52       * Log4j logger sent to view.
53       */
54      private static final Logger LOGGER = LogManager.getRootLogger();
55  
56      /**
57       * Console for java exception messages.
58       */
59      private final JavaConsoleAdapter javaTextPane = new JavaConsoleAdapter("Java", "Java unhandled exception");
60      
61      /**
62       * Console for raw SQL results.
63       */
64      private JTextArea chunkTextArea;
65  
66      /**
67       * Panel displaying table of HTTP requests and responses.
68       */
69      private JSplitPaneWithZeroSizeDivider networkSplitPane;
70  
71      /**
72       * Console for binary representation of characters found with blind/time injection.
73       */
74      private JTextArea binaryTextArea;
75  
76      private final TabConsoles tabConsoles = new TabConsoles();
77      private TabbedPaneNetworkTab tabbedPaneNetworkTab;
78      private NetworkTable networkTable;
79      
80      private final BasicArrowButton buttonShowNorth = new BasicArrowButton(SwingConstants.NORTH);
81      private int location = 0;
82      
83      /**
84       * Create panel at the bottom with different consoles to report injection process.
85       */
86      public PanelConsoles() {
87          
88          this.javaTextPane.getProxy().setEditable(false);
89          JTextPaneAppender.register(this.javaTextPane);
90          
91          this.initializeSplit();
92  
93          MediatorHelper.register(this.tabConsoles);
94          this.initializeTabsConsoles();
95  
96          this.setLayout(new OverlayLayout(this));
97  
98          JPanel expandPanel = this.initializeExpandPanel();
99          
100         this.add(expandPanel);
101         this.add(this.tabConsoles);
102 
103         // Do Overlay
104         expandPanel.setAlignmentX(FlowLayout.TRAILING);
105         expandPanel.setAlignmentY(Component.TOP_ALIGNMENT);
106         this.tabConsoles.setAlignmentX(FlowLayout.LEADING);
107         this.tabConsoles.setAlignmentY(Component.TOP_ALIGNMENT);
108     }
109 
110     private void initializeSplit() {
111         
112         this.networkSplitPane = new JSplitPaneWithZeroSizeDivider(JSplitPane.HORIZONTAL_SPLIT);
113         
114         this.networkSplitPane.setResizeWeight(1);
115         this.networkSplitPane.setDividerSize(0);
116         this.networkSplitPane.setDividerLocation(600);
117         this.networkSplitPane.setBorder(BorderFactory.createEmptyBorder());
118         
119         this.tabbedPaneNetworkTab = new TabbedPaneNetworkTab();
120         
121         this.networkSplitPane.setRightComponent(this.tabbedPaneNetworkTab);
122         
123         this.networkTable = new NetworkTable(this.tabbedPaneNetworkTab);
124         
125         JScrollIndicator scrollerNetwork = this.initializeScrollerTable();
126         this.networkSplitPane.setLeftComponent(scrollerNetwork);
127     }
128 
129     private JScrollIndicator initializeScrollerTable() {
130         
131         var scrollerNetwork = new JScrollIndicator(this.networkTable, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
132         scrollerNetwork.getScrollPane().setBorder(BorderFactory.createEmptyBorder(0, 0, -1, -1));
133         scrollerNetwork.getScrollPane().setViewportBorder(BorderFactory.createEmptyBorder(0, 0, -1, -1));
134         
135         AdjustmentListener singleItemScroll = adjustmentEvent -> {
136             
137             // The user scrolled the List (using the bar, mouse wheel or something else):
138             if (adjustmentEvent.getAdjustmentType() == AdjustmentEvent.TRACK) {
139                 
140                 // Jump to the next "block" (which is a row".
141                 adjustmentEvent.getAdjustable().setBlockIncrement(100);
142                 adjustmentEvent.getAdjustable().setUnitIncrement(100);
143             }
144         };
145 
146         scrollerNetwork.getScrollPane().getVerticalScrollBar().addAdjustmentListener(singleItemScroll);
147         scrollerNetwork.getScrollPane().getHorizontalScrollBar().addAdjustmentListener(singleItemScroll);
148         
149         return scrollerNetwork;
150     }
151 
152     private void initializeTabsConsoles() {
153         
154         this.chunkTextArea = new JPopupTextArea(new JTextAreaPlaceholderConsole("Raw data extracted during injection")).getProxy();
155         this.chunkTextArea.setEditable(false);
156         
157         this.binaryTextArea = new JPopupTextArea(new JTextAreaPlaceholderConsole("Characters extracted during blind or time injection")).getProxy();
158         this.binaryTextArea.setEditable(false);
159 
160         this.chunkTextArea.setLineWrap(true);
161         this.binaryTextArea.setLineWrap(true);
162         
163         var consoleTextPane = new SimpleConsoleAdapter("Console", "Event logging");
164         
165         // Object creation after customization
166         consoleTextPane.getProxy().setEditable(false);
167 
168         JTextPaneAppender.register(consoleTextPane);
169         
170         this.tabConsoles.setUI(new CustomMetalTabbedPaneUI() {
171             
172             @Override protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
173                 return Math.max(80, super.calculateTabWidth(tabPlacement, tabIndex, metrics));
174             }
175         });
176         
177         this.tabConsoles.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
178 
179         this.buildI18nTab(
180             "CONSOLE_MAIN_LABEL",
181             "CONSOLE_MAIN_TOOLTIP",
182             UiUtil.ICON_CONSOLE,
183             new LightScrollPane(1, 0, 0, 0, consoleTextPane.getProxy()),
184             0
185         );
186 
187         // Order is important
188         var preferences = Preferences.userRoot().node(InjectionModel.class.getName());
189         if (preferences.getBoolean(UiUtil.JAVA_VISIBLE, false)) {
190             this.insertJavaTab();
191         }
192         
193         if (preferences.getBoolean(UiUtil.NETWORK_VISIBLE, true)) {
194             this.insertNetworkTab();
195         }
196         
197         if (preferences.getBoolean(UiUtil.CHUNK_VISIBLE, true)) {
198             this.insertChunkTab();
199         }
200         
201         if (preferences.getBoolean(UiUtil.BINARY_VISIBLE, true)) {
202             this.insertBooleanTab();
203         }
204 
205         // Reset Font when tab is selected
206         this.tabConsoles.addChangeListener(changeEvent -> {
207             
208             JTabbedPane tabs = this.tabConsoles;
209             
210             if (tabs.getSelectedIndex() > -1) {
211                 
212                 var currentTabHeader = tabs.getTabComponentAt(tabs.getSelectedIndex());
213                 
214                 if (currentTabHeader != null) {
215                     
216                     currentTabHeader.setFont(currentTabHeader.getFont().deriveFont(Font.PLAIN));
217                     currentTabHeader.setForeground(Color.BLACK);
218                 }
219             }
220         });
221     }
222 
223     private JPanel initializeExpandPanel() {
224 
225         var buttonShowSouth = new BasicArrowButton(SwingConstants.SOUTH);
226         buttonShowSouth.setName("buttonShowSouth");
227 
228         buttonShowSouth.setBorderPainted(false);
229         buttonShowSouth.setPreferredSize(new Dimension(buttonShowSouth.getPreferredSize().width, buttonShowSouth.getPreferredSize().height));
230         buttonShowSouth.setMaximumSize(buttonShowSouth.getPreferredSize());
231         buttonShowSouth.setOpaque(false);
232         buttonShowSouth.setBorder(BorderFactory.createEmptyBorder());
233         buttonShowSouth.addActionListener(SplitHorizontalTopBottom.getActionHideShowConsole());
234         
235         this.buttonShowNorth.setBorderPainted(false);
236         this.buttonShowNorth.setPreferredSize(new Dimension(this.buttonShowNorth.getPreferredSize().width, this.buttonShowNorth.getPreferredSize().height));
237         this.buttonShowNorth.setMaximumSize(this.buttonShowNorth.getPreferredSize());
238         this.buttonShowNorth.setOpaque(false);
239         this.buttonShowNorth.setBorder(BorderFactory.createEmptyBorder());
240         this.buttonShowNorth.addActionListener(SplitHorizontalTopBottom.getActionHideShowResult());
241         this.buttonShowNorth.setName("buttonShowNorth");
242 
243         var arrowDownPanel = new JPanel();
244         arrowDownPanel.setLayout(new BorderLayout());
245         arrowDownPanel.setOpaque(false);
246         
247         // Disable overlap with zerosizesplitter
248         arrowDownPanel.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
249         arrowDownPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 26));
250         arrowDownPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 26));
251 
252         var panelExpander = new JPanel(new BorderLayout());
253         panelExpander.setBorder(BorderFactory.createEmptyBorder());
254         panelExpander.add(buttonShowSouth, BorderLayout.LINE_END);
255         panelExpander.add(this.buttonShowNorth, BorderLayout.LINE_START);
256         arrowDownPanel.add(panelExpander, BorderLayout.LINE_END);
257         
258         return arrowDownPanel;
259     }
260 
261     public void reset() {
262         
263         this.networkTable.getListHttpHeader().clear();
264         
265         // Empty infos tabs
266         this.getChunkTab().setText(StringUtils.EMPTY);
267         this.getBinaryTab().setText(StringUtils.EMPTY);
268         
269         // Fix #4657, Fix #1860: Multiple Exceptions on setRowCount()
270         try {
271             ((DefaultTableModel) this.networkTable.getModel()).setRowCount(0);
272         } catch(NullPointerException | ArrayIndexOutOfBoundsException e) {
273             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
274         }
275         
276         this.javaTextPane.getProxy().setText(StringUtils.EMPTY);
277         
278         this.tabbedPaneNetworkTab.reset();
279     }
280 
281     /**
282      * Add Chunk console to bottom panel.
283      */
284     public void insertChunkTab() {
285         this.buildI18nTab(
286             "CONSOLE_CHUNK_LABEL",
287             "CONSOLE_CHUNK_TOOLTIP",
288             UiUtil.ICON_CHUNK,
289             new LightScrollPane(1, 0, 0, 0, PanelConsoles.this.chunkTextArea),
290             1
291         );
292     }
293 
294     /**
295      * Add Binary console to bottom panel.
296      */
297     public void insertBooleanTab() {
298         this.buildI18nTab(
299             "CONSOLE_BINARY_LABEL",
300             "CONSOLE_BINARY_TOOLTIP",
301             UiUtil.ICON_BINARY,
302             new LightScrollPane(1, 0, 0, 0, PanelConsoles.this.binaryTextArea),
303             1 + (MediatorHelper.menubar().getChunkMenu().isSelected() ? 1 : 0)
304         );
305     }
306 
307     /**
308      * Add Network tab to bottom panel.
309      */
310     public void insertNetworkTab() {
311         this.buildI18nTab(
312             "CONSOLE_NETWORK_LABEL",
313             "CONSOLE_NETWORK_TOOLTIP",
314             UiUtil.ICON_HEADER,
315             new LightScrollPane(1, 0, 0, 0, PanelConsoles.this.networkSplitPane),
316             this.tabConsoles.getTabCount() - (MediatorHelper.menubar().getJavaDebugMenu().isSelected() ? 1 : 0)
317         );
318     }
319 
320     /**
321      * Add Java console to bottom panel.
322      */
323     public void insertJavaTab() {
324         this.buildI18nTab(
325             "CONSOLE_JAVA_LABEL",
326             "CONSOLE_JAVA_TOOLTIP",
327             UiUtil.ICON_CUP,
328             new LightScrollPane(1, 0, 0, 0, this.javaTextPane.getProxy()),
329             this.tabConsoles.getTabCount()
330         );
331     }
332     
333     private void buildI18nTab(String keyLabel, String keyTooltip, Icon icon, Component manager, int position) {
334         
335         final var refJToolTipI18n = new JToolTipI18n[]{ new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip)) };
336         
337         var labelTab = new JLabel(I18nViewUtil.valueByKey(keyLabel), icon, SwingConstants.CENTER) {
338             @Override
339             public JToolTip createToolTip() {
340                 
341                 JToolTipI18n tipI18n = new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip));
342                 refJToolTipI18n[0] = tipI18n;
343                 
344                 return tipI18n;
345             }
346         };
347         
348         labelTab.setName(keyLabel);
349         labelTab.addMouseListener(new MouseAdapter() {
350             @Override
351             public void mousePressed(MouseEvent event) {
352                 
353                 // Fix #90428: IllegalArgumentException in setSelectedComponent()
354                 // ArrayIndexOutOfBoundsException #92973 on setSelectedComponent()
355                 try {
356                     PanelConsoles.this.tabConsoles.setSelectedComponent(manager);
357                 } catch (IllegalArgumentException e) {
358                     LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
359                 }
360                 
361                 super.mousePressed(event);
362             }
363         });
364         
365         this.tabConsoles.insertTab(I18nViewUtil.valueByKey(keyLabel), icon, manager, null, position);
366         this.tabConsoles.setTabComponentAt(
367             this.tabConsoles.indexOfTab(I18nViewUtil.valueByKey(keyLabel)),
368             labelTab
369         );
370         
371         I18nViewUtil.addComponentForKey(keyLabel, labelTab);
372         I18nViewUtil.addComponentForKey(keyTooltip, refJToolTipI18n[0]);
373         labelTab.setToolTipText(I18nViewUtil.valueByKey(keyTooltip));
374     }
375     
376     public void messageChunk(String text) {
377         try {
378             this.chunkTextArea.append(text +"\n");
379             this.chunkTextArea.setCaretPosition(this.chunkTextArea.getDocument().getLength());
380             
381         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
382             // Fix #67063: NullPointerException on chunkTab.append()
383             // Fix #4770 on chunkTab.append()
384             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
385         }
386     }
387     
388     public void messageBinary(String text) {
389         try {
390             this.binaryTextArea.append(
391                 String.format("\t%s", text)
392             );
393             this.binaryTextArea.setCaretPosition(this.binaryTextArea.getDocument().getLength());
394         
395         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
396             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
397         }
398     }
399     
400     
401     // Getter and setter
402 
403     public JTextArea getChunkTab() {
404         return this.chunkTextArea;
405     }
406 
407     public JSplitPaneWithZeroSizeDivider getNetworkSplitPane() {
408         return this.networkSplitPane;
409     }
410 
411     public JTextArea getBinaryTab() {
412         return this.binaryTextArea;
413     }
414 
415     public int getDividerLocation() {
416         return this.location;
417     }
418 
419     public void setDividerLocation(int location) {
420         this.location = location;
421     }
422 
423     public BasicArrowButton getButtonShowNorth() {
424         return this.buttonShowNorth;
425     }
426 
427     public NetworkTable getNetworkTable() {
428         return this.networkTable;
429     }
430 }