// Run a Lua file as a configuration script. Also has access to the userstate and permissions. // Returns an error if there was a problem with running the lua script, otherwise nil. func runConfiguration(filename string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, mux *http.ServeMux, singleFileMode bool) error { // Retrieve a Lua state L := luapool.Get() // Retrieve the userstate userstate := perm.UserState() // Server configuration functions exportServerConfigFunctions(L, perm, filename, luapool) // Other basic system functions, like log() exportBasicSystemFunctions(L) // Simpleredis data structures (could be used for storing server stats) exportList(L, userstate) exportSet(L, userstate) exportHash(L, userstate) exportKeyValue(L, userstate) // For handling JSON data exportJSONFunctions(L) exportJFile(L, filepath.Dir(filename)) // For saving and loading Lua functions exportCodeLibrary(L, userstate) // Plugins exportPluginFunctions(L, nil) // Cache exportCacheFunctions(L, cache) if singleFileMode { // Lua HTTP handlers exportLuaHandlerFunctions(L, filename, perm, luapool, cache, mux, false) } // Run the script if err := L.DoFile(filename); err != nil { // Close the Lua state L.Close() // Logging and/or HTTP response is handled elsewhere return err } // Only put the Lua state back if there were no errors luapool.Put(L) return nil }
// Return a *lua.LState object that contains several exposed functions func exportCommonFunctions(w http.ResponseWriter, req *http.Request, filename string, perm pinterface.IPermissions, L *lua.LState, luapool *lStatePool, flushFunc func(), cache *FileCache) { // Retrieve the userstate userstate := perm.UserState() // Make basic functions, like print, available to the Lua script. // Only exports functions that can relate to HTTP responses or requests. exportBasicWeb(w, req, L, filename, flushFunc) // Functions for serving files in the same directory as a script exportServeFile(w, req, L, filename, perm, luapool, cache) // Make other basic functions available exportBasicSystemFunctions(L) // Functions for rendering markdown or amber exportRenderFunctions(w, req, L) // Make the functions related to userstate available to the Lua script exportUserstate(w, req, L, userstate) // Simpleredis data structures exportList(L, userstate) exportSet(L, userstate) exportHash(L, userstate) exportKeyValue(L, userstate) // For handling JSON data exportJSONFunctions(L) exportJFile(L, filepath.Dir(filename)) // For saving and loading Lua functions exportCodeLibrary(L, userstate) // pprint //exportREPL(L) // Plugins exportPluginFunctions(L, nil) // Cache exportCacheFunctions(L, cache) // File uploads exportUploadedFile(L, w, req, filepath.Dir(filename)) }
// REPL provides a "Read Eveal Print" loop for interacting with Lua. // A variety of functions are exposed to the Lua state. func REPL(perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, ready, done chan bool) error { var ( historyFilename string err error ) historydir, err := homedir.Dir() if err != nil { log.Error("Could not find a user directory to store the REPL history.") historydir = "." } if runtime.GOOS == "windows" { historyFilename = filepath.Join(historydir, "algernon", "repl.txt") } else { historyFilename = filepath.Join(historydir, ".algernon_history") } // Retrieve the userstate userstate := perm.UserState() // Retrieve a Lua state L := luapool.Get() // Don't re-use the Lua state defer L.Close() // Server configuration functions exportServerConfigFunctions(L, perm, "", luapool) // Other basic system functions, like log() exportBasicSystemFunctions(L) // Simpleredis data structures exportList(L, userstate) exportSet(L, userstate) exportHash(L, userstate) exportKeyValue(L, userstate) // For handling JSON data exportJSONFunctions(L) exportJFile(L, serverDir) // For saving and loading Lua functions exportCodeLibrary(L, userstate) // Pretty printing exportREPL(L) // Colors and input enableColors := runtime.GOOS != "windows" o := term.NewTextOutput(enableColors, true) // Plugin functionality exportPluginFunctions(L, o) // Cache exportCacheFunctions(L, cache) // Getting ready o.Println(o.LightBlue(versionString)) <-ready // Wait for the server to be ready // Tell the user that the server is ready o.Println(o.LightGreen("Ready")) // Start the read, eval, print loop var ( line string prompt = o.LightGreen("lua> ") EOF bool EOFcount int ) if loadHistory(historyFilename) != nil && fs.exists(historyFilename) { log.Error("Could not load REPL history:", historyFilename) } // To be run at server shutdown atShutdown(func() { // Verbose mode has different log output at shutdown if !verboseMode { o.Println(o.LightBlue(exitMessage)) } }) for { // Retrieve user input EOF = false if line, err = getInput(prompt); err != nil { if err.Error() == "EOF" { if debugMode { o.Println(o.LightPurple(err.Error())) } EOF = true } else { log.Error("Error reading line(" + err.Error() + ").") continue } } else { addHistory(line) // Save the REPL history at every line. // This proved to be safer than only saving the history at shutdown // (due to how ctrl-c was handled on some systems) // and it's hard to imagine performance issues with this. saveHistory(historyFilename) } if EOF { switch EOFcount { case 0: o.Err("Press ctrl-d again to exit.") EOFcount++ continue default: done <- true return nil } } line = strings.TrimSpace(line) if line == "" { continue } switch line { case "help": outputHelp(o, generalHelpText) continue case "webhelp": outputHelp(o, webHelpText) continue case "confighelp": outputHelp(o, configHelpText) continue case "quit", "exit", "shutdown", "halt": done <- true return nil case "zalgo": // Easter egg o.ErrExit("Ḫ̷̲̫̰̯̭̀̂̑̈ͅĚ̥̖̩̘̱͔͈͈ͬ̚ ̦̦͖̲̀ͦ͂C̜͓̲̹͐̔ͭ̏Oͭ͛͂̋ͭͬͬ͆͏̺͓̰͚͠ͅM̢͉̼̖͍̊̕Ḛ̭̭͗̉̀̆ͬ̐ͪ̒S͉̪͂͌̄") } // If the line starts with print, don't touch it if strings.HasPrefix(line, "print(") { if err = L.DoString(line); err != nil { // Output the error message o.Err(err.Error()) } } else { // Wrap the line in "pprint" if err = L.DoString("pprint(" + line + ")"); err != nil { // If there was a syntax error, try again without pprint if strings.Contains(err.Error(), "syntax error") { if err = L.DoString(line); err != nil { // Output the error message o.Err(err.Error()) } // For other kinds of errors, output the error } else { // Output the error message o.Err(err.Error()) } } } } }