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