diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..7e7ee62 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..7e340a7 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 144e535..3bf49a2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index b36c046..79a7a85 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'com.github.akasakashota' -version 'v1.1.0' +version 'v1.2.0' repositories { mavenCentral() diff --git a/src/main/kotlin/BurpExtender.kt b/src/main/kotlin/BurpExtender.kt index c11603a..fd4ab12 100644 --- a/src/main/kotlin/BurpExtender.kt +++ b/src/main/kotlin/BurpExtender.kt @@ -2,15 +2,15 @@ package burp import burp.IRequestInfo.* import java.awt.Toolkit -import java.awt.datatransfer.Clipboard -import java.awt.datatransfer.StringSelection +import java.awt.datatransfer.* import java.awt.event.MouseEvent import java.awt.event.MouseListener +import java.io.ByteArrayInputStream +import java.io.IOException import java.io.PrintWriter -import java.lang.Exception -import java.lang.RuntimeException import javax.swing.JMenuItem -import kotlin.collections.MutableList +import javax.swing.table.DefaultTableModel +import javax.swing.table.TableModel class BurpExtender : IBurpExtender, IContextMenuFactory { @@ -53,9 +53,17 @@ class BurpExtender : IBurpExtender, IContextMenuFactory { private inner class CopyListener(private val formattedItems: List) : MouseListener { override fun mouseReleased(e: MouseEvent?) { try { + val model = DefaultTableModel(0, 4) + for (item in formattedItems) { + model.addRow(item.toArray()) + } + val clip: Clipboard = Toolkit.getDefaultToolkit()!!.systemClipboard - val ss = StringSelection(formattedItems.joinToString("\n")) - clip.setContents(ss, ss) + clip.setContents( + TableTransferable(model), + ClipboardOwner { _, _ -> println("lost")} + ) + out("Copied ${formattedItems.size} item(s).") } catch (e: Exception) { err(e) } } @@ -115,7 +123,57 @@ class BurpExtender : IBurpExtender, IContextMenuFactory { } override fun toString(): String { - return arrayOf(method, url, requestBody, responseBody).joinToString("\t") + return toArray().joinToString("\t") + } + + public fun toArray(): Array { + return arrayOf(method, url, requestBody, responseBody) + } + } + + class TableTransferable(private val table: TableModel) : Transferable { + companion object { + val HTML_DATA_FLAVOR = DataFlavor("text/html", "HTML") + } + + override fun getTransferDataFlavors(): Array { + return arrayOf(HTML_DATA_FLAVOR) + } + + override fun isDataFlavorSupported(flavor: DataFlavor): Boolean { + return transferDataFlavors.any { + it == flavor + } + } + + @Throws(UnsupportedFlavorException::class, IOException::class) + override fun getTransferData(flavor: DataFlavor): Any { + val data: Any = if (HTML_DATA_FLAVOR.equals(flavor)) { + ByteArrayInputStream(formatAsHTML().toByteArray()) + } else { + throw UnsupportedFlavorException(flavor) + } + return data + } + + private fun formatAsHTML(): String { + val builder = StringBuilder() + + builder.append("") + for (i in 0 until table.rowCount) { + builder.append("") + for (j in 0 until table.columnCount) { + val v = table.getValueAt(i, j) + builder + .append("") + .append(v?.toString() ?: "") + .append("") + } + builder.append("") + } + builder.append("") + + return builder.toString() } }