hermeticum/server/witch/witch.go

118 lines
2.8 KiB
Go
Raw Normal View History

2022-12-20 08:38:47 +00:00
package witch
import (
2022-12-28 05:19:42 +00:00
"fmt"
2022-12-24 06:34:21 +00:00
"log"
2022-12-28 05:19:42 +00:00
"regexp"
2022-12-24 06:34:21 +00:00
2022-12-20 08:38:47 +00:00
"github.com/vilmibm/hermeticum/server/db"
lua "github.com/yuin/gopher-lua"
)
/*
allows({
read = "world",
write = "owner"
carry = "owner",
execute = "world",
})
2022-12-29 04:39:14 +00:00
hears(".*eat.*", function()
2022-12-20 08:38:47 +00:00
does("quivers nervously")
end)
`
*/
type ServerAPI struct {
Tell func(int, string)
Show func(int, string)
}
2022-12-24 06:34:21 +00:00
type VerbContext struct {
Verb string
Rest string
Sender db.Object
Target db.Object
}
2022-12-20 08:38:47 +00:00
2022-12-24 06:34:21 +00:00
type ScriptContext struct {
script string
incoming chan VerbContext
serverAPI ServerAPI
2022-12-24 06:34:21 +00:00
}
2022-12-20 08:38:47 +00:00
func NewScriptContext(sAPI ServerAPI) (*ScriptContext, error) {
2022-12-28 05:19:42 +00:00
sc := &ScriptContext{
serverAPI: sAPI,
2022-12-28 05:19:42 +00:00
}
2022-12-24 06:34:21 +00:00
sc.incoming = make(chan VerbContext)
go func() {
var l *lua.LState
var err error
var vc VerbContext
for {
vc = <-sc.incoming
2022-12-29 04:39:14 +00:00
if vc.Target.Script != sc.script {
sc.script = vc.Target.Script
2022-12-24 06:34:21 +00:00
l = lua.NewState()
2022-12-28 05:19:42 +00:00
l.SetGlobal("has", l.NewFunction(witchHas))
l.SetGlobal("hears", l.NewFunction(witchHears))
l.SetGlobal("sees", l.NewFunction(witchSees))
2022-12-24 06:34:21 +00:00
l.SetGlobal("_handlers", l.NewTable())
2022-12-29 04:39:14 +00:00
if err := l.DoString(vc.Target.Script); err != nil {
log.Printf("error parsing script %s: %s", vc.Target.Script, err.Error())
2022-12-24 06:34:21 +00:00
}
}
2022-12-29 04:39:14 +00:00
l.SetGlobal("tellMe", l.NewFunction(func(l *lua.LState) int {
sender := l.GetGlobal("sender").(*lua.LTable)
senderID := int(lua.LVAsNumber(sender.RawGetString("ID")))
sc.serverAPI.Tell(senderID, l.ToString(1))
return 0
}))
l.SetGlobal("showMe", l.NewFunction(func(l *lua.LState) int {
sender := l.GetGlobal("sender").(*lua.LTable)
senderID := int(lua.LVAsNumber(sender.RawGetString("ID")))
sc.serverAPI.Show(senderID, l.ToString(1))
2022-12-29 04:39:14 +00:00
return 0
}))
2022-12-28 05:19:42 +00:00
// TODO check execute permission and bail out potentially
log.Printf("%#v", vc)
2022-12-28 05:19:42 +00:00
senderT := l.NewTable()
senderT.RawSetString("name", lua.LString(vc.Sender.Data["name"]))
2022-12-29 04:39:14 +00:00
senderT.RawSetString("ID", lua.LNumber(vc.Sender.ID))
2022-12-28 05:19:42 +00:00
l.SetGlobal("sender", senderT)
l.SetGlobal("msg", lua.LString(vc.Rest))
handlers := l.GetGlobal("_handlers").(*lua.LTable)
handlers.ForEach(func(k, v lua.LValue) {
if k.String() != vc.Verb {
return
}
v.(*lua.LTable).ForEach(func(kk, vv lua.LValue) {
pattern := regexp.MustCompile(kk.String())
if pattern.MatchString(vc.Rest) {
// TODO TODO TODO TODO TODO
// this could be a remote code execution vuln; but by being here, I
// believe vc.Verb has been effectively validated as "not a pile of
// lua code" since it matched a handler.
if err = l.DoString(fmt.Sprintf(`_handlers.%s["%s"]()`, vc.Verb, pattern)); err != nil {
log.Println(err.Error())
}
}
})
})
2022-12-24 06:34:21 +00:00
}
}()
return sc, nil
2022-12-20 08:38:47 +00:00
}
2022-12-24 06:34:21 +00:00
func (sc *ScriptContext) Handle(vc VerbContext) {
sc.incoming <- vc
2022-12-22 05:57:57 +00:00
}