Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Declare vars
- local bRun = true
- local nStage = 1
- local nAnimationStage = 1
- local tAnimatedPoints = {".", "..", "...", " ..", " .", " "}
- local sUsername = ""
- local sPasswd = ""
- local sHashPasswd = ""
- local sToken = ""
- local nAccessLevel = 1
- local usersFile = {Users = {}, Password = {}, Biolock = {}, AccessLevel = {}, Token = {}, Where = {}}
- -- Check dependencies
- if not fs.exists("/StringUtils") then shell.run("pastebin get ad3aUsVw StringUtils") end
- os.loadAPI("StringUtils")
- -- Check peripherals
- if not peripheral.find("biolock") then
- --error("No biolock attached to this computer", 0)
- end
- -- Setup UI
- term.setBackgroundColor(colors.lightGray)
- term.clear()
- paintutils.drawLine(1, 1, 51, 1, colors.gray)
- -- Functions
- local function updateWindowTitle(newTitle)
- -- Function to change the window title
- paintutils.drawLine(1, 1, 50, 1, colors.gray)
- term.setCursorPos(1, 1)
- term.write(newTitle)
- end
- local function close(errorMessage)
- -- Function to clear the screen and stop the loops
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setCursorPos(1, 1)
- if errorMessage then
- error(tostring(errorMessage), 0)
- end
- bRun = false
- end
- local function clearWorkingArea(clearTitle)
- -- Clear the screen except for the title bar
- if not clearTitle then
- paintutils.drawFilledBox(1, 2, 51, 19, colors.lightGray)
- else
- paintutils.drawFilledBox(1, 4, 51, 19, colors.lightGray)
- end
- end
- local function updateStage(newStage, clearTitle)
- -- Update the stage:
- --[[
- 1: Waiting for a biolock event
- 2: Waiting for the user to enter his/her username
- 3: Waiting for the user to enter his/her password
- 4: Waiting for the user to enter his/her access level
- 5: Waiting for the user to verify his/her settings
- ]]
- clearWorkingArea(clearTitle)
- nStage = newStage
- end
- local function updateTitle(newTitle)
- -- Function to change the working area title
- term.setBackgroundColor(colors.lightGray)
- term.setCursorPos(1, 3)
- term.clearLine()
- term.write(newTitle)
- end
- local function allowCloseButton(isCloseButtonAllowed)
- -- Change the visibility of the close button in the title bar (Shown in stage 1)
- if isCloseButtonAllowed then
- if term.isColor() then
- paintutils.drawPixel(51, 1, colors.red)
- term.setCursorPos(51, 1)
- term.write("X")
- else
- term.setCursorPos(51, 1)
- term.setTextColor(colors.lightGray)
- term.write("X")
- term.setTextColor(colors.white)
- end
- else
- paintutils.drawPixel(51, 1, colors.gray)
- end
- end
- local function checkAccessLevel(isIncreasing)
- -- Check if nAccessLevel is in a valid range [-1; 5] \ {0}
- if nAccessLevel == 0 then
- if isIncreasing then
- nAccessLevel = 1
- else
- nAccessLevel = -1
- end
- elseif nAccessLevel < -1 then
- nAccessLevel = -1
- elseif nAccessLevel > 5 then
- nAccessLevel = 5
- end
- end
- local function getLastID()
- -- Get the number of players registered
- local nLastID = 0
- for k, v in pairs(usersFile) do
- if #v > nLastID then
- nLastID = #v
- end
- end
- return nLastID
- end
- local function loadUsersFile()
- -- Load the /users file if it exists and is valid
- if fs.exists("/users") then
- local file = fs.open("/users", "r")
- local fileContent = file.readAll()
- local tFileContent = textutils.unserialise(fileContent)
- if tFileContent then
- usersFile = tFileContent
- end
- file.close()
- end
- end
- local function saveUsersFile()
- -- Save the current usersFile var to a file
- local file = fs.open("/users", "w")
- file.write(textutils.serialise(usersFile))
- file.close()
- end
- local function checkUsersFile()
- -- Check the current usersFile var to find corrupted entries
- local entryCount = getLastID()
- local repairDatabase = false
- local isCorrupted = false
- local corruptedEntries = {}
- -- Checking all the entries
- for i = 1, entryCount do
- if usersFile.Users[i] == nil or usersFile.Password[i] == nil or usersFile.Biolock[i] == nil or usersFile.AccessLevel[i] == nil or usersFile.Token[i] == nil or usersFile.Where[i] == nil then
- isCorrupted = true
- table.insert(corruptedEntries, i)
- end
- end
- if isCorrupted then
- -- Asking the user if the database needs to be fixed
- updateWindowTitle("userAPI - Database corrupted")
- updateTitle("Do you want to fix the database ?")
- -- Draw the UI
- if term.isColor() then
- paintutils.drawFilledBox(1, 17, 3, 19, colors.orange)
- else
- paintutils.drawFilledBox(1, 17, 3, 19, colors.black)
- end
- term.setCursorPos(2, 18)
- term.write("X")
- if term.isColor() then
- paintutils.drawFilledBox(49, 17, 51, 19, colors.green)
- else
- paintutils.drawFilledBox(49, 17, 51, 19, colors.white)
- term.setTextColor(colors.black)
- end
- term.setCursorPos(50, 18)
- term.write(">")
- term.setTextColor(colors.white)
- while true do
- local event, p1, p2, p3 = os.pullEvent()
- if event == "mouse_click" then
- if p2 >= 1 and p2 <= 3 and p3 >= 17 and p3 <= 19 then
- -- Cancel button clicked
- break
- elseif p2 >= 49 and p2 <= 51 and p3 >= 17 and p3 <= 19 then
- -- Accept button clicked
- repairDatabase = true
- break
- end
- elseif event == "key" then
- if p1 == 14 or p1 == 45 then
- -- Cancel button pressed
- break
- elseif p1 == 28 or p1 == 205 then
- -- Accept button pressed
- repairDatabase = true
- break
- end
- end
- end
- if repairDatabase then
- for i = #corruptedEntries, 1, -1 do
- for k, v in pairs(usersFile) do
- table.remove(v, corruptedEntries[i])
- end
- end
- for k, v in pairs(usersFile) do
- local tableIndexs = {}
- for k, v in pairs(v) do
- table.insert(tableIndexs, k)
- end
- for i = 1, #tableIndexs do
- table.insert(v, i, table.remove(v, tableIndexs[i]))
- end
- end
- saveUsersFile()
- end
- end
- end
- local function TOKEN_GEN()
- local TOKEN_LENGHT = 25
- local TOKEN = ""
- local TOKEN_TABLE = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","#", "$", "*", "!"}
- local x = 0
- while TOKEN_LENGHT > 0 do
- x=x+1
- local mRandom = math.random(1,#TOKEN_TABLE)
- TOKEN = TOKEN..TOKEN_TABLE[mRandom]
- TOKEN_LENGHT = TOKEN_LENGHT - 1
- end
- return TOKEN
- end
- local function checkTokenAvailability(tokenToVerify)
- -- Check if a generated token is not already used
- for i = 1, #usersFile.Token do
- if usersFile.Token[i] == tokenToVerify then
- return false
- end
- end
- return true
- end
- local function resetPersonalData()
- -- Reset vars used in the creation procedure
- sUsername = ""
- sPasswd = ""
- sHashPasswd = ""
- sToken = ""
- nAccessLevel = 1
- end
- local function manageStageFour()
- -- Manage the Stage 4 UI
- while nStage == 4 do
- -- Reset the NumericUpDown UI
- if term.isColor() then
- paintutils.drawLine(16, 6, 35, 6, colors.lightBlue)
- else
- paintutils.drawLine(16, 6, 35, 6, colors.white)
- term.setTextColor(colors.black)
- end
- -- Write the actual nAccessLevel
- term.setCursorPos(26 - math.floor(tostring(nAccessLevel):len() / 2), 6)
- term.write(tostring(nAccessLevel))
- term.setTextColor(colors.white)
- -- Wait for user interaction
- event, p1, p2, p3 = os.pullEvent()
- if event == "mouse_click" then
- if p2 == 15 and p3 == 6 then
- -- Minus button clicked
- nAccessLevel = nAccessLevel - 1
- checkAccessLevel(false)
- elseif p2 == 36 and p3 == 6 then
- -- Plus button clicked
- nAccessLevel = nAccessLevel + 1
- checkAccessLevel(true)
- elseif p2 == 51 and p3 == 6 then
- -- Next button clicked
- updateStage(5)
- end
- elseif event == "key" then
- if p1 == 74 or p1 == 208 then
- -- Minus button pressed
- nAccessLevel = nAccessLevel - 1
- checkAccessLevel(false)
- elseif p1 == 78 or p1 == 200 then
- -- Plus button pressed
- nAccessLevel = nAccessLevel + 1
- checkAccessLevel(true)
- elseif p1 == 28 or p1 == 205 then
- -- Next button pressed
- updateStage(5)
- end
- end
- end
- end
- local function manageStageFive()
- -- Manage the Stage 5 UI
- while nStage == 5 do
- -- Wait for user interaction
- event, p1, p2, p3 = os.pullEvent()
- if event == "mouse_click" then
- if p2 >= 49 and p2 <= 51 and p3 >= 17 and p3 <= 19 then
- -- Accept button clicked
- return false, true
- elseif p2 >= 1 and p2 <= 3 and p3 >= 17 and p3 <= 19 then
- -- Back button clicked
- return true, false
- elseif p2 >= 24 and p2 <= 26 and p3 >= 17 and p3 <= 19 then
- -- Cancel button clicked
- return false, false
- end
- elseif event == "key" then
- if p1 == 28 or p1 == 205 then
- -- Accept button pressed
- return false, true
- elseif p1 == 14 or p1 == 203 then
- -- Back button pressed
- return true, false
- elseif p1 == 45 then
- -- Cancel button clicked
- return false, false
- end
- end
- end
- end
- local function isUserExisting(username)
- -- Check if the username already exists in the database
- for i = 1, #usersFile.Users do
- if usersFile.Users[i] == username then
- return true
- end
- end
- return false
- end
- local function isBiolockExisting(biolockID)
- -- Check if the bioclock ID already exists in the database
- for i = 1, #usersFile.Biolock do
- if usersFile.Biolock[i] == biolockID then
- return true, i
- end
- end
- return false, -1
- end
- local function isStringEmpty(string)
- -- Function to check if a string is empty
- return string:match("%S") == nil
- end
- local function sleepCheck(seconds)
- -- Sleep function that returns if bRun is false (The program needs to be stopped)
- for i = 1, seconds * 10 do
- sleep(0.1)
- if not bRun then
- break
- end
- end
- end
- local function userSetup(biolockID, biolockSide)
- -- A biolock event has been queued, create/modify a user
- -- Def var
- local bNotAccepted = true
- local saveChanges = true
- local bSettingOK = false
- -- Check if the user already exists to modify
- local isAlreadyRegistered, registringID = isBiolockExisting(biolockID)
- if not isAlreadyRegistered then
- updateWindowTitle("userAPI - Create a new user")
- else
- updateWindowTitle("userAPI - Modify an existing user")
- nAccessLevel = usersFile.AccessLevel[registringID]
- end
- while bNotAccepted do
- -- Stage 1: Asking for a username
- allowCloseButton(false)
- updateStage(2)
- updateTitle("User setup 1/4 - "..biolockID:sub(1, 4).."..."..biolockID:sub(biolockID:len() - 3, biolockID:len()))
- -- Draw the UI
- term.setCursorPos(1, 5)
- term.write("Enter your username")
- if term.isColor() then
- paintutils.drawLine(1, 6, 51, 6, colors.lightBlue)
- else
- paintutils.drawLine(1, 6, 51, 6, colors.white)
- term.setTextColor(colors.black)
- end
- -- Loop to check the username
- bSettingOK = false
- while not bSettingOK do
- term.setCursorPos(1, 6)
- local username = read()
- --[[
- The username is verified if:
- - The username is NOT empty
- - The username IS empty AND a username is already set (if the user wants to edit/modify his/her settings)
- ]]
- if isStringEmpty(username) and isAlreadyRegistered then
- sUsername = usersFile.Users[registringID]
- bSettingOK = true
- elseif isStringEmpty(username) and not isStringEmpty(sUsername) then
- bSettingOK = true
- elseif not isStringEmpty(username) then
- sUsername = username
- bSettingOK = true
- end
- end
- term.setTextColor(colors.white)
- -- Stage 2: Asking for a password
- updateStage(3)
- updateTitle("User setup 2/4 - "..sUsername)
- -- Draw the UI
- term.setBackgroundColor(colors.lightGray)
- term.setCursorPos(1, 5)
- term.write("Enter your password")
- if term.isColor() then
- paintutils.drawLine(1, 6, 51, 6, colors.lightBlue)
- else
- paintutils.drawLine(1, 6, 51, 6, colors.white)
- term.setTextColor(colors.black)
- end
- -- Loop to check the password
- bSettingOK = false
- while not bSettingOK do
- term.setCursorPos(1, 6)
- local password = read("*")
- --[[
- The password is verified if:
- - The password is NOT empty
- - The password IS empty AND a password is already set (if the user wants to edit/modify his/her settings)
- ]]
- if isStringEmpty(password) and isAlreadyRegistered then
- sHashPasswd = usersFile.Password[registringID]
- bSettingOK = true
- elseif isStringEmpty(password) and not isStringEmpty(sPasswd) then
- bSettingOK = true
- elseif not isStringEmpty(password) then
- sHashPasswd = StringUtils.SHA1(password)
- sPasswd = password
- bSettingOK = true
- end
- end
- term.setTextColor(colors.white)
- -- Stage 3: Asking for the access level
- updateStage(4)
- updateTitle("User setup 3/4 - "..sUsername)
- -- Draw the UI
- term.setCursorPos(1, 5)
- term.write("Set up your access level")
- if term.isColor() then
- paintutils.drawLine(16, 6, 35, 6, colors.lightBlue)
- paintutils.drawPixel(15, 6, colors.blue)
- paintutils.drawPixel(36, 6, colors.blue)
- else
- paintutils.drawLine(16, 6, 35, 6, colors.white)
- paintutils.drawPixel(15, 6, colors.black)
- paintutils.drawPixel(36, 6, colors.black)
- end
- term.setCursorPos(15, 6)
- term.write("-")
- term.setCursorPos(36, 6)
- term.write("+")
- if term.isColor() then
- paintutils.drawPixel(51, 6, colors.green)
- else
- paintutils.drawPixel(51, 6, colors.black)
- end
- term.setCursorPos(51, 6)
- term.write(">")
- -- Manage the UI
- manageStageFour()
- -- Generate a new token and check it's availability
- local bGenerateToken = true
- while bGenerateToken do
- sToken = TOKEN_GEN()
- bGenerateToken = not checkTokenAvailability(sToken)
- end
- -- Stage 4: Verify the settings and modify them if necessary
- updateStage(5)
- updateTitle("User setup 4/4 - Verify your settings")
- -- Draw the UI
- term.setCursorPos(1, 5)
- term.write("Username: "..sUsername)
- term.setCursorPos(1, 6)
- term.write("Password: "..sPasswd:sub(1, 1).."***"..sPasswd:sub(sPasswd:len(), sPasswd:len()))
- term.setCursorPos(1, 7)
- term.write("Access level: "..nAccessLevel)
- term.setCursorPos(1, 9)
- term.write("Token: "..sToken:sub(1, 4).."..."..sToken:sub(sToken:len() - 3, sToken:len()))
- term.setCursorPos(1, 10)
- term.write("Password hash: "..sHashPasswd:sub(1, 4).."..."..sHashPasswd:sub(sHashPasswd:len() - 3, sHashPasswd:len()))
- if term.isColor() then
- paintutils.drawFilledBox(1, 17, 3, 19, colors.orange)
- else
- paintutils.drawFilledBox(1, 17, 3, 19, colors.white)
- term.setTextColor(colors.black)
- end
- term.setCursorPos(2, 18)
- term.write("<")
- if term.isColor() then
- paintutils.drawFilledBox(49, 17, 51, 19, colors.green)
- else
- paintutils.drawFilledBox(49, 17, 51, 19, colors.white)
- term.setTextColor(colors.black)
- end
- term.setCursorPos(50, 18)
- term.write(">")
- if term.isColor() then
- paintutils.drawFilledBox(24, 17, 26, 19, colors.red)
- else
- paintutils.drawFilledBox(24, 17, 26, 19, colors.black)
- term.setTextColor(colors.white)
- end
- term.setCursorPos(25, 18)
- term.write("X")
- -- Manage the UI, check if the user wants to accept, modify or abort
- bNotAccepted, saveChanges = manageStageFive()
- end
- -- Save changes if asked, create a new entry if the user isn't already existing
- if saveChanges then
- if not isAlreadyRegistered then
- local lastID = getLastID()
- lastID = lastID + 1
- table.insert(usersFile.Users, lastID, sUsername)
- table.insert(usersFile.Password, lastID, sHashPasswd)
- table.insert(usersFile.Biolock, lastID, biolockID)
- table.insert(usersFile.AccessLevel, lastID, nAccessLevel)
- table.insert(usersFile.Token, lastID, sToken)
- table.insert(usersFile.Where, lastID, "unknown")
- else
- usersFile.Users[registringID] = sUsername
- usersFile.Password[registringID] = sHashPasswd
- usersFile.AccessLevel[registringID] = nAccessLevel
- usersFile.Token[registringID] = sToken
- end
- saveUsersFile()
- end
- -- Reset the app, return to Stage 1
- updateStage(1)
- updateWindowTitle("userAPI - Database manager")
- allowCloseButton(true)
- resetPersonalData()
- end
- -- Main loop
- local function loop()
- while bRun do
- -- Wait for an event
- local event, p1, p2, p3, p4, p5 = os.pullEvent()
- if event == "mouse_click" then
- -- Clicked on the X button
- if p2 == 51 and p3 == 1 then
- close()
- end
- elseif event == "peripheral_detach" then
- -- Check if there's a biolock left
- if not peripheral.find("biolock") then
- close("No biolock(s) left")
- end
- elseif event == "biolock" then
- -- A connected biolock has been triggered, start the user registration
- userSetup(p1, p2)
- elseif event == "key" then
- if p1 == 19 then
- -- R key pressed
- os.reboot()
- elseif p1 == 57 then
- --space
- userSetup("test", "troll")
- elseif p1 == 45 then
- -- X key pressed
- close()
- end
- end
- end
- end
- -- Secondaries loops
- local function animate()
- -- Title animation for Stage 1
- while bRun do
- if nStage == 1 then
- updateTitle("Waiting for the biolock"..tAnimatedPoints[nAnimationStage])
- nAnimationStage = nAnimationStage + 1
- if nAnimationStage > #tAnimatedPoints then
- nAnimationStage = 1
- end
- sleepCheck(1)
- else
- while nStage ~= 1 and bRun do
- sleep(0.1)
- end
- nAnimationStage = 1
- term.setBackgroundColor(colors.lightGray)
- term.setCursorPos(1, 19)
- term.write("Press R to reboot")
- end
- end
- end
- -- First time init
- loadUsersFile()
- checkUsersFile()
- updateWindowTitle("userAPI - Database manager")
- allowCloseButton(true)
- clearWorkingArea()
- term.setBackgroundColor(colors.lightGray)
- term.setCursorPos(1, 19)
- term.write("Press R to reboot")
- term.setCursorPos(1, 3)
- -- Start loops in parallel
- parallel.waitForAll(loop, animate)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement