View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
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 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.I18nUtil;
15  import com.jsql.util.LogLevelUtil;
16  import com.jsql.util.PreferencesUtil;
17  import com.jsql.view.swing.console.JTextPaneAppender;
18  import com.jsql.view.swing.console.SimpleConsoleAdapter;
19  import com.jsql.view.swing.panel.consoles.NetworkTable;
20  import com.jsql.view.swing.panel.consoles.TabbedPaneNetworkTab;
21  import com.jsql.view.swing.panel.split.SplitNS;
22  import com.jsql.view.swing.tab.TabbedPaneWheeled;
23  import com.jsql.view.swing.text.JPopupTextArea;
24  import com.jsql.view.swing.text.JTextAreaPlaceholderConsole;
25  import com.jsql.view.swing.text.JToolTipI18n;
26  import com.jsql.view.swing.util.I18nViewUtil;
27  import com.jsql.view.swing.util.JSplitPaneWithZeroSizeDivider;
28  import com.jsql.view.swing.util.MediatorHelper;
29  import com.jsql.view.swing.util.UiUtil;
30  import org.apache.commons.lang3.StringUtils;
31  import org.apache.logging.log4j.LogManager;
32  import org.apache.logging.log4j.Logger;
33  
34  import javax.swing.*;
35  import javax.swing.table.DefaultTableModel;
36  import java.awt.*;
37  import java.awt.event.MouseAdapter;
38  import java.awt.event.MouseEvent;
39  import java.util.concurrent.atomic.AtomicReference;
40  import java.util.prefs.Preferences;
41  
42  /**
43   * A panel with different consoles displayed on the bottom.
44   */
45  public class PanelConsoles extends JPanel {
46  
47      private static final Logger LOGGER = LogManager.getRootLogger();
48  
49      public static final String CONSOLE_JAVA_TOOLTIP = "CONSOLE_JAVA_TOOLTIP";
50      public static final String CONSOLE_CHUNK_TOOLTIP = "CONSOLE_CHUNK_TOOLTIP";
51      public static final String CONSOLE_BINARY_TOOLTIP = "CONSOLE_BINARY_TOOLTIP";
52      public static final String CONSOLE_MAIN_TOOLTIP = "CONSOLE_MAIN_TOOLTIP";
53  
54      /**
55       * Console for java exception messages.
56       */
57      private final SimpleConsoleAdapter javaTextPane = new SimpleConsoleAdapter("Java", I18nUtil.valueByKey(PanelConsoles.CONSOLE_JAVA_TOOLTIP));
58      
59      /**
60       * Console for raw SQL results.
61       */
62      private JTextArea chunkTextArea;
63  
64      /**
65       * Panel displaying table of HTTP requests and responses.
66       */
67      private JSplitPane networkSplitPane;
68  
69      /**
70       * Console for binary representation of characters found with blind/time injection.
71       */
72      private JTextArea binaryTextArea;
73  
74      private final TabbedPaneWheeled tabConsoles = new TabbedPaneWheeled();
75      private TabbedPaneNetworkTab tabbedPaneNetworkTab;
76      private NetworkTable networkTable;
77      
78      private final JLabel labelShowNorth = new JLabel(UiUtil.ARROW_UP.getIcon());
79      private int dividerLocation = 0;
80      
81      /**
82       * Create panel at the bottom with different consoles to report injection process.
83       */
84      public PanelConsoles() {
85          I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_JAVA_TOOLTIP, this.javaTextPane.getProxy());
86          this.javaTextPane.getProxy().setEditable(false);
87          JTextPaneAppender.registerJavaConsole(this.javaTextPane);
88          
89          this.initSplit();
90  
91          MediatorHelper.register(this.tabConsoles);
92          this.initTabsConsoles();
93          this.setLayout(new BorderLayout());
94  
95          JPanel expandPanel = this.initExpandPanel();
96          this.tabConsoles.putClientProperty("JTabbedPane.trailingComponent", expandPanel);
97          this.add(this.tabConsoles);
98  
99          this.tabConsoles.setAlignmentX(FlowLayout.LEADING);
100     }
101 
102     private void initSplit() {
103         this.networkSplitPane = new JSplitPaneWithZeroSizeDivider(JSplitPane.HORIZONTAL_SPLIT);
104         this.networkSplitPane.setDividerLocation(600);
105         this.networkSplitPane.setPreferredSize(new Dimension(0,0));  // required for correct scroll placement
106 
107         this.tabbedPaneNetworkTab = new TabbedPaneNetworkTab();
108         this.networkSplitPane.setRightComponent(this.tabbedPaneNetworkTab);
109         this.networkTable = new NetworkTable(this.tabbedPaneNetworkTab);
110         
111         JPanel panelTable = new JPanel(new BorderLayout());  // required for correct scroll placement
112         panelTable.add(new JScrollPane(this.networkTable), BorderLayout.CENTER);
113         this.networkSplitPane.setLeftComponent(panelTable);
114     }
115 
116     private void initTabsConsoles() {
117         var proxyChunk = new JTextAreaPlaceholderConsole(I18nUtil.valueByKey(PanelConsoles.CONSOLE_CHUNK_TOOLTIP));
118         this.chunkTextArea = new JPopupTextArea(proxyChunk).getProxy();
119         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_CHUNK_TOOLTIP, proxyChunk);
120         this.chunkTextArea.setLineWrap(true);
121         this.chunkTextArea.setEditable(false);
122 
123         var proxyBinary = new JTextAreaPlaceholderConsole(I18nUtil.valueByKey(PanelConsoles.CONSOLE_BINARY_TOOLTIP));
124         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_BINARY_TOOLTIP, proxyBinary);
125         this.binaryTextArea = new JPopupTextArea(proxyBinary).getProxy();
126         this.binaryTextArea.setLineWrap(true);
127         this.binaryTextArea.setEditable(false);
128 
129         var consoleTextPane = new SimpleConsoleAdapter("Console", I18nUtil.valueByKey(PanelConsoles.CONSOLE_MAIN_TOOLTIP));
130         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_MAIN_TOOLTIP, consoleTextPane.getProxy());
131         consoleTextPane.getProxy().setEditable(false);
132         JTextPaneAppender.register(consoleTextPane);
133 
134         this.buildI18nTab(
135             "CONSOLE_MAIN_LABEL",
136             PanelConsoles.CONSOLE_MAIN_TOOLTIP,
137                 UiUtil.CONSOLE.getIcon(),
138             new JScrollPane(consoleTextPane.getProxy()),
139             0
140         );
141 
142         var preferences = Preferences.userRoot().node(InjectionModel.class.getName());  // Order is important
143         if (preferences.getBoolean(PreferencesUtil.JAVA_VISIBLE, false)) {
144             this.insertJavaTab();
145         }
146         if (preferences.getBoolean(PreferencesUtil.NETWORK_VISIBLE, true)) {
147             this.insertNetworkTab();
148         }
149         if (preferences.getBoolean(PreferencesUtil.CHUNK_VISIBLE, true)) {
150             this.insertChunkTab();
151         }
152         if (preferences.getBoolean(PreferencesUtil.BINARY_VISIBLE, true)) {
153             this.insertBooleanTab();
154         }
155 
156         this.tabConsoles.addMouseListener(new MouseAdapter() {
157             @Override
158             public void mousePressed(MouseEvent e) {
159                 int tabIndex = PanelConsoles.this.tabConsoles.indexAtLocation(e.getX(), e.getY());
160                 if (tabIndex == -1 && e.getButton() == MouseEvent.BUTTON2) {  // middle click on header with no tab
161                     SplitNS.getActionHideShowConsole().actionPerformed(null);
162                 }
163             }
164         });
165         this.tabConsoles.addChangeListener(changeEvent -> {  // Reset Font when tab is selected
166             JTabbedPane tabs = this.tabConsoles;
167             if (tabs.getSelectedIndex() > -1) {
168                 var currentTabHeader = tabs.getTabComponentAt(tabs.getSelectedIndex());
169                 if (currentTabHeader != null) {
170                     currentTabHeader.setFont(currentTabHeader.getFont().deriveFont(Font.PLAIN));
171                     currentTabHeader.setForeground(UIManager.getColor("TabbedPane.foreground"));
172                 }
173             }
174         });
175     }
176 
177     private JPanel initExpandPanel() {
178         var labelShowSouth = new JLabel(UiUtil.ARROW_DOWN.getIcon());
179         labelShowSouth.setName("buttonShowSouth");
180         labelShowSouth.addMouseListener(new MouseAdapter() {
181             @Override
182             public void mouseClicked(MouseEvent e) {
183                 SplitNS.getActionHideShowConsole().actionPerformed(null);
184             }
185         });
186         
187         this.labelShowNorth.setName("buttonShowNorth");
188         this.labelShowNorth.addMouseListener(new MouseAdapter() {
189             @Override
190             public void mouseClicked(MouseEvent e) {
191                 SplitNS.getActionHideShowResult().actionPerformed(null);
192             }
193         });
194 
195         var panelExpander = new JPanel();
196         panelExpander.setLayout(new BoxLayout(panelExpander, BoxLayout.X_AXIS));
197         panelExpander.add(Box.createGlue());
198         panelExpander.add(this.labelShowNorth);
199         panelExpander.add(labelShowSouth);
200         return panelExpander;
201     }
202 
203     public void reset() {
204         // Empty infos tabs
205         this.chunkTextArea.setText(StringUtils.EMPTY);
206         this.binaryTextArea.setText(StringUtils.EMPTY);
207         this.javaTextPane.getProxy().setText(StringUtils.EMPTY);
208 
209         this.networkTable.getListHttpHeader().clear();
210         // Fix #4657, Fix #1860: Multiple Exceptions on setRowCount()
211         try {
212             ((DefaultTableModel) this.networkTable.getModel()).setRowCount(0);
213         } catch(NullPointerException | ArrayIndexOutOfBoundsException e) {
214             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
215         }
216 
217         this.tabbedPaneNetworkTab.reset();
218     }
219 
220     /**
221      * Add Chunk console to bottom panel.
222      */
223     public void insertChunkTab() {
224         this.buildI18nTab(
225             "CONSOLE_CHUNK_LABEL",
226             PanelConsoles.CONSOLE_CHUNK_TOOLTIP,
227             UiUtil.CHUNK.getIcon(),
228             new JScrollPane(this.chunkTextArea),
229             1
230         );
231     }
232 
233     /**
234      * Add Binary console to bottom panel.
235      */
236     public void insertBooleanTab() {
237         var positionFromChunk = this.tabConsoles.indexOfTab(UiUtil.CHUNK.getIcon()) != -1 ? 1 : 0;
238         this.buildI18nTab(
239             "CONSOLE_BINARY_LABEL",
240             PanelConsoles.CONSOLE_BINARY_TOOLTIP,
241             UiUtil.BINARY.getIcon(),
242             new JScrollPane(this.binaryTextArea),
243             1 + positionFromChunk
244         );
245     }
246 
247     /**
248      * Add Network tab to bottom panel.
249      */
250     public void insertNetworkTab() {
251         var positionFromJava = this.tabConsoles.indexOfTab(UiUtil.CUP.getIcon()) != -1 ? 1 : 0;
252         this.buildI18nTab(
253             "CONSOLE_NETWORK_LABEL",
254             "CONSOLE_NETWORK_TOOLTIP",
255             UiUtil.NETWORK.getIcon(),
256             new JScrollPane(this.networkSplitPane),
257             this.tabConsoles.getTabCount() - positionFromJava
258         );
259     }
260 
261     /**
262      * Add Java console to bottom panel.
263      */
264     public void insertJavaTab() {
265         this.buildI18nTab(
266             "CONSOLE_JAVA_LABEL",
267             PanelConsoles.CONSOLE_JAVA_TOOLTIP,
268             UiUtil.CUP.getIcon(),
269             new JScrollPane(this.javaTextPane.getProxy()),
270             this.tabConsoles.getTabCount()
271         );
272     }
273     
274     private void buildI18nTab(String keyLabel, String keyTooltip, Icon icon, Component manager, int position) {
275         var refJToolTipI18n = new AtomicReference<>(new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip)));
276         
277         var labelTab = new JLabel(I18nViewUtil.valueByKey(keyLabel), icon, SwingConstants.CENTER) {
278             @Override
279             public JToolTip createToolTip() {
280                 refJToolTipI18n.set(new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip)));
281                 return refJToolTipI18n.get();
282             }
283         };
284         
285         labelTab.setName(keyLabel);
286         labelTab.addMouseListener(new MouseAdapter() {
287             @Override
288             public void mousePressed(MouseEvent event) {
289                 // Fix #90428: IllegalArgumentException
290                 // Fix #92973: ArrayIndexOutOfBoundsException
291                 try {
292                     PanelConsoles.this.tabConsoles.setSelectedComponent(manager);
293                 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
294                     LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
295                 }
296             }
297         });
298         
299         this.tabConsoles.insertTab(I18nViewUtil.valueByKey(keyLabel), icon, manager, null, position);
300         this.tabConsoles.setTabComponentAt(this.tabConsoles.indexOfTab(I18nViewUtil.valueByKey(keyLabel)), labelTab);
301         
302         I18nViewUtil.addComponentForKey(keyLabel, labelTab);
303         I18nViewUtil.addComponentForKey(keyTooltip, refJToolTipI18n.get());
304         labelTab.setToolTipText(I18nViewUtil.valueByKey(keyTooltip));
305     }
306     
307     public void messageChunk(String text) {
308         try {
309             this.chunkTextArea.append(text +"\n");
310             this.chunkTextArea.setCaretPosition(this.chunkTextArea.getDocument().getLength());
311         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
312             // Fix #67063: NullPointerException on chunkTab.append()
313             // Fix #4770 on chunkTab.append()
314             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
315         }
316     }
317     
318     public void messageBinary(String text) {
319         try {
320             this.binaryTextArea.append(String.format("\t%s", text));
321             this.binaryTextArea.setCaretPosition(this.binaryTextArea.getDocument().getLength());
322         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
323             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
324         }
325     }
326     
327     
328     // Getter and setter
329 
330     public int getDividerLocation() {
331         return this.dividerLocation;
332     }
333 
334     public void setDividerLocation(int location) {
335         this.dividerLocation = location;
336     }
337 
338     public JLabel getLabelShowNorth() {
339         return this.labelShowNorth;
340     }
341 
342     public NetworkTable getNetworkTable() {
343         return this.networkTable;
344     }
345 
346     public TabbedPaneNetworkTab getTabbedPaneNetworkTab() {
347         return this.tabbedPaneNetworkTab;
348     }
349 }