[
  {
    "path": ".gitignore",
    "content": "burp-send-to-extension/.idea\nburp-send-to-extension/.gradle\nburp-send-to-extension/build\nburp-send-to-extension/out\n"
  },
  {
    "path": "BappDescription.html",
    "content": "<p>Adds a customizable \"Send to...\"-context-menu to your BurpSuite.</p>\n\n<p><b>Configuration</b></p>\n\n<p>After loading the extension the \"Send to\"-Tab contains all necessary options to configure the \"Send to\"-context-menu. </p>\n\n<p>New context-menu-entries can be added using the \"Add\"-button. Each entry consists of following fields:</p>\n\n<ul>\n    <li><strong>Name:</strong> the name of the context-menu-entry</li>\n\n    <li><strong>Command:</strong> the command to be executed. You can use following placeholders:\n\n    <ul>\n            <li><strong>%H:</strong> will be replaced with the host</li>\n\n            <li><strong>%P:</strong> will be replaced with the port</li>\n\n            <li><strong>%T:</strong> will be replaced with the protocol</li>\n\n            <li><strong>%U:</strong> will be replaced with the url</li>\n\n            <li><strong>%A:</strong> will be replaced with the url path</li>\n\n            <li><strong>%Q:</strong> will be replaced with the url query</li>\n\n            <li><strong>%C:</strong> will be replaced with the cookies</li>\n\n            <li><strong>%M:</strong> will be replaced with the HTTP-method</li>\n\n            <li><strong>%S:</strong> will be replaced with the selected text</li>\n\n            <li><strong>%F:</strong> will be replaced with the path to a temporary file containing the selected text</li>\n\n            <li><strong>%R:</strong> will be replaced with the path to a temporary file containing the content of the focused request/response</li>\n\n            <li><strong>%E:</strong> will be replaced with the path to a temporary file containing the header of the focused request/response</li>\n\n            <li><strong>%B:</strong> will be replaced with the path to a temporary file containing the body of the focused request/response</li>\n    </ul>\n\n    <li><strong>Run in terminal:</strong> defines whether a terminal-window should appear in which the configured command is executed. By default \"xterm\" is used as terminal-emulator. You can change the terminal-emulator in the \"Terminal Options\" to your liking.</li>\n\n    <li><strong>Show preview:</strong> gives you the chance to preview and change the command before executing it</li>\n\n    <li><strong>Output should replace selection:</strong> will replace the selection with the output of the to be executed command</li>\n</ul>\n\n<p>\nIn addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the \"Advanced\"-button.\nBy default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator.\n</p>\n\n<p>After creating new context-menu-entries using the \"Add\"-button they can be edited or deleted again using the \"Edit\"- and \"Remove\"-button. In addition the order in which they appear in the context-menu can be altered using the \"Up\"- and \"Down\"-button.</p>\n\n<p><b>Terminal Options</b></p>\n\n<p>\nThe \"Terminal Options\" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled.\n</p>\n\n<p><b>Context-Menu</b></p>\n\n<p>The \"Send to...\" context-menu contains all entries which were added in the \"Send to\"-Tab.\n    In addition you can add new entries via the \"Custom command...\"-context-menu-entry.</p>\n\n<p><b>Save and load options</b></p>\n\n<p>Usually the options of the \"Send to\"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the \"Send to\"-Tab and select the appropriate context-menu-entry.</p>\n\n<p><b>Security Notes</b></p>\n\n<p>Executing commands based on untrusted input always introduces the risk of command injection. This is especially true when using the <strong>%S</strong> placeholder. Thus it is recommended to always activate the <strong>Show preview</strong> option when using the <strong>%S</strong> placeholder and closely analyse commands in the preview window prior to execution.</p>\n\n"
  },
  {
    "path": "BappManifest.bmf",
    "content": "Uuid: f089f1ad056545489139cb9f32900f8e\nExtensionType: 1\nName: Custom Send To\nRepoName: custom-send-to\nScreenVersion: 1.4\nSerialVersion: 1\nMinPlatformVersion: 0\nProOnly: False\nAuthor: Thomas Engel\nShortDescription: Add a customizable \"Send to...\" menu to the context menu\nEntryPoint: burp-send-to-extension/build/libs/burp-send-to-extension-1.4.jar\nBuildCommand: cd burp-send-to-extension; ./gradlew fatJar\n"
  },
  {
    "path": "README.md",
    "content": "# Burp-Send-To-Extension\n\nAdds a customizable \"Send to...\"-context-menu to your BurpSuite.\n\n![Burp-Send-To-Extension Tab](images/burp-send-to-extension-intro.png)\n\n## Configuration\n\nAfter loading the extension the \"Send to\"-Tab contains all necessary options to configure the \"Send to\"-context-menu. \n\nNew context-menu-entries can be added using the \"Add\"-button. Each entry consists of following fields:\n* **Name:** the name of the context-menu-entry.\n* **Command:** the command to be executed. You can use following placeholders:\n\t* **%H:** will be replaced with the host\n\t* **%P:** will be replaced with the port\n\t* **%T:** will be replaced with the protocol\n\t* **%U:** will be replaced with the url\n\t* **%A:** will be replaced with the url path\n\t* **%Q:** will be replaced with the url query\n\t* **%C:** will be replaced with the cookies\n\t* **%L:** will be replaced with the HTTP-content-length\n\t* **%M:** will be replaced with the HTTP-method\n\t* **%O:** will be replaced with the HTTP-status-code\n\t* **%S:** will be replaced with the selected text\n\t* **%F:** will be replaced with the path to a temporary file containing the selected text\n\t* **%R:** will be replaced with the path to a temporary file containing the content of the focused request/response\n\t* **%E:** will be replaced with the path to a temporary file containing the header of the focused request/response\n\t* **%B:** will be replaced with the path to a temporary file containing the body of the focused request/response\n* **Group:** the name of the sub-menu in which this entry will be shown. Can be left blank.\n* **Run in terminal:** defines whether a terminal-window should appear in which the configured command is executed. By default \"xterm\" is used as terminal-emulator. You can change the terminal-emulator in the \"Miscellaneous Options\" to your liking.\n* **Show preview:** gives you the chance to preview and change the command before executing it.\n* **Output should replace selection:** will replace the selection with the output of the to be executed command.\n\n![Burp-Send-To-Extension Add-/Edit-Dialog](images/burp-send-to-extension-add-edit-dialog.png)\n\nIn addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the \"Advanced\"-button. \nBy default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator, or to store all values of a specific placeholder within a file.\n\n![Burp-Send-To-Extension Advanced-Dialog](images/burp-send-to-extension-advanced-dialog.png)\n\nAfter creating new context-menu-entries using the \"Add\"-button they can be edited or deleted again using the \"Edit\"- and \"Remove\"-button. In addition the order in which they appear in the context-menu can be altered using the \"Up\"- and \"Down\"-button.\n\n![Burp-Send-To-Extension Tab](images/burp-send-to-extension-tab.png)\n\n## Terminal Options\n\nThe \"Terminal Options\" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled.\n\n## Context-Menu\n\nThe \"Send to...\" context-menu contains all entries which were added in the \"Send to\"-Tab.\nIn addition you can add new entries via the \"Custom command...\"-context-menu-entry.\n\n#### Request Field\n\n![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-repeater.png)\n\n#### Proxy History\n\n![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-target-sitemap.png)\n\n## Save and load options\n\nUsually the options of the \"Send to\"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the \"Send to\"-Tab and select the appropriate context-menu-entry.\n\n![Burp-Send-To-Extension Options](images/burp-send-to-extension-options.png)\n\n## Extending the Extension\n\nNot satisfied yet? The [Wiki Page](https://github.com/bytebutcher/burp-send-to/wiki/Examples) lists some additional context-menu entries which might come in handy.\n\n## Security Notes\n\nExecuting commands based on untrusted input always introduces the risk of command injection. This is especially true when using the **%S** placeholder. Thus it is recommended to always activate the **Show preview** option when using the **%S** placeholder and closely analyse commands in the preview window prior to execution.\n\n![Burp-Send-To-Extension Options](images/burp-send-to-extension-forkbomb.png)\n\n## Build\n\nThis project was built using IntelliJ and Gradle. When you make changes to the source (and especially the GUI) you should apply following settings within Intellij to make sure that everything builds successfully:\n* File -> Settings -> Editor -> GUI Designer -> Generate GUI into: Java source\n* File -> Settings -> Build, Execution, Deployment -> Compiler -> Build project automatically\n* File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using: IntelliJ IDEA\n\nWhen the GUI is not updated correctly you may rebuild the project manually:\n* Build -> Rebuild Project\n\nAfter that you can execute the \"fatJar\"-task within the \"build.gradle\"-file. This will produce a jar in the \"build/libs/\" directory called \"burp-send-to-extension-{version}.jar\".\n"
  },
  {
    "path": "burp-send-to-extension/build.gradle",
    "content": "apply plugin: 'java'\n\ngroup 'net.bytebutcher'\nversion '1.6'\n\nsourceCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    testCompile group: 'junit', name: 'junit', version: '4.12'\n    compile 'com.intellij:forms_rt:7.0.3'\n    compile 'net.portswigger.burp.extender:burp-extender-api:1.7.22'\n    compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'\n    compile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre'\n\n}\n\ntask fatJar(type: Jar) {\n    baseName = project.name\n    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }\n    with jar\n}\n"
  },
  {
    "path": "burp-send-to-extension/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Mar 17 11:46:47 CET 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10-all.zip\n"
  },
  {
    "path": "burp-send-to-extension/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "burp-send-to-extension/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "burp-send-to-extension/settings.gradle",
    "content": "rootProject.name = 'burp-send-to-extension'\n\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/BurpExtender.java",
    "content": "package burp;\n\nimport net.bytebutcher.burpsendtoextension.gui.SendToContextMenu;\nimport net.bytebutcher.burpsendtoextension.gui.SendToTab;\nimport net.bytebutcher.burpsendtoextension.gui.SendToTable;\nimport net.bytebutcher.burpsendtoextension.models.Config;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.io.PrintWriter;\n\npublic class BurpExtender implements IBurpExtender, ITab {\n\n    private JPanel tab = null;\n    private SendToContextMenu sendToContextMenu;\n    private SendToTable sendToTable;\n\n    private static IBurpExtenderCallbacks callbacks;\n    private static Config config;\n    private static SendToTab sendToTab = null;\n\n    private static PrintWriter stdout;\n    private static PrintWriter stderr;\n\n    @Override\n    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {\n        BurpExtender.callbacks = callbacks;\n        initLogHandler(callbacks);\n        BurpExtender.printOut(\"Initializing Send to Extension...\");\n        BurpExtender.callbacks.setExtensionName(\"Send to\");\n        BurpExtender.printOut(\"Initializing config...\");\n        BurpExtender.config = new Config(this);\n        BurpExtender.printOut(\"Initializing tab...\");\n        BurpExtender.sendToTab = new SendToTab(this);\n        BurpExtender.printOut(\"Registering context menu...\");\n        this.sendToContextMenu = new SendToContextMenu(this, sendToTab.getSendToTableListener());\n        BurpExtender.callbacks.registerContextMenuFactory(sendToContextMenu);\n        this.tab = sendToTab.getRootPanel();\n        BurpExtender.printOut(\"Loading table data...\");\n        this.sendToTable = sendToTab.getSendToTable();\n        this.sendToTable.addCommandObjects(config.getSendToTableData());\n        callbacks.addSuiteTab(this);\n        BurpExtender.printOut(\"----------------------------------------------------------------------\");\n    }\n\n    private void initLogHandler(IBurpExtenderCallbacks callbacks) {\n        stderr = new PrintWriter(callbacks.getStderr(), true);\n        stdout = new PrintWriter(callbacks.getStdout(), true);\n    }\n\n    @Override\n    public String getTabCaption() {\n        return \"Send to\";\n    }\n\n    @Override\n    public Component getUiComponent() {\n        return this.tab;\n    }\n\n    public ImageIcon createImageIcon(String path, String description, int width, int height) {\n        java.net.URL imgURL = getClass().getResource(path);\n        if (imgURL != null) {\n            ImageIcon icon = new ImageIcon(imgURL);\n            Image image = icon.getImage().getScaledInstance(width, height,  Image.SCALE_SMOOTH);\n            return new ImageIcon(image, description);\n        } else {\n            BurpExtender.printErr(\"Couldn't find file: \" + path);\n            return null;\n        }\n    }\n\n    public static JFrame getParent() {\n        return sendToTab.getParent();\n    }\n\n    public static IBurpExtenderCallbacks getCallbacks() {\n        return callbacks;\n    }\n\n    public static Config getConfig() {\n        return config;\n    }\n\n    public static void printOut(String s) {\n        stdout.println(s);\n    }\n\n    public static void printErr(String s) {\n        stderr.println(s);\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/Cookie.java",
    "content": "package burp;\n\nimport com.google.common.collect.Lists;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Implements the ICookie interface which holds details about an HTTP cookie.\n * @author Thomas Engel\n * @author August Detlefsen [augustd at codemagi dot com]\n */\npublic class Cookie implements ICookie {\n\n    private String name;\n    private String value;\n    private String domain;\n    private String path;\n    private Date expiration;\n    private Long maxAge;\n    private Boolean secure = false;\n    private Boolean httpOnly = false;\n\n    public Cookie(String name, String value) {\n        this.name = name;\n        this.value = value;\n    }\n\n    public Cookie(ICookie cookie) {\n        this.name = cookie.getName();\n        this.value = cookie.getValue();\n        this.domain = cookie.getDomain();\n        this.path = cookie.getPath();\n        this.expiration = cookie.getExpiration();\n    }\n\n    /**\n     * Parses a list of HTTP response header fields containing the raw cookie\n     * _value_ (Minus \"Set-Cookie:\").\n     *\n     * @param rawCookies A list of string containing the raw cookie\n     * @return A list of ICookie objects parsed from a list of raw cookie strings\n     */\n    public static List<ICookie> parseResponseCookies(List<String> rawCookies) {\n        return rawCookies.stream().map(Cookie::parseResponseCookie).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());\n    }\n\n    /**\n     * Parses a cookie from a String containing the raw HTTP response header\n     * _value_ (Minus \"Set-Cookie:\").\n     *\n     * @param rawCookie A String containing the raw cookie\n     * @return A Cookie object parsed from the raw cookie string\n     */\n    private static Optional<ICookie> parseResponseCookie(String rawCookie) {\n        String[] rawCookieParams = rawCookie.split(\";\");\n\n        //get the cookie name, check for valid cookie\n        String[] rawCookieNameAndValue = rawCookieParams[0].split(\"=\");\n        String cookieName = rawCookieNameAndValue[0].trim();\n        if (cookieName.isEmpty()) {\n            BurpExtender.printErr(\"Invalid cookie: missing name\");\n            return Optional.empty();\n        }\n\n        //get the cookie value\n        String cookieValue = rawCookieNameAndValue[1].trim();\n\n        //construct output\n        Cookie output = new Cookie(cookieName, cookieValue);\n\n        //parse other cookie params\n        for (int i = 1; i < rawCookieParams.length; i++) {\n            String[] rawCookieParam = rawCookieParams[i].trim().split(\"=\");\n\n            String paramName = rawCookieParam[0].trim();\n\n            if (\"secure\".equalsIgnoreCase(paramName)) {\n                output.setSecure(true);\n\n            } else if (\"HttpOnly\".equalsIgnoreCase(paramName)) {\n                output.setHttpOnly(true);\n\n            } else {\n                if (rawCookieParam.length != 2) {\n                    //attribute not a flag or missing value\n                    continue;\n                }\n                String paramValue = rawCookieParam[1].trim();\n\n                if (\"expires\".equalsIgnoreCase(paramName)) {\n                    try {\n                        SimpleDateFormat format = new SimpleDateFormat(\"EEE, d MMM yyyy HH:mm:ss zzz\");\n                        Date expiryDate = format.parse(paramValue);\n                        output.setExpiration(expiryDate);\n                    } catch (Exception e) {\n                        //couldn't parse date, ignore\n                        BurpExtender.printErr(\"WARNING: unable to parse cookie expiration: \" + paramValue);\n                    }\n                } else if (\"max-age\".equalsIgnoreCase(paramName)) {\n                    long maxAge = Long.parseLong(paramValue);\n                    output.setMaxAge(maxAge);\n                } else if (\"domain\".equalsIgnoreCase(paramName)) {\n                    output.setDomain(paramValue);\n                } else if (\"path\".equalsIgnoreCase(paramName)) {\n                    output.setPath(paramValue);\n                }\n            }\n        }\n\n        return Optional.of(output);\n    }\n\n    /**\n     * Parses cookies from a list of raw HTTP request headers\n     * _value_ (Minus \"Cookie:\").\n     *\n     * @param rawCookies A list of strings containing the raw cookie\n     * @return A list of ICookie objects parsed from a list of raw cookie strings\n     */\n    public static List<ICookie> parseRequestCookies(List<String> rawCookies) {\n        return rawCookies.stream().map(Cookie::parseRequestCookies).flatMap(Collection::stream).collect(Collectors.toList());\n    }\n\n    /**\n     * Parses a cookie from a String containing the raw HTTP request header\n     * _value_ (Minus \"Cookie:\").\n     *\n     * @param rawCookie A String containing the raw cookie\n     * @return A list of Cookie objects parsed from the raw cookie string\n     */\n    public static List<ICookie> parseRequestCookies(String rawCookie) {\n        List<ICookie> cookies = Lists.newArrayList();\n        String[] rawCookieParams = rawCookie.split(\";\");\n        for (String rawCookieParam : rawCookieParams) {\n            //get the cookie name, check for valid cookie\n            String[] rawCookieNameAndValue = rawCookieParam.split(\"=\");\n            String cookieName = rawCookieNameAndValue[0].trim();\n            if (cookieName.isEmpty() || !rawCookieParam.contains(\"=\")) {\n                BurpExtender.printErr(\"Invalid cookie: missing name\");\n                continue;\n            }\n\n            //get the cookie value\n            String cookieValue = \"\";\n            if (rawCookieNameAndValue.length != 1) {\n                cookieValue = rawCookieNameAndValue[1].trim();\n            }\n\n            //construct output\n            cookies.add(new Cookie(cookieName, cookieValue));\n        }\n\n        return cookies;\n    }\n\n    @Override\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public String getDomain() {\n        return domain;\n    }\n\n    public void setDomain(String domain) {\n        this.domain = domain;\n    }\n\n    @Override\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    @Override\n    public Date getExpiration() {\n        return expiration;\n    }\n\n    public void setExpiration(Date expiration) {\n        this.expiration = expiration;\n    }\n\n    public Long getMaxAge() {\n        return maxAge;\n    }\n\n    public void setMaxAge(Long maxAge) {\n        this.maxAge = maxAge;\n    }\n\n    public Boolean getSecure() {\n        return secure;\n    }\n\n    public void setSecure(Boolean secure) {\n        this.secure = secure;\n    }\n\n    public Boolean getHttpOnly() {\n        return httpOnly;\n    }\n\n    public void setHttpOnly(Boolean httpOnly) {\n        this.httpOnly = httpOnly;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        Cookie cookie = (Cookie) o;\n        return Objects.equals(name, cookie.name) &&\n                Objects.equals(value, cookie.value) &&\n                Objects.equals(domain, cookie.domain) &&\n                Objects.equals(path, cookie.path) &&\n                Objects.equals(expiration, cookie.expiration) &&\n                Objects.equals(maxAge, cookie.maxAge) &&\n                Objects.equals(secure, cookie.secure) &&\n                Objects.equals(httpOnly, cookie.httpOnly);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(name, value, domain, path, expiration, maxAge, secure, httpOnly);\n    }\n}"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/IRequestInfoWrapper.java",
    "content": "package burp;\n\nimport java.util.List;\n\npublic interface IRequestInfoWrapper extends IRequestInfo {\n\n    List<ICookie> getCookies();\n    String getBody();\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/IRequestResponseHolder.java",
    "content": "package burp;\n\npublic interface IRequestResponseHolder {\n    IRequestInfo getRequestInfo();\n\n    IResponseInfo getResponseInfo();\n\n    IBurpExtenderCallbacks getBurpExtenderCallbacks();\n\n    IHttpRequestResponse getHttpRequestResponse();\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/IResponseInfoWrapper.java",
    "content": "package burp;\n\nimport java.util.List;\n\npublic interface IResponseInfoWrapper extends IResponseInfo {\n\n    List<ICookie> getCookies();\n    String getBody();\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/RequestInfoWrapper.java",
    "content": "package burp;\n\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class RequestInfoWrapper implements IRequestInfoWrapper {\n\n    private IHttpRequestResponse httpRequestResponse;\n    private IRequestInfo requestInfo;\n    private List<ICookie> cookies;\n    private String requestBody;\n\n    public RequestInfoWrapper(IHttpRequestResponse httpRequestResponse, IRequestInfo requestInfo) {\n        this.httpRequestResponse = httpRequestResponse;\n        this.requestInfo = requestInfo;\n    }\n\n    @Override\n    public List<ICookie> getCookies() {\n        if (cookies == null) {\n            String cookieHeaderPrefix = \"cookie: \";\n            cookies = Cookie.parseRequestCookies(requestInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList()));\n        }\n        return cookies;\n    }\n\n    @Override\n    public String getBody() {\n        if (requestBody == null) {\n            byte[] request = httpRequestResponse.getRequest();\n            int bodyOffset = this.getBodyOffset();\n            requestBody = new String(Arrays.copyOfRange(request, bodyOffset, request.length));\n        }\n        return requestBody;\n    }\n\n    @Override\n    public String getMethod() {\n        return requestInfo.getMethod();\n    }\n\n    @Override\n    public URL getUrl() {\n        return requestInfo.getUrl();\n    }\n\n    @Override\n    public List<String> getHeaders() {\n        return requestInfo.getHeaders();\n    }\n\n    @Override\n    public List<IParameter> getParameters() {\n        return requestInfo.getParameters();\n    }\n\n    @Override\n    public int getBodyOffset() {\n        return requestInfo.getBodyOffset();\n    }\n\n    @Override\n    public byte getContentType() {\n        return requestInfo.getContentType();\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/RequestResponseHolder.java",
    "content": "package burp;\n\npublic class RequestResponseHolder implements IRequestResponseHolder {\n\n    private final IBurpExtenderCallbacks burpExtenderCallbacks;\n    private final IHttpRequestResponse httpRequestResponse;\n    private IRequestInfoWrapper requestInfo;\n    private IResponseInfoWrapper responseInfo;\n\n    public RequestResponseHolder(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse httpRequestResponse) {\n        this.burpExtenderCallbacks = burpExtenderCallbacks;\n        this.httpRequestResponse = httpRequestResponse;\n    }\n\n    @Override\n    public IRequestInfoWrapper getRequestInfo() {\n        if (requestInfo == null) {\n            requestInfo = new RequestInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeRequest(httpRequestResponse.getHttpService(), httpRequestResponse.getRequest()));\n        }\n        return requestInfo;\n    }\n\n    @Override\n    public IResponseInfoWrapper getResponseInfo() {\n        if (responseInfo == null) {\n            responseInfo = new ResponseInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeResponse(httpRequestResponse.getResponse()));\n        }\n        return responseInfo;\n    }\n\n    @Override\n    public IBurpExtenderCallbacks getBurpExtenderCallbacks() {\n        return burpExtenderCallbacks;\n    }\n\n    @Override\n    public IHttpRequestResponse getHttpRequestResponse() {\n        return httpRequestResponse;\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/burp/ResponseInfoWrapper.java",
    "content": "package burp;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class ResponseInfoWrapper implements IResponseInfoWrapper {\n\n    private IHttpRequestResponse httpRequestResponse;\n    private IResponseInfo responseInfo;\n    private List<ICookie> cookies;\n    private String responseBody;\n\n    public ResponseInfoWrapper(IHttpRequestResponse httpRequestResponse, IResponseInfo responseInfo) {\n        this.httpRequestResponse = httpRequestResponse;\n        this.responseInfo = responseInfo;\n    }\n\n    @Override\n    public List<String> getHeaders() {\n        return responseInfo.getHeaders();\n    }\n\n    @Override\n    public int getBodyOffset() {\n        return responseInfo.getBodyOffset();\n    }\n\n    @Override\n    public short getStatusCode() {\n        return responseInfo.getStatusCode();\n    }\n\n    @Override\n    public List<ICookie> getCookies() {\n        if (cookies == null) {\n            String cookieHeaderPrefix = \"set-cookie: \";\n            cookies = Cookie.parseResponseCookies(responseInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList()));\n        }\n        return cookies;\n    }\n\n    @Override\n    public String getStatedMimeType() {\n        return responseInfo.getStatedMimeType();\n    }\n\n    @Override\n    public String getInferredMimeType() {\n        return responseInfo.getInferredMimeType();\n    }\n\n    @Override\n    public String getBody() {\n        if (responseBody == null) {\n            byte[] response = httpRequestResponse.getResponse();\n            int bodyOffset = this.getBodyOffset();\n            responseBody = new String(Arrays.copyOfRange(response, bodyOffset, response.length));\n        }\n        return responseBody;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java",
    "content": "/*\n * Copyright (C) 2011 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.gson.typeadapters;\n\nimport com.google.gson.*;\nimport com.google.gson.internal.Streams;\nimport com.google.gson.reflect.TypeToken;\nimport com.google.gson.stream.JsonReader;\nimport com.google.gson.stream.JsonWriter;\n\nimport java.io.IOException;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Adapts values whose runtime type may differ from their declaration type. This\n * is necessary when a field's type is not the same type that GSON should create\n * when deserializing that field. For example, consider these types:\n * <pre>   {@code\n *   abstract class Shape {\n *     int x;\n *     int y;\n *   }\n *   class Circle extends Shape {\n *     int radius;\n *   }\n *   class Rectangle extends Shape {\n *     int width;\n *     int height;\n *   }\n *   class Diamond extends Shape {\n *     int width;\n *     int height;\n *   }\n *   class Drawing {\n *     Shape bottomShape;\n *     Shape topShape;\n *   }\n * }</pre>\n * <p>Without additional type information, the serialized JSON is ambiguous. Is\n * the bottom shape in this drawing a rectangle or a diamond? <pre>   {@code\n *   {\n *     \"bottomShape\": {\n *       \"width\": 10,\n *       \"height\": 5,\n *       \"x\": 0,\n *       \"y\": 0\n *     },\n *     \"topShape\": {\n *       \"radius\": 2,\n *       \"x\": 4,\n *       \"y\": 1\n *     }\n *   }}</pre>\n * This class addresses this problem by adding type information to the\n * serialized JSON and honoring that type information when the JSON is\n * deserialized: <pre>   {@code\n *   {\n *     \"bottomShape\": {\n *       \"type\": \"Diamond\",\n *       \"width\": 10,\n *       \"height\": 5,\n *       \"x\": 0,\n *       \"y\": 0\n *     },\n *     \"topShape\": {\n *       \"type\": \"Circle\",\n *       \"radius\": 2,\n *       \"x\": 4,\n *       \"y\": 1\n *     }\n *   }}</pre>\n * Both the type field name ({@code \"type\"}) and the type labels ({@code\n * \"Rectangle\"}) are configurable.\n *\n * <h3>Registering Types</h3>\n * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field\n * name to the {@link #of} factory method. If you don't supply an explicit type\n * field name, {@code \"type\"} will be used. <pre>   {@code\n *   RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory\n *       = RuntimeTypeAdapterFactory.of(Shape.class, \"type\");\n * }</pre>\n * Next register all of your subtypes. Every subtype must be explicitly\n * registered. This protects your application from injection attacks. If you\n * don't supply an explicit type label, the type's simple name will be used.\n * <pre>   {@code\n *   shapeAdapterFactory.registerSubtype(Rectangle.class, \"Rectangle\");\n *   shapeAdapterFactory.registerSubtype(Circle.class, \"Circle\");\n *   shapeAdapterFactory.registerSubtype(Diamond.class, \"Diamond\");\n * }</pre>\n * Finally, register the type adapter factory in your application's GSON builder:\n * <pre>   {@code\n *   Gson gson = new GsonBuilder()\n *       .registerTypeAdapterFactory(shapeAdapterFactory)\n *       .create();\n * }</pre>\n * Like {@code GsonBuilder}, this API supports chaining: <pre>   {@code\n *   RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)\n *       .registerSubtype(Rectangle.class)\n *       .registerSubtype(Circle.class)\n *       .registerSubtype(Diamond.class);\n * }</pre>\n *\n * <h3>Serialization and deserialization</h3>\n * In order to serialize and deserialize a polymorphic object,\n * you must specify the base type explicitly.\n * <pre>   {@code\n *   Diamond diamond = new Diamond();\n *   String json = gson.toJson(diamond, Shape.class);\n * }</pre>\n * And then:\n * <pre>   {@code\n *   Shape shape = gson.fromJson(json, Shape.class);\n * }</pre>\n */\npublic final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {\n  private final Class<?> baseType;\n  private final String typeFieldName;\n  private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();\n  private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();\n  private final boolean maintainType;\n\n  private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {\n    if (typeFieldName == null || baseType == null) {\n      throw new NullPointerException();\n    }\n    this.baseType = baseType;\n    this.typeFieldName = typeFieldName;\n    this.maintainType = maintainType;\n  }\n\n  /**\n   * Creates a new runtime type adapter using for {@code baseType} using {@code\n   * typeFieldName} as the type field name. Type field names are case sensitive.\n   * {@code maintainType} flag decide if the type will be stored in pojo or not.\n   */\n  public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {\n    return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, maintainType);\n  }\n  \n  /**\n   * Creates a new runtime type adapter using for {@code baseType} using {@code\n   * typeFieldName} as the type field name. Type field names are case sensitive.\n   */\n  public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {\n    return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, false);\n  }\n\n  /**\n   * Creates a new runtime type adapter for {@code baseType} using {@code \"type\"} as\n   * the type field name.\n   */\n  public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {\n    return new RuntimeTypeAdapterFactory<T>(baseType, \"type\", false);\n  }\n\n  /**\n   * Registers {@code type} identified by {@code label}. Labels are case\n   * sensitive.\n   *\n   * @throws IllegalArgumentException if either {@code type} or {@code label}\n   *     have already been registered on this type adapter.\n   */\n  public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {\n    if (type == null || label == null) {\n      throw new NullPointerException();\n    }\n    if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {\n      throw new IllegalArgumentException(\"types and labels must be unique\");\n    }\n    labelToSubtype.put(label, type);\n    subtypeToLabel.put(type, label);\n    return this;\n  }\n\n  /**\n   * Registers {@code type} identified by its {@link Class#getSimpleName simple\n   * name}. Labels are case sensitive.\n   *\n   * @throws IllegalArgumentException if either {@code type} or its simple name\n   *     have already been registered on this type adapter.\n   */\n  public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {\n    return registerSubtype(type, type.getSimpleName());\n  }\n\n  public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {\n    if (type.getRawType() != baseType) {\n      return null;\n    }\n\n    final Map<String, TypeAdapter<?>> labelToDelegate\n        = new LinkedHashMap<String, TypeAdapter<?>>();\n    final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate\n        = new LinkedHashMap<Class<?>, TypeAdapter<?>>();\n    for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {\n      TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));\n      labelToDelegate.put(entry.getKey(), delegate);\n      subtypeToDelegate.put(entry.getValue(), delegate);\n    }\n\n    return new TypeAdapter<R>() {\n      @Override public R read(JsonReader in) throws IOException {\n        JsonElement jsonElement = Streams.parse(in);\n        JsonElement labelJsonElement;\n        if (maintainType) {\n            labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);\n        } else {\n            labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);\n        }\n        \n        if (labelJsonElement == null) {\n          throw new JsonParseException(\"cannot deserialize \" + baseType\n              + \" because it does not define a field named \" + typeFieldName);\n        }\n        String label = labelJsonElement.getAsString();\n        @SuppressWarnings(\"unchecked\") // registration requires that subtype extends T\n        TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);\n        if (delegate == null) {\n          throw new JsonParseException(\"cannot deserialize \" + baseType + \" subtype named \"\n              + label + \"; did you forget to register a subtype?\");\n        }\n        return delegate.fromJsonTree(jsonElement);\n      }\n\n      @Override public void write(JsonWriter out, R value) throws IOException {\n        Class<?> srcType = value.getClass();\n        String label = subtypeToLabel.get(srcType);\n        @SuppressWarnings(\"unchecked\") // registration requires that subtype extends T\n        TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);\n        if (delegate == null) {\n          throw new JsonParseException(\"cannot serialize \" + srcType.getName()\n              + \"; did you forget to register a subtype?\");\n        }\n        JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();\n\n        if (maintainType) {\n          Streams.write(jsonObject, out);\n          return;\n        }\n\n        JsonObject clone = new JsonObject();\n\n        if (jsonObject.has(typeFieldName)) {\n          throw new JsonParseException(\"cannot serialize \" + srcType.getName()\n              + \" because it already defines a field named \" + typeFieldName);\n        }\n        clone.add(typeFieldName, new JsonPrimitive(label));\n        \n        for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {\n          clone.add(e.getKey(), e.getValue());\n        }\n        Streams.write(clone, out);\n      }\n    }.nullSafe();\n  }\n}"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/builder/CommandBuilder.java",
    "content": "package net.bytebutcher.burpsendtoextension.builder;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.Context;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\npublic class CommandBuilder {\n\n    // The model of the context menu entry with the format string and various options.\n    private final CommandObject commandObject;\n\n    // List of selected messages containing the placeholders and their values.\n    private final List<Map<String, IPlaceholderParser>> placeholderMap;\n\n    // List of placeholders and merged values.\n    private final Map<Placeholder, String> placeholderValues;\n\n    private final Context context;\n\n    private static class Placeholder {\n\n        private final String name;\n        private final IPlaceholderBehaviour behaviour;\n\n        public Placeholder(CommandObject.Placeholder placeholder) {\n            this.name = placeholder.getName();\n            this.behaviour = placeholder.getBehaviour();\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            Placeholder that = (Placeholder) o;\n            return Objects.equals(name, that.name) && Objects.equals(behaviour, that.behaviour);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(name, behaviour);\n        }\n    }\n\n    public CommandBuilder(CommandObject commandObject, List<Map<String, IPlaceholderParser>> placeholderMap, Context context) throws Exception {\n        this.commandObject = commandObject;\n        this.placeholderMap = placeholderMap;\n        this.context = context;\n        this.placeholderValues = initPlaceholderValues();\n    }\n\n    public Map<Placeholder, String> initPlaceholderValues() throws Exception {\n        Map<Placeholder, String> placeholderValues = Maps.newHashMap();\n        for (CommandObject.Placeholder coPlaceholder : commandObject.getPlaceholders()) {\n            Placeholder cbPlaceholder = new Placeholder(coPlaceholder);\n            if (!placeholderValues.containsKey(cbPlaceholder)) {\n                if (coPlaceholder.getBehaviour() instanceof StringSeparatedPlaceholderBehaviour){\n                    // combine the values of all messages using the defined placeholder separator\n                    placeholderValues.put(cbPlaceholder, commandObject.getValid(placeholderMap, context).stream()\n                            .map(m -> m.get(coPlaceholder.getName()))\n                            .map(iPlaceholder -> iPlaceholder.getValue(context))\n                            .collect(Collectors.joining(((StringSeparatedPlaceholderBehaviour) coPlaceholder.getBehaviour()).getSeparator())));\n                } else if (coPlaceholder.getBehaviour() instanceof FileSeparatedPlaceholderBehaviour){\n                    // combine the values of all messages and write them into a file.\n                    placeholderValues.put(cbPlaceholder, writeToFile(commandObject.getValid(placeholderMap, context).stream()\n                            .map(m -> m.get(coPlaceholder.getName()))\n                            .map(iPlaceholder -> iPlaceholder.getValue(context))\n                            .collect(Collectors.joining(\"\\n\"))));\n                }\n            }\n        }\n        return placeholderValues;\n    }\n\n    /**\n     * Returns the command while all placeholders are replaced with their associated value as String.\n     * @throws Exception when retrieving/replacing a placeholder failed.\n     */\n    public String build() throws Exception {\n        try {\n            List<String> result = Lists.newArrayList();\n            boolean containsCommandSeparatedPlaceholderBehaviour = commandObject.getPlaceholders().stream()\n                    .map(CommandObject.Placeholder::getBehaviour)\n                    .anyMatch(c -> c instanceof CommandSeparatedPlaceholderBehaviour);\n            if (containsCommandSeparatedPlaceholderBehaviour) {\n                for (int messageIndex = 0; messageIndex < placeholderMap.size(); messageIndex++) {\n                    result.add(buildByMessage(messageIndex));\n                }\n            } else {\n                result.add(buildByMessage(0));\n            }\n            return String.join(\"\\n\", result);\n        } catch (RuntimeException e) {\n            // Rethrow from unchecked to checked exception. We only deal with RuntimeException here, since streams\n            // (here: placeholderMap.stream()) does not handle checked exceptions well.\n            throw new Exception(e);\n        }\n    }\n\n    private String buildByMessage(int messageIndex) throws Exception {\n        StringBuffer format = new StringBuffer(commandObject.getFormat());\n        // For each placeholder, starting from the placeholder at the very end, replace it with the value from the message.\n        List<CommandObject.Placeholder> placeholders = commandObject.getPlaceholders().stream().sorted(\n                Comparator.comparing(CommandObject.Placeholder::getEnd).reversed()\n        ).collect(Collectors.toList());\n        for (CommandObject.Placeholder placeholder : placeholders) {\n            replaceCommandPlaceholder(placeholder, placeholderMap, messageIndex, format, context);\n        }\n        return format.toString();\n    }\n\n    private void replaceCommandPlaceholder(CommandObject.Placeholder placeholder, List<Map<String, IPlaceholderParser>> placeholderMap, int messageIndex, StringBuffer command, Context context) throws Exception {\n        String value;\n        if (placeholderValues.containsKey(new Placeholder(placeholder))) {\n            // merged values\n            value = placeholderValues.get(new Placeholder(placeholder));\n        } else {\n            // command separated - use the value from the actual message\n            value = placeholderMap.get(messageIndex).get(placeholder.getName()).getValue(context);\n        }\n        command.replace(placeholder.getStart(), placeholder.getEnd(), value);\n    }\n\n    private String writeToFile(String value) throws Exception {\n        try {\n            File tmp = File.createTempFile(\"burp_\", \".snd\");\n            PrintWriter out = new PrintWriter(tmp.getPath());\n            out.write(value);\n            out.flush();\n            return tmp.getAbsolutePath();\n        } catch (RuntimeException e) {\n            throw new Exception(this.getClass().getSimpleName() + \": Error writing to temporary file!\", e);\n        }\n    }\n}"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/executioner/CommandExecutioner.java",
    "content": "package net.bytebutcher.burpsendtoextension.executioner;\n\nimport burp.BurpExtender;\nimport burp.IHttpRequestResponse;\nimport com.google.common.collect.Lists;\nimport net.bytebutcher.burpsendtoextension.gui.util.SelectionUtil;\nimport net.bytebutcher.burpsendtoextension.models.Context;\nimport net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour;\nimport net.bytebutcher.burpsendtoextension.utils.OsUtils;\nimport net.bytebutcher.burpsendtoextension.utils.StringUtils;\n\nimport java.io.IOException;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class CommandExecutioner {\n\n    private final ERunInTerminalBehaviour runInTerminalBehaviour;\n    private final boolean shouldOutputReplaceSelection;\n    private final Context context;\n\n    public CommandExecutioner(boolean shouldOutputReplaceSelection, Context context) {\n        this.runInTerminalBehaviour = null;\n        this.shouldOutputReplaceSelection = shouldOutputReplaceSelection;\n        this.context = context;\n    }\n\n    public CommandExecutioner(ERunInTerminalBehaviour runInTerminalBehaviour, boolean shouldOutputReplaceSelection, Context context) {\n        this.runInTerminalBehaviour = runInTerminalBehaviour;\n        this.shouldOutputReplaceSelection = shouldOutputReplaceSelection;\n        this.context = context;\n    }\n\n    public void execute(String commands) throws Exception {\n        if (commands != null) {\n            List<String> commandOutput = Lists.newArrayList();\n            if (runInTerminalBehaviour != null && runInTerminalBehaviour == ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL) {\n                // Run commands sequential within terminal\n                String command = Arrays.stream(commands.split(\"\\n\")).collect(Collectors.joining(\" ; \"));\n                execute(command, commandOutput);\n            } else {\n                // Run commands in parallel (within separate terminals or in background)\n                for (String command : commands.split(\"\\n\")) {\n                    execute(command, commandOutput);\n                }\n            }\n            if (!commandOutput.isEmpty()) {\n                replaceSelectedText(context, commandOutput.stream().collect(Collectors.joining(\"\\n\")));\n            }\n        }\n    }\n\n    private void execute(String command, List<String> commandOutput) throws IOException {\n        ProcessBuilder commandProcessBuilder = getProcessBuilder(command);\n        logCommandToBeExecuted(commandProcessBuilder.command().toArray(new String[commandProcessBuilder.command().size()]));\n        Process process = commandProcessBuilder.start();\n        if (shouldOutputReplaceSelection) {\n            commandOutput.add(StringUtils.fromInputStream(process.getInputStream()));\n        }\n    }\n\n    private ProcessBuilder getProcessBuilder(String command) {\n        if (runInTerminalBehaviour != null) {\n            return new ProcessBuilder(formatCommandForRunningInTerminal(command));\n        } else {\n            return new ProcessBuilder(formatCommandForRunningOnOperatingSystem(command));\n        }\n    }\n\n    private String[] formatCommandForRunningOnOperatingSystem(String command) {\n        String[] commandToBeExecuted;\n        if (OsUtils.isWindows()) {\n            commandToBeExecuted = new String[]{\"cmd\", \"/c\", command};\n        } else {\n            commandToBeExecuted = new String[]{\"/bin/bash\", \"-c\", command};\n        }\n        return commandToBeExecuted;\n    }\n\n    private String[] formatCommandForRunningInTerminal(String command) {\n        String[] commandToBeExecuted = BurpExtender.getConfig().getRunInTerminalCommand().split(\" \");\n        for (int i = 0; i < commandToBeExecuted.length; i++) {\n            String commandPart = commandToBeExecuted[i];\n            if (\"%C\".equals(commandPart)) {\n                commandToBeExecuted[i] = command;\n            }\n        }\n        return commandToBeExecuted;\n    }\n\n    private void replaceSelectedText(Context context, String replaceText) throws Exception {\n        if (context.getSelectedMessages() != null && context.getSelectedMessages().length > 0) {\n            IHttpRequestResponse message = context.getSelectedMessages()[0];\n            switch (context.getOrigin()) {\n                case HTTP_REQUEST:\n                    message.setRequest(SelectionUtil.replaceSelectedText(message.getRequest(), context.getSelectionBounds(), replaceText));\n                    break;\n                case HTTP_RESPONSE:\n                    message.setResponse(SelectionUtil.replaceSelectedText(message.getResponse(), context.getSelectionBounds(), replaceText));\n                    break;\n            }\n        }\n    }\n\n    private void logCommandToBeExecuted(String[] commandToBeExecuted) {\n        String commandToBeExecutedWithoutControlCharacters = String.join(\" \", commandToBeExecuted).replaceAll(\"[\\u0000-\\u001f]\", \"\");\n        String dateTime = ZonedDateTime.now().format(DateTimeFormatter.ofPattern(\"uuuu/MM/dd HH:mm:ss\"));\n        BurpExtender.printOut(\"[\" + dateTime + \"] \" + commandToBeExecutedWithoutControlCharacters);\n        BurpExtender.printOut(\"----------------------------------------------------------------------\");\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/CommandsChangeListener.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\r\n\r\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\r\n\r\nimport java.util.List;\r\n\r\npublic interface CommandsChangeListener {\r\n\r\n    void commandsChanged(List<CommandObject> commandObjects);\r\n}\r\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToAddAdvancedDialog\">\n  <grid id=\"cbd77\" binding=\"contentPane\" layout-manager=\"GridLayoutManager\" row-count=\"2\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"10\" left=\"10\" bottom=\"10\" right=\"10\"/>\n    <constraints>\n      <xy x=\"48\" y=\"54\" width=\"635\" height=\"297\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <grid id=\"94766\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <hspacer id=\"98af6\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <grid id=\"9538f\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"true\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"e7465\" class=\"javax.swing.JButton\" binding=\"buttonOK\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"OK\"/>\n                </properties>\n              </component>\n              <component id=\"5723f\" class=\"javax.swing.JButton\" binding=\"buttonCancel\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"Cancel\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n        </children>\n      </grid>\n      <grid id=\"e3588\" layout-manager=\"GridLayoutManager\" row-count=\"2\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"57e4a\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Customize the behaviour of each placeholder when multiple HTTP messages are selected:\"/>\n            </properties>\n          </component>\n          <grid id=\"4c127\" layout-manager=\"GridLayoutManager\" row-count=\"2\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <vspacer id=\"b4d73\">\n                <constraints>\n                  <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n              </vspacer>\n              <grid id=\"8d102\" binding=\"pnlPlaceholderBehaviour\" custom-create=\"true\" layout-manager=\"BorderLayout\" hgap=\"0\" vgap=\"0\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties/>\n                <border type=\"none\"/>\n                <children/>\n              </grid>\n            </children>\n          </grid>\n        </children>\n      </grid>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport com.google.common.collect.Lists;\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport com.intellij.uiDesigner.core.Spacer;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.util.List;\n\npublic class SendToAddAdvancedDialog extends JDialog {\n    private final Component parent;\n    private final List<CommandObject.Placeholder> placeholders;\n    private JPanel contentPane;\n    private JButton buttonOK;\n    private JButton buttonCancel;\n    private JPanel pnlPlaceholderBehaviour;\n\n    private boolean success = false;\n\n    public SendToAddAdvancedDialog(Component parent, List<CommandObject.Placeholder> placeholders) {\n        this.parent = parent;\n        this.placeholders = placeholders;\n        $$$setupUI$$$();\n\n        setContentPane(contentPane);\n        setTitle(\"Customize Placeholder Behaviour\");\n        setModal(true);\n        getRootPane().setDefaultButton(buttonOK);\n\n        buttonOK.addActionListener(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                onOK();\n            }\n        });\n\n        buttonCancel.addActionListener(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                onCancel();\n            }\n        });\n\n        // call onCancel() when cross is clicked\n        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\n        addWindowListener(new WindowAdapter() {\n            public void windowClosing(WindowEvent e) {\n                onCancel();\n            }\n        });\n\n        // call onCancel() on ESCAPE\n        contentPane.registerKeyboardAction(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                onCancel();\n            }\n        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);\n    }\n\n    public List<CommandObject.Placeholder> getPlaceholders() {\n        List<CommandObject.Placeholder> placeholders = Lists.newArrayList();\n        for (Component component : pnlPlaceholderBehaviour.getComponents()) {\n            placeholders.add(((SendToAddAdvancedPlaceholderBehaviourPanel) component).getPlaceholder());\n        }\n        return placeholders;\n    }\n\n    private void onOK() {\n        success = true;\n        dispose();\n    }\n\n    private void onCancel() {\n        success = false;\n        dispose();\n    }\n\n    public boolean run() {\n        for (CommandObject.Placeholder placeholder : placeholders) {\n            pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel(placeholder));\n        }\n        this.setSize(450, 250);\n        int x = DialogUtil.getX(parent, this);\n        int y = DialogUtil.getY(parent, this);\n        this.setLocation(x, y);\n        this.pack();\n        this.setVisible(true);\n        return success;\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        createUIComponents();\n        contentPane = new JPanel();\n        contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));\n        final JPanel panel1 = new JPanel();\n        panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false));\n        final Spacer spacer1 = new Spacer();\n        panel1.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n        final JPanel panel2 = new JPanel();\n        panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false));\n        panel1.add(panel2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        buttonOK = new JButton();\n        buttonOK.setText(\"OK\");\n        panel2.add(buttonOK, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        buttonCancel = new JButton();\n        buttonCancel.setText(\"Cancel\");\n        panel2.add(buttonCancel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel3 = new JPanel();\n        panel3.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));\n        contentPane.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JLabel label1 = new JLabel();\n        label1.setText(\"Customize the behaviour of each placeholder when multiple HTTP messages are selected:\");\n        panel3.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel4 = new JPanel();\n        panel4.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));\n        panel3.add(panel4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final Spacer spacer2 = new Spacer();\n        panel4.add(spacer2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));\n        panel4.add(pnlPlaceholderBehaviour, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return contentPane;\n    }\n\n    private void createUIComponents() {\n        pnlPlaceholderBehaviour = new JPanel();\n        pnlPlaceholderBehaviour.setLayout(new BoxLayout(this.pnlPlaceholderBehaviour, BoxLayout.Y_AXIS));\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToAddAdvancedPlaceholderBehaviourPanel\">\n  <grid id=\"27dc6\" binding=\"pnlMain\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"3\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"461\" height=\"59\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <component id=\"7ee4a\" class=\"javax.swing.JTextField\" binding=\"txtSeparator\">\n        <constraints>\n          <grid row=\"0\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n            <preferred-size width=\"150\" height=\"-1\"/>\n          </grid>\n        </constraints>\n        <properties/>\n      </component>\n      <component id=\"e079c\" class=\"javax.swing.JComboBox\" binding=\"cmbSeperator\">\n        <constraints>\n          <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"2\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <model>\n            <item value=\"split into separate commands\"/>\n            <item value=\"separate by string\"/>\n            <item value=\"merge into file\"/>\n          </model>\n        </properties>\n      </component>\n      <component id=\"c94a2\" class=\"javax.swing.JLabel\" binding=\"lblPlaceholder\">\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"...\"/>\n        </properties>\n      </component>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\npublic class SendToAddAdvancedPlaceholderBehaviourPanel extends JPanel {\n\n    private final CommandObject.Placeholder placeholder;\n\n    private JTextField txtSeparator;\n    private JPanel pnlMain;\n    private JComboBox cmbSeperator;\n    private JLabel lblPlaceholder;\n\n    public SendToAddAdvancedPlaceholderBehaviourPanel(CommandObject.Placeholder placeholder) {\n        this.placeholder = placeholder;\n        this.add(pnlMain);\n        this.lblPlaceholder.setText(placeholder.getName());\n        initFields();\n        initEventListener();\n    }\n\n    private void initFields() {\n        IPlaceholderBehaviour placeholderBehaviour = placeholder.getBehaviour();\n        if (placeholderBehaviour instanceof StringSeparatedPlaceholderBehaviour) {\n            this.txtSeparator.setText(((StringSeparatedPlaceholderBehaviour) placeholderBehaviour).getSeparator());\n            this.txtSeparator.setEnabled(true);\n            this.cmbSeperator.setSelectedIndex(1);\n        } else if (placeholderBehaviour instanceof FileSeparatedPlaceholderBehaviour) {\n            this.txtSeparator.setText(\"\");\n            this.txtSeparator.setEnabled(false);\n            this.cmbSeperator.setSelectedIndex(2);\n        } else {\n            this.txtSeparator.setText(\"\");\n            this.txtSeparator.setEnabled(false);\n            this.cmbSeperator.setSelectedIndex(0);\n        }\n    }\n\n    private void initEventListener() {\n        this.cmbSeperator.addActionListener(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                if (cmbSeperator.getSelectedIndex() == 1) {\n                    txtSeparator.setEnabled(true);\n                    txtSeparator.setText(\"\");\n                } else {\n                    txtSeparator.setEnabled(false);\n                    txtSeparator.setText(\"\");\n                }\n            }\n        });\n    }\n\n    public CommandObject.Placeholder getPlaceholder() {\n        switch (cmbSeperator.getSelectedIndex()) {\n            case 1:\n                placeholder.setBehaviour(new StringSeparatedPlaceholderBehaviour(txtSeparator.getText()));\n                break;\n            case 2:\n                placeholder.setBehaviour(new FileSeparatedPlaceholderBehaviour());\n                break;\n            default:\n                placeholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour());\n                break;\n        }\n        return placeholder;\n    }\n\n    {\n// GUI initializer generated by IntelliJ IDEA GUI Designer\n// >>> IMPORTANT!! <<<\n// DO NOT EDIT OR ADD ANY CODE HERE!\n        $$$setupUI$$$();\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        pnlMain = new JPanel();\n        pnlMain.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1));\n        txtSeparator = new JTextField();\n        pnlMain.add(txtSeparator, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));\n        cmbSeperator = new JComboBox();\n        final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel();\n        defaultComboBoxModel1.addElement(\"split into separate commands\");\n        defaultComboBoxModel1.addElement(\"separate by string\");\n        defaultComboBoxModel1.addElement(\"merge into file\");\n        cmbSeperator.setModel(defaultComboBoxModel1);\n        pnlMain.add(cmbSeperator, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        lblPlaceholder = new JLabel();\n        lblPlaceholder.setText(\"...\");\n        pnlMain.add(lblPlaceholder, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return pnlMain;\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddDialog.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToAddDialog\">\n  <grid id=\"27dc6\" binding=\"formPanel\" layout-manager=\"GridLayoutManager\" row-count=\"3\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"10\" left=\"10\" bottom=\"10\" right=\"10\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"623\" height=\"306\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <component id=\"fb1e8\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Enter the details for the &quot;Send to...&quot; context menu entry.\"/>\n        </properties>\n      </component>\n      <grid id=\"8234f\" layout-manager=\"GridBagLayout\">\n        <constraints>\n          <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"e14c7\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag top=\"2\" left=\"2\" bottom=\"2\" right=\"10\" weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <labelFor value=\"ab56a\"/>\n              <text value=\"&amp;Name:\"/>\n            </properties>\n          </component>\n          <component id=\"8cad7\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag top=\"2\" left=\"2\" bottom=\"2\" right=\"10\" weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <labelFor value=\"476e7\"/>\n              <text value=\"Co&amp;mmand:\"/>\n            </properties>\n          </component>\n          <grid id=\"5d0aa\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"476e7\" class=\"javax.swing.JTextField\" binding=\"txtCommand\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"150\" height=\"-1\"/>\n                  </grid>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                </properties>\n              </component>\n              <component id=\"9f55b\" class=\"javax.swing.JButton\" binding=\"btnCommandHelp\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"?\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"c7d44\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag top=\"2\" left=\"2\" bottom=\"2\" right=\"10\" weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <labelFor value=\"532e8\"/>\n              <text value=\"&amp;Group:\"/>\n            </properties>\n          </component>\n          <component id=\"938ed\" class=\"javax.swing.JRadioButton\" binding=\"chkRunInTerminal\">\n            <constraints>\n              <grid row=\"4\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <text value=\"Run in &amp;terminal\"/>\n            </properties>\n          </component>\n          <component id=\"579d8\" class=\"javax.swing.JRadioButton\" binding=\"chkOutputShouldReplaceSelection\">\n            <constraints>\n              <grid row=\"5\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <text value=\"Output should &amp;replace selection\"/>\n            </properties>\n          </component>\n          <component id=\"1d8bf\" class=\"javax.swing.JCheckBox\" binding=\"chkShowPreviewPriorToExecution\">\n            <constraints>\n              <grid row=\"6\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag top=\"5\" left=\"2\" bottom=\"2\" right=\"2\" weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <selected value=\"true\"/>\n              <text value=\"Show &amp;preview prior to execution\"/>\n            </properties>\n          </component>\n          <component id=\"532e8\" class=\"javax.swing.JTextField\" binding=\"txtGroup\">\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"150\" height=\"-1\"/>\n              </grid>\n              <gridbag top=\"2\" left=\"2\" bottom=\"2\" right=\"2\" weightx=\"1.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"ab56a\" class=\"javax.swing.JTextField\" binding=\"txtName\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                <preferred-size width=\"150\" height=\"-1\"/>\n              </grid>\n              <gridbag top=\"2\" left=\"2\" bottom=\"2\" right=\"2\" weightx=\"1.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties/>\n          </component>\n          <component id=\"29c23\" class=\"javax.swing.JRadioButton\" binding=\"chkRunInBackground\">\n            <constraints>\n              <grid row=\"3\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n              <gridbag weightx=\"0.0\" weighty=\"0.0\"/>\n            </constraints>\n            <properties>\n              <selected value=\"true\"/>\n              <text value=\"Run in &amp;background\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"86a29\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"4\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"6cbe\" class=\"javax.swing.JButton\" binding=\"btnCancel\">\n            <constraints>\n              <grid row=\"0\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"&amp;Cancel\"/>\n            </properties>\n          </component>\n          <component id=\"816b1\" class=\"javax.swing.JButton\" binding=\"btnOk\">\n            <constraints>\n              <grid row=\"0\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"&amp;Ok\"/>\n            </properties>\n          </component>\n          <hspacer id=\"d81ad\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"6e292\" class=\"javax.swing.JButton\" binding=\"btnAdvanced\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"&amp;Advanced\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n    </children>\n  </grid>\n  <buttonGroups>\n    <group name=\"groupOutput\">\n      <member id=\"938ed\"/>\n      <member id=\"579d8\"/>\n      <member id=\"29c23\"/>\n    </group>\n  </buttonGroups>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddDialog.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport com.google.common.collect.Lists;\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport com.intellij.uiDesigner.core.Spacer;\nimport net.bytebutcher.burpsendtoextension.gui.listener.ToolTipActionListener;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.ERuntimeBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.Placeholders;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.util.List;\n\npublic class SendToAddDialog {\n    private JTextField txtName;\n    private JTextField txtCommand;\n    private JButton btnCancel;\n    private JButton btnOk;\n    private JPanel formPanel;\n    private JButton btnCommandHelp;\n    private JCheckBox chkShowPreviewPriorToExecution;\n    private JTextField txtGroup;\n    private JRadioButton chkRunInTerminal;\n    private JRadioButton chkOutputShouldReplaceSelection;\n    private JRadioButton chkRunInBackground;\n    private JButton btnAdvanced;\n    private final JDialog dialog;\n\n    private boolean success = false;\n    private AbstractAction onOkActionListener;\n    private AbstractAction onCancelActionListener;\n\n    // This list is used to check whether the currently entered command object already exists (duplicate-check).\n    private List<CommandObject> commandObjects;\n    private List<CommandObject.Placeholder> placeholders = Lists.newArrayList();\n\n    public SendToAddDialog(JFrame parent, String title, List<CommandObject> commandObjects) {\n        this.commandObjects = commandObjects;\n        this.dialog = initDialog(parent, title);\n        initEventListener();\n        initKeyboardShortcuts();\n        initButtonState();\n    }\n\n    public SendToAddDialog(JFrame parent, String title, List<CommandObject> commandObjects, CommandObject commandObject) {\n        this(parent, title, commandObjects);\n        commandObjects.remove(commandObject);\n        txtName.setText(commandObject.getName());\n        txtCommand.setText(commandObject.getFormat());\n        txtGroup.setText(commandObject.getGroup());\n        chkShowPreviewPriorToExecution.setSelected(commandObject.shouldShowPreview());\n        chkRunInBackground.setSelected(commandObject.shouldRunInBackground());\n        chkRunInTerminal.setSelected(commandObject.shouldRunInTerminal());\n        chkOutputShouldReplaceSelection.setSelected(commandObject.shouldOutputReplaceSelection());\n        placeholders = Lists.newArrayList(commandObject.getPlaceholders());\n    }\n\n    private void initKeyboardShortcuts() {\n        bindKeyStrokeToAction(\"ESCAPE\", onCancelActionListener);\n        bindKeyStrokeToAction(\"ENTER\", onOkActionListener);\n    }\n\n    private void bindKeyStrokeToAction(String keyStroke, Action action) {\n        KeyStroke stroke = KeyStroke.getKeyStroke(keyStroke);\n        InputMap inputMap = formPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\n        inputMap.put(stroke, keyStroke);\n        formPanel.getActionMap().put(keyStroke, action);\n    }\n\n    private void initEventListener() {\n        txtCommand.getDocument().addDocumentListener(new DocumentListener() {\n            @Override\n            public void insertUpdate(DocumentEvent e) {\n                updateAdvancedButton();\n            }\n\n            @Override\n            public void removeUpdate(DocumentEvent e) {\n                updateAdvancedButton();\n            }\n\n            @Override\n            public void changedUpdate(DocumentEvent e) {\n                updateAdvancedButton();\n            }\n\n            private void updateAdvancedButton() {\n                btnAdvanced.setEnabled(!Placeholders.get(txtCommand.getText()).isEmpty());\n            }\n        });\n        btnAdvanced.addActionListener((e) -> {\n            placeholders = getCommandObject().getPlaceholders();\n            SendToAddAdvancedDialog dialog = new SendToAddAdvancedDialog(this.dialog, placeholders);\n            if (dialog.run()) {\n                placeholders = dialog.getPlaceholders();\n            }\n        });\n        onOkActionListener = new AbstractAction() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                if (getName().isEmpty()) {\n                    DialogUtil.showErrorDialog(\n                            dialog,\n                            \"Name should not be empty!\",\n                            \"Name is empty!\"\n                    );\n                    return;\n                }\n                if (!commandObjects.stream().noneMatch(commandObject -> getName().equals(commandObject.getName()) && getGroup().equals(commandObject.getGroup()))) {\n                    DialogUtil.showErrorDialog(\n                            dialog,\n                            \"Name already exists within the specified group!\",\n                            \"Combination of name and group already exists!\"\n                    );\n                    return;\n                }\n                if (getCommand().isEmpty()) {\n                    DialogUtil.showErrorDialog(\n                            dialog,\n                            \"Command should not be empty!\",\n                            \"Command is empty!\"\n                    );\n                    return;\n                }\n                success = true;\n                dialog.dispose();\n            }\n        };\n        btnOk.addActionListener(onOkActionListener);\n        onCancelActionListener = new AbstractAction() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                success = false;\n                dialog.dispose();\n            }\n        };\n        btnCancel.addActionListener(onCancelActionListener);\n        btnCommandHelp.addActionListener(new ToolTipActionListener(btnCommandHelp, \"\" +\n                \"<html>\" +\n                \"<p>%H = Host</p>\" +\n                \"<p>%P = Port</p>\" +\n                \"<p>%T = Protocol</p>\" +\n                \"<p>%U = URL</p>\" +\n                \"<p>%A = URL-Path</p>\" +\n                \"<p>%Q = URL-Query</p>\" +\n                \"<p>%C = Cookies</p>\" +\n                \"<p>%L = HTTP-Content-Length</p>\" +\n                \"<p>%M = HTTP-Method</p>\" +\n                \"<p>%O = HTTP-Status-Code</p>\" +\n                \"<p>%S = Selected text</p>\" +\n                \"<p>%F = Path to file containing selected text</p>\" +\n                \"<p>%R = Path to file containing HTTP-request/-response</p>\" +\n                \"<p>%B = Path to file containing body of HTTP-request/-response</p>\" +\n                \"<p>%E = Path to file containing header of HTTP-request/-response</p>\" +\n                \"</html>\")\n        );\n    }\n\n    private void initButtonState() {\n        btnAdvanced.setEnabled(!Placeholders.get(txtCommand.getText()).isEmpty());\n    }\n\n    private JDialog initDialog(JFrame parent, String title) {\n        JDialog dialog = new JDialog(parent, title, true);\n        dialog.getContentPane().add(this.getRootPanel());\n        dialog.setSize(450, 250);\n        int x = DialogUtil.getX(parent, dialog);\n        int y = DialogUtil.getY(parent, dialog);\n        dialog.setLocation(x, y);\n        dialog.pack();\n        return dialog;\n    }\n\n    public boolean run() {\n        this.dialog.setVisible(true);\n        return this.success;\n    }\n\n    private JPanel getRootPanel() {\n        return formPanel;\n    }\n\n    private String getName() {\n        return txtName.getText();\n    }\n\n    private String getCommand() {\n        return txtCommand.getText();\n    }\n\n    private String getGroup() {\n        return txtGroup.getText();\n    }\n\n    private ERuntimeBehaviour getRuntimeBehaviour() {\n        if (chkRunInTerminal.isSelected()) {\n            return ERuntimeBehaviour.RUN_IN_TERMINAL;\n        }\n        if (chkOutputShouldReplaceSelection.isSelected()) {\n            return ERuntimeBehaviour.OUTPUT_SHOULD_REPLACE_SELECTION;\n        }\n        if (chkRunInBackground.isSelected()) {\n            return ERuntimeBehaviour.RUN_IN_BACKGROUND;\n        }\n        BurpExtender.printErr(\"Error parsing runtime behaviour. Please file a bug report if you encounter this error more often.\");\n        return ERuntimeBehaviour.RUN_IN_TERMINAL; // Return sane default\n    }\n\n    private boolean shouldShowPreview() {\n        return chkShowPreviewPriorToExecution.isSelected();\n    }\n\n    private List<CommandObject.Placeholder> getPlaceholders() {\n        return placeholders;\n    }\n\n    public CommandObject getCommandObject() {\n        return new CommandObject(getName(), getCommand(), getGroup(), getRuntimeBehaviour(), shouldShowPreview(), getPlaceholders());\n    }\n\n    {\n// GUI initializer generated by IntelliJ IDEA GUI Designer\n// >>> IMPORTANT!! <<<\n// DO NOT EDIT OR ADD ANY CODE HERE!\n        $$$setupUI$$$();\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        formPanel = new JPanel();\n        formPanel.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1));\n        final JLabel label1 = new JLabel();\n        label1.setText(\"Enter the details for the \\\"Send to...\\\" context menu entry.\");\n        formPanel.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel1 = new JPanel();\n        panel1.setLayout(new GridBagLayout());\n        formPanel.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JLabel label2 = new JLabel();\n        label2.setText(\"Name:\");\n        label2.setDisplayedMnemonic('N');\n        label2.setDisplayedMnemonicIndex(0);\n        GridBagConstraints gbc;\n        gbc = new GridBagConstraints();\n        gbc.gridx = 0;\n        gbc.gridy = 0;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.insets = new Insets(2, 2, 2, 10);\n        panel1.add(label2, gbc);\n        final JLabel label3 = new JLabel();\n        label3.setText(\"Command:\");\n        label3.setDisplayedMnemonic('M');\n        label3.setDisplayedMnemonicIndex(2);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 0;\n        gbc.gridy = 1;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.insets = new Insets(2, 2, 2, 10);\n        panel1.add(label3, gbc);\n        final JPanel panel2 = new JPanel();\n        panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 1;\n        gbc.fill = GridBagConstraints.BOTH;\n        panel1.add(panel2, gbc);\n        txtCommand = new JTextField();\n        txtCommand.setText(\"\");\n        panel2.add(txtCommand, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));\n        btnCommandHelp = new JButton();\n        btnCommandHelp.setText(\"?\");\n        panel2.add(btnCommandHelp, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label4 = new JLabel();\n        label4.setText(\"Group:\");\n        label4.setDisplayedMnemonic('G');\n        label4.setDisplayedMnemonicIndex(0);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 0;\n        gbc.gridy = 2;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.insets = new Insets(2, 2, 2, 10);\n        panel1.add(label4, gbc);\n        chkRunInTerminal = new JRadioButton();\n        chkRunInTerminal.setText(\"Run in terminal\");\n        chkRunInTerminal.setMnemonic('T');\n        chkRunInTerminal.setDisplayedMnemonicIndex(7);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 4;\n        gbc.anchor = GridBagConstraints.WEST;\n        panel1.add(chkRunInTerminal, gbc);\n        chkOutputShouldReplaceSelection = new JRadioButton();\n        chkOutputShouldReplaceSelection.setText(\"Output should replace selection\");\n        chkOutputShouldReplaceSelection.setMnemonic('R');\n        chkOutputShouldReplaceSelection.setDisplayedMnemonicIndex(14);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 5;\n        gbc.anchor = GridBagConstraints.WEST;\n        panel1.add(chkOutputShouldReplaceSelection, gbc);\n        chkShowPreviewPriorToExecution = new JCheckBox();\n        chkShowPreviewPriorToExecution.setSelected(true);\n        chkShowPreviewPriorToExecution.setText(\"Show preview prior to execution\");\n        chkShowPreviewPriorToExecution.setMnemonic('P');\n        chkShowPreviewPriorToExecution.setDisplayedMnemonicIndex(5);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 6;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.insets = new Insets(5, 2, 2, 2);\n        panel1.add(chkShowPreviewPriorToExecution, gbc);\n        txtGroup = new JTextField();\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 2;\n        gbc.weightx = 1.0;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.fill = GridBagConstraints.HORIZONTAL;\n        gbc.insets = new Insets(2, 2, 2, 2);\n        panel1.add(txtGroup, gbc);\n        txtName = new JTextField();\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 0;\n        gbc.weightx = 1.0;\n        gbc.anchor = GridBagConstraints.WEST;\n        gbc.fill = GridBagConstraints.HORIZONTAL;\n        gbc.insets = new Insets(2, 2, 2, 2);\n        panel1.add(txtName, gbc);\n        chkRunInBackground = new JRadioButton();\n        chkRunInBackground.setSelected(true);\n        chkRunInBackground.setText(\"Run in background\");\n        chkRunInBackground.setMnemonic('B');\n        chkRunInBackground.setDisplayedMnemonicIndex(7);\n        gbc = new GridBagConstraints();\n        gbc.gridx = 1;\n        gbc.gridy = 3;\n        gbc.anchor = GridBagConstraints.WEST;\n        panel1.add(chkRunInBackground, gbc);\n        final JPanel panel3 = new JPanel();\n        panel3.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1));\n        formPanel.add(panel3, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        btnCancel = new JButton();\n        btnCancel.setText(\"Cancel\");\n        btnCancel.setMnemonic('C');\n        btnCancel.setDisplayedMnemonicIndex(0);\n        panel3.add(btnCancel, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnOk = new JButton();\n        btnOk.setText(\"Ok\");\n        btnOk.setMnemonic('O');\n        btnOk.setDisplayedMnemonicIndex(0);\n        panel3.add(btnOk, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final Spacer spacer1 = new Spacer();\n        panel3.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n        btnAdvanced = new JButton();\n        btnAdvanced.setText(\"Advanced\");\n        btnAdvanced.setMnemonic('A');\n        btnAdvanced.setDisplayedMnemonicIndex(0);\n        panel3.add(btnAdvanced, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        label2.setLabelFor(txtName);\n        label3.setLabelFor(txtCommand);\n        label4.setLabelFor(txtGroup);\n        ButtonGroup buttonGroup;\n        buttonGroup = new ButtonGroup();\n        buttonGroup.add(chkRunInTerminal);\n        buttonGroup.add(chkOutputShouldReplaceSelection);\n        buttonGroup.add(chkRunInBackground);\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return formPanel;\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenu.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport burp.IContextMenuFactory;\nimport burp.IContextMenuInvocation;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.Context;\nimport net.bytebutcher.burpsendtoextension.models.Placeholders;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;\n\nimport javax.swing.*;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class SendToContextMenu implements IContextMenuFactory {\n\n    private BurpExtender burpExtender;\n    private SendToTableListener sendToTableListener;\n\n    public SendToContextMenu(BurpExtender burpExtender, SendToTableListener sendToTableListener) {\n        this.burpExtender = burpExtender;\n        this.sendToTableListener = sendToTableListener;\n    }\n\n    @Override\n    public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {\n        List<Map<String, IPlaceholderParser>> placeholders = Placeholders.get(BurpExtender.getCallbacks(), invocation.getSelectedMessages());\n        List<CommandObject> commandObjects = BurpExtender.getConfig().getSendToTableData();\n        if (commandObjects.isEmpty()) {\n            return Lists.newArrayList();\n        }\n\n        JMenu sendToMenu = new JMenu(\"Send to...\");\n        HashMap<String, List<CommandObject>> groupedCommandObjects = Maps.newLinkedHashMap();\n        boolean hasEmptyGroup = false;\n        for (final CommandObject commandObject : commandObjects) {\n            String group = commandObject.getGroup();\n            if (group.isEmpty()) {\n                addMenuItem(sendToMenu, commandObject, placeholders, invocation);\n                hasEmptyGroup = true;\n                continue;\n            }\n            if (!groupedCommandObjects.containsKey(group)) {\n                groupedCommandObjects.put(group, Lists.newArrayList());\n            }\n            groupedCommandObjects.get(group).add(commandObject);\n        }\n        if (hasEmptyGroup && !groupedCommandObjects.isEmpty()) {\n            sendToMenu.addSeparator();\n        }\n        for (String group : groupedCommandObjects.keySet()) {\n            JMenu menuItem = new JMenu(group);\n            for (CommandObject commandObject : groupedCommandObjects.get(group)) {\n                addMenuItem(menuItem, commandObject, placeholders, invocation);\n            }\n            sendToMenu.add(menuItem);\n        }\n        return  Lists.newArrayList(sendToMenu);\n    }\n\n    private void addMenuItem(JMenu menu, CommandObject commandObject, List<Map<String, IPlaceholderParser>> placeholders, IContextMenuInvocation invocation) {\n\n        JMenuItem item;\n        Context context = new Context(invocation);\n        if (commandObject.doesRequireRequestResponse(placeholders.get(0)) && context.getOrigin() == Context.Origin.UNKNOWN) {\n            item = new JMenu(commandObject.getName());\n            SendToContextMenuItem request = new SendToContextMenuItem(\"request\", commandObject, placeholders, new Context(Context.Origin.HTTP_REQUEST, invocation), sendToTableListener);\n            SendToContextMenuItem response = new SendToContextMenuItem(\"response\", commandObject, placeholders, new Context(Context.Origin.HTTP_RESPONSE, invocation), sendToTableListener);\n            item.add(request);\n            item.add(response);\n            item.setEnabled(request.isEnabled() || response.isEnabled());\n        } else {\n            item = new SendToContextMenuItem(commandObject.getName(), commandObject, placeholders, context, sendToTableListener);\n        }\n        menu.add(item);\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenuItem.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport net.bytebutcher.burpsendtoextension.gui.action.SendToContextMenuItemAction;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.Context;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;\n\nimport javax.swing.*;\nimport java.util.List;\nimport java.util.Map;\n\npublic class SendToContextMenuItem extends JMenuItem {\n\n    public SendToContextMenuItem(String title, CommandObject commandObject, List<Map<String, IPlaceholderParser>> placeholders, Context context, SendToTableListener sendToTableListener) {\n        String text = \"\";\n        List<Map<String, IPlaceholderParser>> validEntries = commandObject.getValid(placeholders, context);\n        if (placeholders.size() > 1) {\n            text = title + \" (\" + validEntries.size() + \"/\" + placeholders.size() + \")\";\n        } else {\n            text = title;\n        }\n        this.setAction(new SendToContextMenuItemAction(text, commandObject, placeholders, sendToTableListener, context));\n        if (commandObject.shouldOutputReplaceSelection() && context.getSelectionBounds() == null) {\n            // Always disable context menu item, when command should replace selection but no selection was made.\n            this.setEnabled(false);\n        } else {\n            // Do only enable context menu item, when at least one HTTP-message can be used to construct the command.\n            this.setEnabled(validEntries.size() > 0);\n        }\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToPreviewDialog\">\n  <grid id=\"27dc6\" binding=\"formPanel\" layout-manager=\"GridLayoutManager\" row-count=\"3\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"10\" left=\"10\" bottom=\"10\" right=\"10\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"607\" height=\"154\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <component id=\"6d061\" class=\"javax.swing.JLabel\">\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties>\n          <text value=\"Do you really want to execute the following command(s)?\"/>\n        </properties>\n      </component>\n      <grid id=\"ffa03\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"4\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <hspacer id=\"3ca82\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"336c9\" class=\"javax.swing.JButton\" binding=\"cancelButton\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"0\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"&amp;Cancel\"/>\n            </properties>\n          </component>\n          <component id=\"d2f34\" class=\"javax.swing.JButton\" binding=\"okButton\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"0\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"&amp;Ok\"/>\n            </properties>\n          </component>\n          <component id=\"57542\" class=\"javax.swing.JCheckBox\" binding=\"alwaysShowThisDialogCheckBox\" default-binding=\"true\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <selected value=\"true\"/>\n              <text value=\"Always show this dialog\"/>\n              <visible value=\"false\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <scrollpane id=\"5b762\">\n        <constraints>\n          <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"7\" hsize-policy=\"7\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"95319\" class=\"javax.swing.JTextArea\" binding=\"txtCommandPreview\">\n            <constraints/>\n            <properties/>\n          </component>\n        </children>\n      </scrollpane>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport com.intellij.uiDesigner.core.Spacer;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\npublic class SendToPreviewDialog {\n\n    private JTextArea txtCommandPreview;\n    private JButton cancelButton;\n    private JButton okButton;\n    private JCheckBox alwaysShowThisDialogCheckBox;\n    private JPanel formPanel;\n\n    private SendToTableListener sendToTableListener;\n    private AbstractAction onOkAction;\n    private AbstractAction onCancelAction;\n    private final JDialog dialog;\n    private boolean success = false;\n\n    public SendToPreviewDialog(JFrame parent, String title, final String command) {\n        this.dialog = initDialog(parent, title);\n        this.txtCommandPreview.setText(command);\n        initEventListener();\n        initKeyboardShortcuts();\n    }\n\n    public SendToPreviewDialog(JFrame parent, String title, final String command, final String commandId, final SendToTableListener sendToTableListener) {\n        this(parent, title, command);\n        this.sendToTableListener = sendToTableListener;\n        initEventListener(commandId, sendToTableListener);\n    }\n\n    private void initEventListener() {\n        onOkAction = new AbstractAction() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                success = true;\n                dialog.dispose();\n            }\n        };\n        okButton.addActionListener(onOkAction);\n        onCancelAction = new AbstractAction() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                success = false;\n                dialog.dispose();\n            }\n        };\n        cancelButton.addActionListener(onCancelAction);\n    }\n\n    private void initEventListener(final String commandId, final SendToTableListener sendToTableListener) {\n        alwaysShowThisDialogCheckBox.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                sendToTableListener.onShowPreviewChange(e, commandId, alwaysShowThisDialogCheckBox.isSelected());\n            }\n        });\n        alwaysShowThisDialogCheckBox.setVisible(true);\n    }\n\n    private JDialog initDialog(JFrame parent, String title) {\n        JDialog dialog = new JDialog(parent, title, true);\n        dialog.getContentPane().add(this.getRootPanel());\n        dialog.pack();\n        dialog.setSize(540, 200);\n        int x = DialogUtil.getX(parent, dialog);\n        int y = DialogUtil.getY(parent, dialog);\n        dialog.setLocation(x, y);\n        return dialog;\n    }\n\n    private void initKeyboardShortcuts() {\n        bindKeyStrokeToAction(\"ESCAPE\", onCancelAction);\n        bindKeyStrokeToAction(\"ENTER\", onOkAction);\n    }\n\n    private void bindKeyStrokeToAction(String keyStroke, Action action) {\n        KeyStroke stroke = KeyStroke.getKeyStroke(keyStroke);\n        InputMap inputMap = formPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);\n        inputMap.put(stroke, keyStroke);\n        formPanel.getActionMap().put(keyStroke, action);\n    }\n\n    public boolean run() {\n        this.dialog.setVisible(true);\n        return this.success;\n    }\n\n    public String getCommand() {\n        return this.txtCommandPreview.getText().replace(\"\\r\", \"\");\n    }\n\n    private Component getRootPanel() {\n        return formPanel;\n    }\n\n    {\n// GUI initializer generated by IntelliJ IDEA GUI Designer\n// >>> IMPORTANT!! <<<\n// DO NOT EDIT OR ADD ANY CODE HERE!\n        $$$setupUI$$$();\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        formPanel = new JPanel();\n        formPanel.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1));\n        final JLabel label1 = new JLabel();\n        label1.setText(\"Do you really want to execute the following command(s)?\");\n        formPanel.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel1 = new JPanel();\n        panel1.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1));\n        formPanel.add(panel1, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final Spacer spacer1 = new Spacer();\n        panel1.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n        cancelButton = new JButton();\n        cancelButton.setText(\"Cancel\");\n        cancelButton.setMnemonic('C');\n        cancelButton.setDisplayedMnemonicIndex(0);\n        panel1.add(cancelButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        okButton = new JButton();\n        okButton.setText(\"Ok\");\n        okButton.setMnemonic('O');\n        okButton.setDisplayedMnemonicIndex(0);\n        panel1.add(okButton, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        alwaysShowThisDialogCheckBox = new JCheckBox();\n        alwaysShowThisDialogCheckBox.setSelected(true);\n        alwaysShowThisDialogCheckBox.setText(\"Always show this dialog\");\n        alwaysShowThisDialogCheckBox.setVisible(false);\n        panel1.add(alwaysShowThisDialogCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JScrollPane scrollPane1 = new JScrollPane();\n        formPanel.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));\n        txtCommandPreview = new JTextArea();\n        scrollPane1.setViewportView(txtCommandPreview);\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return formPanel;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToRunInTerminalBehaviourChoiceDialog\">\n  <grid id=\"cbd77\" binding=\"contentPane\" layout-manager=\"GridLayoutManager\" row-count=\"2\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"10\" left=\"10\" bottom=\"10\" right=\"10\"/>\n    <constraints>\n      <xy x=\"48\" y=\"264\" width=\"728\" height=\"112\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <grid id=\"94766\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"6\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <hspacer id=\"98af6\">\n            <constraints>\n              <grid row=\"0\" column=\"5\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"9551d\" class=\"javax.swing.JButton\" binding=\"btnRunInSeparateTerminals\">\n            <constraints>\n              <grid row=\"0\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Run in &amp;separate terminals\"/>\n            </properties>\n          </component>\n          <component id=\"5723f\" class=\"javax.swing.JButton\" binding=\"btnCancel\">\n            <constraints>\n              <grid row=\"0\" column=\"4\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Cancel\"/>\n            </properties>\n          </component>\n          <hspacer id=\"ebf39\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n          <component id=\"9f65a\" class=\"javax.swing.JButton\" binding=\"btnReviewCommands\">\n            <constraints>\n              <grid row=\"0\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Review c&amp;ommands\"/>\n            </properties>\n          </component>\n          <component id=\"e7465\" class=\"javax.swing.JButton\" binding=\"btnRunInSingleTerminal\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Run in s&amp;ingle terminal\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <grid id=\"e3588\" layout-manager=\"GridLayoutManager\" row-count=\"2\" column-count=\"4\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"71538\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"4\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Please select how the commands should be executed.\"/>\n            </properties>\n          </component>\n          <component id=\"57df7\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"You are going to execute\"/>\n            </properties>\n          </component>\n          <component id=\"4e660\" class=\"javax.swing.JLabel\" binding=\"lblCommandCount\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"0\"/>\n            </properties>\n          </component>\n          <component id=\"76f3c\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"2\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"commands.\"/>\n            </properties>\n          </component>\n          <hspacer id=\"48714\">\n            <constraints>\n              <grid row=\"0\" column=\"3\" row-span=\"1\" col-span=\"1\" vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n          </hspacer>\n        </children>\n      </grid>\n    </children>\n  </grid>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport com.intellij.uiDesigner.core.Spacer;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.*;\n\npublic class SendToRunInTerminalBehaviourChoiceDialog extends JDialog {\n\n    private EChoice choice;\n\n    public enum EChoice {\n        RUN_IN_SINGLE_TERMINAL,\n        RUN_IN_SEPARATE_TERMINALS,\n        REVIEW_COMMANDS,\n        CANCEL\n    }\n\n    private final JFrame parent;\n    private JPanel contentPane;\n    private JButton btnRunInSingleTerminal;\n    private JButton btnCancel;\n    private JButton btnRunInSeparateTerminals;\n    private JLabel lblCommandCount;\n    private JButton btnReviewCommands;\n\n    public SendToRunInTerminalBehaviourChoiceDialog(JFrame parent, ERunInTerminalBehaviour defaultChoice, int nrOfCommands) {\n        this.parent = parent;\n        this.choice = getDefaultChoice(defaultChoice);\n        this.lblCommandCount.setText(String.valueOf(nrOfCommands));\n        setContentPane(contentPane);\n        setTitle(\"Select execution behaviour\");\n        setModal(true);\n\n        btnRunInSeparateTerminals.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SEPARATE_TERMINALS));\n        btnRunInSingleTerminal.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SINGLE_TERMINAL));\n        btnReviewCommands.addActionListener(e -> onButtonPress(EChoice.REVIEW_COMMANDS));\n        btnCancel.addActionListener(e -> onButtonPress(EChoice.CANCEL));\n\n        // call onCancel() when cross is clicked\n        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);\n        addWindowListener(new WindowAdapter() {\n            public void windowClosing(WindowEvent e) {\n                onCancel();\n            }\n        });\n\n        // call onCancel() on ESCAPE\n        contentPane.registerKeyboardAction(new ActionListener() {\n            public void actionPerformed(ActionEvent e) {\n                onCancel();\n            }\n        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);\n        this.pack();\n        initButtonState(defaultChoice);\n    }\n\n    private void initButtonState(ERunInTerminalBehaviour defaultChoice) {\n        switch (defaultChoice) {\n            case RUN_IN_SEPARATE_TERMINALS:\n                SwingUtilities.getRootPane(btnRunInSeparateTerminals).setDefaultButton(btnRunInSeparateTerminals);\n                btnRunInSeparateTerminals.grabFocus();\n                break;\n            case RUN_IN_SINGLE_TERMINAL:\n                // fall through\n            default:\n                SwingUtilities.getRootPane(btnRunInSingleTerminal).setDefaultButton(btnRunInSingleTerminal);\n                btnRunInSingleTerminal.grabFocus();\n        }\n    }\n\n    private EChoice getDefaultChoice(ERunInTerminalBehaviour defaultChoice) {\n        switch (defaultChoice) {\n            case RUN_IN_SEPARATE_TERMINALS:\n                return EChoice.RUN_IN_SEPARATE_TERMINALS;\n            case RUN_IN_SINGLE_TERMINAL:\n                // fall through\n            default:\n                return EChoice.RUN_IN_SINGLE_TERMINAL;\n        }\n    }\n\n    public EChoice run() {\n        int x = DialogUtil.getX(parent, this);\n        int y = DialogUtil.getY(parent, this);\n        this.setLocation(x, y);\n        this.setVisible(true);\n        return choice;\n    }\n\n    private void onButtonPress(EChoice choice) {\n        this.choice = choice;\n        dispose();\n    }\n\n    private void onCancel() {\n        this.choice = EChoice.CANCEL;\n        dispose();\n    }\n\n    {\n// GUI initializer generated by IntelliJ IDEA GUI Designer\n// >>> IMPORTANT!! <<<\n// DO NOT EDIT OR ADD ANY CODE HERE!\n        $$$setupUI$$$();\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        contentPane = new JPanel();\n        contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1));\n        final JPanel panel1 = new JPanel();\n        panel1.setLayout(new GridLayoutManager(1, 6, new Insets(0, 0, 0, 0), -1, -1));\n        contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false));\n        final Spacer spacer1 = new Spacer();\n        panel1.add(spacer1, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n        btnRunInSeparateTerminals = new JButton();\n        btnRunInSeparateTerminals.setText(\"Run in separate terminals\");\n        btnRunInSeparateTerminals.setMnemonic('S');\n        btnRunInSeparateTerminals.setDisplayedMnemonicIndex(7);\n        panel1.add(btnRunInSeparateTerminals, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnCancel = new JButton();\n        btnCancel.setText(\"Cancel\");\n        panel1.add(btnCancel, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final Spacer spacer2 = new Spacer();\n        panel1.add(spacer2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n        btnReviewCommands = new JButton();\n        btnReviewCommands.setText(\"Review commands\");\n        btnReviewCommands.setMnemonic('O');\n        btnReviewCommands.setDisplayedMnemonicIndex(8);\n        panel1.add(btnReviewCommands, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnRunInSingleTerminal = new JButton();\n        btnRunInSingleTerminal.setText(\"Run in single terminal\");\n        btnRunInSingleTerminal.setMnemonic('I');\n        btnRunInSingleTerminal.setDisplayedMnemonicIndex(8);\n        panel1.add(btnRunInSingleTerminal, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel2 = new JPanel();\n        panel2.setLayout(new GridLayoutManager(2, 4, new Insets(0, 0, 0, 0), -1, -1));\n        contentPane.add(panel2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JLabel label1 = new JLabel();\n        label1.setText(\"Please select how the commands should be executed.\");\n        panel2.add(label1, new GridConstraints(1, 0, 1, 4, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label2 = new JLabel();\n        label2.setText(\"You are going to execute\");\n        panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        lblCommandCount = new JLabel();\n        lblCommandCount.setText(\"0\");\n        panel2.add(lblCommandCount, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label3 = new JLabel();\n        label3.setText(\"commands.\");\n        panel2.add(label3, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final Spacer spacer3 = new Spacer();\n        panel2.add(spacer3, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false));\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return contentPane;\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTab.form",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<form xmlns=\"http://www.intellij.com/uidesigner/form/\" version=\"1\" bind-to-class=\"net.bytebutcher.burpsendtoextension.gui.SendToTab\">\n  <grid id=\"27dc6\" binding=\"formPanel\" layout-manager=\"GridLayoutManager\" row-count=\"5\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n    <margin top=\"10\" left=\"10\" bottom=\"10\" right=\"10\"/>\n    <constraints>\n      <xy x=\"20\" y=\"20\" width=\"729\" height=\"556\"/>\n    </constraints>\n    <properties/>\n    <border type=\"none\"/>\n    <children>\n      <grid id=\"f2af0\" layout-manager=\"GridLayoutManager\" row-count=\"3\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"0\" column=\"0\" row-span=\"3\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <grid id=\"cba21\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <grid id=\"df57f\" layout-manager=\"GridLayoutManager\" row-count=\"6\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n                <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties/>\n                <border type=\"none\"/>\n                <children>\n                  <component id=\"ace94\" class=\"javax.swing.JButton\" binding=\"btnRemove\">\n                    <constraints>\n                      <grid row=\"2\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                    <properties>\n                      <text value=\"Remove\"/>\n                    </properties>\n                  </component>\n                  <component id=\"cae4c\" class=\"javax.swing.JButton\" binding=\"btnEdit\">\n                    <constraints>\n                      <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                    <properties>\n                      <text value=\"Edit\"/>\n                    </properties>\n                  </component>\n                  <component id=\"3e059\" class=\"javax.swing.JButton\" binding=\"btnAdd\">\n                    <constraints>\n                      <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                    <properties>\n                      <text value=\"Add\"/>\n                    </properties>\n                  </component>\n                  <vspacer id=\"6bab6\">\n                    <constraints>\n                      <grid row=\"5\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                  </vspacer>\n                  <component id=\"25cb2\" class=\"javax.swing.JButton\" binding=\"btnUp\">\n                    <constraints>\n                      <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                    <properties>\n                      <text value=\"Up\"/>\n                    </properties>\n                  </component>\n                  <component id=\"921f5\" class=\"javax.swing.JButton\" binding=\"btnDown\">\n                    <constraints>\n                      <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                    </constraints>\n                    <properties>\n                      <text value=\"Down\"/>\n                    </properties>\n                  </component>\n                </children>\n              </grid>\n              <scrollpane id=\"84f17\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"7\" hsize-policy=\"7\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties/>\n                <border type=\"none\"/>\n                <children>\n                  <component id=\"b4a94\" class=\"javax.swing.JTable\" binding=\"tblSendTo\" custom-create=\"true\">\n                    <constraints/>\n                    <properties/>\n                  </component>\n                </children>\n              </scrollpane>\n            </children>\n          </grid>\n          <component id=\"aa82c\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Manage entries of the &quot;Send to...&quot; context menu.\"/>\n            </properties>\n          </component>\n          <component id=\"b8d92\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font name=\"Tahoma\" size=\"14\" style=\"1\"/>\n              <foreground color=\"-1341440\"/>\n              <text value=\"Context Menu Entries\"/>\n            </properties>\n          </component>\n          <grid id=\"a5939\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"2\" left=\"2\" bottom=\"2\" right=\"2\"/>\n            <constraints>\n              <grid row=\"1\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\">\n                <maximum-size width=\"26\" height=\"26\"/>\n              </grid>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"fc873\" class=\"javax.swing.JLabel\" binding=\"lblSettings\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"4b9ac\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"2\" left=\"2\" bottom=\"2\" right=\"2\"/>\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\">\n                <maximum-size width=\"26\" height=\"26\"/>\n              </grid>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"6b01d\" class=\"javax.swing.JLabel\" binding=\"lblHelp\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n        </children>\n      </grid>\n      <grid id=\"1886c\" layout-manager=\"GridLayoutManager\" row-count=\"8\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n        <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n        <constraints>\n          <grid row=\"3\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n        <properties/>\n        <border type=\"none\"/>\n        <children>\n          <component id=\"9692c\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <font name=\"Tahoma\" size=\"14\" style=\"1\"/>\n              <foreground color=\"-1341440\"/>\n              <text value=\"Terminal Options\"/>\n            </properties>\n          </component>\n          <grid id=\"659d\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"1\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"2\" left=\"2\" bottom=\"2\" right=\"2\"/>\n            <constraints>\n              <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\">\n                <minimum-size width=\"26\" height=\"26\"/>\n                <maximum-size width=\"26\" height=\"26\"/>\n              </grid>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"d0ebd\" class=\"javax.swing.JLabel\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"48258\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"3\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"When multiple commands are going to be executed at once\"/>\n            </properties>\n          </component>\n          <component id=\"dc65d\" class=\"javax.swing.JLabel\">\n            <constraints>\n              <grid row=\"1\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Specify how to run commands in terminal:\"/>\n            </properties>\n          </component>\n          <grid id=\"f9cb9\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"2\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"be92\" class=\"javax.swing.JTextField\" binding=\"txtRunInTerminal\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\">\n                    <preferred-size width=\"150\" height=\"-1\"/>\n                  </grid>\n                </constraints>\n                <properties>\n                  <text value=\"/bin/bash -c {CMD}\"/>\n                </properties>\n              </component>\n              <component id=\"11e77\" class=\"javax.swing.JButton\" binding=\"btnRunInTerminalHelp\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"?\"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"cb82a\" class=\"javax.swing.JCheckBox\" binding=\"chkShowRunInTerminalBehaviourChoiceDialog\">\n            <constraints>\n              <grid row=\"6\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <text value=\"Show dialog to select execution behaviour when multiple commands are going to be executed\"/>\n            </properties>\n          </component>\n          <grid id=\"b191c\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"4\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"f8f83\" class=\"javax.swing.JRadioButton\" binding=\"chkRunInSingleTerminal\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"execute commands sequential in single terminal\"/>\n                </properties>\n              </component>\n              <component id=\"9cac8\" class=\"javax.swing.JLabel\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"  \"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <grid id=\"fad08\" layout-manager=\"GridLayoutManager\" row-count=\"1\" column-count=\"2\" same-size-horizontally=\"false\" same-size-vertically=\"false\" hgap=\"-1\" vgap=\"-1\">\n            <margin top=\"0\" left=\"0\" bottom=\"0\" right=\"0\"/>\n            <constraints>\n              <grid row=\"5\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties/>\n            <border type=\"none\"/>\n            <children>\n              <component id=\"17ad9\" class=\"javax.swing.JRadioButton\" binding=\"chkRunInSeparateTerminals\">\n                <constraints>\n                  <grid row=\"0\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"execute commands in parallel in separate terminals\"/>\n                </properties>\n              </component>\n              <component id=\"a17fb\" class=\"javax.swing.JLabel\">\n                <constraints>\n                  <grid row=\"0\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n                </constraints>\n                <properties>\n                  <text value=\"  \"/>\n                </properties>\n              </component>\n            </children>\n          </grid>\n          <component id=\"6f3d4\" class=\"javax.swing.JCheckBox\" binding=\"chkSafeMode\">\n            <constraints>\n              <grid row=\"7\" column=\"1\" row-span=\"1\" col-span=\"1\" vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" indent=\"0\" use-parent-layout=\"false\"/>\n            </constraints>\n            <properties>\n              <selected value=\"true\"/>\n              <text value=\"Surround placeholders with single quotes automatically (safe mode)\"/>\n            </properties>\n          </component>\n        </children>\n      </grid>\n      <vspacer id=\"39bff\">\n        <constraints>\n          <grid row=\"4\" column=\"0\" row-span=\"1\" col-span=\"1\" vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" indent=\"0\" use-parent-layout=\"false\"/>\n        </constraints>\n      </vspacer>\n    </children>\n  </grid>\n  <buttonGroups>\n    <group name=\"multi_command_handling\">\n      <member id=\"17ad9\"/>\n      <member id=\"17ad9\"/>\n      <member id=\"f8f83\"/>\n    </group>\n  </buttonGroups>\n</form>\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTab.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport com.intellij.uiDesigner.core.GridConstraints;\nimport com.intellij.uiDesigner.core.GridLayoutManager;\nimport com.intellij.uiDesigner.core.Spacer;\nimport net.bytebutcher.burpsendtoextension.gui.listener.ToolTipActionListener;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.gui.util.WebUtil;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.plaf.FontUIResource;\nimport javax.swing.text.StyleContext;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Locale;\n\npublic class SendToTab {\n    private BurpExtender burpExtender;\n    private JButton btnRemove;\n    private JButton btnEdit;\n    private JButton btnAdd;\n    private JTable tblSendTo;\n\n    private SendToTable sendToTable;\n    private JPanel formPanel;\n    private JButton btnUp;\n    private JButton btnDown;\n    private JLabel lblSettings;\n    private JLabel lblHelp;\n    private JTextField txtRunInTerminal;\n    private JButton btnRunInTerminalHelp;\n    private JRadioButton chkRunInSingleTerminal;\n    private JRadioButton chkRunInSeparateTerminals;\n    private JCheckBox chkShowRunInTerminalBehaviourChoiceDialog;\n    private JCheckBox chkSafeMode;\n    private SendToTableListener sendToTableListener;\n    private final SendToTabSettingsContextMenu sendToTabSettingsContextMenu;\n\n\n    public SendToTab(final BurpExtender burpExtender) {\n        this.burpExtender = burpExtender;\n        $$$setupUI$$$();\n        this.lblHelp.setIcon(this.burpExtender.createImageIcon(\"/panel_help.png\", \"\", 24, 24));\n        this.lblSettings.setIcon(this.burpExtender.createImageIcon(\"/panel_settings.png\", \"\", 24, 24));\n        this.sendToTableListener = new SendToTableListener(this.sendToTable);\n        this.tblSendTo.getModel().addTableModelListener(sendToTableListener);\n        btnAdd.addActionListener(e -> new Thread(() -> {\n            SendToAddDialog addDialog = new SendToAddDialog(getParent(), \"Add context menu entry\", sendToTable.getCommandObjects());\n            if (addDialog.run()) {\n                sendToTableListener.onAddButtonClick(e, addDialog.getCommandObject());\n            }\n        }).start());\n        btnEdit.addActionListener(e -> {\n            CommandObject selectedCommandObject = sendToTable.getSelectedCommandObject();\n            SendToAddDialog editDialog = new SendToAddDialog(getParent(), \"Edit context menu entry\", sendToTable.getCommandObjects(), selectedCommandObject);\n            if (editDialog.run()) {\n                sendToTableListener.onEditButtonClick(e, editDialog.getCommandObject());\n            }\n        });\n        btnRemove.addActionListener(e -> {\n            boolean result = DialogUtil.showConfirmationDialog(getParent(), \"Delete context menu entries\",\n                    \"Do you really want to delete the selected context menu entries?\");\n            if (result) {\n                sendToTableListener.onRemoveButtonClick(e);\n            }\n        });\n        btnUp.addActionListener(e -> sendToTableListener.onUpButtonClick(e));\n        btnDown.addActionListener(e -> sendToTableListener.onDownButtonClick(e));\n        lblHelp.addMouseListener(new LabelIconImageHoverAdapter(lblHelp, \"/panel_help.png\", \"/panel_help_highlighted.png\"));\n        lblSettings.addMouseListener(new LabelIconImageHoverAdapter(lblSettings, \"/panel_settings.png\", \"/panel_settings_highlighted.png\"));\n        lblHelp.addMouseListener(new MouseAdapter() {\n\n            @Override\n            public void mouseClicked(MouseEvent e) {\n                try {\n                    WebUtil.openWebpage(new URL(\"https://github.com/bytebutcher/burp-send-to\"));\n                } catch (MalformedURLException e1) {\n                    // Nothing to do here...\n                }\n            }\n        });\n        sendToTabSettingsContextMenu = new SendToTabSettingsContextMenu(burpExtender, this);\n        lblSettings.addMouseListener(new MouseAdapter() {\n\n            @Override\n            public void mouseClicked(MouseEvent e) {\n                sendToTabSettingsContextMenu.show(lblSettings, lblSettings.getX() + lblSettings.getWidth(), lblSettings.getY());\n            }\n        });\n        txtRunInTerminal.setText(BurpExtender.getConfig().getRunInTerminalCommand());\n        txtRunInTerminal.getDocument().addDocumentListener(new DocumentListener() {\n            public void changedUpdate(DocumentEvent e) {\n                save();\n            }\n\n            public void removeUpdate(DocumentEvent e) {\n                save();\n            }\n\n            public void insertUpdate(DocumentEvent e) {\n                save();\n            }\n\n            public void save() {\n                BurpExtender.getConfig().setRunInTerminalCommand(txtRunInTerminal.getText());\n            }\n        });\n        btnRunInTerminalHelp.addActionListener(new ToolTipActionListener(btnRunInTerminalHelp, \"\" +\n                \"<html>\" +\n                \"<p>%C = Command</p>\" +\n                \"</html>\")\n        );\n        chkRunInSingleTerminal.setSelected(BurpExtender.getConfig().getRunInTerminalBehaviour() == ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL);\n        chkRunInSingleTerminal.addChangeListener(e -> {\n            BurpExtender.getConfig().setRunInTerminalBehaviour(chkRunInSingleTerminal.isSelected() ? ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL : ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS);\n        });\n        chkRunInSeparateTerminals.setSelected(BurpExtender.getConfig().getRunInTerminalBehaviour() == ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS);\n        chkRunInSeparateTerminals.addChangeListener(e -> {\n            BurpExtender.getConfig().setRunInTerminalBehaviour(chkRunInSeparateTerminals.isSelected() ? ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS : ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL);\n        });\n        chkShowRunInTerminalBehaviourChoiceDialog.setSelected(BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog());\n        chkShowRunInTerminalBehaviourChoiceDialog.addChangeListener(e -> BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog(chkShowRunInTerminalBehaviourChoiceDialog.isSelected()));\n        this.chkSafeMode.setSelected(BurpExtender.getConfig().isSafeModeActivated());\n        this.chkSafeMode.addChangeListener(e -> BurpExtender.getConfig().setSafeMode(this.chkSafeMode.isSelected()));\n    }\n\n    public void resetOptions() {\n        resetSendToTableData();\n        resetRunInTerminalOption();\n    }\n\n    private void resetSendToTableData() {\n        sendToTable.clearTable();\n        sendToTable.addCommandObjects(BurpExtender.getConfig().getDefaultSendToTableData());\n    }\n\n    private void resetRunInTerminalOption() {\n        BurpExtender.getConfig().resetRunInTerminalCommand();\n        txtRunInTerminal.setText(BurpExtender.getConfig().getRunInTerminalCommand());\n    }\n\n    /**\n     * Method generated by IntelliJ IDEA GUI Designer\n     * >>> IMPORTANT!! <<<\n     * DO NOT edit this method OR call it in your code!\n     *\n     * @noinspection ALL\n     */\n    private void $$$setupUI$$$() {\n        createUIComponents();\n        formPanel = new JPanel();\n        formPanel.setLayout(new GridLayoutManager(5, 1, new Insets(10, 10, 10, 10), -1, -1));\n        final JPanel panel1 = new JPanel();\n        panel1.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 0, 0), -1, -1));\n        formPanel.add(panel1, new GridConstraints(0, 0, 3, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JPanel panel2 = new JPanel();\n        panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        panel1.add(panel2, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JPanel panel3 = new JPanel();\n        panel3.setLayout(new GridLayoutManager(6, 1, new Insets(0, 0, 0, 0), -1, -1));\n        panel2.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        btnRemove = new JButton();\n        btnRemove.setText(\"Remove\");\n        panel3.add(btnRemove, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnEdit = new JButton();\n        btnEdit.setText(\"Edit\");\n        panel3.add(btnEdit, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnAdd = new JButton();\n        btnAdd.setText(\"Add\");\n        panel3.add(btnAdd, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final Spacer spacer1 = new Spacer();\n        panel3.add(spacer1, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));\n        btnUp = new JButton();\n        btnUp.setText(\"Up\");\n        panel3.add(btnUp, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        btnDown = new JButton();\n        btnDown.setText(\"Down\");\n        panel3.add(btnDown, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JScrollPane scrollPane1 = new JScrollPane();\n        panel2.add(scrollPane1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));\n        scrollPane1.setViewportView(tblSendTo);\n        final JLabel label1 = new JLabel();\n        label1.setText(\"Manage entries of the \\\"Send to...\\\" context menu.\");\n        panel1.add(label1, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label2 = new JLabel();\n        Font label2Font = this.$$$getFont$$$(\"Tahoma\", Font.BOLD, 14, label2.getFont());\n        if (label2Font != null) label2.setFont(label2Font);\n        label2.setForeground(new Color(-1341440));\n        label2.setText(\"Context Menu Entries\");\n        panel1.add(label2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel4 = new JPanel();\n        panel4.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1));\n        panel1.add(panel4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, new Dimension(26, 26), 0, false));\n        lblSettings = new JLabel();\n        lblSettings.setText(\"\");\n        panel4.add(lblSettings, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel5 = new JPanel();\n        panel5.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1));\n        panel1.add(panel5, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, new Dimension(26, 26), 0, false));\n        lblHelp = new JLabel();\n        lblHelp.setText(\"\");\n        panel5.add(lblHelp, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel6 = new JPanel();\n        panel6.setLayout(new GridLayoutManager(8, 2, new Insets(0, 0, 0, 0), -1, -1));\n        formPanel.add(panel6, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        final JLabel label3 = new JLabel();\n        Font label3Font = this.$$$getFont$$$(\"Tahoma\", Font.BOLD, 14, label3.getFont());\n        if (label3Font != null) label3.setFont(label3Font);\n        label3.setForeground(new Color(-1341440));\n        label3.setText(\"Terminal Options\");\n        panel6.add(label3, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel7 = new JPanel();\n        panel7.setLayout(new GridLayoutManager(1, 1, new Insets(2, 2, 2, 2), -1, -1));\n        panel6.add(panel7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(26, 26), null, new Dimension(26, 26), 0, false));\n        final JLabel label4 = new JLabel();\n        label4.setText(\"\");\n        panel7.add(label4, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label5 = new JLabel();\n        label5.setText(\"When multiple commands are going to be executed at once\");\n        panel6.add(label5, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label6 = new JLabel();\n        label6.setText(\"Specify how to run commands in terminal:\");\n        panel6.add(label6, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel8 = new JPanel();\n        panel8.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        panel6.add(panel8, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        txtRunInTerminal = new JTextField();\n        txtRunInTerminal.setText(\"/bin/bash -c {CMD}\");\n        panel8.add(txtRunInTerminal, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));\n        btnRunInTerminalHelp = new JButton();\n        btnRunInTerminalHelp.setText(\"?\");\n        panel8.add(btnRunInTerminalHelp, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        chkShowRunInTerminalBehaviourChoiceDialog = new JCheckBox();\n        chkShowRunInTerminalBehaviourChoiceDialog.setText(\"Show dialog to select execution behaviour when multiple commands are going to be executed\");\n        panel6.add(chkShowRunInTerminalBehaviourChoiceDialog, new GridConstraints(6, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel9 = new JPanel();\n        panel9.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        panel6.add(panel9, new GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        chkRunInSingleTerminal = new JRadioButton();\n        chkRunInSingleTerminal.setText(\"execute commands sequential in single terminal\");\n        panel9.add(chkRunInSingleTerminal, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label7 = new JLabel();\n        label7.setText(\"  \");\n        panel9.add(label7, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JPanel panel10 = new JPanel();\n        panel10.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));\n        panel6.add(panel10, new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));\n        chkRunInSeparateTerminals = new JRadioButton();\n        chkRunInSeparateTerminals.setText(\"execute commands in parallel in separate terminals\");\n        panel10.add(chkRunInSeparateTerminals, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final JLabel label8 = new JLabel();\n        label8.setText(\"  \");\n        panel10.add(label8, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        chkSafeMode = new JCheckBox();\n        chkSafeMode.setSelected(true);\n        chkSafeMode.setText(\"Surround placeholders with single quotes automatically (safe mode)\");\n        panel6.add(chkSafeMode, new GridConstraints(7, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));\n        final Spacer spacer2 = new Spacer();\n        formPanel.add(spacer2, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));\n        ButtonGroup buttonGroup;\n        buttonGroup = new ButtonGroup();\n        buttonGroup.add(chkRunInSeparateTerminals);\n        buttonGroup.add(chkRunInSeparateTerminals);\n        buttonGroup.add(chkRunInSingleTerminal);\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {\n        if (currentFont == null) return null;\n        String resultName;\n        if (fontName == null) {\n            resultName = currentFont.getName();\n        } else {\n            Font testFont = new Font(fontName, Font.PLAIN, 10);\n            if (testFont.canDisplay('a') && testFont.canDisplay('1')) {\n                resultName = fontName;\n            } else {\n                resultName = currentFont.getName();\n            }\n        }\n        Font font = new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());\n        boolean isMac = System.getProperty(\"os.name\", \"\").toLowerCase(Locale.ENGLISH).startsWith(\"mac\");\n        Font fontWithFallback = isMac ? new Font(font.getFamily(), font.getStyle(), font.getSize()) : new StyleContext().getFont(font.getFamily(), font.getStyle(), font.getSize());\n        return fontWithFallback instanceof FontUIResource ? fontWithFallback : new FontUIResource(fontWithFallback);\n    }\n\n    /**\n     * @noinspection ALL\n     */\n    public JComponent $$$getRootComponent$$$() {\n        return formPanel;\n    }\n\n    class LabelIconImageHoverAdapter extends MouseAdapter {\n\n        private String resource;\n        private String resourceHovered;\n        private JLabel label;\n\n        public LabelIconImageHoverAdapter(JLabel label, String resource, String resourceHovered) {\n            this.label = label;\n            this.resource = resource;\n            this.resourceHovered = resourceHovered;\n        }\n\n        @Override\n        public void mouseEntered(MouseEvent e) {\n            label.setIcon(SendToTab.this.burpExtender.createImageIcon(resourceHovered, \"\", 24, 24));\n        }\n\n        @Override\n        public void mouseExited(MouseEvent e) {\n            label.setIcon(SendToTab.this.burpExtender.createImageIcon(resource, \"\", 24, 24));\n        }\n    }\n\n    public JPanel getRootPanel() {\n        return formPanel;\n    }\n\n    public JFrame getParent() {\n        return (JFrame) SwingUtilities.getRootPane(this.getRootPanel()).getParent();\n    }\n\n    public SendToTable getSendToTable() {\n        return sendToTable;\n    }\n\n    /**\n     * Creates Custom GUI forms\n     */\n    private void createUIComponents() {\n        this.tblSendTo = this.sendToTable = new SendToTable(this.burpExtender);\n    }\n\n    public SendToTableListener getSendToTableListener() {\n        return this.sendToTableListener;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTabSettingsContextMenu.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\n\nimport javax.swing.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\nclass SendToTabSettingsContextMenu extends JPopupMenu {\n\n    private BurpExtender burpExtender;\n    private final JMenuItem restoreDefaults;\n    private final JMenuItem loadOptions;\n    private final JMenuItem saveOptions;\n\n    private SendToTable sendToTable;\n    private SendToTab sendToTab;\n\n    public SendToTabSettingsContextMenu(final BurpExtender burpExtender, final SendToTab sendToTab) {\n        this.burpExtender = burpExtender;\n        this.sendToTab = sendToTab;\n        this.sendToTable = sendToTab.getSendToTable();\n        restoreDefaults = new JMenuItem(\"Restore defaults\");\n        restoreDefaults.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                boolean result = DialogUtil.showConfirmationDialog(sendToTab.getParent(), \"Reset \\\"Send to\\\"-options\",\n                        \"Do you really want to reset the \\\"Send to\\\"-options?\");\n                if (result) {\n                    sendToTab.resetOptions();\n                }\n            }\n        });\n        add(restoreDefaults);\n        loadOptions = new JMenuItem(\"Load options\");\n        loadOptions.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                burpExtender.getCallbacks().printOutput(\"Loading options...\");\n                JFileChooser fileChooser = new JFileChooser();\n                fileChooser.setDialogTitle(\"Load \\\"Send to\\\" options from file...\");\n                fileChooser.setCurrentDirectory(new File(System.getProperty(\"user.home\")));\n                int result = fileChooser.showOpenDialog(getParent());\n                if (result == JFileChooser.APPROVE_OPTION) {\n                    File selectedFile = fileChooser.getSelectedFile();\n                    try {\n                        burpExtender.getCallbacks().printOutput(\"Reading selected file: \" + selectedFile.getAbsolutePath());\n                        List<CommandObject> commandObjectList = new Gson().fromJson(new FileReader(selectedFile), new TypeToken<List<CommandObject>>(){}.getType());\n                        burpExtender.getCallbacks().printOutput(\"Adding \" + commandObjectList.size() + \" items to table...\");\n                        sendToTable.removeAll();\n                        sendToTable.addCommandObjects(commandObjectList);\n                        burpExtender.getCallbacks().printOutput(\"Successfully loaded options into table!\");\n                    } catch (FileNotFoundException e1) {\n                        DialogUtil.showErrorDialog(\n                                sendToTab.getParent(),\n                                \"Error while loading options!\",\n                                \"<html><p>There was an unknown error while loading the options!</p>\" +\n                                        \"<p>For more information check out the \\\"Send to\\\" extension error log!</p></html>\"\n                        );\n                        burpExtender.getCallbacks().printError(\"Error while loading options: \" + e1);\n                        return;\n                    } catch (Exception e2) {\n                        burpExtender.getCallbacks().printError(\"Error while loading options: \" + e2);\n                        return;\n                    }\n                }\n            }\n        });\n        add(loadOptions);\n        saveOptions = new JMenuItem(\"Save options\");\n        saveOptions.addActionListener(new ActionListener() {\n            @Override\n            public void actionPerformed(ActionEvent e) {\n                JFileChooser fileChooser = new JFileChooser();\n                fileChooser.setDialogTitle(\"Save \\\"Send to\\\" options to file...\");\n\n                int userSelection = fileChooser.showSaveDialog(getParent());\n                if (userSelection == JFileChooser.APPROVE_OPTION) {\n                    File fileToSave = fileChooser.getSelectedFile();\n                    String json = new Gson().toJson(sendToTable.getCommandObjects());\n                    try (PrintWriter out = new PrintWriter(fileToSave)) {\n                        out.write(json);\n                    } catch (FileNotFoundException e1) {\n                        DialogUtil.showErrorDialog(\n                                sendToTab.getParent(),\n                                \"Error while saving options!\",\n                                \"<html><p>There was an unknown error while saving the options!</p>\" +\n                                        \"<p>For more information check out the \\\"Send to\\\" extension error log!</p></html>\"\n                        );\n                        burpExtender.getCallbacks().printError(\"Error while saving options: \" + e1);\n                        return;\n                    }\n                    burpExtender.getCallbacks().printOutput(\"Successfully saved options in '\" + fileToSave.getAbsolutePath() + \"'!\");\n                }\n            }\n        });\n        add(saveOptions);\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTable.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport com.google.common.collect.Lists;\nimport com.google.common.primitives.Ints;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.ERuntimeBehaviour;\n\nimport javax.swing.*;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.table.DefaultTableModel;\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class SendToTable extends JTable {\n\n    private final DefaultTableModel defaultModel;\n    private BurpExtender burpExtender;\n\n    private enum Column {\n        ID(0),\n        NAME(1),\n        COMMAND(2),\n        GROUP(3),\n        RUNTIME_BEHAVIOUR(4),\n        SHOW_PREVIEW(5),\n        PLACEHOLDERS(6);\n\n        private final int index;\n\n        Column(int id) {\n            this.index = id;\n        }\n\n        public int getIndex() {\n            return index;\n        }\n    }\n\n    private class SendToTableModel extends DefaultTableModel {\n\n        private boolean areEventsBlocked;\n\n        @Override\n        public boolean isCellEditable(int row, int column) {\n            return false;\n        }\n\n        @Override\n        public void fireTableChanged(TableModelEvent e) {\n            if (!areEventsBlocked) {\n                super.fireTableChanged(e);\n            }\n        }\n\n        public void setBlockEvents(boolean areEventsBlocked) {\n            this.areEventsBlocked = areEventsBlocked;\n        }\n    }\n\n    public SendToTable(BurpExtender burpExtender) {\n        this.burpExtender = burpExtender;\n        this.defaultModel = new SendToTableModel();\n        this.defaultModel.addColumn(\"Id\");\n        this.defaultModel.addColumn(\"Name\");\n        this.defaultModel.addColumn(\"Command\");\n        this.defaultModel.addColumn(\"Group name\");\n        this.defaultModel.addColumn(\"Runtime behaviour\");\n        this.defaultModel.addColumn(\"Show preview\");\n        this.defaultModel.addColumn(\"Placeholder behaviour\");\n        setModel(this.defaultModel);\n        hideColumns(Column.ID, Column.COMMAND, Column.PLACEHOLDERS);\n    }\n\n    private void hideColumns(Column ... c) {\n        List<Column> collect = Arrays.stream(c).sorted(Comparator.comparingInt(Column::getIndex).reversed()).collect(Collectors.toList());\n        for (Column column : collect) {\n            this.removeColumn(this.getColumnModel().getColumn(column.getIndex()));\n        }\n    }\n\n    public CommandObject getSelectedCommandObject() {\n        int[] selectedRows = this.getSelectedRows();\n        if (selectedRows.length > 0) {\n            int selectedRow = selectedRows[0];\n            return getCommandObjectByRowIndex(selectedRow);\n        }\n        throw new IllegalStateException(\"No row selected!\");\n    }\n\n    public DefaultTableModel getDefaultModel() {\n        return defaultModel;\n    }\n\n    public String getSelectedNames() {\n        int[] selectedRows = this.getSelectedRows();\n        if (selectedRows.length > 0) {\n            int selectedRow = selectedRows[0];\n            return getNameByRowIndex(selectedRow);\n        }\n        throw new IllegalStateException(\"No row selected!\");\n    }\n\n    private String getNameByRowIndex(int rowIndex) {\n        return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.NAME.getIndex())).orElse(\"\").toString();\n    }\n\n    private boolean getShowPreviewByRowIndex(int rowIndex) {\n        return Boolean.parseBoolean(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.SHOW_PREVIEW.getIndex())).orElse(\"\").toString());\n    }\n\n    private ERuntimeBehaviour getRuntimeBehaviourByRowIndex(int rowIndex) {\n        return ERuntimeBehaviour.getEnum(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex())).orElse(\"\").toString());\n    }\n\n    private String getGroupByRowIndex(int rowIndex) {\n        return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.GROUP.getIndex())).orElse(\"\").toString();\n    }\n\n    private String getCommandByRowIndex(int rowIndex) {\n        return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.COMMAND.getIndex())).orElse(\"\").toString();\n    }\n\n    private List<CommandObject.Placeholder> getPlaceholders(int rowIndex) {\n        try {\n            return Lists.newArrayList((List<CommandObject.Placeholder>) this.getModel().getValueAt(rowIndex, Column.PLACEHOLDERS.getIndex()));\n        } catch (Exception e) {\n            BurpExtender.printErr(\"Casting of placeholder behaviour failed for row \" + rowIndex);\n            return Lists.newArrayList();\n        }\n    }\n\n    public List<CommandObject> getCommandObjects() {\n        List<CommandObject> commandObjects = Lists.newArrayList();\n        for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) {\n            commandObjects.add(getCommandObjectByRowIndex(i));\n        }\n        return commandObjects;\n    }\n\n    private CommandObject getCommandObjectByRowIndex(int rowIndex) {\n        String id = this.getModel().getValueAt(rowIndex, Column.ID.getIndex()).toString();\n        String name = getNameByRowIndex(rowIndex);\n        String command = getCommandByRowIndex(rowIndex);\n        String group = getGroupByRowIndex(rowIndex);\n        ERuntimeBehaviour runtimeBehaviour = getRuntimeBehaviourByRowIndex(rowIndex);\n        boolean showPreview = getShowPreviewByRowIndex(rowIndex);\n        List<CommandObject.Placeholder> placeholders = getPlaceholders(rowIndex);\n        return new CommandObject(id, name, command, group, runtimeBehaviour, showPreview, placeholders);\n    }\n\n    public CommandObject getCommandObjectById(String commandId) {\n        if (commandId == null) {\n            BurpExtender.printErr(\"CommandObject id should not be null!\");\n            throw new IllegalArgumentException(\"CommandObject id should not be null!\");\n        }\n        for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) {\n            CommandObject commandObject = getCommandObjectByRowIndex(i);\n            if (commandId.equals(commandObject.getId())) {\n                return commandObject;\n            }\n        }\n        BurpExtender.printErr(\"No command found with the specified id!\");\n        throw new IllegalStateException(\"No command found with the specified id!\");\n    }\n\n    public void addCommandObjects(List<CommandObject> commandObjectList) {\n        for (CommandObject commandObject : commandObjectList) {\n            addCommandObject(commandObject);\n        }\n    }\n\n    public void addCommandObject(CommandObject commandObject) {\n        getDefaultModel().addRow(new Object[]{\n                commandObject.getId(),\n                commandObject.getName(),\n                commandObject.getFormat(),\n                commandObject.getGroup(),\n                commandObject.getRuntimeBehaviour().alternateName(),\n                commandObject.shouldShowPreview(),\n                commandObject.getPlaceholders()\n        });\n    }\n\n    public void editSelectedCommandObject(CommandObject commandObject) {\n        int selectedRowIndex = this.getSelectedRow();\n        if (selectedRowIndex >= 0) {\n            editRow(selectedRowIndex, commandObject);\n        }\n    }\n\n    private void editRow(int rowIndex, CommandObject commandObject) {\n        DefaultTableModel model = getDefaultModel();\n        ((SendToTableModel) getDefaultModel()).setBlockEvents(true);\n        model.setValueAt(commandObject.getId(), rowIndex, Column.ID.getIndex());\n        model.setValueAt(commandObject.getName(), rowIndex, Column.NAME.getIndex());\n        model.setValueAt(commandObject.getGroup(), rowIndex, Column.GROUP.getIndex());\n        model.setValueAt(commandObject.getFormat(), rowIndex, Column.COMMAND.getIndex());\n        model.setValueAt(commandObject.getRuntimeBehaviour().alternateName(), rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex());\n        model.setValueAt(commandObject.shouldShowPreview(), rowIndex, Column.SHOW_PREVIEW.getIndex());\n        model.setValueAt(commandObject.getPlaceholders(), rowIndex, Column.PLACEHOLDERS.getIndex());\n        ((SendToTableModel) getDefaultModel()).setBlockEvents(false);\n        getDefaultModel().fireTableDataChanged();\n    }\n\n    public void editCommandObject(CommandObject commandObject) {\n        for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) {\n            CommandObject commandObjectFromRow = getCommandObjectByRowIndex(i);\n            if (commandObjectFromRow.getId().equals(commandObject.getId())) {\n                editRow(i, commandObject);\n                return;\n            }\n        }\n    }\n\n    public void removeSelectedRow() {\n        List<Integer> rows = Ints.asList(this.getSelectedRows());\n        Collections.sort(rows, Collections.reverseOrder());\n        for (Integer row : rows) {\n            getDefaultModel().removeRow(row);\n        }\n    }\n\n    public void clearTable() {\n        for (int row = this.getRowCount() - 1; row >= 0; row--) {\n            getDefaultModel().removeRow(row);\n        }\n    }\n\n    public void moveSelectedRowUp() {\n        moveRowBy(-1);\n    }\n\n    public void moveSelectedRowDown() {\n        moveRowBy(1);\n    }\n\n    private void moveRowBy(int index) {\n        DefaultTableModel model = (DefaultTableModel) this.getModel();\n        int[] rows = this.getSelectedRows();\n        int destination = rows[0] + index;\n        int rowCount = model.getRowCount();\n\n        if (destination < 0 || destination >= rowCount) {\n            return;\n        }\n\n        model.moveRow(rows[0], rows[rows.length - 1], destination);\n        this.setRowSelectionInterval(rows[0] + index, rows[rows.length - 1] + index);\n    }\n\n    @Override\n    public void removeAll() {\n        DefaultTableModel model = (DefaultTableModel) this.getModel();\n        for (int i = 0; i < model.getRowCount(); i++) {\n            model.removeRow(i);\n        }\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTableListener.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui;\n\nimport burp.BurpExtender;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\n\nimport javax.swing.*;\nimport javax.swing.event.TableModelEvent;\nimport javax.swing.event.TableModelListener;\nimport java.awt.event.ActionEvent;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class SendToTableListener implements TableModelListener {\n\n    private SendToTable sendToTable;\n\n    public SendToTableListener(SendToTable sendToTable) {\n        this.sendToTable = sendToTable;\n    }\n\n    @Override\n    public void tableChanged(TableModelEvent e) {\n        BurpExtender.getConfig().saveSendToTableData(sendToTable.getCommandObjects());\n    }\n\n    public void onAddButtonClick(ActionEvent e, CommandObject commandObject) {\n        sendToTable.addCommandObject(commandObject);\n    }\n\n    public void onEditButtonClick(ActionEvent e, CommandObject commandObject) {\n        sendToTable.editSelectedCommandObject(commandObject);\n    }\n\n    public void onRemoveButtonClick(ActionEvent e) {\n        sendToTable.removeSelectedRow();\n    }\n\n    public void onUpButtonClick(ActionEvent e) {\n        sendToTable.moveSelectedRowUp();\n    }\n\n    public void onDownButtonClick(ActionEvent e) {\n        sendToTable.moveSelectedRowDown();\n    }\n\n    public void onShowPreviewChange(ActionEvent e, String commandId, boolean showPreview) {\n        CommandObject commandObject = sendToTable.getCommandObjectById(commandId);\n        commandObject.setShowPreview(showPreview);\n        sendToTable.editCommandObject(commandObject);\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/action/SendToContextMenuItemAction.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui.action;\n\nimport burp.BurpExtender;\nimport net.bytebutcher.burpsendtoextension.builder.CommandBuilder;\nimport net.bytebutcher.burpsendtoextension.executioner.CommandExecutioner;\nimport net.bytebutcher.burpsendtoextension.gui.SendToPreviewDialog;\nimport net.bytebutcher.burpsendtoextension.gui.SendToRunInTerminalBehaviourChoiceDialog;\nimport net.bytebutcher.burpsendtoextension.gui.SendToTableListener;\nimport net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;\nimport net.bytebutcher.burpsendtoextension.models.CommandObject;\nimport net.bytebutcher.burpsendtoextension.models.Context;\nimport net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;\n\nimport javax.swing.*;\nimport java.awt.event.ActionEvent;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.List;\nimport java.util.Map;\n\npublic class SendToContextMenuItemAction extends AbstractAction {\n\n    private final CommandObject commandObject;\n    private final List<Map<String, IPlaceholderParser>> placeholders;\n    private final SendToTableListener sendToTableListener;\n    private final Context context;\n\n    public SendToContextMenuItemAction(String title, CommandObject commandObject, List<Map<String, IPlaceholderParser>> placeholders, SendToTableListener sendToTableListener, Context context) {\n        super(title);\n        this.commandObject = commandObject;\n        this.placeholders = placeholders;\n        this.sendToTableListener = sendToTableListener;\n        this.context = context;\n    }\n    @Override\n    public void actionPerformed(ActionEvent e) {\n        try {\n            String command = new CommandBuilder(commandObject, placeholders, context).build();\n            if (commandObject.shouldShowPreview()) {\n                command = showSendToPreviewDialog(commandObject.getId(), command);\n            }\n            if (command == null) {\n                return;\n            }\n            if (commandObject.shouldRunInTerminal()) {\n                runCommandInTerminal(command);\n            } else {\n                runCommandInBackground(command);\n            }\n        } catch (Exception e1) {\n            DialogUtil.showErrorDialog(\n                    BurpExtender.getParent(),\n                    \"Error during command execution!\",\n                    \"<html><p>There was an unknown error during command execution!</p>\" +\n                            \"<p>For more information check out the \\\"Send to\\\" extension error log!</p></html>\"\n            );\n            BurpExtender.printErr(\"Error during command execution: \" + e1);\n            BurpExtender.printErr(stackTraceToString(e1));\n        }\n    }\n\n    private void runCommandInBackground(String command) throws Exception {\n        new CommandExecutioner(commandObject.shouldOutputReplaceSelection(), context).execute(command);\n    }\n\n    private void runCommandInTerminal(String command) throws Exception {\n        ERunInTerminalBehaviour runInTerminalBehaviour = BurpExtender.getConfig().getRunInTerminalBehaviour();\n        boolean containsMultipleCommands = command.contains(\"\\n\");\n        if (containsMultipleCommands && BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog()) {\n            SendToRunInTerminalBehaviourChoiceDialog.EChoice choice = null;\n            while (choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SEPARATE_TERMINALS && choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SINGLE_TERMINAL) {\n                choice = new SendToRunInTerminalBehaviourChoiceDialog(BurpExtender.getParent(), runInTerminalBehaviour, command.split(\"\\n\").length).run();\n                switch (choice) {\n                    case RUN_IN_SINGLE_TERMINAL:\n                        runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL;\n                        break;\n                    case RUN_IN_SEPARATE_TERMINALS:\n                        runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS;\n                        break;\n                    case REVIEW_COMMANDS:\n                        command = showSendToPreviewDialog(command);\n                        if (command == null) {\n                            return;\n                        }\n                    case CANCEL:\n                        return;\n                }\n            }\n        }\n        new CommandExecutioner(runInTerminalBehaviour, commandObject.shouldOutputReplaceSelection(), context).execute(command);\n    }\n\n    private String showSendToPreviewDialog(String command) {\n        SendToPreviewDialog previewDialog = new SendToPreviewDialog(BurpExtender.getParent(), \"Review commands\", command);\n        return previewDialog.run() ? previewDialog.getCommand() : null;\n    }\n\n    private String showSendToPreviewDialog(String id, String command) throws Exception {\n        SendToPreviewDialog previewDialog = new SendToPreviewDialog(\n                BurpExtender.getParent(),\n                \"Execute command?\",\n                command, id,\n                sendToTableListener\n        );\n        if (!previewDialog.run()) {\n            return null;\n        }\n        return previewDialog.getCommand();\n    }\n\n    private String stackTraceToString(Exception e) {\n        StringWriter sw = new StringWriter();\n        PrintWriter pw = new PrintWriter(sw);\n        e.printStackTrace(pw);\n        return sw.toString();\n    }\n}"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/listener/ToolTipActionListener.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui.listener;\n\nimport javax.swing.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\n\npublic class ToolTipActionListener implements ActionListener {\n\n    private JComponent component;\n    private String toolTipText;\n\n    public ToolTipActionListener(JComponent component, String toolTipText) {\n        this.component = component;\n        this.toolTipText = toolTipText;\n    }\n\n    @Override\n    public void actionPerformed(ActionEvent e) {\n        JToolTip toolTip = this.component.createToolTip();\n        toolTip.setTipText(this.toolTipText);\n        PopupFactory popupFactory = PopupFactory.getSharedInstance();\n        int x = this.component.getLocationOnScreen().x;\n        int y = this.component.getLocationOnScreen().y;\n        x += this.component.getWidth() / 2;\n        y += this.component.getHeight();\n        final Popup tooltipContainer = popupFactory.getPopup(this.component, toolTip, x, y);\n        tooltipContainer.show();\n        (new Thread(new Runnable() {\n            public void run() {\n                try {\n                    Thread.sleep(10000);\n                } catch (InterruptedException ex) {\n                    // Nothing to do here.\n                }\n                tooltipContainer.hide();\n            }\n\n        })).start();\n    }\n\n    public String getToolTipText() {\n        return toolTipText;\n    }\n\n    public void setToolTipText(String toolTipText) {\n        this.toolTipText = toolTipText;\n    }\n\n    public JComponent getComponent() {\n        return component;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/DialogUtil.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui.util;\r\n\r\nimport javax.swing.*;\r\nimport java.awt.*;\r\n\r\npublic final class DialogUtil {\r\n\r\n    public static void showErrorDialog(Component parent, String title, String message) {\r\n        final JDialog dlgError = new JOptionPane(message, JOptionPane.ERROR_MESSAGE).createDialog(parent, title);\r\n        dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError));\r\n        dlgError.setVisible(true);\r\n    }\r\n\r\n    public static boolean showConfirmationDialog(Component parent, String title, String message) {\r\n        JOptionPane jOptionPane = new JOptionPane(message, JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION);\r\n        final JDialog dlgError = jOptionPane.createDialog(parent, title);\r\n        dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError));\r\n        dlgError.setVisible(true);\r\n        return ((Integer) jOptionPane.getValue()).intValue() == JOptionPane.YES_OPTION;\r\n    }\r\n\r\n    public static int getX(Component parent, Component child) {\r\n        try {\r\n            return parent.getX() + (parent.getWidth() / 2) - (child.getWidth() / 2);\r\n        } catch (NullPointerException e) {\r\n            return 0;\r\n        }\r\n    }\r\n\r\n    public static int getY(Component parent, Component child) {\r\n        try {\r\n            return parent.getY() + (parent.getHeight() / 2) - (child.getHeight() / 2);\r\n        } catch (NullPointerException e) {\r\n            return 0;\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/SelectionUtil.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.util.Arrays;\n\npublic class SelectionUtil {\n\n    public static byte[] replaceSelectedText(byte[] message, int[] bounds, String replaceText) throws IOException {\n        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n        outputStream.write(Arrays.copyOfRange(message, 0, bounds[0]));\n        outputStream.write(replaceText.getBytes());\n        outputStream.write(Arrays.copyOfRange(message, bounds[1], message.length));\n        outputStream.flush();\n        return outputStream.toByteArray();\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/WebUtil.java",
    "content": "package net.bytebutcher.burpsendtoextension.gui.util;\r\n\r\nimport java.awt.*;\r\nimport java.net.URI;\r\nimport java.net.URISyntaxException;\r\nimport java.net.URL;\r\n\r\npublic class WebUtil {\r\n\r\n    public static boolean openWebpage(URI uri) {\r\n        Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;\r\n        if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {\r\n            try {\r\n                desktop.browse(uri);\r\n                return true;\r\n            } catch (Exception e) {\r\n                // Nothing to do here...\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public static boolean openWebpage(URL url) {\r\n        try {\r\n            return openWebpage(url.toURI());\r\n        } catch (URISyntaxException e) {\r\n            // Nothing to do here...\r\n        }\r\n        return false;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/CommandObject.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\nimport burp.BurpExtender;\nimport com.google.common.collect.Lists;\nimport com.google.gson.annotations.SerializedName;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponseInfoPlaceholder;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponsePlaceholder;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;\n\nimport java.util.*;\n\npublic class CommandObject {\n\n    private String id = UUID.randomUUID().toString();\n    private String name;\n    @SerializedName(value=\"format\", alternate={\"command\"}) // Changed field name from \"command\" to \"format\" in version 1.1\n    private String format;\n    private String group;\n    private ERuntimeBehaviour runtimeBehaviour;\n    private boolean showPreview;\n    private List<CommandObject.Placeholder> placeholders;\n\n    public static class Placeholder {\n\n        // The name of the placeholder (e.g. \"%U\").\n        private final String name;\n\n        // Defines at which position the placeholder is found in the format string.\n        private final int start;\n        private final int end;\n\n        // The behavior of the placeholder.\n        private IPlaceholderBehaviour behaviour;\n\n        public Placeholder(String placeholder, IPlaceholderBehaviour behaviour, int start, int end) {\n            this.name = placeholder;\n            this.behaviour = behaviour;\n            this.start = start;\n            this.end = end;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public int getStart() {\n            return start;\n        }\n\n        public int getEnd() {\n            return end;\n        }\n\n        @Override\n        public String toString() {\n            return \"Placeholder{\" +\n                    \"name='\" + name + '\\'' +\n                    \", start=\" + start +\n                    \", end=\" + end +\n                    \", behaviour=\" + behaviour +\n                    '}';\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n            Placeholder that = (Placeholder) o;\n            return Objects.equals(name, that.name);\n        }\n\n        @Override\n        public int hashCode() {\n            return Objects.hash(name);\n        }\n\n        public IPlaceholderBehaviour getBehaviour() {\n            return behaviour;\n        }\n\n        public void setBehaviour(IPlaceholderBehaviour behaviour) {\n            this.behaviour = behaviour;\n        }\n\n    }\n\n    public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List<Placeholder> placeholders) {\n        this.name = name;\n        this.format = format;\n        this.group = group;\n        this.runtimeBehaviour = runtimeBehaviour;\n        this.showPreview = showPreview;\n        this.placeholders = initPlaceholders(Lists.newArrayList(placeholders));\n    }\n\n    public CommandObject(String id, String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List<Placeholder> placeholders) {\n        this(name, format, group, runtimeBehaviour, showPreview, placeholders);\n        this.id = id;\n    }\n\n    public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview) {\n        this(name, format, group, runtimeBehaviour, showPreview, Lists.newArrayList());\n    }\n\n    /**\n     * Each context menu entry has a string format. The string format may contain placeholders.\n     * The user can specify the behaviour of the placeholder which is stored in configuration.\n     * This method links the placeholders and it's behavior.\n     *\n     * This method addresses the problem that the string format might have been changed (e.g. added/removed placeholder)\n     * while the behavior was not. As a result this method need to autodetect whether the stored behavior can still be\n     * applied or whether a default behaviour needs to be set.\n     *\n     * @param storedPlaceholders a (incomplete) list of placeholders. if this list is\n     *                                 empty (e.g. when no placeholder was defined by the user), each\n     *                                 placeholder found in the command is associated with a default placeholder behaviour.\n     *                                 Otherwise the placeholder behaviour in the given list is used.\n     * @return the behaviour of each placeholder.\n     */\n    private List<CommandObject.Placeholder> initPlaceholders(List<CommandObject.Placeholder> storedPlaceholders) {\n        // Get the placeholders defined in the format string\n        List<CommandObject.Placeholder> actualPlaceholders = Placeholders.get(this.format);\n        for (CommandObject.Placeholder actualPlaceholder : actualPlaceholders) {\n            // Check whether a placeholder behavior exists for this placeholder.\n            // We pick the first one we find which might not always be correct - but we can't do it any other way.\n            Placeholder match = storedPlaceholders.stream().filter(x -> x.getName().equals(actualPlaceholder.getName())).findFirst().orElse(null);\n            if (match != null && match.getBehaviour() != null) {\n                actualPlaceholder.setBehaviour(match.getBehaviour());\n            } else {\n                actualPlaceholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour());\n            }\n            storedPlaceholders.remove(match);\n        }\n        return actualPlaceholders;\n    }\n\n    public String getId() { return this.id; }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public String getFormat() {\n        return this.format;\n    }\n\n    public String getGroup() {\n        return group;\n    }\n\n    public ERuntimeBehaviour getRuntimeBehaviour() {\n        return this.runtimeBehaviour != null ? this.runtimeBehaviour : ERuntimeBehaviour.RUN_IN_TERMINAL;\n    }\n\n    public boolean shouldRunInTerminal() {\n        return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_TERMINAL;\n    }\n\n    public boolean shouldOutputReplaceSelection() {\n        return getRuntimeBehaviour() == ERuntimeBehaviour.OUTPUT_SHOULD_REPLACE_SELECTION;\n    }\n\n    public boolean shouldRunInBackground() {\n        return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_BACKGROUND;\n    }\n\n    public void setShowPreview(boolean showPreview) {\n        this.showPreview = showPreview;\n    }\n\n    public boolean shouldShowPreview() {\n        return this.showPreview;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n        CommandObject that = (CommandObject) o;\n        return id.equals(that.id);\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.hash(id);\n    }\n\n    public List<Map<String, IPlaceholderParser>> getValid(List<Map<String, IPlaceholderParser>> placeholders, Context context) {\n        List<Map<String, IPlaceholderParser>> validItems = Lists.newArrayList();\n        for (Map<String, IPlaceholderParser> placeholderMap : placeholders) {\n            if (isValid(placeholderMap, context)) {\n                validItems.add(placeholderMap);\n            }\n        }\n        return validItems;\n    }\n\n    private boolean isValid(Map<String, IPlaceholderParser> placeholderMap, Context context) {\n        for (CommandObject.Placeholder placeholder : getPlaceholders()) {\n            if (placeholderMap.containsKey(placeholder.getName())) {\n                if (!placeholderMap.get(placeholder.getName()).isValid(context)) {\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    public boolean doesRequireRequestResponse(Map<String, IPlaceholderParser> placeholderMap) {\n        for (Placeholder placeholder : getPlaceholders()) {\n            if (placeholderMap.containsKey(placeholder.getName())) {\n                if (placeholderMap.get(placeholder.getName()) instanceof AbstractRequestResponseInfoPlaceholder || placeholderMap.get(placeholder) instanceof AbstractRequestResponsePlaceholder) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    public List<CommandObject.Placeholder> getPlaceholders() {\n        if (placeholders == null) {\n            // placeholder list might be null, when ContextMenuEntry was loaded from config\n            // and no placeholders were defined.\n            placeholders = initPlaceholders(Lists.newArrayList());\n        }\n        return Lists.newArrayList(placeholders);\n    }\n\n    @Override\n    public String toString() {\n        return \"CommandObject{\" +\n                \"id='\" + id + '\\'' +\n                \", name='\" + name + '\\'' +\n                \", format='\" + format + '\\'' +\n                \", group='\" + group + '\\'' +\n                \", runtimeBehaviour=\" + runtimeBehaviour +\n                \", showPreview=\" + showPreview +\n                \", placeholders=\" + placeholders +\n                '}';\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Config.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\nimport burp.BurpExtender;\nimport burp.IBurpExtenderCallbacks;\nimport com.google.common.collect.Lists;\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.reflect.TypeToken;\nimport com.google.gson.typeadapters.RuntimeTypeAdapterFactory;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;\nimport net.bytebutcher.burpsendtoextension.utils.OsUtils;\n\nimport java.util.List;\n\npublic class Config {\n\n    private final IBurpExtenderCallbacks callbacks;\n    private final Gson gson;\n    private BurpExtender burpExtender;\n    private String version = \"1.6\";\n\n    public Config(BurpExtender burpExtender) {\n        this.burpExtender = burpExtender;\n        this.callbacks = BurpExtender.getCallbacks();\n        this.gson = initGson();\n        refreshVersion();\n    }\n\n    private Gson initGson() {\n        RuntimeTypeAdapterFactory<IPlaceholderBehaviour> placeholderBehaviourAdapterFactory = RuntimeTypeAdapterFactory.of(IPlaceholderBehaviour.class, \"type\")\n                .registerSubtype(StringSeparatedPlaceholderBehaviour.class, \"StringSeparated\")\n                .registerSubtype(CommandSeparatedPlaceholderBehaviour.class, \"CommandSeparated\")\n                .registerSubtype(FileSeparatedPlaceholderBehaviour.class, \"FileSeparated\");\n        return new GsonBuilder().registerTypeAdapterFactory(placeholderBehaviourAdapterFactory).create();\n    }\n\n    public void saveSendToTableData(List<CommandObject> sendToTableData) {\n        this.callbacks.saveExtensionSetting(\"SendToTableData\",\n                gson.toJson(sendToTableData));\n    }\n\n    public List<CommandObject> getSendToTableData() {\n        List<CommandObject> commandObjectList = Lists.newArrayList();\n        try {\n            String sendToTableData = this.callbacks.loadExtensionSetting(\"SendToTableData\");\n            if (sendToTableData == null || sendToTableData.isEmpty() || \"[]\".equals(sendToTableData)) {\n                if (isFirstStart()) {\n                    BurpExtender.printOut(\"Initializing default table data...\");\n                    commandObjectList = initializeDefaultSendToTableData();\n                }\n                return commandObjectList;\n            }\n            return gson.fromJson(sendToTableData, new TypeToken<List<CommandObject>>() {}.getType());\n        } catch (Exception e) {\n            BurpExtender.printErr(\"Error retrieving table data!\");\n            BurpExtender.printErr(e.toString());\n            return commandObjectList;\n        }\n    }\n\n    private List<CommandObject> initializeDefaultSendToTableData() {\n        List<CommandObject> commandObjectList = getDefaultSendToTableData();\n        saveSendToTableData(commandObjectList);\n        unsetFirstStart();\n        return commandObjectList;\n    }\n\n    public List<CommandObject> getDefaultSendToTableData() {\n        String groupFuzz = \"fuzz\";\n        String groupCMS = \"cms\";\n        String groupSQL = \"sql\";\n        String groupSSL = \"ssl\";\n        String groupOther = \"other\";\n        return Lists.newArrayList(\n                // cms\n                new CommandObject(\"droopescan\", \"droopescan scan drupal -u %U -t 10\", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"mooscan\", \"mooscan -v --url %U\", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"wpscan\", \"wpscan --url %U --threads 10\", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                // fuzz\n                new CommandObject(\"bfac\", \"bfac --url %U\", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"gobuster\", \"gobuster -u %U -s 403,404 -w /usr/share/wfuzz/wordlist/general/common.txt\", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"nikto\", \"nikto %U\", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"wfuzz\", \"wfuzz -c -w /usr/share/wfuzz/wordlist/general/common.txt --hc 404,403 %U\", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                // sql\n                new CommandObject(\"sqlmap (GET)\", \"sqlmap -o -u %U --level=5 --risk=3\", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"sqlmap (POST)\", \"sqlmap -r %R  --level=5 --risk=3\", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                // ssl\n                new CommandObject(\"sslscan\", \"sslscan %H:%P\", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"sslyze\", \"sslyze --regular %H:%P\", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"testssl\", \"testssl.sh %H:%P\", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                // other\n                new CommandObject(\"Host (%H)\", \"echo %H\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"Port (%P)\", \"echo %P\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"Protocol (%T)\", \"echo %T\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"URL (%U)\", \"echo %U\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"URL-Path (%A)\", \"echo %A\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"URL-Query (%Q)\", \"echo %Q\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"Cookies (%C)\", \"echo %C\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"HTTP-Method (%M)\", \"echo %M\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"Selected text (%S)\", \"echo %S\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"Selected text as file (%F)\", \"echo %F && cat %F\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"HTTP-Request/-Response as file (%R)\", \"echo %R && cat %R\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"HTTP-Headers as file (%E)\", \"echo %E && cat %E\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true),\n                new CommandObject(\"HTTP-Body as file (%B)\", \"echo %B && cat %B\", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true)\n        );\n    }\n\n    private void refreshVersion() {\n        if (!this.version.equals(this.callbacks.loadExtensionSetting(\"version\"))) {\n            this.callbacks.saveExtensionSetting(\"version\", version);\n            setFirstStart();\n        }\n    }\n\n    private boolean isFirstStart() {\n        String isFirstStart = this.callbacks.loadExtensionSetting(\"isFirstStart\");\n        return isFirstStart == null || \"true\".equals(isFirstStart);\n    }\n\n    private void setFirstStart() {\n        this.callbacks.saveExtensionSetting(\"isFirstStart\", null);\n    }\n\n    private void unsetFirstStart() {\n        this.callbacks.saveExtensionSetting(\"isFirstStart\", \"false\");\n    }\n\n    public void setRunInTerminalBehaviour(ERunInTerminalBehaviour runInTerminalBehaviour) {\n        this.callbacks.saveExtensionSetting(\"runInTerminalBehaviour\", runInTerminalBehaviour.name());\n    }\n\n    public ERunInTerminalBehaviour getRunInTerminalBehaviour() {\n        String runInTerminalBehaviour = this.callbacks.loadExtensionSetting(\"runInTerminalBehaviour\");\n        if (runInTerminalBehaviour == null || runInTerminalBehaviour.isEmpty()) {\n            return ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL;\n        }\n        return ERunInTerminalBehaviour.valueOf(runInTerminalBehaviour);\n    }\n\n    public boolean shouldShowRunInTerminalBehaviourChoiceDialog() {\n        String showDialog = this.callbacks.loadExtensionSetting(\"showRunInTerminalBehaviourDialog\");\n        if (showDialog == null || showDialog.isEmpty()) {\n            return true; // Default\n        }\n        return Boolean.parseBoolean(showDialog);\n    }\n\n    public void shouldShowRunInTerminalBehaviourChoiceDialog(boolean status) {\n        this.callbacks.saveExtensionSetting(\"showRunInTerminalBehaviourDialog\", String.valueOf(status));\n    }\n\n    public String getRunInTerminalCommand() {\n        if (OsUtils.isWindows()) {\n            return getRunInTerminalCommand(\"runInTerminalSettingWindows\", \"cmd  /c start cmd /K %C\");\n        } else {\n            return getRunInTerminalCommand(\"runInTerminalSettingUnix\", \"xterm -hold -e %C\");\n        }\n    }\n\n    private String getRunInTerminalCommand(String label, String defaultValue) {\n        String runInTerminalSetting = this.callbacks.loadExtensionSetting(label);\n        if (runInTerminalSetting == null || runInTerminalSetting.isEmpty()) {\n            runInTerminalSetting = defaultValue;\n            this.callbacks.saveExtensionSetting(label, runInTerminalSetting);\n        }\n        return runInTerminalSetting;\n    }\n\n    public void setRunInTerminalCommand(String command) {\n        if (OsUtils.isWindows()) {\n            this.callbacks.saveExtensionSetting(\"runInTerminalSettingWindows\", command);\n        } else {\n            this.callbacks.saveExtensionSetting(\"runInTerminalSettingUnix\", command);\n        }\n    }\n\n    public void resetRunInTerminalCommand() {\n        this.callbacks.saveExtensionSetting(\"runInTerminalSettingWindows\", \"cmd  /c start cmd /K %C\");\n        this.callbacks.saveExtensionSetting(\"runInTerminalSettingUnix\", \"xterm -hold -e %C\");\n\n    }\n\n    public void setSafeMode(boolean status) {\n        this.callbacks.saveExtensionSetting(\"safeMode\", String.valueOf(status));\n    }\n\n    public boolean isSafeModeActivated() {\n        String safeMode = this.callbacks.loadExtensionSetting(\"safeMode\");\n        if (safeMode == null || safeMode.isEmpty())\n            return true;\n        return Boolean.parseBoolean(safeMode);\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Context.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\nimport burp.IContextMenuInvocation;\nimport burp.IHttpRequestResponse;\nimport com.google.common.collect.Lists;\n\nimport java.util.ArrayList;\n\npublic class Context {\n    private final Origin origin;\n    private final IContextMenuInvocation invocation;\n\n    public enum Origin {\n        HTTP_RESPONSE, HTTP_REQUEST, UNKNOWN;\n\n        public static Origin getTarget(IContextMenuInvocation contextMenuInvocation) {\n            ArrayList<Byte> requestContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST);\n            ArrayList<Byte> responseContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_RESPONSE, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE);\n            if (requestContext.contains(contextMenuInvocation.getInvocationContext())) {\n                return Origin.HTTP_REQUEST;\n            } else if (responseContext.contains(contextMenuInvocation.getInvocationContext())) {\n                return Origin.HTTP_RESPONSE;\n            } else {\n                return Origin.UNKNOWN;\n            }\n        }\n\n    }\n\n    public Context(Origin origin, IContextMenuInvocation invocation) {\n        this.origin = origin;\n        this.invocation = invocation;\n    }\n\n    public Context(IContextMenuInvocation invocation) {\n        this(Origin.getTarget(invocation), invocation);\n    }\n\n    public Origin getOrigin() {\n        return origin;\n    }\n\n    /**\n     * NOTE: Access to getSelectedMessages should be restricted, since messages should always be accessed via\n     *       RequestResponseHolder. Notably the definition and usage of this function here is a minor design issue.\n     */\n    public IHttpRequestResponse[] getSelectedMessages() {\n        return invocation.getSelectedMessages();\n    }\n\n    public int[] getSelectionBounds() {\n        return invocation.getSelectionBounds();\n    }\n\n}"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERunInTerminalBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\npublic enum ERunInTerminalBehaviour {\n    RUN_IN_SEPARATE_TERMINALS,\n    RUN_IN_SINGLE_TERMINAL\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERuntimeBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\npublic enum ERuntimeBehaviour {\n\n    RUN_IN_BACKGROUND(\"Run in Background\"),\n    RUN_IN_TERMINAL(\"Run in Terminal\"),\n    OUTPUT_SHOULD_REPLACE_SELECTION(\"Output should replace Selection\");\n\n    private String alternateName;\n\n    ERuntimeBehaviour(String alternateName) {\n        this.alternateName = alternateName;\n    }\n\n    public String alternateName() {\n        return this.alternateName;\n    }\n\n    public static ERuntimeBehaviour getEnum(String name) {\n        for (ERuntimeBehaviour value : ERuntimeBehaviour.values()) {\n            if (value.name().equals(name) || value.alternateName().equals(name)) {\n                return value;\n            }\n        }\n        throw new IllegalArgumentException();\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Placeholders.java",
    "content": "package net.bytebutcher.burpsendtoextension.models;\n\nimport burp.IBurpExtenderCallbacks;\nimport burp.IHttpRequestResponse;\nimport burp.RequestResponseHolder;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport net.bytebutcher.burpsendtoextension.models.placeholder.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\npublic class Placeholders {\n\n    public static List<AbstractPlaceholder> get() {\n        return Lists.newArrayList(\n                new CookiesPlaceholder(),\n                new HostPlaceholder(),\n                new HttpBodyToFilePlaceholder(),\n                new HttpContentLengthPlaceholder(),\n                new HttpHeadersToFilePlaceholder(),\n                new HttpMethodPlaceholder(),\n                new HttpRequestResponsePlaceholder(),\n                new HttpStatusCodePlaceholder(),\n                new PortPlaceholder(),\n                new ProtocolPlaceholder(),\n                new SelectedTextPlaceholder(),\n                new SelectedTextToFilePlaceholder(),\n                new UrlPathPlaceholder(),\n                new UrlPlaceholder(),\n                new UrlQueryPlaceholder());\n    }\n\n    /**\n     * Initializes the placeholders for each selected message and returns them in a list.\n     */\n    public static List<Map<String, IPlaceholderParser>> get(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse[] selectedMessages) {\n        List<Map<String, IPlaceholderParser>> result = Lists.newArrayList();\n        if (selectedMessages == null) {\n            return result;\n        }\n        List<AbstractPlaceholder> placeholderList = Placeholders.get();\n        for (IHttpRequestResponse selectedMessage : selectedMessages) {\n            RequestResponseHolder requestResponseHolder = new RequestResponseHolder(burpExtenderCallbacks, selectedMessage);\n            Map<String, IPlaceholderParser> placeholderMap = Maps.newHashMap();\n            for (AbstractPlaceholder placeholder : placeholderList) {\n                placeholderMap.put(placeholder.getPlaceholder(), placeholder.createParser(requestResponseHolder));\n            }\n            result.add(placeholderMap);\n        }\n        return result;\n    }\n\n    public static List<CommandObject.Placeholder> get(String format) {\n        List<String> validPlaceholders = Placeholders.get().stream().map(AbstractPlaceholder::getPlaceholder).collect(Collectors.toList());\n        List<CommandObject.Placeholder> placeholders = Lists.newArrayList();\n        if (format != null && !format.isEmpty()) {\n            Matcher m = Pattern.compile(\"(\\\\%[A-Z])\").matcher(format);\n            while (m.find()) {\n                String placeholder = m.group(1);\n                if (validPlaceholders.contains(placeholder)) {\n                    placeholders.add(new CommandObject.Placeholder(placeholder, null, m.start(), m.end()));\n                }\n            }\n        }\n        return placeholders;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\n\npublic abstract class AbstractPlaceholder implements IPlaceholder, IPlaceholderParserFactory {\n\n    private final String placeholder;\n    private final boolean doesRequireShellEscape;\n    private final boolean shouldWriteToFile;\n\n    public AbstractPlaceholder(String placeholder, boolean doesRequireShellEscape, boolean shouldWriteToFile) {\n        this.placeholder = placeholder;\n        this.doesRequireShellEscape = doesRequireShellEscape;\n        this.shouldWriteToFile = shouldWriteToFile;\n    }\n\n    public String getPlaceholder() {\n        return placeholder;\n    }\n\n    /**\n     * Returns whether the placeholder requires shell-escaping.\n     */\n    public boolean doesRequireShellEscape() {\n        return doesRequireShellEscape;\n    }\n\n\n    public boolean shouldWriteToFile() {\n        return shouldWriteToFile;\n    }\n\n    public abstract IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder);\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestInfoPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.IRequestInfoWrapper;\nimport burp.RequestResponseHolder;\n\n/**\n * Placeholders which do require additional information from a request.\n */\npublic abstract class AbstractRequestInfoPlaceholder extends AbstractRequestResponsePlaceholderBase {\n\n    private final RequestResponseHolder requestResponseHolder;\n\n    public AbstractRequestInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) {\n        super(placeholder, requestResponseHolder);\n        this.requestResponseHolder = requestResponseHolder;\n    }\n\n    protected IRequestInfoWrapper getRequestInfo() {\n        return requestResponseHolder.getRequestInfo();\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.IHttpRequestResponse;\nimport burp.RequestResponseHolder;\n\npublic abstract class AbstractRequestPlaceholder extends AbstractRequestResponsePlaceholderBase {\n\n    private final RequestResponseHolder requestResponseHolder;\n\n    public AbstractRequestPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) {\n        super(placeholder, requestResponseHolder);\n        this.requestResponseHolder = requestResponseHolder;\n    }\n\n    protected IHttpRequestResponse getHttpRequestResponse() {\n        return requestResponseHolder.getHttpRequestResponse();\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponseInfoPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.IRequestInfoWrapper;\nimport burp.IResponseInfoWrapper;\nimport burp.RequestResponseHolder;\n\n/**\n * Placeholder which depends on a request/response is selected/focused.\n */\npublic abstract class AbstractRequestResponseInfoPlaceholder extends AbstractRequestResponsePlaceholderBase {\n\n    private final RequestResponseHolder requestResponseHolder;\n\n    public AbstractRequestResponseInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) {\n        super(placeholder, requestResponseHolder);\n        this.requestResponseHolder = requestResponseHolder;\n    }\n\n    protected IRequestInfoWrapper getRequestInfo() {\n        return requestResponseHolder.getRequestInfo();\n    }\n\n    protected IResponseInfoWrapper getResponseInfo() {\n        return requestResponseHolder.getResponseInfo();\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\npublic abstract class AbstractRequestResponsePlaceholder extends AbstractRequestResponsePlaceholderBase {\n\n    private final RequestResponseHolder requestResponseHolder;\n\n    public AbstractRequestResponsePlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) {\n        super(placeholder, requestResponseHolder);\n        this.requestResponseHolder = requestResponseHolder;\n    }\n\n    protected byte[] getRequestResponseAsByteArray(Context context) {\n        switch(context.getOrigin()) {\n            case HTTP_REQUEST:\n                return requestResponseHolder.getHttpRequestResponse().getRequest();\n            case HTTP_RESPONSE:\n                return requestResponseHolder.getHttpRequestResponse().getResponse();\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholderBase.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.util.Optional;\n\npublic abstract class AbstractRequestResponsePlaceholderBase implements IPlaceholderParser {\n\n    private final IPlaceholder placeholder;\n    private final RequestResponseHolder requestResponseHolder;\n\n    public AbstractRequestResponsePlaceholderBase(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) {\n        this.placeholder = placeholder;\n        this.requestResponseHolder = requestResponseHolder;\n    }\n\n    public String getPlaceholder() {\n        return placeholder.getPlaceholder();\n    }\n\n    public boolean doesRequireShellEscape() {\n        return placeholder.doesRequireShellEscape();\n    }\n\n    public boolean shouldWriteToFile() { return placeholder.shouldWriteToFile(); }\n\n    /**\n     * Returns the value associated with the placeholder.\n     *\n     * @return the value associated with the placeholder.\n     */\n    @Nullable\n    protected abstract String getInternalValue(Context context) throws Exception;\n\n    @Override\n    public String getValue(Context context) throws RuntimeException {\n        try {\n            String value = Optional.ofNullable(getInternalValue(context)).orElse(\"\");\n            if (placeholder.shouldWriteToFile()) {\n                value = writeToFile(value);\n            }\n            return value;\n        } catch (Exception e) {\n            // This exception is thrown when the placeholder can not be constructed (e.g. no text selected,\n            // no url-query-parameter present, etc.). This is done in favor of returning empty text.\n            // In practice this exception should not be thrown anyway since menu items which contain this placeholder\n            // are disabled and can not be selected anyway (see isValid()).\n            throw new RuntimeException(\"Error replacing placeholder \" + placeholder.getPlaceholder() + \" !\", e);\n        }\n    }\n\n    private String writeToFile(String value) throws Exception {\n        try {\n            File tmp = File.createTempFile(\"burp_\", \".snd\");\n            PrintWriter out = new PrintWriter(tmp.getPath());\n            out.write(value);\n            out.flush();\n            return tmp.getAbsolutePath();\n        } catch (RuntimeException e) {\n            throw new Exception(this.getClass().getSimpleName() + \": Error writing to temporary file!\", e);\n        }\n    }\n\n    @Override\n    public boolean isValid(Context context) {\n        try {\n            return getInternalValue(context) != null;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/CookiesPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.ICookie;\nimport burp.RequestResponseHolder;\nimport com.google.common.collect.Lists;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class CookiesPlaceholder extends AbstractPlaceholder {\n\n    public CookiesPlaceholder() {\n        super(\"%C\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) {\n\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                List<ICookie> cookies = Lists.newArrayList();\n                switch (context.getOrigin()) {\n                    case HTTP_REQUEST:\n                        cookies = getRequestInfo().getCookies();\n                        break;\n                    case HTTP_RESPONSE:\n                        cookies = getResponseInfo().getCookies();\n                        break;\n                }\n                return cookies.isEmpty() ? null : cookies.stream().map(iCookie -> iCookie.getName() + \"=\" + iCookie.getValue()).collect(Collectors.joining(\",\"));\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HostPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HostPlaceholder extends AbstractPlaceholder {\n\n    public HostPlaceholder() {\n        super(\"%H\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestPlaceholder(this, requestResponseHolder) {\n\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return getHttpRequestResponse().getHttpService().getHost();\n            }\n\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpBodyToFilePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HttpBodyToFilePlaceholder extends AbstractPlaceholder {\n\n    public HttpBodyToFilePlaceholder() {\n        super(\"%B\", false, true);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                switch(context.getOrigin()) {\n                    case HTTP_REQUEST:\n                        return getRequestInfo().getBody();\n                    case HTTP_RESPONSE:\n                        return getResponseInfo().getBody();\n                }\n                return null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpContentLengthPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HttpContentLengthPlaceholder extends AbstractPlaceholder {\n\n    public HttpContentLengthPlaceholder() {\n        super(\"%L\", false, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                switch(context.getOrigin()) {\n                    case HTTP_REQUEST:\n                         return String.valueOf(getRequestInfo().getBody() == null ? 0 : getRequestInfo().getBody().length());\n                    case HTTP_RESPONSE:\n                        return String.valueOf(getResponseInfo().getBody() == null ? 0 : getResponseInfo().getBody().length());\n                }\n                return null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpHeadersToFilePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport com.google.common.collect.Lists;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.util.List;\n\npublic class HttpHeadersToFilePlaceholder extends AbstractPlaceholder {\n\n    public HttpHeadersToFilePlaceholder() {\n        super(\"%E\", false, true);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                List<String> headers = Lists.newArrayList();\n                switch(context.getOrigin()) {\n                    case HTTP_REQUEST:\n                        headers = getRequestInfo().getHeaders();\n                        break;\n                    case HTTP_RESPONSE:\n                        headers = getResponseInfo().getHeaders();\n                        break;\n                }\n                return headers.isEmpty() ? null : String.join(System.lineSeparator(), headers);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpMethodPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HttpMethodPlaceholder extends AbstractPlaceholder {\n\n    public HttpMethodPlaceholder() {\n        super(\"%M\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return getRequestInfo().getMethod();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpRequestResponsePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HttpRequestResponsePlaceholder extends AbstractPlaceholder {\n\n    public HttpRequestResponsePlaceholder() {\n        super(\"%R\", false, true);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                byte[] requestResponse = getRequestResponseAsByteArray(context);\n                return requestResponse != null ? new String(requestResponse) : null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpStatusCodePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class HttpStatusCodePlaceholder extends AbstractPlaceholder {\n\n    public HttpStatusCodePlaceholder() {\n        super(\"%O\", false, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                short statusCode = -1;\n                if (context.getOrigin() == Context.Origin.HTTP_RESPONSE) {\n                    statusCode = getResponseInfo().getStatusCode();\n                }\n                return statusCode == -1 ? null : String.valueOf(statusCode);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\npublic interface IPlaceholder {\n\n    String getPlaceholder();\n\n    boolean doesRequireShellEscape();\n\n    boolean shouldWriteToFile();\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParser.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\npublic interface IPlaceholderParser extends IPlaceholder {\n\n    String getValue(Context context) throws RuntimeException;\n\n    boolean isValid(Context context);\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParserFactory.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\n\npublic interface IPlaceholderParserFactory {\n\n    IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder);\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/PortPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class PortPlaceholder extends AbstractPlaceholder {\n\n    public PortPlaceholder() {\n        super(\"%P\", false, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return String.valueOf(getHttpRequestResponse().getHttpService().getPort());\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/ProtocolPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class ProtocolPlaceholder extends AbstractPlaceholder {\n\n    public ProtocolPlaceholder() {\n        super(\"%T\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return getHttpRequestResponse().getHttpService().getProtocol();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\n\npublic class SelectedTextPlaceholder extends AbstractPlaceholder {\n\n    public SelectedTextPlaceholder() {\n        super(\"%S\", true, false);\n    }\n\n    protected SelectedTextPlaceholder(String placeholder, boolean doShellEscape, boolean doWriteToFile) {\n        super(placeholder, doShellEscape, doWriteToFile);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                String value = null;\n                int[] selectionBounds = context.getSelectionBounds();\n                if (selectionBounds != null) {\n                    byte[] requestResponse = getRequestResponseAsByteArray(context);\n                    if (requestResponse != null) {\n                        boolean isSelectionEmpty = selectionBounds[0] == selectionBounds[1];\n                        if (!isSelectionEmpty) {\n                            value = new String(requestResponse).substring(selectionBounds[0], selectionBounds[1]);\n                        }\n                    }\n                }\n                return value;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextToFilePlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\npublic class SelectedTextToFilePlaceholder extends SelectedTextPlaceholder {\n\n    public SelectedTextToFilePlaceholder() {\n        super(\"%F\", false, true);\n    }\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPathPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.net.URL;\nimport java.util.Optional;\n\npublic class UrlPathPlaceholder extends AbstractPlaceholder {\n\n    public UrlPathPlaceholder() {\n        super(\"%A\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getPath).orElse(null);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.util.Objects;\n\npublic class UrlPlaceholder extends AbstractPlaceholder {\n\n    public UrlPlaceholder() {\n        super(\"%U\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return Objects.toString(getRequestInfo().getUrl());\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlQueryPlaceholder.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder;\n\nimport burp.RequestResponseHolder;\nimport net.bytebutcher.burpsendtoextension.models.Context;\n\nimport javax.annotation.Nullable;\nimport java.net.URL;\nimport java.util.Optional;\n\npublic class UrlQueryPlaceholder extends AbstractPlaceholder {\n\n    public UrlQueryPlaceholder() {\n        super(\"%Q\", true, false);\n    }\n\n    @Override\n    public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) {\n        return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) {\n            @Nullable\n            @Override\n            protected String getInternalValue(Context context) {\n                return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getQuery).orElse(null);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/CommandSeparatedPlaceholderBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour;\n\npublic class CommandSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour {\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/FileSeparatedPlaceholderBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour;\n\npublic class FileSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour {\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/IPlaceholderBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour;\n\npublic interface IPlaceholderBehaviour {\n\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/StringSeparatedPlaceholderBehaviour.java",
    "content": "package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour;\n\npublic class StringSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour {\n\n    private final String separator;\n\n    public StringSeparatedPlaceholderBehaviour(String separator) {\n        this.separator = separator;\n    }\n\n    public String getSeparator() {\n        return separator;\n    }\n}\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/OsUtils.java",
    "content": "package net.bytebutcher.burpsendtoextension.utils;\r\n\r\npublic final class OsUtils {\r\n\r\n    private static String OS = null;\r\n\r\n    public static String getOsName() {\r\n        if(OS == null) {\r\n            OS = System.getProperty(\"os.name\");\r\n        }\r\n        return OS;\r\n    }\r\n\r\n    public static boolean isWindows() {\r\n        return getOsName().startsWith(\"Windows\");\r\n    }\r\n\r\n    public static boolean isUnix() {\r\n        return !isWindows();\r\n    }\r\n}\r\n"
  },
  {
    "path": "burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/StringUtils.java",
    "content": "package net.bytebutcher.burpsendtoextension.utils;\n\nimport com.google.common.escape.Escaper;\nimport com.google.common.escape.Escapers;\nimport com.google.common.io.CharStreams;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\n\npublic class StringUtils {\n\n    public static final Escaper shellEscaper;\n    static {\n        final Escapers.Builder builder = Escapers.builder();\n        builder.addEscape('\\'', \"'\\\"'\\\"'\");\n        shellEscaper = builder.build();\n    }\n\n    public static String shellEscape(String command) {\n        return shellEscaper.escape(command);\n    }\n\n    public static String fromInputStream(InputStream inputStream) throws IOException {\n        return CharStreams.toString(new InputStreamReader(inputStream));\n    }\n\n}\n"
  }
]