new: added removeEventListener builtin function (closes #1139)

This commit is contained in:
evilsocket 2024-11-20 14:30:17 +01:00
parent a7e4572416
commit 169b0cb8c9
2 changed files with 85 additions and 26 deletions

View file

@ -2,8 +2,10 @@ package session
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"sync"
"github.com/bettercap/bettercap/v2/js" "github.com/bettercap/bettercap/v2/js"
"github.com/evilsocket/islazy/fs" "github.com/evilsocket/islazy/fs"
@ -14,6 +16,8 @@ import (
// see https://github.com/robertkrimen/otto/issues/213 // see https://github.com/robertkrimen/otto/issues/213
var jsRuntime = otto.New() var jsRuntime = otto.New()
var jsListeners = sync.Map{}
func jsRunFunc(call otto.FunctionCall) otto.Value { func jsRunFunc(call otto.FunctionCall) otto.Value {
argv := call.ArgumentList argv := call.ArgumentList
argc := len(argv) argc := len(argv)
@ -57,40 +61,94 @@ func jsOnEventFunc(call otto.FunctionCall) otto.Value {
cb = argv[1] cb = argv[1]
} }
listenerKey := fmt.Sprintf("%s:%s", filterExpr, cb.String())
if _, found := jsListeners.Load(listenerKey); found {
return js.ReportError("listener already exists")
}
// add to listeners
closeChan := make(chan bool)
jsListeners.Store(listenerKey, closeChan)
// start a go routine for this event listener // start a go routine for this event listener
go func(expr string, cb otto.Value) { go func(expr string, cb otto.Value, closeChan chan bool) {
listener := I.Events.Listen() listener := I.Events.Listen()
defer I.Events.Unlisten(listener) defer I.Events.Unlisten(listener)
defer close(closeChan)
for event := range listener { for {
if expr == "" || event.Tag == expr { select {
// some objects don't do well with js, so convert them to a generic map case event := <-listener:
// before passing them to the callback if expr == "" || event.Tag == expr {
var opaque interface{} // some objects don't do well with js, so convert them to a generic map
if raw, err := json.Marshal(event); err != nil { // before passing them to the callback
I.Events.Log(log.ERROR, "error serializing event %s: %v", event.Tag, err) var opaque interface{}
} else if err = json.Unmarshal(raw, &opaque); err != nil { if raw, err := json.Marshal(event); err != nil {
I.Events.Log(log.ERROR, "error serializing event %s: %v", event.Tag, err) I.Events.Log(log.ERROR, "error serializing event %s: %v", event.Tag, err)
} else if err = json.Unmarshal(raw, &opaque); err != nil {
I.Events.Log(log.ERROR, "error serializing event %s: %v", event.Tag, err)
}
// lock vm if ready and available
locked := false
if I.script != nil {
I.script.Lock()
locked = true
}
if _, err := cb.Call(otto.NullValue(), opaque); err != nil {
I.Events.Log(log.ERROR, "error dispatching event %s: %v", event.Tag, err)
}
// unlock vm if ready and available
if locked {
I.script.Unlock()
}
} }
// lock vm if ready and available case <-closeChan:
locked := false return
if I.script != nil {
I.script.Lock()
locked = true
}
if _, err := cb.Call(otto.NullValue(), opaque); err != nil {
I.Events.Log(log.ERROR, "error dispatching event %s: %v", event.Tag, err)
}
// unlock vm if ready and available
if locked {
I.script.Unlock()
}
} }
} }
}(filterExpr, cb) }(filterExpr, cb, closeChan)
return js.NullValue
}
func jsRemoveEventListenerFunc(call otto.FunctionCall) otto.Value {
argv := call.ArgumentList
argc := len(argv)
cb := otto.NullValue()
filterExpr := ""
// just one argument, a function to receive all events
if argc == 1 {
if argv[0].IsFunction() == false {
return js.ReportError("the single argument must be a function")
}
cb = argv[0]
} else {
if argc != 2 {
return js.ReportError("expected two arguments (event_name, callback), got %d", argc)
} else if argv[0].IsString() == false {
return js.ReportError("first argument must be a string")
} else if argv[1].IsFunction() == false {
return js.ReportError("second argument must be a function")
}
filterExpr = argv[0].String()
cb = argv[1]
}
listenerKey := fmt.Sprintf("%s:%s", filterExpr, cb.String())
if closer, found := jsListeners.Load(listenerKey); found {
closer.(chan bool) <- true
jsListeners.Delete(listenerKey)
} else {
return js.ReportError("listener not found")
}
return js.NullValue return js.NullValue
} }

View file

@ -330,6 +330,7 @@ func (s *Session) Start() error {
plugin.Defines["saveJSON"] = jsSaveJSONFunc plugin.Defines["saveJSON"] = jsSaveJSONFunc
plugin.Defines["saveToFile"] = jsSaveToFileFunc plugin.Defines["saveToFile"] = jsSaveToFileFunc
plugin.Defines["onEvent"] = jsOnEventFunc plugin.Defines["onEvent"] = jsOnEventFunc
plugin.Defines["removeEventListener"] = jsRemoveEventListenerFunc
plugin.Defines["session"] = s plugin.Defines["session"] = s
// load the script here so the session and its internal objects are ready // load the script here so the session and its internal objects are ready