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      public 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          this.setLayout(new BorderLayout());
86          I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_JAVA_TOOLTIP, this.javaTextPane.getProxy());
87          this.javaTextPane.getProxy().setEditable(false);
88          JTextPaneAppender.registerJavaConsole(this.javaTextPane);
89  
90          this.initSplit();
91  
92          MediatorHelper.register(this.tabConsoles);
93          this.initTabsConsoles();
94  
95          JPanel expandPanel = this.initExpandPanel();
96          this.tabConsoles.putClientProperty("JTabbedPane.trailingComponent", expandPanel);
97          this.add(this.tabConsoles);
98      }
99  
100     private void initSplit() {
101         this.networkSplitPane = new JSplitPaneWithZeroSizeDivider(JSplitPane.HORIZONTAL_SPLIT);
102         this.networkSplitPane.setDividerLocation(600);
103 
104         this.tabbedPaneNetworkTab = new TabbedPaneNetworkTab();
105         this.networkSplitPane.setRightComponent(this.tabbedPaneNetworkTab);
106         this.networkTable = new NetworkTable(this.tabbedPaneNetworkTab);
107 
108         JPanel panelTable = new JPanel(new BorderLayout());  // required for correct scroll placement
109         panelTable.add(new JScrollPane(this.networkTable), BorderLayout.CENTER);
110         this.networkSplitPane.setLeftComponent(panelTable);
111     }
112 
113     private void initTabsConsoles() {
114         var proxyChunk = new JTextAreaPlaceholderConsole(I18nUtil.valueByKey(PanelConsoles.CONSOLE_CHUNK_TOOLTIP));
115         this.chunkTextArea = new JPopupTextArea(proxyChunk).getProxy();
116         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_CHUNK_TOOLTIP, proxyChunk);
117         this.chunkTextArea.setLineWrap(true);
118         this.chunkTextArea.setEditable(false);
119 
120         var proxyBinary = new JTextAreaPlaceholderConsole(I18nUtil.valueByKey(PanelConsoles.CONSOLE_BINARY_TOOLTIP));
121         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_BINARY_TOOLTIP, proxyBinary);
122         this.binaryTextArea = new JPopupTextArea(proxyBinary).getProxy();
123         this.binaryTextArea.setLineWrap(true);
124         this.binaryTextArea.setEditable(false);
125 
126         var consoleTextPane = new SimpleConsoleAdapter("Console", I18nUtil.valueByKey(PanelConsoles.CONSOLE_MAIN_TOOLTIP));
127         I18nViewUtil.addComponentForKey(PanelConsoles.CONSOLE_MAIN_TOOLTIP, consoleTextPane.getProxy());
128         consoleTextPane.getProxy().setEditable(false);
129         JTextPaneAppender.register(consoleTextPane);
130 
131         this.buildI18nTab(
132             "CONSOLE_MAIN_LABEL",
133             PanelConsoles.CONSOLE_MAIN_TOOLTIP,
134             UiUtil.CONSOLE.getIcon(),
135             new JScrollPane(consoleTextPane.getProxy()),
136             0
137         );
138 
139         var preferences = Preferences.userRoot().node(InjectionModel.class.getName());  // Order is important
140         if (preferences.getBoolean(PreferencesUtil.JAVA_VISIBLE, false)) {
141             this.insertJavaTab();
142         }
143         if (preferences.getBoolean(PreferencesUtil.NETWORK_VISIBLE, true)) {
144             this.insertNetworkTab();
145         }
146         if (preferences.getBoolean(PreferencesUtil.CHUNK_VISIBLE, true)) {
147             this.insertChunkTab();
148         }
149         if (preferences.getBoolean(PreferencesUtil.BINARY_VISIBLE, true)) {
150             this.insertBooleanTab();
151         }
152 
153         this.tabConsoles.addMouseListener(new MouseAdapter() {
154             @Override
155             public void mousePressed(MouseEvent e) {
156                 int tabIndex = PanelConsoles.this.tabConsoles.indexAtLocation(e.getX(), e.getY());
157                 if (tabIndex == -1 && e.getButton() == MouseEvent.BUTTON2) {  // middle click on header with no tab
158                     SplitNS.getActionHideShowConsole().actionPerformed(null);
159                 }
160             }
161         });
162         this.tabConsoles.addChangeListener(changeEvent -> {  // Reset Font when tab is selected
163             JTabbedPane tabs = this.tabConsoles;
164             if (tabs.getSelectedIndex() > -1) {
165                 var currentTabHeader = tabs.getTabComponentAt(tabs.getSelectedIndex());
166                 if (currentTabHeader != null) {
167                     currentTabHeader.setFont(currentTabHeader.getFont().deriveFont(Font.PLAIN));
168                     currentTabHeader.setForeground(UIManager.getColor("TabbedPane.foreground"));
169                 }
170             }
171         });
172     }
173 
174     private JPanel initExpandPanel() {
175         var labelShowSouth = new JLabel(UiUtil.ARROW_DOWN.getIcon());
176         labelShowSouth.setName("buttonShowSouth");
177         labelShowSouth.addMouseListener(new MouseAdapter() {
178             @Override
179             public void mouseClicked(MouseEvent e) {
180                 SplitNS.getActionHideShowConsole().actionPerformed(null);
181             }
182         });
183         
184         this.labelShowNorth.setName("buttonShowNorth");
185         this.labelShowNorth.addMouseListener(new MouseAdapter() {
186             @Override
187             public void mouseClicked(MouseEvent e) {
188                 SplitNS.getActionHideShowResult().actionPerformed(null);
189             }
190         });
191 
192         var panelExpander = new JPanel();
193         panelExpander.setLayout(new BoxLayout(panelExpander, BoxLayout.LINE_AXIS));
194         panelExpander.add(Box.createGlue());
195         panelExpander.add(this.labelShowNorth);
196         panelExpander.add(labelShowSouth);
197         return panelExpander;
198     }
199 
200     public void reset() {
201         // Empty infos tabs
202         this.chunkTextArea.setText(StringUtils.EMPTY);
203         this.binaryTextArea.setText(StringUtils.EMPTY);
204         this.javaTextPane.getProxy().setText(StringUtils.EMPTY);
205 
206         this.networkTable.getListHttpHeader().clear();
207         // Fix #4657, Fix #1860: Multiple Exceptions on setRowCount()
208         try {
209             ((DefaultTableModel) this.networkTable.getModel()).setRowCount(0);
210         } catch(NullPointerException | ArrayIndexOutOfBoundsException e) {
211             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
212         }
213 
214         this.tabbedPaneNetworkTab.reset();
215     }
216 
217     /**
218      * Add Chunk console to bottom panel.
219      */
220     public void insertChunkTab() {
221         this.buildI18nTab(
222             "CONSOLE_CHUNK_LABEL",
223             PanelConsoles.CONSOLE_CHUNK_TOOLTIP,
224             UiUtil.CHUNK.getIcon(),
225             new JScrollPane(this.chunkTextArea),
226             1
227         );
228     }
229 
230     /**
231      * Add Binary console to bottom panel.
232      */
233     public void insertBooleanTab() {
234         var positionFromChunk = this.tabConsoles.indexOfTab(UiUtil.CHUNK.getIcon()) != -1 ? 1 : 0;
235         this.buildI18nTab(
236             "CONSOLE_BINARY_LABEL",
237             PanelConsoles.CONSOLE_BINARY_TOOLTIP,
238             UiUtil.BINARY.getIcon(),
239             new JScrollPane(this.binaryTextArea),
240             1 + positionFromChunk
241         );
242     }
243 
244     /**
245      * Add Network tab to bottom panel.
246      */
247     public void insertNetworkTab() {
248         var positionFromJava = this.tabConsoles.indexOfTab(UiUtil.CUP.getIcon()) != -1 ? 1 : 0;
249         this.buildI18nTab(
250             "CONSOLE_NETWORK_LABEL",
251             "CONSOLE_NETWORK_TOOLTIP",
252             UiUtil.NETWORK.getIcon(),
253             this.networkSplitPane,  // no scroller on split, instead managed by scrollers on network table and tabs
254             this.tabConsoles.getTabCount() - positionFromJava
255         );
256     }
257 
258     /**
259      * Add Java console to bottom panel.
260      */
261     public void insertJavaTab() {
262         this.buildI18nTab(
263             "CONSOLE_JAVA_LABEL",
264             PanelConsoles.CONSOLE_JAVA_TOOLTIP,
265             UiUtil.CUP.getIcon(),
266             new JScrollPane(this.javaTextPane.getProxy()),
267             this.tabConsoles.getTabCount()
268         );
269     }
270     
271     private void buildI18nTab(String keyLabel, String keyTooltip, Icon icon, Component manager, int position) {
272         var refJToolTipI18n = new AtomicReference<>(new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip)));
273         
274         var labelTab = new JLabel(I18nViewUtil.valueByKey(keyLabel), icon, SwingConstants.CENTER) {
275             @Override
276             public JToolTip createToolTip() {
277                 refJToolTipI18n.set(new JToolTipI18n(I18nViewUtil.valueByKey(keyTooltip)));
278                 return refJToolTipI18n.get();
279             }
280         };
281         
282         labelTab.setName(keyLabel);
283         labelTab.addMouseListener(new MouseAdapter() {
284             @Override
285             public void mousePressed(MouseEvent event) {
286                 // Fix #90428: IllegalArgumentException
287                 // Fix #92973: ArrayIndexOutOfBoundsException
288                 try {
289                     PanelConsoles.this.tabConsoles.setSelectedComponent(manager);
290                 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
291                     LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
292                 }
293             }
294         });
295         
296         this.tabConsoles.insertTab(I18nViewUtil.valueByKey(keyLabel), icon, manager, null, position);
297         this.tabConsoles.setTabComponentAt(this.tabConsoles.indexOfTab(I18nViewUtil.valueByKey(keyLabel)), labelTab);
298         
299         I18nViewUtil.addComponentForKey(keyLabel, labelTab);
300         I18nViewUtil.addComponentForKey(keyTooltip, refJToolTipI18n.get());
301         labelTab.setToolTipText(I18nViewUtil.valueByKey(keyTooltip));
302     }
303     
304     public void messageChunk(String text) {
305         try {
306             this.chunkTextArea.append(text +"\n");
307             this.chunkTextArea.setCaretPosition(this.chunkTextArea.getDocument().getLength());
308         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
309             // Fix #67063: NullPointerException on chunkTab.append()
310             // Fix #4770 on chunkTab.append()
311             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
312         }
313     }
314     
315     public void messageBinary(String text) {
316         try {
317             this.binaryTextArea.append(String.format("\t%s", text));
318             this.binaryTextArea.setCaretPosition(this.binaryTextArea.getDocument().getLength());
319         } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
320             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
321         }
322     }
323     
324     
325     // Getter and setter
326 
327     public int getDividerLocation() {
328         return this.dividerLocation;
329     }
330 
331     public void setDividerLocation(int location) {
332         this.dividerLocation = location;
333     }
334 
335     public JLabel getLabelShowNorth() {
336         return this.labelShowNorth;
337     }
338 
339     public NetworkTable getNetworkTable() {
340         return this.networkTable;
341     }
342 
343     public TabbedPaneNetworkTab getTabbedPaneNetworkTab() {
344         return this.tabbedPaneNetworkTab;
345     }
346 }