Exemple #1
0
// NewEngine - Return a new Engine from the given config.
func NewEngine(cfg *Config) (*Engine, error) {
	var err error
	if cfg == nil {
		return nil, errors.New("Fatal error, Cannot load Listless engine with empty configuration.")
	}
	E := new(Engine)
	E.Config = cfg
	E.Lua = lua.NewState()
	// Preload a few extra libs..
	luajson.Preload(E.Lua)
	E.Lua.PreloadModule("url", gluaurl.Loader)
	// Disabled for security, right now:
	// E.Lua.PreloadModule("http", gluahttp.NewHttpModule(&http.Client{}).Loader)
	E.DB, err = NewDatabase(cfg.Database)
	if err != nil {
		return nil, err
	}
	E.Client = imapclient.NewClientTLS(cfg.IMAPHost, cfg.IMAPPort, cfg.IMAPUsername, cfg.IMAPPassword)
	E.Shutdown = make(chan struct{})
	err = applyLuarWhitelists(E.Lua)
	if err != nil {
		log15.Error("Error setting method whitelists in lua runtime", log15.Ctx{"context": "lua", "error": err})
		return nil, err
	}
	return E, nil
}
Exemple #2
0
func (lua *LuaExt) exec(reqLogger log.Logger, app *LuaApp, appID, reqId, script string, w http.ResponseWriter, r *http.Request) int {
	// FIXME(tsileo) a debug mode, with a defer/recover
	// also parse the Lu error and show the bugging line!
	start := time.Now()
	httpClient := &http.Client{}
	// Initialize internal Lua module written in Go
	logger := loggerModule.New(reqLogger.New("ctx", "Lua"), start, reqId)
	response := responseModule.New()
	request := requestModule.New(r, reqId, lua.authFunc)
	blobstore := blobstoreModule.New(lua.blobStore)
	kvstore := kvstoreModule.New(lua.kvStore)
	bewit := bewitModule.New(reqLogger.New("ctx", "Lua bewit module"), r)
	template := templateModule.New()

	// Initialize Lua state
	L := luamod.NewState()
	defer L.Close()
	setCustomGlobals(L)
	L.PreloadModule("request", request.Loader)
	L.PreloadModule("response", response.Loader)
	L.PreloadModule("logger", logger.Loader)
	L.PreloadModule("blobstore", blobstore.Loader)
	L.PreloadModule("kvstore", kvstore.Loader)
	L.PreloadModule("bewit", bewit.Loader)
	L.PreloadModule("template", template.Loader)
	// TODO(tsileo) docstore module
	// TODO(tsileo) cookies module
	// TODO(tsileo) lru module
	// TODO(tsileo) cache module => to cache response
	// TODO(tsileo) load module from github directly?
	// TODO(tsileo) ETag support

	// 3rd party module
	luajson.Preload(L)
	L.PreloadModule("http", gluahttp.NewHttpModule(httpClient).Loader)

	// Set some global variables
	L.SetGlobal("reqID", luamod.LString(reqId))
	L.SetGlobal("appID", luamod.LString(appID))

	// Execute the code
	if err := L.DoString(script); err != nil {
		// FIXME better error, with debug mode?
		panic(err)
	}

	// Apply the Response object to the actual response
	response.WriteTo(w)
	// TODO save the logRecords in the AppStats and find a way to serve them over Server-Sent Events
	// keep them in memory with the ability to dump them in bulk as blob for later query
	// logRecords := logger.Records()
	for _, logRecord := range logger.Records() {
		app.logs = append(app.logs, logRecord)
	}

	reqLogger.Info("Script executed", "response", response, "duration", time.Since(start))
	return response.Status()
}
Exemple #3
0
func newLuaState(conf string) *lua.LState {
	L := lua.NewState()

	registerIRCChatClientType(L)
	registerSlackChatClientType(L)
	registerHipchatChatClientType(L)
	registerNullChatClientType(L)
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"newbot": func(L *lua.LState) int {
			opt := L.OptTable(2, L.NewTable())
			co := newCommonClientOption(conf)
			switch v := L.GetField(opt, "log").(type) {
			case *lua.LFunction:
				co.Logger = log.New(&luaLogger{L, v}, "", log.LstdFlags)
			case *lua.LTable:
				l, err := seelog.LoggerFromConfigAsString(luaToXml(v))
				if err != nil {
					L.RaiseError(err.Error())
				}
				co.Logger = log.New(&seelogLogger{l}, "", 0)
			}
			if s, ok := getStringField(L, opt, "http"); ok {
				co.HttpAddr = s
			}
			if tbl, ok := L.GetField(opt, "https").(*lua.LTable); ok {
				if s, ok := getStringField(L, tbl, "addr"); ok {
					co.Https.Addr = s
				}
				if s, ok := getStringField(L, tbl, "cert"); ok {
					co.Https.CertFile = s
				}
				if s, ok := getStringField(L, tbl, "key"); ok {
					co.Https.KeyFile = s
				}
			}
			if tbl, ok := L.GetField(opt, "crons").(*lua.LTable); ok {
				co.Crons = []CronEntry{}
				tbl.ForEach(func(key, value lua.LValue) {
					entry := value.(*lua.LTable)
					co.Crons = append(co.Crons, CronEntry{entry.RawGetInt(1).String(), entry.RawGetInt(2).String()})
				})
			}

			switch L.CheckString(1) {
			case "IRC":
				newIRCChatClient(L, co, opt)
			case "Slack":
				newSlackChatClient(L, co, opt)
			case "Hipchat":
				newHipchatChatClient(L, co, opt)
			case "Null":
				newNullChatClient(L, co, opt)
			default:
				L.RaiseError("unknown chat type: %s", L.ToString(1))
			}
			return 1
		},
		"newlogger": func(L *lua.LState) int {
			logger, err := seelog.LoggerFromConfigAsString(luaToXml(L.CheckTable(1)))
			if err != nil {
				L.RaiseError(err.Error())
			}
			L.Push(luar.New(L, log.New(&seelogLogger{logger}, "", 0)))
			return 1
		},
	})
	L.SetField(mod, "cmain", lua.LChannel(luaMainChan))
	L.SetField(mod, "cworker", lua.LChannel(luaWorkerChan))
	proxyLuar(L, MessageEvent{}, nil)
	proxyLuar(L, log.Logger{}, nil)
	proxyLuar(L, url.Values{}, nil)
	proxyLuar(L, url.Userinfo{}, nil)
	proxyLuar(L, url.URL{}, nil)
	proxyLuar(L, http.Cookie{}, nil)
	proxyLuar(L, http.Header{}, nil)
	proxyLuar(L, http.Request{}, func(L *lua.LState, key string) bool {
		if key == "readbody" || key == "ReadBody" {
			L.Push(L.NewFunction(func(L *lua.LState) int {
				r := L.CheckUserData(1).Value.(*http.Request)
				b, err := ioutil.ReadAll(r.Body)
				defer r.Body.Close()
				if err != nil {
					pushN(L, lua.LNil, lua.LString(err.Error()))
					return 2
				}
				pushN(L, lua.LString(b))
				return 1
			}))
			return true
		}
		return false
	})

	L.PreloadModule("golbot", func(L *lua.LState) int {
		L.Push(mod)
		return 1
	})
	L.PreloadModule("charset", func(L *lua.LState) int {
		L.Push(L.SetFuncs(L.NewTable(), charsetMod))
		return 1
	})
	L.PreloadModule("requests", func(L *lua.LState) int {
		L.Push(L.SetFuncs(L.NewTable(), requestsMod))
		return 1
	})
	luajson.Preload(L)
	L.PreloadModule("re", gluare.Loader)
	L.PreloadModule("sh", gluash.Loader)
	L.PreloadModule("fs", gluafs.Loader)
	L.SetGlobal("goworker", L.NewFunction(func(L *lua.LState) int {
		go func() {
			L := newLuaState(conf)
			pushN(L, L.GetGlobal("worker"), <-luaWorkerChan)
			L.PCall(1, 0, nil)
		}()
		luaWorkerChan <- L.CheckAny(1)
		return 0
	}))

	if err := L.DoString(`
      local golbot = require("golbot")
      local requests = require("requests")
      local json = require("json")
      notifymain  = function(msg) golbot.cmain:send(msg) end
      requestmain = function(msg)
        msg._result = channel.make()
        golbot.cmain:send(msg)
        return msg._result:receive()
      end
      respond = function(msg, value)
        if msg and msg._result then
          msg._result:send(value)
        end
      end
      requests.json = function(opt)
	    local headers = opt.headers or {}
		opt.headers = headers
		local found = false
		for i, v in ipairs(headers) do
		    if i%2 == 0 and string.lower(v) == "content-type" then
			  found = true
			  break
			end
		end
		if not found then
		  table.insert(headers, "Content-Type")
		  table.insert(headers, "application/json")
		end
        jdata, e = json.encode(opt.json)
		if jdata == nil then
		  return jdata, e
		end
		opt.data = jdata
		body, resp = requests.request(opt)
		if body == nil then
		  return body, resp
		end
		return json.decode(body), resp
      end
	`); err != nil {
		panic(err)
	}
	if err := L.DoFile(conf); err != nil {
		panic(err)
	}
	return L
}