1- from javax .swing import JTabbedPane , JPanel , JButton , JLabel , SwingConstants , JOptionPane , GroupLayout , JCheckBox , JSplitPane
1+ from javax .swing import JTabbedPane , JPanel , JButton , JLabel , SwingConstants , JOptionPane , GroupLayout , JCheckBox , JSplitPane , JRadioButton , ButtonGroup , JFileChooser
22from javax .swing .event import ChangeListener , DocumentListener
33from javax .swing .LayoutStyle .ComponentPlacement import RELATED , UNRELATED
44from java .awt import BorderLayout , Font , Component
55from java .beans import PropertyChangeListener
66from org .python .core .util import StringUtil
7+ from burp import IExtensionStateListener
78from uicomponents import BurpUI , TabComponent , TabComponentEditableTabMixin , TabComponentCloseableMixin , TabComponentCloseListener , TabComponentTitleChangedListener
89from models import ObservableCollection , Script
910from utils import EditorFileAdapter
@@ -118,6 +119,7 @@ def __init__(self, callbacks, script):
118119 self .scriptEditor = callbacks .createTextEditor ()
119120 self .scriptEditor .text = script .content
120121 self .scriptText = self .scriptEditor .component
122+ self .loadButton = JButton ('Load' , actionPerformed = self .load )
121123 self .compileButton = JButton ('Compile' , actionPerformed = self .compile , enabled = False )
122124
123125 editingLayout = GroupLayout (self , autoCreateGaps = True , autoCreateContainerGaps = True )
@@ -128,7 +130,10 @@ def __init__(self, callbacks, script):
128130 )
129131 .addGroup (editingLayout .createParallelGroup ()
130132 .addComponent (self .scriptText )
131- .addComponent (self .compileButton )
133+ )
134+ .addGroup (editingLayout .createSequentialGroup ()
135+ .addComponent (self .loadButton )
136+ .addComponent (self .compileButton )
132137 )
133138 )
134139
@@ -137,8 +142,11 @@ def __init__(self, callbacks, script):
137142 .addComponent (self .enabledCheckbox )
138143 )
139144 .addGroup (editingLayout .createSequentialGroup ()
140- .addComponent (self .scriptText )
141- .addComponent (self .compileButton )
145+ .addComponent (self .scriptText )
146+ )
147+ .addGroup (editingLayout .createParallelGroup ()
148+ .addComponent (self .loadButton )
149+ .addComponent (self .compileButton )
142150 )
143151 )
144152 self .layout = editingLayout
@@ -148,6 +156,13 @@ def __init__(self, callbacks, script):
148156 def enabled_changed (self , event ):
149157 self .script .enabled = self .enabledCheckbox .isSelected ()
150158
159+ def load (self , event ):
160+ file_chooser = JFileChooser ()
161+ choice = file_chooser .showOpenDialog (None )
162+ if choice == JFileChooser .APPROVE_OPTION :
163+ with open (file_chooser .selectedFile .path , 'r' ) as input_file :
164+ self .scriptEditor .text = '' .join (input_file .readlines ())
165+
151166 def compile (self , event ):
152167 self .script .compile ()
153168 self .compileButton .enabled = False
@@ -173,8 +188,8 @@ def _can_compile(self, event):
173188 self .compileButton .enabled = self .script .requires_compile
174189
175190
176- class ScriptOutputPanel (JPanel , PropertyChangeListener ):
177-
191+ class ScriptOutputPanel (JPanel , PropertyChangeListener , IExtensionStateListener ):
192+
178193 def __init__ (self , callbacks , script ):
179194 super (ScriptOutputPanel , self ).__init__ ()
180195 self .callbacks = callbacks
@@ -189,37 +204,93 @@ def __init__(self, callbacks, script):
189204 self .add (self .tabbedPane , BorderLayout .CENTER )
190205 self .script .stdout = EditorFileAdapter (self .outputEditor )
191206 self .script .stderr = EditorFileAdapter (self .errorEditor )
207+ self .output_file = None
208+
209+ # register to be notified when the extension is unloaded so if a output_file ref is in use it can be closed
210+ callbacks .registerExtensionStateListener (self )
192211
193212 def clear_stderr (self , event ):
194213 self .errorEditor .text = ''
195214
196215 def clear_stdout (self , event ):
197216 self .outputEditor .text = ''
198217
218+ def save_file_output (self , event ):
219+ self .outputFileBrowseButton .enabled = True
220+ if self .output_file : # already have an output_file
221+ self .script .stdout = self .output_file
222+ return
223+
224+ if not self .set_output_file ():
225+ # didn't choose a file then revert to using UI
226+ self .outputUIRadioButton .selected = True
227+
228+ def view_ui_output (self , event ):
229+ self .outputFileBrowseButton .enabled = False
230+ self .script .stdout = EditorFileAdapter (self .outputEditor )
231+
232+ def set_output_file (self , event = None ):
233+ file_chooser = JFileChooser ()
234+ choice = file_chooser .showSaveDialog (None )
235+ if choice == JFileChooser .APPROVE_OPTION :
236+ self .outputFileLabel .text = file_chooser .selectedFile .path
237+ self .output_file = open (file_chooser .selectedFile .path , 'a' )
238+ self .script .stdout = self .output_file
239+ return True
240+
241+ return False
242+
199243 def propertyChange (self , event ):
200244 if event .propertyName == Script .Properties .IS_COMPILED :
201245 if event .newValue :
202246 self .errorEditor .text = ''
203247 elif event .propertyName == Script .Properties .COMPILATION_ERROR :
204248 self .errorEditor .text = event .newValue
205- self .tabbedPane .selectedIndex = 1
249+ self .tabbedPane .selectedIndex = 1
250+
251+ def extensionUnloaded (self ):
252+ if self .output_file : # if we have a file ref then close it
253+ print ('Closing output file reference' )
254+ self .output_file .close ()
206255
207256 def _create_output_panel (self ):
208257 self .outputPanel = JPanel ()
209258 self .outputEditor = self .callbacks .createTextEditor ()
210259 self .outputEditor .editable = False
211260 self .outputText = self .outputEditor .component
212261 self .clearOutputButton = JButton ('Clear' , actionPerformed = self .clear_stdout )
262+ self .outputButtonGroup = ButtonGroup ()
263+ self .outputFileRadioButton = JRadioButton ('Save to File:' , actionPerformed = self .save_file_output )
264+ self .outputUIRadioButton = JRadioButton ('Show in UI:' , selected = True , actionPerformed = self .view_ui_output )
265+ self .outputFileLabel = JLabel ()
266+ self .outputFileBrowseButton = JButton ('Browse...' , enabled = False , actionPerformed = self .set_output_file )
267+
268+ self .outputButtonGroup .add (self .outputFileRadioButton )
269+ self .outputButtonGroup .add (self .outputUIRadioButton )
213270
214271 outputLayout = GroupLayout (self .outputPanel , autoCreateGaps = True , autoCreateContainerGaps = True )
215272 outputLayout .setHorizontalGroup (outputLayout .createParallelGroup ()
273+ .addGroup (
274+ outputLayout .createSequentialGroup ()
275+ .addComponent (self .outputFileRadioButton )
276+ .addComponent (self .outputFileLabel )
277+ .addComponent (self .outputFileBrowseButton )
278+ )
279+ .addComponent (self .outputUIRadioButton )
216280 .addComponent (self .outputText )
217281 .addComponent (self .clearOutputButton )
218282 )
219283
220284 outputLayout .setVerticalGroup (outputLayout .createSequentialGroup ()
221- .addComponent (self .outputText )
222- .addComponent (self .clearOutputButton )
285+ .addGroup (
286+ outputLayout .createParallelGroup ()
287+ .addComponent (self .outputFileRadioButton )
288+ .addComponent (self .outputFileLabel )
289+ .addComponent (self .outputFileBrowseButton )
290+ )
291+ .addComponent (self .outputUIRadioButton )
292+ .addComponent (self .outputText )
293+ .addComponent (self .clearOutputButton )
223294 )
224295 self .outputPanel .layout = outputLayout
225296
@@ -241,7 +312,6 @@ def _create_error_panel(self):
241312 )
242313 self .errorPanel .layout = errorLayout
243314
244-
245315class ScriptPanel (JPanel ):
246316
247317 def __init__ (self , script , callbacks ):
0 commit comments