Skip to content

Commit c8252ae

Browse files
committed
Adding support for writing script output to file or UI
1 parent b33d7d0 commit c8252ae

1 file changed

Lines changed: 64 additions & 8 deletions

File tree

gui.py

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from javax.swing import JTabbedPane, JPanel, JButton, JLabel, SwingConstants, JOptionPane, GroupLayout, JCheckBox, JSplitPane
1+
from javax.swing import JTabbedPane, JPanel, JButton, JLabel, SwingConstants, BorderFactory, JOptionPane, GroupLayout, JCheckBox, JSplitPane, JRadioButton, ButtonGroup, JFileChooser
22
from javax.swing.event import ChangeListener, DocumentListener
33
from javax.swing.LayoutStyle.ComponentPlacement import RELATED, UNRELATED
4-
from java.awt import BorderLayout, Font, Component
4+
from java.awt import BorderLayout, Font, Component, Color
55
from java.beans import PropertyChangeListener
66
from org.python.core.util import StringUtil
7+
from burp import IExtensionStateListener
78
from uicomponents import BurpUI, TabComponent, TabComponentEditableTabMixin, TabComponentCloseableMixin, TabComponentCloseListener, TabComponentTitleChangedListener
89
from models import ObservableCollection, Script
910
from utils import EditorFileAdapter
@@ -173,8 +174,8 @@ def _can_compile(self, event):
173174
self.compileButton.enabled = self.script.requires_compile
174175

175176

176-
class ScriptOutputPanel(JPanel, PropertyChangeListener):
177-
177+
class ScriptOutputPanel(JPanel, PropertyChangeListener, IExtensionStateListener):
178+
178179
def __init__(self, callbacks, script):
179180
super(ScriptOutputPanel, self).__init__()
180181
self.callbacks = callbacks
@@ -189,37 +190,93 @@ def __init__(self, callbacks, script):
189190
self.add(self.tabbedPane, BorderLayout.CENTER)
190191
self.script.stdout = EditorFileAdapter(self.outputEditor)
191192
self.script.stderr = EditorFileAdapter(self.errorEditor)
193+
self.output_file = None
194+
195+
# register to be notified when the extension is unloaded so if a output_file ref is in use it can be closed
196+
callbacks.registerExtensionStateListener(self)
192197

193198
def clear_stderr(self, event):
194199
self.errorEditor.text = ''
195200

196201
def clear_stdout(self, event):
197202
self.outputEditor.text = ''
198203

204+
def save_file_output(self, event):
205+
self.outputFileBrowseButton.enabled = True
206+
if self.output_file: # already have an output_file
207+
self.script.stdout = self.output_file
208+
return
209+
210+
if not self.set_output_file():
211+
# didn't choose a file then revert to using UI
212+
self.outputUIRadioButton.selected = True
213+
214+
def view_ui_output(self, event):
215+
self.outputFileBrowseButton.enabled = False
216+
self.script.stdout = EditorFileAdapter(self.outputEditor)
217+
218+
def set_output_file(self, event=None):
219+
file_chooser = JFileChooser()
220+
choice = file_chooser.showSaveDialog(None)
221+
if choice == JFileChooser.APPROVE_OPTION:
222+
self.outputFileLabel.text = file_chooser.selectedFile.path
223+
self.output_file = open(file_chooser.selectedFile.path, 'a')
224+
self.script.stdout = self.output_file
225+
return True
226+
227+
return False
228+
199229
def propertyChange(self, event):
200230
if event.propertyName == Script.Properties.IS_COMPILED:
201231
if event.newValue:
202232
self.errorEditor.text = ''
203233
elif event.propertyName == Script.Properties.COMPILATION_ERROR:
204234
self.errorEditor.text = event.newValue
205-
self.tabbedPane.selectedIndex = 1
235+
self.tabbedPane.selectedIndex = 1
236+
237+
def extensionUnloaded(self):
238+
if self.output_file: # if we have a file ref then close it
239+
print('Closing output file reference')
240+
self.output_file.close()
206241

207242
def _create_output_panel(self):
208243
self.outputPanel = JPanel()
209244
self.outputEditor = self.callbacks.createTextEditor()
210245
self.outputEditor.editable = False
211246
self.outputText = self.outputEditor.component
212247
self.clearOutputButton = JButton('Clear', actionPerformed=self.clear_stdout)
248+
self.outputButtonGroup = ButtonGroup()
249+
self.outputFileRadioButton = JRadioButton('Save to File:', actionPerformed=self.save_file_output)
250+
self.outputUIRadioButton = JRadioButton('Show in UI:', selected=True, actionPerformed=self.view_ui_output)
251+
self.outputFileLabel = JLabel()
252+
self.outputFileBrowseButton = JButton('Browse...', enabled=False, actionPerformed=self.set_output_file)
253+
254+
self.outputButtonGroup.add(self.outputFileRadioButton)
255+
self.outputButtonGroup.add(self.outputUIRadioButton)
213256

214257
outputLayout = GroupLayout(self.outputPanel, autoCreateGaps=True, autoCreateContainerGaps=True)
215258
outputLayout.setHorizontalGroup(outputLayout.createParallelGroup()
259+
.addGroup(
260+
outputLayout.createSequentialGroup()
261+
.addComponent(self.outputFileRadioButton)
262+
.addComponent(self.outputFileLabel)
263+
.addComponent(self.outputFileBrowseButton)
264+
)
265+
.addComponent(self.outputUIRadioButton)
216266
.addComponent(self.outputText)
217267
.addComponent(self.clearOutputButton)
218268
)
219269

220270
outputLayout.setVerticalGroup(outputLayout.createSequentialGroup()
221-
.addComponent(self.outputText)
222-
.addComponent(self.clearOutputButton)
271+
.addGroup(
272+
outputLayout.createParallelGroup()
273+
.addComponent(self.outputFileRadioButton)
274+
.addComponent(self.outputFileLabel)
275+
.addComponent(self.outputFileBrowseButton)
276+
)
277+
.addComponent(self.outputUIRadioButton)
278+
.addComponent(self.outputText)
279+
.addComponent(self.clearOutputButton)
223280
)
224281
self.outputPanel.layout = outputLayout
225282

@@ -241,7 +298,6 @@ def _create_error_panel(self):
241298
)
242299
self.errorPanel.layout = errorLayout
243300

244-
245301
class ScriptPanel(JPanel):
246302

247303
def __init__(self, script, callbacks):

0 commit comments

Comments
 (0)