diff --git a/Gopkg.lock b/Gopkg.lock index 383bb319..ade218a1 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -59,18 +59,19 @@ revision = "f58a169a71a51037728990b2d3597a14f56b525b" [[projects]] - digest = "1:32d8e0c62dc075d198abf73428828689f4e9473cadb8ef270be45fb78ae8648b" + digest = "1:a029ce916ee511044c6b7fc41133249a15e8d3c16219274666205efd6431e9cd" name = "github.com/evilsocket/islazy" packages = [ "fs", "log", + "plugin", "str", "tui", "zip", ] pruneopts = "UT" - revision = "72e580f7bbdfb45d1332e06653248bc609bf0d50" - version = "v1.4.0" + revision = "db3058040a83dba4e35a8931a3e1287c0b802869" + version = "v1.6.0" [[projects]] branch = "master" @@ -288,6 +289,7 @@ "github.com/elazarl/goproxy", "github.com/evilsocket/islazy/fs", "github.com/evilsocket/islazy/log", + "github.com/evilsocket/islazy/plugin", "github.com/evilsocket/islazy/str", "github.com/evilsocket/islazy/tui", "github.com/evilsocket/islazy/zip", diff --git a/Gopkg.toml b/Gopkg.toml index 4ab4d559..38c8ed36 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -23,7 +23,9 @@ # non-go = false # go-tests = true # unused-packages = true - +[[constraint]] + name = "github.com/evilsocket/islazy" + version = "1.6.0" [[constraint]] branch = "master" diff --git a/modules/http_proxy_script.go b/modules/http_proxy_script.go index 61a1f115..5a191e83 100644 --- a/modules/http_proxy_script.go +++ b/modules/http_proxy_script.go @@ -1,129 +1,64 @@ package modules import ( - "io/ioutil" "net/http" "github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/session" "github.com/robertkrimen/otto" + + "github.com/evilsocket/islazy/plugin" ) type HttpProxyScript struct { - *ProxyScript - onRequestScript *otto.Script - onResponseScript *otto.Script - onCommandScript *otto.Script -} + *plugin.Plugin -func LoadHttpProxyScriptSource(path, source string, sess *session.Session) (err error, s *HttpProxyScript) { - err, ps := LoadProxyScriptSource(path, source, sess) - if err != nil { - return - } - - s = &HttpProxyScript{ - ProxyScript: ps, - onRequestScript: nil, - onResponseScript: nil, - onCommandScript: nil, - } - - if s.hasCallback("onRequest") { - s.onRequestScript, err = s.VM.Compile("", "onRequest(req, res)") - if err != nil { - log.Error("Error while compiling onRequest callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - } - - if s.hasCallback("onResponse") { - s.onResponseScript, err = s.VM.Compile("", "onResponse(req, res)") - if err != nil { - log.Error("Error while compiling onResponse callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - } - - if s.hasCallback("onCommand") { - s.onCommandScript, err = s.VM.Compile("", "onCommand(cmd)") - if err != nil { - log.Error("Error while compiling onCommand callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - } - - return + doOnRequest bool + doOnResponse bool + doOnCommand bool } func LoadHttpProxyScript(path string, sess *session.Session) (err error, s *HttpProxyScript) { log.Info("loading proxy script %s ...", path) - raw, err := ioutil.ReadFile(path) + plug, err := plugin.Load(path) if err != nil { return } - return LoadHttpProxyScriptSource(path, string(raw), sess) -} - -func (s *HttpProxyScript) doRequestDefines(req *http.Request) (err error, jsreq *JSRequest, jsres *JSResponse) { - jsreq = NewJSRequest(req) - if err = s.VM.Set("req", jsreq); err != nil { - log.Error("Error while defining request: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + // define session pointer + if err = plug.Set("env", sess.Env.Data); err != nil { + log.Error("Error while defining environment: %+v", err) return } - jsres = NewJSResponse(nil) - if err = s.VM.Set("res", jsres); err != nil { - log.Error("Error while defining response: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - return -} - -func (s *HttpProxyScript) doResponseDefines(res *http.Response) (err error, jsreq *JSRequest, jsres *JSResponse) { - jsreq = NewJSRequest(res.Request) - if err = s.VM.Set("req", jsreq); err != nil { - log.Error("Error while defining request: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return + // run onLoad if defined + if plug.HasFunc("onLoad") { + if _, err = plug.Call("onLoad"); err != nil { + log.Error("Error while executing onLoad callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + return + } } - jsres = NewJSResponse(res) - if err = s.VM.Set("res", jsres); err != nil { - log.Error("Error while defining response: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - - return -} - -func (s *HttpProxyScript) doCommandDefines(cmd string) (err error) { - if err = s.VM.Set("cmd", cmd); err != nil { - log.Error("Error while defining cmd: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + s = &HttpProxyScript{ + Plugin: plug, + doOnRequest: plug.HasFunc("onRequest"), + doOnResponse: plug.HasFunc("onResponse"), + doOnCommand: plug.HasFunc("onCommand"), } return } func (s *HttpProxyScript) OnRequest(original *http.Request) (jsreq *JSRequest, jsres *JSResponse) { - var err error + if s.doOnRequest { + jsreq := NewJSRequest(original) + jsres := NewJSResponse(nil) - if s.onRequestScript != nil { - s.Lock() - defer s.Unlock() - - if err, jsreq, jsres = s.doRequestDefines(original); err != nil { - log.Error("Error while running bootstrap definitions: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + if _, err := s.Call("onRequest", jsreq, jsres); err != nil { + log.Error("%s", err) return nil, nil - } - - if _, err = s.VM.Run(s.onRequestScript); err != nil { - log.Error("Error while executing onRequest callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return nil, nil - } - - if jsreq.WasModified() { + } else if jsreq.WasModified() { jsreq.UpdateHash() return jsreq, nil } else if jsres.WasModified() { @@ -136,23 +71,14 @@ func (s *HttpProxyScript) OnRequest(original *http.Request) (jsreq *JSRequest, j } func (s *HttpProxyScript) OnResponse(res *http.Response) (jsreq *JSRequest, jsres *JSResponse) { - var err error + if s.doOnResponse { + jsreq := NewJSRequest(res.Request) + jsres := NewJSResponse(res) - if s.onResponseScript != nil { - s.Lock() - defer s.Unlock() - - if err, jsreq, jsres = s.doResponseDefines(res); err != nil { - log.Error("Error while running bootstrap definitions: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + if _, err := s.Call("onResponse", jsreq, jsres); err != nil { + log.Error("%s", err) return nil, nil - } - - if _, err = s.VM.Run(s.onResponseScript); err != nil { - log.Error("Error while executing onRequest callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return nil, nil - } - - if jsres.WasModified() { + } else if jsres.WasModified() { jsres.UpdateHash() return nil, jsres } @@ -162,19 +88,11 @@ func (s *HttpProxyScript) OnResponse(res *http.Response) (jsreq *JSRequest, jsre } func (s *HttpProxyScript) OnCommand(cmd string) bool { - if s.onCommandScript != nil { - s.Lock() - defer s.Unlock() - - if err := s.doCommandDefines(cmd); err != nil { - log.Error("Error while running bootstrap onCommand definitions: %s", "\nTraceback:\n "+err.(*otto.Error).String()) + if s.doOnCommand { + if ret, err := s.Call("onCommand", cmd); err != nil { + log.Error("Error while executing onCommand callback: %+v", err) return false - } - - if ret, err := s.VM.Run(s.onCommandScript); err != nil { - log.Error("Error while executing onCommand callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return false - } else if v, err := ret.ToBoolean(); err == nil { + } else if v, ok := ret.(bool); ok { return v } } diff --git a/modules/http_proxy_script_benchmark_test.go b/modules/http_proxy_script_benchmark_test.go deleted file mode 100644 index 9d47c5c8..00000000 --- a/modules/http_proxy_script_benchmark_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package modules - -import ( - "net/http" - "testing" - - "github.com/bettercap/bettercap/log" - "github.com/bettercap/bettercap/session" -) - -func getScript(src string) *HttpProxyScript { - sess := session.Session{} - sess.Env, _ = session.NewEnvironment("") - - err, script := LoadHttpProxyScriptSource("", src, &sess) - if err != nil { - log.Fatal("%s", err) - } - return script -} - -func getRequest() *http.Request { - req, err := http.NewRequest("GET", "http://www.google.com/", nil) - if err != nil { - log.Fatal("%s", err) - } - return req -} - -func BenchmarkOnRequest(b *testing.B) { - script := getScript("function onRequest(req,res){}") - req := getRequest() - - for n := 0; n < b.N; n++ { - script.OnRequest(req) - } -} diff --git a/modules/base_proxy_script.go b/modules/script_builtins.go similarity index 50% rename from modules/base_proxy_script.go rename to modules/script_builtins.go index 3adf0e6c..ee6f6508 100644 --- a/modules/base_proxy_script.go +++ b/modules/script_builtins.go @@ -3,11 +3,12 @@ package modules import ( "encoding/base64" "io/ioutil" - "sync" "github.com/bettercap/bettercap/log" "github.com/bettercap/bettercap/session" + "github.com/evilsocket/islazy/plugin" + "github.com/robertkrimen/otto" ) @@ -18,71 +19,9 @@ func errOtto(format string, args ...interface{}) otto.Value { return nullOtto } -type ProxyScript struct { - sync.Mutex - - Path string - Source string - VM *otto.Otto - - sess *session.Session - cbCacheLock *sync.Mutex - cbCache map[string]bool -} - -func LoadProxyScriptSource(path, source string, sess *session.Session) (err error, s *ProxyScript) { - s = &ProxyScript{ - Path: path, - Source: source, - VM: otto.New(), - sess: sess, - cbCacheLock: &sync.Mutex{}, - cbCache: make(map[string]bool), - } - - // this will define callbacks and global objects - _, err = s.VM.Run(s.Source) - if err != nil { - return - } - - // define session pointer - err = s.VM.Set("env", sess.Env.Data) - if err != nil { - log.Error("Error while defining environment: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - - err = s.defineBuiltins() - if err != nil { - log.Error("Error while defining builtin functions: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - - // run onLoad if defined - if s.hasCallback("onLoad") { - _, err = s.VM.Run("onLoad()") - if err != nil { - log.Error("Error while executing onLoad callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) - return - } - } - - return -} - -func LoadProxyScript(path string, sess *session.Session) (err error, s *ProxyScript) { - raw, err := ioutil.ReadFile(path) - if err != nil { - return - } - - return LoadProxyScriptSource(path, string(raw), sess) -} - -func (s *ProxyScript) defineBuiltins() error { +func init() { // used to read a file ... doh - s.VM.Set("readFile", func(call otto.FunctionCall) otto.Value { + plugin.Defines["readFile"] = func(call otto.FunctionCall) otto.Value { argv := call.ArgumentList argc := len(argv) if argc != 1 { @@ -95,14 +34,14 @@ func (s *ProxyScript) defineBuiltins() error { return errOtto("Could not read %s: %s", filename, err) } - v, err := s.VM.ToValue(string(raw)) + v, err := otto.ToValue(string(raw)) if err != nil { return errOtto("Could not convert to string: %s", err) } return v - }) + } - s.VM.Set("writeFile", func(call otto.FunctionCall) otto.Value { + plugin.Defines["writeFile"] = func(call otto.FunctionCall) otto.Value { argv := call.ArgumentList argc := len(argv) if argc != 2 { @@ -118,89 +57,89 @@ func (s *ProxyScript) defineBuiltins() error { } return otto.NullValue() - }) + } // log something - s.VM.Set("log", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Info("%s", v.String()) } return otto.Value{} - }) + } // log debug - s.VM.Set("log_debug", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log_debug"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Debug("%s", v.String()) } return otto.Value{} - }) + } // log info - s.VM.Set("log_info", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log_info"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Info("%s", v.String()) } return otto.Value{} - }) + } // log warning - s.VM.Set("log_warn", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log_warn"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Warning("%s", v.String()) } return otto.Value{} - }) + } // log error - s.VM.Set("log_error", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log_error"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Error("%s", v.String()) } return otto.Value{} - }) + } // log fatal - s.VM.Set("log_fatal", func(call otto.FunctionCall) otto.Value { + plugin.Defines["log_fatal"] = func(call otto.FunctionCall) otto.Value { for _, v := range call.ArgumentList { log.Fatal("%s", v.String()) } return otto.Value{} - }) + } // javascript btoa function - s.VM.Set("btoa", func(call otto.FunctionCall) otto.Value { + plugin.Defines["btoa"] = func(call otto.FunctionCall) otto.Value { varValue := base64.StdEncoding.EncodeToString([]byte(call.Argument(0).String())) - v, err := s.VM.ToValue(varValue) + v, err := otto.ToValue(varValue) if err != nil { return errOtto("Could not convert to string: %s", varValue) } return v - }) + } // javascript atob function - s.VM.Set("atob", func(call otto.FunctionCall) otto.Value { + plugin.Defines["atob"] = func(call otto.FunctionCall) otto.Value { varValue, err := base64.StdEncoding.DecodeString(call.Argument(0).String()) if err != nil { return errOtto("Could not decode string: %s", call.Argument(0).String()) } - v, err := s.VM.ToValue(string(varValue)) + v, err := otto.ToValue(string(varValue)) if err != nil { return errOtto("Could not convert to string: %s", varValue) } return v - }) + } // read or write environment variable - s.VM.Set("env", func(call otto.FunctionCall) otto.Value { + plugin.Defines["env"] = func(call otto.FunctionCall) otto.Value { argv := call.ArgumentList argc := len(argv) if argc == 1 { // get varName := call.Argument(0).String() - if found, varValue := s.sess.Env.Get(varName); found { - v, err := s.VM.ToValue(varValue) + if found, varValue := session.I.Env.Get(varName); found { + v, err := otto.ToValue(varValue) if err != nil { return errOtto("Could not convert to string: %s", varValue) } @@ -211,33 +150,11 @@ func (s *ProxyScript) defineBuiltins() error { // set varName := call.Argument(0).String() varValue := call.Argument(1).String() - s.sess.Env.Set(varName, varValue) + session.I.Env.Set(varName, varValue) } else { return errOtto("env: expected 1 or 2 arguments, %d given instead.", argc) } return nullOtto - }) - - return nil -} - -func (s *ProxyScript) hasCallback(name string) bool { - s.cbCacheLock.Lock() - defer s.cbCacheLock.Unlock() - - // check the cache - has, found := s.cbCache[name] - if !found { - // check the VM - cb, err := s.VM.Get(name) - if err == nil && cb.IsFunction() { - has = true - } else { - has = false - } - s.cbCache[name] = has } - - return has } diff --git a/modules/tcp_proxy_script.go b/modules/tcp_proxy_script.go index af50c096..e55c15b2 100644 --- a/modules/tcp_proxy_script.go +++ b/modules/tcp_proxy_script.go @@ -1,7 +1,6 @@ package modules import ( - "io/ioutil" "net" "strings" @@ -9,97 +8,62 @@ import ( "github.com/bettercap/bettercap/session" "github.com/robertkrimen/otto" + + "github.com/evilsocket/islazy/plugin" ) type TcpProxyScript struct { - *ProxyScript - onDataScript *otto.Script + *plugin.Plugin + doOnData bool } -func LoadTcpProxyScriptSource(path, source string, sess *session.Session) (err error, s *TcpProxyScript) { - err, ps := LoadProxyScriptSource(path, source, sess) +func LoadTcpProxyScript(path string, sess *session.Session) (err error, s *TcpProxyScript) { + log.Info("loading tcp proxy script %s ...", path) + + plug, err := plugin.Load(path) if err != nil { return } - s = &TcpProxyScript{ - ProxyScript: ps, - onDataScript: nil, + // define session pointer + if err = plug.Set("env", sess.Env.Data); err != nil { + log.Error("Error while defining environment: %+v", err) + return } - if s.hasCallback("onData") { - s.onDataScript, err = s.VM.Compile("", "onData(from, to, data)") - if err != nil { - log.Error("Error while compiling onData callback: %s", err) + // run onLoad if defined + if plug.HasFunc("onLoad") { + if _, err = plug.Call("onLoad"); err != nil { + log.Error("Error while executing onLoad callback: %s", "\nTraceback:\n "+err.(*otto.Error).String()) return } } - return -} - -func LoadTcpProxyScript(path string, sess *session.Session) (err error, s *TcpProxyScript) { - log.Info("loading TCP proxy script %s ...", path) - - raw, err := ioutil.ReadFile(path) - if err != nil { - return - } - - return LoadTcpProxyScriptSource(path, string(raw), sess) -} - -func (s *TcpProxyScript) doDefines(from, to net.Addr, data []byte) (err error) { - addrFrom := strings.Split(from.String(), ":")[0] - addrTo := strings.Split(to.String(), ":")[0] - - if err = s.VM.Set("from", addrFrom); err != nil { - log.Error("Error while defining from: %s", err) - return - } else if err = s.VM.Set("to", addrTo); err != nil { - log.Error("Error while defining to: %s", err) - return - } else if err = s.VM.Set("data", data); err != nil { - log.Error("Error while defining data: %s", err) - return + s = &TcpProxyScript{ + Plugin: plug, + doOnData: plug.HasFunc("onData"), } return } func (s *TcpProxyScript) OnData(from, to net.Addr, data []byte) []byte { - if s.onDataScript != nil { + if s.doOnData { s.Lock() defer s.Unlock() - err := s.doDefines(from, to, data) - if err != nil { - log.Error("Error while running bootstrap definitions: %s", err) - return nil - } + addrFrom := strings.Split(from.String(), ":")[0] + addrTo := strings.Split(to.String(), ":")[0] - ret, err := s.VM.Run(s.onDataScript) - if err != nil { + if ret, err := s.Call("onData", addrFrom, addrTo, data); err != nil { log.Error("Error while executing onData callback: %s", err) return nil - } - - // do we have any return value to override the buffer with? - if !ret.IsNull() && !ret.IsUndefined() { - exported, err := ret.Export() - if err != nil { - log.Error("Error while exporting results: %s", err) - return nil - } - - array, ok := exported.([]byte) + } else if ret != nil { + array, ok := ret.([]byte) if !ok { - log.Error("Error while casting exported value to array of byte: value = %s", exported) - return nil + log.Error("Error while casting exported value to array of byte: value = %+v", ret) } - return array } } - return nil } diff --git a/vendor/github.com/evilsocket/islazy/plugin/compile.go b/vendor/github.com/evilsocket/islazy/plugin/compile.go new file mode 100644 index 00000000..373c6a47 --- /dev/null +++ b/vendor/github.com/evilsocket/islazy/plugin/compile.go @@ -0,0 +1,36 @@ +package plugin + +import ( + // "unicode" + "github.com/robertkrimen/otto" +) + +func (p *Plugin) compile() (err error) { + // create a new vm + p.vm = otto.New() + // track objects already defined by Otto + predefined := map[string]bool{} + for name := range p.vm.Context().Symbols { + predefined[name] = true + } + // run the code once in order to define all the functions + // and validate the syntax, then get the callbacks + if _, err = p.vm.Run(p.Code); err != nil { + return + } + // every uppercase object is considered exported + for name, sym := range p.vm.Context().Symbols { + // ignore predefined objects + if _, found := predefined[name]; !found { + // ignore lowercase global objects + // if unicode.IsUpper(rune(name[0])) { + if sym.IsFunction() { + p.callbacks[name] = sym + } else { + p.objects[name] = sym + } + // } + } + } + return nil +} diff --git a/vendor/github.com/evilsocket/islazy/plugin/doc.go b/vendor/github.com/evilsocket/islazy/plugin/doc.go new file mode 100644 index 00000000..2c1b5eec --- /dev/null +++ b/vendor/github.com/evilsocket/islazy/plugin/doc.go @@ -0,0 +1,4 @@ +// Package plugin contains objects and functions to load and +// use javascript plugins in order to extend the functionalities +// of your projects. +package plugin diff --git a/vendor/github.com/evilsocket/islazy/plugin/plugin.go b/vendor/github.com/evilsocket/islazy/plugin/plugin.go new file mode 100644 index 00000000..33bee48a --- /dev/null +++ b/vendor/github.com/evilsocket/islazy/plugin/plugin.go @@ -0,0 +1,233 @@ +package plugin + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strings" + "sync" + + "github.com/robertkrimen/otto" +) + +// Defines is a map containing the predefined objects +// and functions for each vm of each plugin. +var Defines = map[string]interface{}{} + +// Plugin is an object representing a javascript +// file exporting functions and variables that +// your project can use to extend its functionalities. +type Plugin struct { + sync.Mutex + // The basename of the plugin. + Name string + // The actual javascript code. + Code string + // The full path of the plugin. + Path string + + vm *otto.Otto + callbacks map[string]otto.Value + objects map[string]otto.Value +} + +// Parse parsesand compiles a plugin given its source code. +func Parse(code string) (*Plugin, error) { + plugin := &Plugin{ + Code: code, + callbacks: make(map[string]otto.Value), + objects: make(map[string]otto.Value), + } + + if err := plugin.compile(); err != nil { + return nil, err + } + + for name, val := range Defines { + if err := plugin.vm.Set(name, val); err != nil { + return nil, err + } + } + + return plugin, nil +} + +// Load loads and compiles a plugin given its path. +func Load(path string) (plug *Plugin, err error) { + if raw, err := ioutil.ReadFile(path); err != nil { + return nil, err + } else if plug, err = Parse(string(raw)); err != nil { + return nil, err + } else { + plug.Path = path + plug.Name = strings.Replace(filepath.Base(path), ".js", "", -1) + } + return plug, nil +} + +// Clone returns a new instance identical to the plugin. +func (p *Plugin) Clone() (clone *Plugin) { + var err error + if p.Path == "" { + clone, err = Parse(p.Code) + } else { + clone, err = Load(p.Path) + } + if err != nil { + panic(err) // this should never happen + } + return clone +} + +// HasFunc returns true if the function with `name` +// has been declared in the plugin code. +func (p *Plugin) HasFunc(name string) bool { + _, found := p.callbacks[name] + return found +} + +// Set sets a variable into the VM of this plugin instance. +func (p *Plugin) Set(name string, v interface{}) error { + p.Lock() + defer p.Unlock() + return p.vm.Set(name, v) +} + +// Call executes one of the declared callbacks of the plugin by its name. +func (p *Plugin) Call(name string, args ...interface{}) (interface{}, error) { + p.Lock() + defer p.Unlock() + + if cb, found := p.callbacks[name]; !found { + return nil, fmt.Errorf("%s does not name a function", name) + } else if ret, err := cb.Call(otto.NullValue(), args...); err != nil { + return nil, err + } else if !ret.IsUndefined() { + exported, err := ret.Export() + if err != nil { + return nil, err + } + return exported, nil + } + return nil, nil +} + +// Methods returns a list of methods exported from the javascript +func (p *Plugin) Methods() []string { + methods := []string{} + for key, _ := range p.callbacks { + methods = append(methods, key) + } + return methods +} + +// Objects returns a list of object exported by the javascript +func (p *Plugin) Objects() []string { + objs := []string{} + for key, _ := range p.callbacks { + objs = append(objs, key) + } + return objs +} + +// GetTypeObject returns the type of the object by its name +func (p *Plugin) GetTypeObject(name string) string { + if obj, found := p.objects[name]; !found { + return "" + } else if obj.IsPrimitive() { + if obj.IsBoolean() { + return "BooleanPrimitive" + } else if obj.IsNumber() { + return "NumberPrimitive" + } else if obj.IsString() { + return "StringPrimitive" + } + } else if obj.IsObject() { + switch obj.Class() { + case "Array": + return "ArrayObject" + case "String": + return "StringObject" + case "Boolean": + return "BooleanObject" + case "Number": + return "NumberObject" + case "Date": + return "DateObject" + case "RegExp": + return "RegExpObject" + case "Error": + return "ErrorObject" + } + } + return "" +} + +// IsStringPrimitive returns true if the object with a +// given name is a javascript primitive string +func (p *Plugin) IsStringPrimitive(name string) bool { + return p.GetTypeObject(name) == "StringPrimitive" +} + +// IsBooleanPrimitive returns true if the object with a +// given name is a javascript primitive boolean, false otherwise +func (p *Plugin) IsBooleanPrimitive(name string) bool { + return p.GetTypeObject(name) == "BooleanPrimitive" +} + +// IsNumberPrimitive returns true if the object with a +// given name is a javascript primitive number, false otherwise +func (p *Plugin) IsNumberPrimitive(name string) bool { + return p.GetTypeObject(name) == "NumberPrimitive" +} + +// IsArrayObject returns true if the object with a +// given name is a javascript array object, false otherwise +func (p *Plugin) IsArrayObject(name string) bool { + return p.GetTypeObject(name) == "ArrayObject" +} + +// IsStringObject returns true if the object with a +// given name is a javascript string object, false otherwise +func (p *Plugin) IsStringObject(name string) bool { + return p.GetTypeObject(name) == "StringObject" +} + +// IsBooleanObject returns true if the object with a +// given name is a javascript boolean object, false otherwise +func (p *Plugin) IsBooleanObject(name string) bool { + return p.GetTypeObject(name) == "BooleanObject" +} + +// IsNumberObject returns true if the object with a +// given name is a javascript Number object, false otherwise +func (p *Plugin) IsNumberObject(name string) bool { + return p.GetTypeObject(name) == "NumberObject" +} + +// IsDateObject returns true if the object with a +// given name is a javascript Date object, false otherwise +func (p *Plugin) IsDateObject(name string) bool { + return p.GetTypeObject(name) == "DateObject" +} + +// IsRegExpObject returns true if the object with a +// given name is a javascript RegExp object, false otherwise +func (p *Plugin) IsRegExpObject(name string) bool { + return p.GetTypeObject(name) == "RegExpObject" +} + +// IsErrorObject returns true if the object with a +// given name is a javascript error object, false otherwise +func (p *Plugin) IsErrorObject(name string) bool { + return p.GetTypeObject(name) == "ErrorObject" +} + +// GetObject returns an interface containing the value of the object by its name +func (p *Plugin) GetObject(name string) (interface{}, error) { + if obj, found := p.objects[name]; !found { + return nil, fmt.Errorf("%s does not name an object", name) + } else { + return obj.Export() + } +}