Skip to content

Commit

Permalink
Add scripting capability
Browse files Browse the repository at this point in the history
  • Loading branch information
wargio committed Mar 27, 2022
1 parent 0566339 commit 8ad3a9e
Show file tree
Hide file tree
Showing 9 changed files with 442 additions and 103 deletions.
212 changes: 112 additions & 100 deletions assets.go

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions assets/static/codelines.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.code-editor, .code-lines {
font-family: lucida console, courier new, courier, monospace;
margin: 0;
height: 75vh;
border-radius: 0;
resize: none;
line-height: 1.2;
outline: none;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: Inconsolata;
}
.code-lines:focus-visible,
.code-editor:focus-visible {
outline:none;
}
.code-lines {
display: flex;
border-color: transparent;
overflow-y: hidden;
text-align: right;
box-shadow: none;
color: #707070;
background-color: #d8d8d8;
position: absolute;
width: 3.5rem;
background-color:#3E3D32;
border-color:#3E3D32;
color:#928869;
}
.code-editor {
padding-left: calc(3.5rem + 5px);
width:100%;
background-color:#272822;
border-color:#272822;
color:#ffffff;
}
2 changes: 1 addition & 1 deletion assets/templates/output.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}
@font-face { font-family: 'Inconsolata'; src: URL('{{ .root }}static/inconsolata.ttf') format('truetype'); }
html, body { width: 100%; height: 100%; background-color: #4b4b4b; color: #eeeeee; font-family: Inconsolata; }
pre { width: 100%; height: 100%; margin: 5px; background-color: inherit; color: inherit; line-height: 8px; }
pre { width: 100%; height: 100%; margin: 5px; background-color: inherit; color: inherit; line-height: 10px; }
</style>
</head>
<body>
Expand Down
36 changes: 35 additions & 1 deletion assets/templates/page-view.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="stylesheet" href="{{ $root }}static/spectre.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
@font-face { font-family: 'Inconsolata'; src: URL('{{ .root }}static/inconsolata.ttf') format('truetype'); }
.spacing-top { margin: 3px 0px 0px 0px; }
.spacing-left { margin: 0px 3px 0px 0px; }
.align-center { text-align: center; }
Expand All @@ -21,6 +22,14 @@
.resizer > .resized { flex-grow: 1; margin: 0; padding: 0; border: 0 }
.syntax-menu { right: 35px; }
.align-right { text-align: right; }
textarea {
margin: 5px;
font-family: Inconsolata;
background-color: inherit;
color: inherit;
line-height: 8px;
resize: none;
}
</style>
<script>
function handle(obj) {
Expand Down Expand Up @@ -57,6 +66,25 @@
div.appendChild(iframe);
body.insertBefore(div, body.childNodes[body.childNodes.length - 2]);
}
function newsc() {
var body = document.getElementById("page-body");
var div = document.createElement("div");
var iframe = document.createElement("iframe");

div.className = "panel spacing-top";
iframe.className = "panel-body no-borders";
iframe.style.height = "300px";
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("marginheight", "0");
iframe.setAttribute("marginwidth", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "110%");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("onload", "output(this)");
iframe.src = "{{ $root }}output/input/script/{{ $page.unique }}";
div.appendChild(iframe);
body.insertBefore(div, body.childNodes[body.childNodes.length - 2]);
}
function newcm() {
var body = document.getElementById("page-body");
var div = document.createElement("div");
Expand All @@ -71,7 +99,7 @@
iframe.setAttribute("height", "100%");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("onload", "output(this)");
iframe.src = "{{ $root }}output/input/{{ $page.unique }}";
iframe.src = "{{ $root }}output/input/console/{{ $page.unique }}";
div.appendChild(iframe);
body.insertBefore(div, body.childNodes[body.childNodes.length - 2]);
}
Expand Down Expand Up @@ -120,7 +148,11 @@
</div>
{{ else }}
<div class="input-group spacing-top">
{{ if eq $line.type "script" }}
<code style="display: block; white-space: pre-wrap;" class="input-group-addon spacing-right spacing-left">{{ $line.script }}</code>
{{ else }}
<code class="input-group-addon spacing-right spacing-left">{{ $line.command }}</code>
{{ end }}
<a class="btn float-right" href="{{ $root }}output/delete/{{ $page.unique }}/{{ $line.unique }}" onclick="confirm_delete(event)">
<i class="icon icon-delete icon-height">Delete</i>
</a>
Expand All @@ -132,6 +164,8 @@
{{ end }}
<div class="input-group spacing-top" style="margin-top: 10px;">
<a class="btn" href="#" onclick="newmd()"><i class="icon icon-message">Markdown</i> Markdown</a>
&nbsp;
<a class="btn" href="#" onclick="newsc()"><i class="icon icon-share">Script</i> Script</a>
{{ if $pipe }}
&nbsp;
<a class="btn" href="#" onclick="newcm()"><i class="icon icon-resize-horiz">Command Line</i> Command Line</a>
Expand Down
57 changes: 57 additions & 0 deletions assets/templates/script.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Rizin Notebook</title>
<link rel="stylesheet" href="{{ .root }}static/spectre-icons.min.css">
<link rel="stylesheet" href="{{ .root }}static/spectre.min.css">
<link rel="stylesheet" href="{{ .root }}static/simplemde.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
html, body { width: 100%; height: 100%; margin: 0 }
.button-edit { width: 75px ! important; margin-top: 9px; margin-right: 9px; }
@font-face { font-family: 'Inconsolata'; src: URL('{{ .root }}static/inconsolata.ttf') format('truetype'); }
</style>
<link rel="stylesheet" href="{{ .root }}static/codelines.css">
</head>
<body>
<form style="margin-top: 15px; height: 100%;" action="{{ .root }}output/script/{{ .unique }}" method="POST">
<div class="form-group">
<p><textarea id="codelines" class="code-lines" wrap="off" readonly>1 </textarea><textarea id="codeeditor" class="code-editor" wrap="off" name="script">console.log(rizin.cmd("?e hello world").trim(), "from the script");</textarea></p>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Run</button>
<a class="btn" href="{{ .root }}output/deleted">Cancel</a>
</div>
</form>
<script type="text/javascript">
var editor = document.getElementById('codeeditor');
var lines = document.getElementById('codelines');
editor.addEventListener('scroll', function() {
lines.scrollTop = editor.scrollTop;
lines.scrollLeft = editor.scrollLeft;
});
editor.addEventListener('keydown', function(e) {
if (e.keyCode === 9) {
// TAB = 9
e.preventDefault();
editor.value = editor.value.slice(0, editor.selectionStart) + '\t' + value.slice(editor.selectionEnd);
editor.setSelectionRange(editor.selectionStart + 2, editor.selectionStart + 2)
}
});
var lineCountCache = 0;
function updateLines() {
var editorLines = editor.value.split('\n');
if (lineCountCache != editorLines.length) {
lines.value = editorLines.map(function(e, n) {
return (n + 1).toString() + ' ';
}).join('\n');
}
lineCountCache = lineCount;
}
editor.addEventListener('input', function() {
updateLines();
});
</script>
</body>
</html>
36 changes: 36 additions & 0 deletions notebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Notebook struct {
uniques []string
storage string
pipes map[string]*Rizin
jsvm *JavaScript
rizin string
}

Expand All @@ -82,12 +83,17 @@ func NewNotebook(storage, rizinbin string) *Notebook {
pages[unique] = page
uniques = append(uniques, unique)
}
jsvm := NewJavaScript()
if jsvm == nil {
panic("failed to create scripting engine")
}
sort.Strings(uniques)
return &Notebook{
pages: pages,
uniques: uniques,
storage: storage,
pipes: map[string]*Rizin{},
jsvm: jsvm,
rizin: rizinbin,
}
}
Expand Down Expand Up @@ -158,6 +164,36 @@ func (n *Notebook) newmd(unique string) string {
return ""
}

func (n *Notebook) newscript(unique, script string) string {
if len(unique) != PAGE_NONCE_SIZE {
return ""
}
n.mutex.Lock()
defer n.mutex.Unlock()
if data, ok := n.pages[unique]; ok {
page := data.(gin.H)
var eunique = Nonce(ELEMENT_NONCE_SIZE)
for {
if _, err := n.file(unique, eunique+".out"); err != nil {
break
}
eunique = Nonce(ELEMENT_NONCE_SIZE)
}
page["lines"] = append(page["lines"].([]interface{}), gin.H{
"type": "script",
"unique": eunique,
"script": script,
})
filepath := path.Join(n.storage, unique, PAGE_FILE)
bytes, _ := json.MarshalIndent(page, "", "\t")
if ioutil.WriteFile(filepath, bytes, 0644) == nil {
n.pages[unique] = page
return eunique
}
}
return ""
}

func (n *Notebook) newcmd(unique, command string) string {
if len(unique) != PAGE_NONCE_SIZE {
return ""
Expand Down
2 changes: 2 additions & 0 deletions pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bufio"
"bytes"
"fmt"
"io"
"os/exec"
Expand Down Expand Up @@ -78,6 +79,7 @@ func (r *Rizin) exec(cmd string) (string, error) {
fmt.Println("pipe error:", err)
return "", err
}
buf = string(bytes.Trim([]byte(buf), "\x00"))
return buf, nil
}

Expand Down
113 changes: 113 additions & 0 deletions scripting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"github.com/dop251/goja"
"golang.org/x/sync/semaphore"
"time"
)

type JavaScript struct {
semaphore *semaphore.Weighted
runtime *goja.Runtime
rizin *Rizin
output string
}

func rizinCmd(command string, rizin *Rizin) (string, error) {
rizin.exec("scr.color=0")
result, err := rizin.exec(command)
rizin.exec("scr.color=3")
return result, err
}

func NewJavaScript() *JavaScript {
runtime := goja.New()
if runtime == nil {
fmt.Println("cannot create a JavaScript runtime.")
return nil
}
sem := semaphore.NewWeighted(1)
js := &JavaScript{semaphore: sem, runtime: runtime, rizin: nil}
rizin := map[string]interface{}{}
rizin["cmd"] = func(args ...interface{}) goja.Value {
if js.rizin == nil {
panic(js.runtime.ToValue("Rizin pipe is closed."))
} else if len(args) < 1 {
panic(js.runtime.ToValue("No string was passed."))
}
switch value := args[0].(type) {
case string:
result, err := rizinCmd(value, js.rizin)
if err != nil {
panic(js.runtime.ToValue(err))
}
return js.runtime.ToValue(result)
default:
panic(js.runtime.ToValue("input is not a string."))
}
}
rizin["cmdj"] = func(args ...interface{}) goja.Value {
if js.rizin == nil {
panic(js.runtime.ToValue("Rizin pipe is closed."))
} else if len(args) < 1 {
panic(js.runtime.ToValue("No string was passed."))
}
switch value := args[0].(type) {
case string:
result, err := rizinCmd(value, js.rizin)
if err != nil {
panic(js.runtime.ToValue(err))
}
array := []interface{}{}
var data interface{}
err = json.Unmarshal([]byte(result), &data)
if err != nil {
err = json.Unmarshal([]byte(result), &array)
data = array
}
if err != nil {
panic(js.runtime.ToValue(err))
}
return js.runtime.ToValue(data)
default:
panic(js.runtime.ToValue("input is not a string."))
}
}

console := map[string]interface{}{}
console["log"] = func(args ...interface{}) {
n_args := len(args)
for i, value := range args {
js.output += fmt.Sprintf("%v", value)
if i+1 < n_args {
js.output += " "
}
}
js.output += "\n"
}

runtime.Set("rizin", rizin)
runtime.Set("console", console)
return js
}

func (js *JavaScript) exec(script string, rizin *Rizin) (string, error) {
if !js.semaphore.TryAcquire(1) {
return "", errors.New("A script is already running.")
}
defer js.semaphore.Release(1)
js.rizin = rizin
js.output = ""
timer := time.AfterFunc(5*time.Minute, func() {
js.runtime.Interrupt("The script execution has timed out.")
})
_, err := js.runtime.RunScript("script.js", script)
result := js.output
js.rizin = nil
js.output = ""
timer.Stop()
return result, err
}
Loading

0 comments on commit 8ad3a9e

Please sign in to comment.