// Make functions related to building a library of Lua code available func exportCodeLibrary(L *lua.LState, userstate pinterface.IUserState) { creator := userstate.Creator() // Register the Library class and the methods that belongs with it. mt := L.NewTypeMetatable(lLibraryClass) mt.RawSetH(lua.LString("__index"), mt) L.SetFuncs(mt, libMethods) // The constructor for new Libraries takes only an optional id L.SetGlobal("CodeLib", L.NewFunction(func(L *lua.LState) int { // Check if the optional argument is given id := defaultID if L.GetTop() == 1 { id = L.ToString(1) } // Create a new Library in Lua userdata, err := newCodeLibrary(L, creator, id) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) L.Push(lua.LNumber(1)) return 3 // Number of returned values } // Return the hash map object L.Push(userdata) return 1 // number of results })) }
func NewUsersHole(state pinterface.IUserState) *UsersHole { uh := new(UsersHole) creator := state.Creator() uh.state = state uh.holes, _ = creator.NewHashMap("holes") uh.seq, _ = creator.NewKeyValue("seq") uh.servers = make(map[string]*HoleApp) return uh }
func NewWikiEngine(state pinterface.IUserState) *WikiEngine { pool := state.Pool() wikiState := new(WikiState) wikiState.pages = simpleredis.NewHashMap(pool, "pages") wikiState.pages.SelectDatabase(state.DatabaseIndex()) wikiState.pool = pool return &WikiEngine{state, wikiState} }
func NewTimeTableEngine(state pinterface.IUserState) *TimeTableEngine { pool := state.Pool() timeTableState := new(TimeTableState) timeTableState.plans = simpleredis.NewHashMap(pool, "plans") timeTableState.plans.SelectDatabase(state.DatabaseIndex()) timeTableState.pool = pool return &TimeTableEngine{state, timeTableState} }
func NewIPEngine(state pinterface.IUserState) *IPEngine { // Create a RedisList for storing IP adresses ips := simpleredis.NewList(state.Pool(), "IPs") ipEngine := new(IPEngine) ipEngine.data = ips ipEngine.state = state return ipEngine }
func GenerateAllUsernames(state pinterface.IUserState) SimpleContextHandle { return func(ctx *web.Context) string { if !state.AdminRights(ctx.Request) { return MessageOKback("List usernames", "Not logged in as Administrator") } s := "" usernames, err := state.AllUsernames() if err == nil { for _, username := range usernames { s += username + "<br />" } } return MessageOKback("Usernames", s) } }
func NewChatEngine(userState pinterface.IUserState) *ChatEngine { pool := userState.Pool() dbindex := userState.DatabaseIndex() chatState := new(ChatState) chatState.active = pinterface.NewSet(pool, "active") chatState.active.SelectDatabase(dbindex) chatState.said = pinterface.NewList(pool, "said") chatState.said.SelectDatabase(dbindex) chatState.userInfo = pinterface.NewHashMap(pool, "userInfo") // lastSeen.time is an encoded timestamp for when the user was last seen chatting chatState.userInfo.SelectDatabase(dbindex) chatState.pool = pool return &ChatEngine{userState, chatState} }
// Create a user by adding the username to the list of usernames func GenerateConfirmUser(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, val string) string { confirmationCode := val unconfirmedUsernames, err := state.AllUnconfirmedUsernames() if err != nil { return MessageOKurl("Confirmation", "All users are confirmed already.", "/register") } // Find the username by looking up the confirmationCode on unconfirmed users username := "" for _, aUsername := range unconfirmedUsernames { aConfirmationCode, err := state.ConfirmationCode(aUsername) if err != nil { // If the confirmation code can not be found, just skip this one continue } if confirmationCode == aConfirmationCode { // Found the right user username = aUsername break } } // Check that the user is there if username == "" { // Say "no longer" because we don't care about people that just try random confirmation links return MessageOKurl("Confirmation", "The confirmation link is no longer valid.", "/register") } hasUser := state.HasUser(username) if !hasUser { return MessageOKurl("Confirmation", "The user you wish to confirm does not exist anymore.", "/register") } // Remove from the list of unconfirmed usernames state.RemoveUnconfirmed(username) // Mark user as confirmed state.MarkConfirmed(username) return MessageOKurl("Confirmation", "Thank you "+username+", you can now log in.", "/login") } }
// Remove an unconfirmed user func GenerateRemoveUnconfirmedUser(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, username string) string { if !state.AdminRights(ctx.Request) { return MessageOKback("Remove unconfirmed user", "Not logged in as Administrator") } if username == "" { return MessageOKback("Remove unconfirmed user", "Can't remove blank user.") } found := false usernames, err := state.AllUnconfirmedUsernames() if err == nil { for _, unconfirmedUsername := range usernames { if username == unconfirmedUsername { found = true break } } } if !found { return MessageOKback("Remove unconfirmed user", "Can't find "+username+" in the list of unconfirmed users.") } // Mark as confirmed state.RemoveUnconfirmed(username) return MessageOKurl("Remove unconfirmed user", "OK, removed "+username+" from the list of unconfirmed users.", "/admin") } }
// Log in a user by changing the loggedin value func GenerateLoginUser(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, val string) string { // Fetch password from ctx password, found := ctx.Params["password"] if !found { return MessageOKback("Login", "Can't log in without a password.") } username := val if username == "" { return MessageOKback("Login", "Can't log in with a blank username.") } if !state.HasUser(username) { return MessageOKback("Login", "User "+username+" does not exist, could not log in.") } if !state.IsConfirmed(username) { return MessageOKback("Login", "The email for "+username+" has not been confirmed, check your email and follow the link.") } if !state.CorrectPassword(username, password) { return MessageOKback("Login", "Wrong password.") } // Log in the user by changing the database and setting a secure cookie state.SetLoggedIn(username) // Also store the username in the browser state.SetUsernameCookie(ctx.ResponseWriter, username) // TODO: Use a welcoming messageOK where the user can see when he/she last logged in and from which host if username == "admin" { ctx.SetHeader("Refresh", "0; url=/admin", true) } else { // TODO: Redirect to the page the user was at before logging in ctx.SetHeader("Refresh", "0; url=/", true) } return "" } }
// Make functions related to HTTP requests and responses available to Lua scripts func exportList(L *lua.LState, userstate pinterface.IUserState) { creator := userstate.Creator() // Register the list class and the methods that belongs with it. mt := L.NewTypeMetatable(lListClass) mt.RawSetH(lua.LString("__index"), mt) L.SetFuncs(mt, listMethods) // The constructor for new lists takes a name and an optional redis db index L.SetGlobal("List", L.NewFunction(func(L *lua.LState) int { name := L.ToString(1) // Check if the optional argument is given if L.GetTop() == 2 { localDBIndex := L.ToInt(2) // Set the DB index, if possible switch rh := creator.(type) { case pinterface.IRedisCreator: rh.SelectDatabase(localDBIndex) } } // Create a new list in Lua userdata, err := newList(L, creator, name) if err != nil { L.Push(lua.LNil) L.Push(lua.LString(err.Error())) L.Push(lua.LNumber(1)) return 3 // Number of returned values } // Return the list object L.Push(userdata) return 1 // Number of returned values })) }
func GenerateToggleAdmin(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, username string) string { if !state.AdminRights(ctx.Request) { return MessageOKback("Admin toggle", "Not logged in as Administrator") } if username == "" { return MessageOKback("Admin toggle", "Can't set toggle empty username") } if !state.HasUser(username) { return MessageOKback("Admin toggle", "Can't toggle non-existing user") } // A special case if username == "admin" { return MessageOKback("Admin toggle", "Can't remove admin rights from the admin user") } if !state.IsAdmin(username) { state.SetAdminStatus(username) return MessageOKurl("Admin toggle", "OK, "+username+" is now an admin", "/admin") } state.RemoveAdminStatus(username) return MessageOKurl("Admin toggle", "OK, "+username+" is now a regular user", "/admin") } }
func GenerateStatusCurrentUser(state pinterface.IUserState) SimpleContextHandle { return func(ctx *web.Context) string { if !state.AdminRights(ctx.Request) { return MessageOKback("Status", "Not logged in as Administrator") } username := state.Username(ctx.Request) if username == "" { return MessageOKback("Current user status", "No user logged in") } hasUser := state.HasUser(username) if !hasUser { return MessageOKback("Current user status", username+" does not exist") } if !(state.IsLoggedIn(username)) { return MessageOKback("Current user status", "User "+username+" is not logged in") } return MessageOKback("Current user status", "User "+username+" is logged in") } }
// Log out a user by changing the loggedin value func GenerateLogoutCurrentUser(state pinterface.IUserState) SimpleContextHandle { return func(ctx *web.Context) string { username := state.Username(ctx.Request) if username == "" { return MessageOKback("Logout", "No user to log out") } if !state.HasUser(username) { return MessageOKback("Logout", "user "+username+" does not exist, could not log out") } // Log out the user by changing the database, the cookie can stay state.SetLoggedOut(username) // Redirect //ctx.SetHeader("Refresh", "0; url=/login", true) return MessageOKurl("Logout", username+" is now logged out. Hope to see you soon!", "/login") } }
// TODO: Undo for removing users // Remove a user func GenerateRemoveUser(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, username string) string { if !state.AdminRights(ctx.Request) { return MessageOKback("Remove user", "Not logged in as Administrator") } if username == "" { return MessageOKback("Remove user", "Can't remove blank user") } if !state.HasUser(username) { return MessageOKback("Remove user", username+" doesn't exists, could not remove") } // Remove the user state.RemoveUser(username) return MessageOKurl("Remove user", "OK, removed "+username, "/admin") } }
func GenerateStatusUser(state pinterface.IUserState) WebHandle { return func(ctx *web.Context, username string) string { if username == "" { return MessageOKback("Status", "No username given") } if !state.HasUser(username) { return MessageOKback("Status", username+" does not exist") } loggedinStatus := "not logged in" if state.IsLoggedIn(username) { loggedinStatus = "logged in" } confirmStatus := "email has not been confirmed" if state.IsConfirmed(username) { confirmStatus = "email has been confirmed" } return MessageOKback("Status", username+" is "+loggedinStatus+" and "+confirmStatus) } }
// TODO: Log and graph when people visit pages and when people contribute content // This one is wrapped by ServeAdminPages func GenerateAdminStatus(state pinterface.IUserState) SimpleContextHandle { return func(ctx *web.Context) string { if !state.AdminRights(ctx.Request) { return "<div class=\"no\">Not logged in as Administrator</div>" } // TODO: List all sorts of info, edit users, etc s := "<h2>Administrator Dashboard</h2>" s += "<strong>User table</strong><br />" s += "<table class=\"whitebg\">" s += "<tr>" s += "<th>Username</th><th>Confirmed</th><th>Logged in</th><th>Administrator</th><th>Admin toggle</th><th>Remove user</th><th>Email</th><th>Password hash</th>" s += "</tr>" usernames, err := state.AllUsernames() if err == nil { for rownr, username := range usernames { if rownr%2 == 0 { s += "<tr class=\"even\">" } else { s += "<tr class=\"odd\">" } s += "<td><a class=\"username\" href=\"/status/" + username + "\">" + username + "</a></td>" s += TableCell(state.IsConfirmed(username)) s += TableCell(state.IsLoggedIn(username)) s += TableCell(state.IsAdmin(username)) s += "<td><a class=\"darkgrey\" href=\"/admintoggle/" + username + "\">admin toggle</a></td>" // TODO: Ask for confirmation first with a MessageOKurl("blabla", "blabla", "/actually/remove/stuff") s += "<td><a class=\"careful\" href=\"/remove/" + username + "\">remove</a></td>" email, err := state.Email(username) if err == nil { // The cleanup happens at registration time, but it's ok with an extra cleanup s += "<td>" + CleanUserInput(email) + "</td>" } passwordHash, err := state.PasswordHash(username) if err == nil { if strings.HasPrefix(passwordHash, "abc123") { s += "<td>" + passwordHash + " (<a href=\"/fixpassword/" + username + "\">fix</a>)</td>" } else { s += "<td>" + symbolhash.New(passwordHash, 16).String() + "</td>" } } s += "</tr>" } } s += "</table>" s += "<br />" s += "<strong>Unconfirmed users</strong><br />" s += "<table>" s += "<tr>" s += "<th>Username</th><th>Confirmation link</th><th>Remove</th>" s += "</tr>" usernames, err = state.AllUnconfirmedUsernames() if err == nil { for _, username := range usernames { s += "<tr>" s += "<td><a class=\"username\" href=\"/status/" + username + "\">" + username + "</a></td>" confirmationCode, err := state.ConfirmationCode(username) if err != nil { panic("ERROR: Could not get confirmation code") } s += "<td><a class=\"somewhatcareful\" href=\"/confirm/" + confirmationCode + "\">" + confirmationCode + "</a></td>" s += "<td><a class=\"careful\" href=\"/removeunconfirmed/" + username + "\">remove</a></td>" s += "</tr>" } } s += "</table>" return s } }
// Register a new user, site is ie. "archlinux.no" func GenerateRegisterUser(state pinterface.IUserState, site string) WebHandle { return func(ctx *web.Context, val string) string { // Password checks password1, found := ctx.Params["password1"] if password1 == "" || !found { return MessageOKback("Register", "Can't register without a password.") } password2, found := ctx.Params["password2"] if password2 == "" || !found { return MessageOKback("Register", "Please confirm the password by typing it in twice.") } if password1 != password2 { return MessageOKback("Register", "The password and confirmation password must be equal.") } // Email checks email, found := ctx.Params["email"] if !found { return MessageOKback("Register", "Can't register without an email address.") } // must have @ and ., but no " " if !strings.Contains(email, "@") || !strings.Contains(email, ".") || strings.Contains(email, " ") { return MessageOKback("Register", "Please use a valid email address.") } if email != CleanUserInput(email) { return MessageOKback("Register", "The sanitized email differs from the given email.") } // Username checks username := val if username == "" { return MessageOKback("Register", "Can't register without a username.") } if state.HasUser(username) { return MessageOKback("Register", "That user already exists, try another username.") } // Only some letters are allowed in the username err := permissions.ValidUsernamePassword(username, password1) if err != nil { return MessageOKback("Register", err.Error()) } adminuser := false // A special case if username == "admin" { // The first user to register with the username "admin" becomes the administrator adminuser = true } // Register the user state.AddUser(username, password1, email) // Mark user as administrator if that is the case if adminuser { // Set admin status state.SetAdminStatus(username) } confirmationCode, err := state.GenerateUniqueConfirmationCode() if err != nil { panic(err.Error()) } // If registering the admin user (first user on the system), don't send a confirmation email, just register it if adminuser { // Mark user as confirmed state.MarkConfirmed(username) // Redirect return MessageOKurl("Registration complete", "Thanks for registering, the admin user has been created.", "/login") } // Send confirmation email ConfirmationEmail(site, "https://"+site+"/confirm/"+confirmationCode, username, email) // Register the need to be confirmed state.AddUnconfirmed(username, confirmationCode) // Redirect return MessageOKurl("Registration complete", "Thanks for registering, the confirmation e-mail has been sent.", "/login") } }
// Make functions related to users and permissions available to Lua scripts func exportUserstate(w http.ResponseWriter, req *http.Request, L *lua.LState, userstate pinterface.IUserState) { // Check if the current user has "user rights", returns bool // Takes no arguments L.SetGlobal("UserRights", L.NewFunction(func(L *lua.LState) int { L.Push(lua.LBool(userstate.UserRights(req))) return 1 // number of results })) // Check if the given username exists, returns bool // Takes a username L.SetGlobal("HasUser", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(userstate.HasUser(username))) return 1 // number of results })) // Get the value from the given boolean field, returns bool // Takes a username and fieldname L.SetGlobal("BooleanField", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) fieldname := L.ToString(2) L.Push(lua.LBool(userstate.BooleanField(username, fieldname))) return 1 // number of results })) // Save a value as a boolean field, returns nothing // Takes a username, fieldname and boolean value L.SetGlobal("SetBooleanField", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) fieldname := L.ToString(2) value := L.ToBool(3) userstate.SetBooleanField(username, fieldname, value) return 0 // number of results })) // Check if a given username is confirmed, returns a bool // Takes a username L.SetGlobal("IsConfirmed", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(userstate.IsConfirmed(username))) return 1 // number of results })) // Check if a given username is logged in, returns a bool // Takes a username L.SetGlobal("IsLoggedIn", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(userstate.IsLoggedIn(username))) return 1 // number of results })) // Check if the current user has "admin rights", returns a bool // Takes no arguments. L.SetGlobal("AdminRights", L.NewFunction(func(L *lua.LState) int { L.Push(lua.LBool(userstate.AdminRights(req))) return 1 // number of results })) // Check if a given username is an admin, returns a bool // Takes a username L.SetGlobal("IsAdmin", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(userstate.IsAdmin(username))) return 1 // number of results })) // Get the username stored in a cookie, or an empty string // Takes no arguments L.SetGlobal("UsernameCookie", L.NewFunction(func(L *lua.LState) int { username, err := userstate.UsernameCookie(req) var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(username) } L.Push(result) return 1 // number of results })) // Store the username in a cookie, returns true if successful // Takes a username L.SetGlobal("SetUsernameCookie", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(nil == userstate.SetUsernameCookie(w, username))) return 1 // number of results })) // Clear the user cookie. The result depends on the browser. L.SetGlobal("ClearCookie", L.NewFunction(func(L *lua.LState) int { userstate.ClearCookie(w) return 0 // number of results })) // Get the username stored in a cookie, or an empty string // Takes no arguments L.SetGlobal("AllUsernames", L.NewFunction(func(L *lua.LState) int { usernames, err := userstate.AllUsernames() var table *lua.LTable if err != nil { table = L.NewTable() } else { table = strings2table(L, usernames) } L.Push(table) return 1 // number of results })) // Get the email for a given username, or an empty string // Takes a username L.SetGlobal("Email", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) email, err := userstate.Email(username) var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(email) } L.Push(result) return 1 // number of results })) // Get the password hash for a given username, or an empty string // Takes a username L.SetGlobal("PasswordHash", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) pw, err := userstate.PasswordHash(username) var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(pw) } L.Push(result) return 1 // number of results })) // Get all unconfirmed usernames // Takes no arguments L.SetGlobal("AllUnconfirmedUsernames", L.NewFunction(func(L *lua.LState) int { usernames, err := userstate.AllUnconfirmedUsernames() var table *lua.LTable if err != nil { table = L.NewTable() } else { table = strings2table(L, usernames) } L.Push(table) return 1 // number of results })) // Get a confirmation code that can be given to a user, or an empty string // Takes a username L.SetGlobal("ConfirmationCode", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) pw, err := userstate.ConfirmationCode(username) var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(pw) } L.Push(result) return 1 // number of results })) // Add a user to the list of unconfirmed users, returns nothing // Takes a username and a confirmation code L.SetGlobal("AddUnconfirmed", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) confirmationCode := L.ToString(2) userstate.AddUnconfirmed(username, confirmationCode) return 0 // number of results })) // Remove a user from the list of unconfirmed users, returns nothing // Takes a username L.SetGlobal("RemoveUnconfirmed", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.RemoveUnconfirmed(username) return 0 // number of results })) // Mark a user as confirmed, returns nothing // Takes a username L.SetGlobal("MarkConfirmed", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.MarkConfirmed(username) return 0 // number of results })) // Removes a user, returns nothing // Takes a username L.SetGlobal("RemoveUser", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.RemoveUser(username) return 0 // number of results })) // Make a user an admin, returns nothing // Takes a username L.SetGlobal("SetAdminStatus", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.SetAdminStatus(username) return 0 // number of results })) // Make an admin user a regular user, returns nothing // Takes a username L.SetGlobal("RemoveAdminStatus", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.RemoveAdminStatus(username) return 0 // number of results })) // Add a user, returns nothing // Takes a username, password and email L.SetGlobal("AddUser", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) password := L.ToString(2) email := L.ToString(3) userstate.AddUser(username, password, email) return 0 // number of results })) // Set a user as logged in on the server (not cookie), returns nothing // Takes a username L.SetGlobal("SetLoggedIn", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.SetLoggedIn(username) return 0 // number of results })) // Set a user as logged out on the server (not cookie), returns nothing // Takes a username L.SetGlobal("SetLoggedOut", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.SetLoggedOut(username) return 0 // number of results })) // Log in a user, both on the server and with a cookie. // Returns true of successful. // Takes a username L.SetGlobal("Login", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LBool(nil == userstate.Login(w, username))) return 1 // number of results })) // Logs out a user, on the server (which is enough). Returns nothing // Takes a username L.SetGlobal("Logout", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.Logout(username) return 0 // number of results })) // Get the current username, from the cookie // Takes nothing L.SetGlobal("Username", L.NewFunction(func(L *lua.LState) int { username := userstate.Username(req) L.Push(lua.LString(username)) return 1 // number of results })) // Get the current cookie timeout // Takes a username L.SetGlobal("CookieTimeout", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) L.Push(lua.LNumber(userstate.CookieTimeout(username))) return 1 // number of results })) // Set the current cookie timeout // Takes a timeout number, measured in seconds L.SetGlobal("SetCookieTimeout", L.NewFunction(func(L *lua.LState) int { timeout := int64(L.ToNumber(1)) userstate.SetCookieTimeout(timeout) return 0 // number of results })) // Get the current password hashing algorithm (bcrypt, bcrypt+ or sha256) // Takes nothing L.SetGlobal("PasswordAlgo", L.NewFunction(func(L *lua.LState) int { algorithm := userstate.PasswordAlgo() L.Push(lua.LString(algorithm)) return 1 // number of results })) // Set the current password hashing algorithm (bcrypt, bcrypt+ or sha256) // Takes a string L.SetGlobal("SetPasswordAlgo", L.NewFunction(func(L *lua.LState) int { algorithm := L.ToString(1) userstate.SetPasswordAlgo(algorithm) return 0 // number of results })) // Hash the password, returns a string // Takes a username and password (username can be used for salting) L.SetGlobal("HashPassword", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) password := L.ToString(2) L.Push(lua.LString(userstate.HashPassword(username, password))) return 1 // number of results })) // Check if a given username and password is correct, returns a bool // Takes a username and password L.SetGlobal("CorrectPassword", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) password := L.ToString(2) L.Push(lua.LBool(userstate.CorrectPassword(username, password))) return 1 // number of results })) // Checks if a confirmation code is already in use, returns a bool // Takes a confirmation code L.SetGlobal("AlreadyHasConfirmationCode", L.NewFunction(func(L *lua.LState) int { confirmationCode := L.ToString(1) L.Push(lua.LBool(userstate.AlreadyHasConfirmationCode(confirmationCode))) return 1 // number of results })) // Find a username based on a given confirmation code, or returns an empty string // Takes a confirmation code L.SetGlobal("FindUserByConfirmationCode", L.NewFunction(func(L *lua.LState) int { confirmationCode := L.ToString(1) username, err := userstate.FindUserByConfirmationCode(confirmationCode) var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(username) } L.Push(result) return 1 // number of results })) // Mark a user as confirmed, returns nothing // Takes a username L.SetGlobal("Confirm", L.NewFunction(func(L *lua.LState) int { username := L.ToString(1) userstate.Confirm(username) return 0 // number of results })) // Mark a user as confirmed, returns true if successful. // Takes a confirmation code. L.SetGlobal("ConfirmUserByConfirmationCode", L.NewFunction(func(L *lua.LState) int { confirmationCode := L.ToString(1) L.Push(lua.LBool(nil == userstate.ConfirmUserByConfirmationCode(confirmationCode))) return 1 // number of results })) // Set the minimum confirmation code length // Takes the minimum number of characters L.SetGlobal("SetMinimumConfirmationCodeLength", L.NewFunction(func(L *lua.LState) int { length := int(L.ToNumber(1)) userstate.SetMinimumConfirmationCodeLength(length) return 0 // number of results })) // Generates and returns a unique confirmation code, or an empty string // Takes no parameters L.SetGlobal("GenerateUniqueConfirmationCode", L.NewFunction(func(L *lua.LState) int { confirmationCode, err := userstate.GenerateUniqueConfirmationCode() var result lua.LString if err != nil { result = lua.LString("") } else { result = lua.LString(confirmationCode) } L.Push(result) return 1 // number of results })) }