initial commit for transformers

This commit is contained in:
Rasmus Moorats 2022-10-06 13:59:29 +03:00
parent 0bb5e00209
commit 2383f2a3dd
Signed by: xx
GPG key ID: FE14255A6AE7241C
6 changed files with 381 additions and 41 deletions

View file

@ -19,7 +19,7 @@ dependencies {
implementation("com.miglayout:miglayout-swing:11.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0")
implementation("com.google.re2j:re2j:1.7")
api("net.portswigger.burp.extender:montoya-api:0.9.5.1")
api("net.portswigger.burp.extender:montoya-api:0.9.25")
}
tasks.withType<KotlinCompile> {

View file

@ -2,12 +2,12 @@ package burp
import burp.api.montoya.BurpExtension
import burp.api.montoya.MontoyaApi
import burp.api.montoya.core.Annotations
import burp.api.montoya.core.HighlightColor
import burp.api.montoya.core.MessageAnnotations
import burp.api.montoya.core.ToolSource
import burp.api.montoya.http.HttpHandler
import burp.api.montoya.http.RequestHandlerResult
import burp.api.montoya.http.ResponseHandlerResult
import burp.api.montoya.http.RequestResult
import burp.api.montoya.http.ResponseResult
import burp.api.montoya.http.message.requests.HttpRequest
import burp.api.montoya.http.message.responses.HttpResponse
@ -16,6 +16,7 @@ const val EXTENSION_NAME = "Value Autoupdater"
class BurpExtender : BurpExtension {
private lateinit var ui: UI
private lateinit var items: ItemStore
private lateinit var transformers: TransformerStore
private lateinit var replacer: Replacer
private lateinit var api: MontoyaApi
@ -32,8 +33,9 @@ class BurpExtender : BurpExtension {
api.misc().setExtensionName(EXTENSION_NAME)
items = ItemStore(api.persistence().userContext())
ui = UI(api, items)
replacer = Replacer(api, items)
transformers = TransformerStore(api.persistence().userContext())
ui = UI(api, items, transformers)
replacer = Replacer(api, items, transformers)
api.http().registerHttpHandler(ExtHttpHandler())
@ -42,30 +44,33 @@ class BurpExtender : BurpExtension {
inner class ExtHttpHandler : HttpHandler {
override fun handleHttpRequest(
request: HttpRequest, annotations: MessageAnnotations, toolSource: ToolSource
): RequestHandlerResult {
if (!ui.isEnabled(toolSource.toolType())) return RequestHandlerResult.from(request, MessageAnnotations.NONE)
request: HttpRequest, annotations: Annotations, toolSource: ToolSource
): RequestResult {
if (!ui.isEnabled(toolSource.toolType())) return RequestResult.requestResult(
request,
Annotations.annotations()
)
val result = replacer.handleRequest(request.toString())
return RequestHandlerResult.from(
api.http().createRequest(request.httpService(), result.contents), constructAnnotations(result)
return RequestResult.requestResult(
HttpRequest.httpRequest(request.httpService(), result.contents), constructAnnotations(result)
)
}
override fun handleHttpResponse(
request: HttpRequest, response: HttpResponse, annotations: MessageAnnotations, toolSource: ToolSource
): ResponseHandlerResult {
if (!ui.isEnabled(toolSource.toolType())) return ResponseHandlerResult.from(
response, MessageAnnotations.NONE
request: HttpRequest, response: HttpResponse, annotations: Annotations, toolSource: ToolSource
): ResponseResult {
if (!ui.isEnabled(toolSource.toolType())) return ResponseResult.responseResult(
response, Annotations.annotations()
)
val result = replacer.handleResponse(response.toString())
return ResponseHandlerResult.from(response, constructAnnotations(result))
return ResponseResult.responseResult(response, constructAnnotations(result))
}
}
fun constructAnnotations(result: ReplaceResult): MessageAnnotations {
if (!result.matched) return MessageAnnotations.NONE
fun constructAnnotations(result: ReplaceResult): Annotations {
if (!result.matched) return Annotations.annotations()
// sometimes newlines are removed from comments, so accommodate for that as well
var comment = when (result.type) {
@ -77,6 +82,6 @@ class BurpExtender : BurpExtension {
comment += "(${it.old}: ${it.new}) \n"
}
return MessageAnnotations.from(comment, HighlightColor.NONE)
return Annotations.annotations(comment, HighlightColor.NONE)
}
}

View file

@ -20,6 +20,7 @@ data class Item(
var enabled: Boolean,
var matchCount: Int,
var replaceCount: Int,
var transformer: String,
)
typealias Items = MutableMap<String, Item> // name -> item

View file

@ -18,11 +18,17 @@ val regexCache = mutableMapOf<String, Pattern>()
interface ReplaceStrategy {
fun updateValue(request: String, match: String): Response
fun matchAndReplace(request: String, key: String, value: String): Response {
fun matchAndReplace(request: String, key: String, item: Item, transformerStore: TransformerStore): Response {
val res = Response(false, "")
if (request.contains("\$$key\$")) {
res.matched = true
var value = item.lastMatch
if (item.transformer != "") {
transformerStore.transformers[item.transformer]?.let {
value = evalTransformer(value, it)
}
}
res.contents = request.replace("\$$key\$", value)
}
@ -65,8 +71,9 @@ class HeaderStrategy : ReplaceStrategy {
}
}
class Replacer(api: MontoyaApi, itemStore: ItemStore) {
class Replacer(api: MontoyaApi, itemStore: ItemStore, transformerStore: TransformerStore) {
private val itemStore: ItemStore
private val transformerStore: TransformerStore
private val api: MontoyaApi
private val strategies: Map<ItemType, ReplaceStrategy> = mapOf(
@ -76,6 +83,7 @@ class Replacer(api: MontoyaApi, itemStore: ItemStore) {
init {
this.itemStore = itemStore
this.transformerStore = transformerStore
this.api = api
}
@ -85,7 +93,7 @@ class Replacer(api: MontoyaApi, itemStore: ItemStore) {
itemStore.items.forEach {
if (!it.value.enabled) return@forEach
val resp = strategies[it.value.type]?.matchAndReplace(result.contents, it.key, it.value.lastMatch)!!
val resp = strategies[it.value.type]?.matchAndReplace(result.contents, it.key, it.value, transformerStore)!!
if (resp.matched) {
it.value.replaceCount += 1

View file

@ -0,0 +1,76 @@
package burp
import burp.api.montoya.persistence.PersistenceContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Source
import org.graalvm.polyglot.Value
val LIB = initLib()
const val TRANSFORMER_STORE = "transformerStore"
fun initLib(): Value? {
val context = Context.newBuilder("js").allowIO(true).build()
val src = "import * as Lib from \"/home/xx/Projects/transformerHook/bundle.mjs\";" + "Lib;"
val source = Source.newBuilder("js", src, "lib.mjs").build()
return context.eval(source)
}
fun evalTransformer(value: String, transformer: Transformer): String {
val context = Context.create()
val bindings = context.getBindings("js")
var res = value
bindings.putMember("value", value)
bindings.putMember("Lib", LIB)
try {
res = context.eval("js", transformer.code).asString()
} catch (e: Exception) {
var error = "Transformer exception!"
e.message?.let {
error += "\n${it}"
transformer.error = it
}
log.error(error)
}
context.close()
return res
}
@Serializable
data class Transformer(
var code: String,
var error: String,
)
typealias Transformers = MutableMap<String, Transformer> // name -> transformer
class TransformerStore(ctx: PersistenceContext) {
// todo: unify stores somehow
private val ctx: PersistenceContext
var transformers: Transformers
init {
this.ctx = ctx
this.transformers = load()
}
private fun load(): Transformers {
// loads and returns transformers from persistent storage
var jsonStr = ctx.getString(TRANSFORMER_STORE) ?: "{}"
return Json.decodeFromString(jsonStr)
}
fun save() {
// saves items in memory to disk
ctx.setString(TRANSFORMER_STORE, Json.encodeToString(transformers))
}
fun nuke() {
ctx.delete(TRANSFORMER_STORE)
}
}

View file

@ -6,7 +6,6 @@ import burp.api.montoya.core.ToolType.*
import burp.api.montoya.persistence.PersistenceContext
import net.miginfocom.swing.MigLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.Font
import java.awt.Window
import java.awt.event.*
@ -17,11 +16,14 @@ import javax.swing.SwingUtilities.getWindowAncestor
import javax.swing.event.TableModelEvent
import javax.swing.table.DefaultTableModel
import javax.swing.table.TableModel
import kotlin.text.Charsets.UTF_8
val VALUES_TABLE = JTable()
val TRANSFORMER_TABLE = JTable()
const val TAB_NAME = "Value updater"
var TAB_VISIBLE = false
var namedRows = mapOf<String, Int>()
var namedTRows = mapOf<String, Int>()
fun updated(name: String, value: String, count: Int) {
if (!TAB_VISIBLE) return
@ -46,7 +48,13 @@ private fun reloadValuesTable(items: Items) {
items.forEach {
dtm.addRow(
arrayOf(
it.value.enabled, it.key, it.value.match, it.value.lastMatch, it.value.matchCount, it.value.replaceCount
it.value.enabled,
it.key,
it.value.match,
it.value.lastMatch,
it.value.matchCount,
it.value.replaceCount,
it.value.transformer
)
)
tmpMap[it.key] = dtm.rowCount - 1
@ -56,16 +64,41 @@ private fun reloadValuesTable(items: Items) {
namedRows = tmpMap
}
private fun reloadTransformersTable(transformers: Transformers) {
val dtm = TRANSFORMER_TABLE.model as DefaultTableModel
val tmpMap = mutableMapOf<String, Int>()
dtm.dataVector.removeAllElements()
transformers.forEach {
dtm.addRow(
arrayOf(
it.key
)
)
tmpMap[it.key] = dtm.rowCount - 1
}
dtm.fireTableDataChanged()
namedTRows = tmpMap
}
private fun rowToName(row: Int): String {
val dtm = VALUES_TABLE.model
return dtm.getValueAt(row, 1).toString()
}
class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
private fun rowToTName(row: Int): String {
val dtm = TRANSFORMER_TABLE.model
return dtm.getValueAt(row, 0).toString()
}
class UI(api: MontoyaApi, itemStore: ItemStore, transformerStore: TransformerStore) : JPanel() {
private val api: MontoyaApi
private val ctx: PersistenceContext
private val itemStore: ItemStore
private val transformerStore: TransformerStore
private var extEnabled = true
private var enabledTools = mutableMapOf(
PROXY to true,
@ -77,6 +110,8 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
)
private val headerPanel = JPanel()
private val leftPanel = JPanel()
private val rightPanel = JPanel()
private val mainLabel = JLabel()
private val headerNestedPanel = JPanel()
private val enabledToggle = JCheckBox()
@ -100,11 +135,21 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
private val repeaterSel = JCheckBox()
private val sequencerSel = JCheckBox()
private val extenderSel = JCheckBox()
private val transformerLabel = JLabel()
private val transformerSelectorPanel = JPanel()
private val transformerButtons = JPanel()
private val transformerAdd = JButton()
private val transformerRemove = JButton()
private val transformerTablePanel = JScrollPane()
private val transformerTable = TRANSFORMER_TABLE
private val transformerEditor = api.userInterface().createRawEditor()
private val transformerEditorSave = JButton()
init {
this.api = api
this.ctx = api.persistence().userContext()
this.itemStore = itemStore
this.transformerStore = transformerStore
initComponents()
loadValuesFromStore()
api.userInterface().registerSuiteTab(TAB_NAME, this)
@ -118,6 +163,7 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
TAB_VISIBLE = false
}
})
}
private fun loadValuesFromStore() {
@ -138,6 +184,7 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
extenderSel.isSelected = enabledTools[EXTENDER]!!
reloadValuesTable(itemStore.items)
reloadTransformersTable(transformerStore.transformers)
}
@ -146,11 +193,17 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
}
private fun valueAdd() {
val window = AddEditDialog(getWindowAncestor(this), -1, itemStore)
val window = AddEditDialog(getWindowAncestor(this), -1, itemStore, transformerStore)
window.title = "Add value"
window.isVisible = true
}
private fun transformerAdd() {
val window = TransformerAddDialog(getWindowAncestor(this), transformerStore)
window.title = "Add transformer"
window.isVisible = true
}
private fun valueEdit() {
val selected = valuesTable.selectedRowCount
if (selected < 1) {
@ -163,7 +216,7 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
val index = valuesTable.selectedRows[0]
val window = AddEditDialog(getWindowAncestor(this), index, itemStore)
val window = AddEditDialog(getWindowAncestor(this), index, itemStore, transformerStore)
window.title = "Edit value"
window.isVisible = true
}
@ -181,6 +234,27 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
reloadValuesTable(itemStore.items)
}
private fun transformerRemove() {
val selectedRows = transformerTable.selectedRows
TRANSFORMER_TABLE.clearSelection() // visual bug work around
if (selectedRows.isEmpty()) return
transformerStore.transformers.remove(rowToTName(selectedRows[0]))
transformerStore.save()
transformerEditor.contents = "".toByteArray(UTF_8)
reloadTransformersTable(transformerStore.transformers)
}
private fun transformerSave() {
transformerStore.transformers[rowToTName(transformerTable.selectedRow)]?.code =
transformerEditor.contents.toString(UTF_8)
transformerStore.save()
}
private fun enabledToggle(e: ItemEvent) {
extEnabled = e.stateChange == SELECTED
ctx.setBoolean("extEnabled", extEnabled)
@ -246,9 +320,27 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
}
}
private fun transformerSelected() {
when (transformerTable.selectedRowCount) {
0 -> {
transformerRemove.isEnabled = false
transformerEditorSave.isEnabled = false
}
else -> {
transformerRemove.isEnabled = true
transformerEditorSave.isEnabled = true
transformerEditor.contents =
transformerStore.transformers[rowToTName(transformerTable.selectedRow)]!!.code.toByteArray(UTF_8)
}
}
}
//<editor-fold desc="UI layout cruft">
private fun initComponents() {
layout = MigLayout("hidemode 3", "[fill][fill]", "[][][][][]")
layout = MigLayout("fill,hidemode 3,align center top", "fill")
leftPanel.layout = MigLayout("fill,hidemode 3,align left top", "[fill]", "[][][][][]")
rightPanel.layout = MigLayout("fill,hidemode 3,align left top", "[fill]", "[][][]")
headerPanel.layout = MigLayout("hidemode 3", "[fill]", "[][][]")
@ -262,8 +354,8 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
enabledToggle.addItemListener { e: ItemEvent -> enabledToggle(e) }
headerNestedPanel.add(enabledToggle, "cell 0 0")
headerPanel.add(headerNestedPanel, "cell 0 1")
add(headerPanel, "cell 0 0")
add(separator2, "cell 0 1")
leftPanel.add(headerPanel, "cell 0 0")
leftPanel.add(separator2, "cell 0 1")
valuesPanel.layout = MigLayout("hidemode 3", "[fill]", "[][][][]")
@ -294,7 +386,7 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
valuesTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
valuesTable.model = object : DefaultTableModel(
arrayOf(), arrayOf(
"", "Name", "Match", "Last value", "Times updated", "Times replaced"
"", "Name", "Match", "Last value", "Times updated", "Times replaced", "Transformer"
)
) {
override fun getColumnClass(columnIndex: Int): Class<*> {
@ -309,16 +401,15 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
}
val cm = valuesTable.columnModel
cm.getColumn(0).resizable = false
cm.getColumn(0).preferredWidth = 25
valuesTable.preferredScrollableViewportSize = Dimension(600, 300)
cm.getColumn(0).width = 25
(valuesTable.model as DefaultTableModel).addTableModelListener { e: TableModelEvent -> tableEdit(e) }
valuesTable.selectionModel.addListSelectionListener { tableSelected() }
valuesTablePanel.setViewportView(valuesTable)
valueSelectorPanel.add(valuesTablePanel, "cell 1 0")
valuesPanel.add(valueSelectorPanel, "cell 0 2")
add(valuesPanel, "cell 0 2")
add(separator1, "cell 0 3")
valueSelectorPanel.add(valuesTablePanel, "cell 1 0,grow,push,span")
valuesPanel.add(valueSelectorPanel, "cell 0 2,grow,push,span")
leftPanel.add(valuesPanel, "cell 0 2")
leftPanel.add(separator1, "cell 0 3")
toolsPanel.layout = MigLayout("hidemode 3", "[fill]", "[][]")
@ -353,17 +444,58 @@ class UI(api: MontoyaApi, itemStore: ItemStore) : JPanel() {
toolSelectionPanel.add(extenderSel, "cell 2 1")
toolsPanel.add(toolSelectionPanel, "cell 0 1")
add(toolsPanel, "cell 0 4")
leftPanel.add(toolsPanel, "cell 0 4")
add(leftPanel, "w 50%,aligny top,growy 0,growx")
transformerLabel.text = "Value Transformers"
transformerLabel.font = transformerLabel.font.deriveFont(transformerLabel.font.style or Font.BOLD)
rightPanel.add(transformerLabel, "cell 0 0")
transformerSelectorPanel.layout = MigLayout("hidemode 3", "[fill][fill]", "[]")
transformerButtons.layout = MigLayout("hidemode 3", "[fill]", "[][][]")
transformerAdd.text = "Add"
transformerAdd.addActionListener { transformerAdd() }
transformerButtons.add(transformerAdd, "cell 0 0")
transformerRemove.text = "Remove"
transformerRemove.addActionListener { transformerRemove() }
transformerRemove.isEnabled = false
transformerButtons.add(transformerRemove, "cell 0 1")
transformerEditorSave.text = "Save"
transformerEditorSave.addActionListener { transformerSave() }
transformerEditorSave.isEnabled = true
transformerButtons.add(transformerEditorSave, "dock south")
transformerSelectorPanel.add(transformerButtons, "cell 0 0,aligny top,growy 0")
transformerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
transformerTable.model = object : DefaultTableModel(
arrayOf(), arrayOf(
"Name"
)
) {}
transformerTable.selectionModel.addListSelectionListener { transformerSelected() }
transformerTablePanel.setViewportView(transformerTable)
transformerSelectorPanel.add(transformerTablePanel, "cell 1 0,grow,push,span")
rightPanel.add(transformerSelectorPanel, "cell 0 1")
rightPanel.add(transformerEditor.uiComponent(), "cell 0 2,grow,push,span")
add(rightPanel, "w 50%,aligny top,growy 0,grow,push,span")
}
//</editor-fold>
}
class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(owner) {
class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore, transformerStore: TransformerStore) :
JDialog(owner) {
private val headerTypeHint = "Matches header names and replaces values "
private val regexTypeHint = "Uses regex for matches (named group: val)"
private var index: Int
private val itemStore: ItemStore
private val transformerStore: TransformerStore
private val panel1 = JPanel()
private val nameLabel = JLabel()
@ -374,6 +506,8 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
private val headerButton = JRadioButton()
private val regexButton = JRadioButton()
private val typeDescription = JLabel()
private val transformerLabel = JLabel()
private val transformerComboBox = JComboBox<String>()
private val errorLabel = JLabel()
private val panel3 = JPanel()
private val okButton = JButton()
@ -383,13 +517,24 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
init {
this.index = index
this.itemStore = itemStore
this.transformerStore = transformerStore
initComponents()
loadTransformers()
if (index != -1) {
loadStoredValues()
}
this.defaultCloseOperation = DISPOSE_ON_CLOSE
}
private fun loadTransformers() {
transformerComboBox.addItem("")
transformerStore.transformers.forEach {
transformerComboBox.addItem(it.key)
}
transformerComboBox.selectedIndex = 0
}
private fun loadStoredValues() {
val name = rowToName(index)
val item = itemStore.items[name]
@ -404,6 +549,8 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
ItemType.REGEX -> regexButton.isSelected = true
}
transformerComboBox.selectedItem = item.transformer
nameField.isEnabled = false
}
@ -439,6 +586,7 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
val name = nameField.text
val match = matchField.text
val type = if (regexButton.isSelected) ItemType.REGEX else ItemType.HEADER
val transformer = transformerComboBox.selectedItem as String
if (name == "") {
showError("Name field cannot be left blank!")
@ -459,7 +607,7 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
}
if (index == -1) {
val item = Item(match, type, "", true, 0, 0)
val item = Item(match, type, "", true, 0, 0, transformer)
if (itemStore.items[name] != null) {
showError("Entry named \"$name\" already exists!")
return false
@ -475,6 +623,7 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
}
item.match = match
item.type = type
item.transformer = transformer
log.debug("Edited item at index $index: $item")
}
@ -496,7 +645,6 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
//<editor-fold desc="UI layout cruft">
private fun initComponents() {
val contentPane = contentPane
contentPane.layout = MigLayout("hidemode 3", "[fill][fill][fill][fill][fill]", "[][][][][][][]")
panel1.layout = MigLayout("hidemode 3", "[fill][fill]", "[][][][]")
@ -536,6 +684,11 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
typeDescription.text = headerTypeHint
panel1.add(typeDescription, "cell 1 3")
transformerLabel.text = "Transformer"
panel1.add(transformerLabel, "cell 0 4")
panel1.add(transformerComboBox, "cell 1 4")
contentPane.add(panel1, "cell 0 0")
errorLabel.foreground = Color.RED
@ -571,3 +724,100 @@ class AddEditDialog(owner: Window?, index: Int, itemStore: ItemStore) : JDialog(
}
//</editor-fold>
}
class TransformerAddDialog(owner: Window?, transformerStore: TransformerStore) : JDialog(owner) {
private val transformerStore: TransformerStore
private val panel1 = JPanel()
private val nameLabel = JLabel()
private val nameField = JTextField()
private val errorLabel = JLabel()
private val panel3 = JPanel()
private val okButton = JButton()
private val cancelButton = JButton()
init {
this.transformerStore = transformerStore
initComponents()
this.defaultCloseOperation = DISPOSE_ON_CLOSE
}
private fun keyTyped() {
showError(" ")
}
private fun ok() {
if (apply()) this.dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
}
private fun apply(): Boolean {
val name = nameField.text
if (name == "") {
showError("Name field cannot be left blank!")
return false
}
val transformer = Transformer("", "")
if (transformerStore.transformers[name] != null) {
showError("Transformer named \"$name\" already exists!")
return false
}
transformerStore.transformers[name] = transformer
transformerStore.save()
reloadTransformersTable(transformerStore.transformers)
return true
}
private fun cancel() {
this.dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
}
private fun showError(err: String) {
errorLabel.text = err
}
//<editor-fold desc="UI layout cruft">
private fun initComponents() {
contentPane.layout = MigLayout("hidemode 3", "[fill][fill][fill][fill][fill]", "[][][][][][][]")
panel1.layout = MigLayout("hidemode 3", "[fill][fill]", "[][][][]")
nameLabel.text = "Name"
panel1.add(nameLabel, "cell 0 0")
nameField.addKeyListener(object : KeyAdapter() {
override fun keyTyped(e: KeyEvent) {
this@TransformerAddDialog.keyTyped()
}
})
panel1.add(nameField, "cell 1 0,wmin 270,grow 0")
contentPane.add(panel1, "cell 0 0")
errorLabel.foreground = Color.RED
showError(" ")
contentPane.add(errorLabel, "cell 0 4")
panel3.layout = MigLayout("fillx,hidemode 3", "[fill][fill][fill][fill][fill]", "[fill]")
okButton.text = "OK"
okButton.background = UIManager.getColor("Button.background")
okButton.font = okButton.font.deriveFont(okButton.font.style or Font.BOLD)
okButton.addActionListener { ok() }
panel3.add(okButton, "west,gapx null 10")
cancelButton.text = "Cancel"
cancelButton.addActionListener { cancel() }
panel3.add(cancelButton, "EAST")
contentPane.add(panel3, "cell 0 5")
setSize(250, 100)
isResizable = false
pack()
setLocationRelativeTo(owner)
}
//</editor-fold>
}