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.terminal;
12  
13  import com.jsql.util.LogLevelUtil;
14  import com.jsql.view.swing.util.MediatorHelper;
15  import com.jsql.view.swing.util.UiUtil;
16  import org.apache.commons.lang3.StringUtils;
17  import org.apache.logging.log4j.LogManager;
18  import org.apache.logging.log4j.Logger;
19  
20  import javax.swing.*;
21  import javax.swing.text.BadLocationException;
22  import javax.swing.text.Style;
23  import javax.swing.text.StyleConstants;
24  import java.awt.*;
25  import java.awt.event.MouseMotionListener;
26  import java.net.MalformedURLException;
27  import java.net.URI;
28  import java.net.URISyntaxException;
29  import java.net.URL;
30  import java.util.UUID;
31  import java.util.concurrent.atomic.AtomicBoolean;
32  
33  /**
34   * A Terminal completely built from swing text pane.
35   */
36  public abstract class AbstractExploit extends JTextPane {
37      
38      /**
39       * Log4j logger sent to view.
40       */
41      private static final Logger LOGGER = LogManager.getRootLogger();
42  
43      /**
44       * True if terminal is processing command.
45       */
46      private final AtomicBoolean isEdited = new AtomicBoolean(false);
47  
48      /**
49       * Server name or IP to display on prompt.
50       */
51      private final String host;
52  
53      /**
54       * User and password for database.
55       */
56      protected String[] loginPassword = null;
57      private final UUID uuidShell;
58      private final String urlShell;
59  
60      /**
61       * Style used for coloring text.
62       */
63      private final transient Style style = this.addStyle("Necrophagist's next album is 2014.", null);
64  
65      /**
66       * Length of prompt.
67       */
68      private String prompt = StringUtils.EMPTY;
69  
70      /**
71       * Text to display next caret.
72       */
73      private final String labelShell;
74      
75      /**
76       * Build a shell instance.
77       * @param uuidShell Unique identifier to discriminate beyond multiple opened terminals
78       * @param urlShell URL of current shell
79       * @param labelShell Type of shell to display on prompt
80       */
81      protected AbstractExploit(UUID uuidShell, String urlShell, String labelShell) throws MalformedURLException, URISyntaxException {
82          this.uuidShell = uuidShell;
83          this.urlShell = urlShell;
84          this.labelShell = labelShell;
85  
86          URL url;
87          if (StringUtils.isEmpty(urlShell)) {  // udf
88              url = new URI(MediatorHelper.model().getMediatorUtils().getConnectionUtil().getUrlByUser()).toURL();
89          } else {
90              url = new URI(urlShell).toURL();
91          }
92          this.host = url.getHost();
93  
94          this.setFont(new Font(UiUtil.FONT_NAME_MONO_NON_ASIAN, Font.PLAIN, UIManager.getFont("TextArea.font").getSize()));
95          this.setCaret(new BlockCaret());
96          this.setBackground(Color.BLACK);
97          this.setForeground(Color.LIGHT_GRAY);
98  
99          this.displayPrompt(true);
100 
101         this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
102         this.setTransferHandler(null);
103         this.setHighlighter(null);
104 
105         this.addMouseListener(new EmptyFocusCopy(this));
106         this.addKeyListener(new KeyAdapterTerminal(this));
107     }
108 
109     /**
110      * Run when cmd is validated.
111      * @param cmd Command to execute
112      * @param terminalID Unique ID for terminal instance
113      * @param wbhPath URL of shell
114      * @param arg Additional parameters (User and password for SQLShell)
115      */
116     public abstract void action(String cmd, UUID terminalID, String wbhPath, String... arg);
117     
118     /**
119      * Update terminal and use default behavior.
120      */
121     public void reset() {
122         this.isEdited.set(false);
123         this.setEditable(true);
124         this.displayPrompt(false);
125         this.setCaretPosition(this.getDocument().getLength());
126         this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
127     }
128 
129     /**
130      * Add a text at the end of textpane.
131      * @param string Text to add
132      */
133     public void append(String string) {
134         try {
135             var doc = this.getDocument();
136             doc.insertString(doc.getLength(), string, null);
137         } catch (BadLocationException e) {
138             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
139         }
140     }
141     
142     /**
143      * Append prompt to textpane, measure prompt the first time is used.
144      * @param isAddingPrompt Should we measure prompt length?
145      */
146     public void displayPrompt(boolean isAddingPrompt) {
147         StyleConstants.setUnderline(this.style, true);
148         this.appendPrompt("jsql", Color.LIGHT_GRAY, isAddingPrompt);
149         StyleConstants.setUnderline(this.style, false);
150 
151         this.appendPrompt(StringUtils.SPACE + this.labelShell, Color.LIGHT_GRAY, isAddingPrompt);
152         this.appendPrompt("[", new Color(0x32BF32), isAddingPrompt);
153         this.appendPrompt(this.host, new Color(0xBFBF19), isAddingPrompt);
154         this.appendPrompt("]", new Color(0x32BF32), isAddingPrompt);
155         this.appendPrompt(" >", new Color(0xBF6464), isAddingPrompt);
156         this.appendPrompt(StringUtils.SPACE, Color.LIGHT_GRAY, isAddingPrompt);
157     }
158 
159     /**
160      * Add a colored string to the textpane, measure prompt at the same time.
161      * @param string Text to append
162      * @param color Color of text
163      * @param isAddingPrompt Should we measure prompt length?
164      */
165     private void appendPrompt(String string, Color color, boolean isAddingPrompt) {
166         try {
167             StyleConstants.setForeground(this.style, color);
168             this.getStyledDocument().insertString(this.getStyledDocument().getLength(), string, this.style);
169             if (isAddingPrompt) {
170                 this.prompt += string;
171             }
172         } catch (BadLocationException e) {
173             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
174         }
175     }
176 
177     /**
178      * NoWrap.
179      */
180     @Override
181     public boolean getScrollableTracksViewportWidth() {
182         return this.getUI().getPreferredSize(this).width <= this.getParent().getSize().width;
183     }
184 
185     /**
186      * Cancel every mouse movement processing like drag/drop.
187      */
188     @Override
189     public synchronized void addMouseMotionListener(MouseMotionListener l) {
190         // Do nothing
191     }
192 
193     /**
194      * Get index of line for current offset (generally cursor position).
195      * @param offset Position on the line
196      * @return Index of the line
197      */
198     public int getLineOfOffset(int offset) throws BadLocationException {
199         var errorMsg = "Can't translate offset to line";
200         var doc = this.getDocument();
201         
202         if (offset < 0) {
203             throw new BadLocationException(errorMsg, -1);
204         } else if (offset > doc.getLength()) {
205             throw new BadLocationException(errorMsg, doc.getLength() + 1);
206         } else {
207             var map = doc.getDefaultRootElement();
208             return map.getElementIndex(offset);
209         }
210     }
211 
212     /**
213      * Get position of the beginning of the line.
214      * @param line Index of the line
215      * @return Offset of line
216      */
217     public int getLineStartOffset(int line) throws BadLocationException {
218         var map = this.getDocument().getDefaultRootElement();
219         
220         if (line < 0) {
221             throw new BadLocationException("Negative line", -1);
222         } else if (line >= map.getElementCount()) {
223             throw new BadLocationException("No such line", this.getDocument().getLength() + 1);
224         } else {
225             var lineElem = map.getElement(line);
226             return lineElem.getStartOffset();
227         }
228     }
229 
230     
231     // Getter and setter
232     
233     public AtomicBoolean getIsEdited() {
234         return this.isEdited;
235     }
236 
237     public UUID getUuidShell() {
238         return this.uuidShell;
239     }
240 
241     public String getUrlShell() {
242         return this.urlShell;
243     }
244 
245     public String getPrompt() {
246         return this.prompt;
247     }
248 }