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