Advertisement
Tatantyler

Rednet Database Server

Oct 19th, 2012
690
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.23 KB | None | 0 0
  1. local commitLocBase = "commits/"
  2. local fileLocBase = "files/"
  3. local usersLoc = "users/"
  4. local inactivityTimeout = 900
  5. local allowGuest = false
  6. local silent = false
  7.  
  8. local args = {...}
  9.  
  10. if #args > 1 then
  11.     for i=1, #args do
  12.         if args[i] == "-silent" then
  13.             silent = true
  14.         elseif args[i] == "-guest_read" then
  15.             allowGuest = true
  16.         end
  17.     end
  18. end
  19.  
  20. if not fs.exists(commitLocBase) then
  21.     fs.makeDir(commitLocBase)
  22. end
  23.  
  24. if not fs.exists(fileLocBase) then
  25.     fs.makeDir(fileLocBase)
  26. end
  27.  
  28. if not fs.exists(usersLoc) then
  29.     fs.makeDir(usersLoc)
  30. end
  31.  
  32.  
  33. local defaultPrint = print
  34. print = function(...)
  35.     local str = ""
  36.     for i=1, #arg do
  37.         str = str..arg[i]
  38.     end
  39.     if not silent then
  40.         defaultPrint(str)
  41.     end
  42. end
  43.  
  44. local loggedIn = {}
  45.  
  46. if fs.exists("arcfour") then
  47.     os.loadAPI("arcfour")
  48.     cipher = arcfour.rc4_cipher
  49. else
  50.     if http then
  51.         local webHandle = http.get("http://pastebin.com/raw.php?i=73q8zCPc")
  52.         local data = webHandle.readAll()
  53.         webHandle.close()
  54.         local fileHandle = fs.open("arcfour", "w")
  55.         fileHandle.write(data)
  56.         fileHandle.close()
  57.         os.loadAPI("arcfour")
  58.         cipher = arcfour.rc4_cipher
  59.     end
  60. end
  61.  
  62. local function makeFileSafe(file)
  63.     file = string.gsub(file, "%/", "#002F")
  64.     file = string.gsub(file, "%\\", "#005C")
  65.     file = string.gsub(file, "%:", "#003A")
  66.     file = string.gsub(file, "%*", "#002A")
  67.     file = string.gsub(file, "%?", "#003F")
  68.     file = string.gsub(file, "%\"", "#0022")
  69.     file = string.gsub(file, "%<", "#003C")
  70.     file = string.gsub(file, "%>", "#003E")
  71.     file = string.gsub(file, "%|", "#007C")
  72.     return file
  73. end
  74.  
  75. local function safeFilenameToTrueName(file)
  76.     file = string.gsub(file, "#002F", "/")
  77.     file = string.gsub(file, "#005C", "\\")
  78.     file = string.gsub(file, "#003A", ":")
  79.     file = string.gsub(file, "#002A", "*")
  80.     file = string.gsub(file, "#003F", "?")
  81.     file = string.gsub(file, "#0022", "\"")
  82.     file = string.gsub(file, "#003C", "<")
  83.     file = string.gsub(file, "#003E", ">")
  84.     file = string.gsub(file, "#007C", "|")
  85.     return file
  86. end
  87.  
  88. local function handleMsg(id, msg)
  89.     local user = loggedIn[id]
  90.     commitLoc = commitLocBase..user[3].."/"
  91.     fileLoc = fileLocBase..user[3].."/"
  92.    
  93.     local function doCommit(id, file, data, commit_msg)
  94.         local timeHandle = http.get("http://timeapi.org/utc/now")
  95.         local currentTime = timeHandle.readAll()
  96.         timeHandle.close()
  97.         local commitID = 0
  98.         if not commit_msg then
  99.             commit_msg = "No Message Given"
  100.         end
  101.         print("Making commit to file "..file..".")
  102.         print("Message: "..commit_msg)
  103.         if not fs.exists(commitLoc..file) then
  104.             fs.makeDir(commitLoc..file)
  105.         end
  106.         if not fs.exists(commitLoc..file.."/.metadata") then
  107.             fs.makeDir(commitLoc..file.."/.metadata")
  108.         end
  109.         while true do
  110.             if fs.exists(commitLoc..file.."/"..tostring(commitID)) then
  111.                 commitID = commitID + 1
  112.             else
  113.                 local commit = fs.open(commitLoc..file.."/"..tostring(commitID), "w")
  114.                 if fs.exists(fileLoc..file) then
  115.                     local fileHandle = fs.open(fileLoc..file, "r")
  116.                     commit.write(fileHandle.readAll())
  117.                     fileHandle.close()
  118.                 else
  119.                     commit.write(data)
  120.                 end
  121.                 commit.close()
  122.                 break
  123.             end
  124.         end
  125.         local commitData = fs.open(commitLoc..file.."/.metadata/"..tostring(commitID), "w")
  126.         commitData.writeLine(commit_msg)
  127.         commitData.writeLine(id)
  128.         commitData.writeLine("UTC: "..currentTime)
  129.         commitData.close()
  130.         if data ~= nil then
  131.             local commit = fs.open(fileLoc..file, "w")
  132.             commit.write(data)
  133.             commit.close()
  134.         end
  135.     end
  136.    
  137.     if not fs.exists(commitLoc) then
  138.         fs.makeDir(commitLoc)
  139.     end
  140.     if not fs.exists(fileLoc) then
  141.         fs.makeDir(fileLoc)
  142.     end
  143.     local oldPrint = print
  144.     print = function(...)
  145.         local printStr = "["..id.."] "
  146.         for i,v in ipairs(arg) do
  147.             printStr = printStr..v
  148.         end
  149.         local logHandle = fs.open("dbLog", "a")
  150.         logHandle.writeLine(printStr)
  151.         logHandle.close()
  152.         oldPrint(printStr)
  153.     end
  154.     msg = textutils.unserialize(msg)
  155.     if type(msg) == "table" then
  156.         if not fs.exists(commitLoc) then
  157.             fs.makeDir(commitLoc)
  158.         end
  159.         if not fs.exists(fileLoc) then
  160.             fs.makeDir(fileLoc)
  161.         end
  162.         if msg[1] == "switch" then
  163.             loggedIn[id][3] = msg[2]
  164.             rednet.send(id, textutils.serialize({"DB", "successful"}))
  165.         elseif msg[1] == "clone_branch" or msg[1] == "copy_branch" then
  166.             fs.copy(fileLoc, fileLocBase..msg[2])
  167.             fs.copy(commitLoc, commitLocBase..msg[2])
  168.             rednet.send(id, textutils.serialize({"DB", "successful"}))
  169.         elseif msg[1] == "new_branch" then
  170.             fs.makeDir(fileLocBase..msg[2])
  171.             fs.makeDir(commitLocBase..msg[2])
  172.             rednet.send(id, textutils.serialize({"DB", "successful"}))
  173.         elseif msg[1] == "get_commit" then
  174.             local file = makeFileSafe(msg[2])
  175.             local commitHandle = fs.open(commitLoc..file.."/"..tostring(msg[3]), "r")
  176.             local commitMsgHandle = fs.open(commitLoc..file.."/.metadata/"..tostring(msg[3]), "r")
  177.             if commitHandle then
  178.                 local commitData = commitHandle.readAll()
  179.                 if commitMsgHandle then
  180.                     local commitMsg = commitMsgHandle.readLine()
  181.                     commitMsgHandle.close()
  182.                 end
  183.                 commitHandle.close()
  184.                 rednet.send(id, textutils.serialize({"DB", "commit_data", msg[2], msg[3], commitData, commitMsg}))
  185.             else
  186.                 rednet.send(id, textutils.serialize({"DB", "error", "That commit does not exist."}))
  187.             end
  188.         elseif msg[1] == "commit" then
  189.             doCommit(id, msg[2], msg[3], msg[4])
  190.             rednet.send(id, textutils.serialize({"DB", "successful"}))
  191.         elseif msg[1] == "undo" or msg[1] == "revert" then
  192.             print("Reverting "..msg[2].." to commit "..tostring(msg[3]))
  193.             local file = makeFileSafe(msg[2])
  194.             local commitID = tostring(msg[3])
  195.             if fs.exists(commitLoc..file.."/"..commitID) then
  196.                 local commit = fs.open(commitLoc..file.."/"..commitID, "r")
  197.                 local fileHandle = fs.open(fileLoc..file, "w")
  198.                 fileHandle.write(commit.readAll())
  199.                 fileHandle.close()
  200.                 commit.close()
  201.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  202.             else
  203.                 rednet.send(id, textutils.serialize({"DB", "error", "That commit does not exist."}))
  204.             end
  205.         elseif msg[1] == "delete" then
  206.             local file = makeFileSafe(msg[2])
  207.             local commitID = 0
  208.             print("Deleting: "..fileLoc..file)
  209.             if fs.exists(fileLoc..file) then
  210.                 doCommit(id, file, nil, "Being deleted...")
  211.                 fs.delete(fileLoc..file)
  212.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  213.             else
  214.                 rednet.send(id, textutils.serialize({"DB", "error", "That file does not exist."}))
  215.             end
  216.         elseif msg[1] == "move" or msg[1] == "rename" then
  217.             local oldFile = makeFileSafe(msg[2])
  218.             local newFile = makeFileSafe(msg[3])
  219.             print("Moving: "..oldFile.." to "..newFile)
  220.             if fs.exists(fileLoc..oldFile) then
  221.                 local fileHandle = fs.open(fileLoc..oldFile, "r")
  222.                 local oldFileData = fileHandle.readAll()
  223.                 fileHandle.close()
  224.                 doCommit(id, oldFile, nil, "Moved to: "..newFile)
  225.                 doCommit(id, newFile, oldFileData, "Moved from: "..oldFile)
  226.                 fs.delete(fileLoc..oldFile)
  227.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  228.             else
  229.                 rednet.send(id, textutils.serialize({"DB", "error", "That file does not exist."}))
  230.             end
  231.         elseif msg[1] == "copy" then
  232.             local oldFile = makeFileSafe(msg[2])
  233.             local newFile = makeFileSafe(msg[3])
  234.             print("Copying: "..oldFile.." to "..newFile)
  235.             if fs.exists(fileLoc..oldFile) then
  236.                 local fileHandle = fs.open(fileLoc..oldFile, "r")
  237.                 local oldFileData = fileHandle.readAll()
  238.                 fileHandle.close()
  239.                 doCommit(id, oldFile, nil, "Copied to: "..newFile)
  240.                 doCommit(id, newFile, oldFileData, "Copied from: "..oldFile)
  241.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  242.             else
  243.                 rednet.send(id, textutils.serialize({"DB", "error", "That file does not exist."}))
  244.             end
  245.         elseif msg[1] == "read" then
  246.             local file = makeFileSafe(msg[2])
  247.             local commitID = tostring(msg[3])
  248.             local data = ""
  249.             if commitID == "-1" or commitID == "nil" then
  250.                 print("Request for "..fileLoc..file..".")
  251.                 if fs.exists(fileLoc..file) then
  252.                     local fileHandle = fs.open(fileLoc..file, "r")
  253.                     data = fileHandle.readAll()
  254.                     fileHandle.close()
  255.                 end
  256.             else
  257.                 print("Request for "..commitLoc..file.."/"..commitID..".")
  258.                 if fs.exists(commitLoc..file.."/"..commitID) then
  259.                     local commit = fs.open(commitLoc..file.."/"..commitID, "r")
  260.                     data = commit.readAll()
  261.                     commit.close()
  262.                 end
  263.             end
  264.             rednet.send(id, textutils.serialize({"DB", "file", msg[2], data}))
  265.         elseif msg[1] == "list_commits" then
  266.             print("Listing commits for file: "..msg[2])
  267.             local file = makeFileSafe(msg[2])
  268.             local commits = {}
  269.             local commit = 0
  270.             while true do
  271.                 if not fs.exists(commitLoc..file.."/"..tostring(commit)) then
  272.                     break
  273.                 end
  274.                 local comHandle = fs.open(commitLoc..file.."/"..tostring(commit), "r")
  275.                 local comMsgHandle = fs.open(commitLoc..file.."/.metadata/"..tostring(commit), "r")
  276.                 if comMsgHandle then
  277.                     local comMsg = comMsgHandle..readLine()
  278.                     comMsgHandle.close()
  279.                 end
  280.                 local data = comHandle.readAll()
  281.                 comHandle.close()
  282.                 commits[commit] = {data, comMsg}
  283.                 commit = commit + 1
  284.             end
  285.             local packet = {"DB", "commits", loggedIn[id][3], msg[2], commits}
  286.             packet = textutils.serialize(packet)
  287.             rednet.send(id, packet)
  288.         --[[elseif msg[1] == "clone" then
  289.             print("Copy of database requested.")
  290.             local commits = {}
  291.             local messages = {}
  292.             local files = {}
  293.             if fs.exists(commitLoc) then
  294.                 for i,file in ipairs(fs.list(commitLoc)) do
  295.                     commits[file] = {}
  296.                     messages[file] = {}
  297.                     for i2, commit in ipairs(fs.list(commitLoc..file)) do
  298.                         if commit ~= ".metadata" then
  299.                             local handle = fs.open(commitLoc..file.."/"..commit, "r")
  300.                             commits[file][commit] = handle.readAll()
  301.                             handle.close()
  302.                         end
  303.                     end
  304.                     if fs.exists(commitLoc..file..".metadata/") then
  305.                         for i2, commit in ipairs(fs.list(commitLoc..file..".metadata/")) do
  306.                             local handle = fs.open(commitLoc..file..".metadata/"..commit, "r")
  307.                             messages[file][commit] = handle.readLine()
  308.                             handle.close()
  309.                         end
  310.                     end
  311.                 end
  312.             end
  313.             if fs.exists(fileLoc) then
  314.                 for i,v in ipairs(fs.list(fileLoc)) do
  315.                     local handle = fs.open(fileLoc..v, "r")
  316.                     files[v] = handle.readAll()
  317.                     handle.close()
  318.                 end
  319.             end
  320.             rednet.send(id, textutils.serialize({"DB", "clone", loggedIn[id][3], messages, commits, files}))]]
  321.         elseif msg[1] == "pastebin_get" then
  322.             local webHandle = http.get("http://pastebin.com/raw.php?i="..msg[2])
  323.             if webHandle then
  324.                 local data = webHandle.readAll()
  325.                 webHandle.close()
  326.                 doCommit(id, msg[3], data, msg[4])
  327.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  328.             else
  329.                 rednet.send(id, textutils.serialize({"DB", "error", "Could not connect to Pastebin."}))
  330.             end
  331.         elseif msg[1] == "github_get" then
  332.             local file = makeFileSafe(msg[2])
  333.             local url = "https://raw.github.com/"..msg[3].."/"..msg[4].."/"..msg[5].."/"..msg[6]
  334.             local webHandle = http.get(url)
  335.             if webHandle then
  336.                 local data = webHandle.readAll()
  337.                 webHandle.close()
  338.                 doCommit(id, msg[2], data, msg[7])
  339.                 rednet.send(id, textutils.serialize({"DB", "successful"}))
  340.             else
  341.                 rednet.send(id, textutils.serialize({"DB", "error", "Could not connect to GitHub."}))
  342.             end
  343.         elseif msg[1] == "list" then
  344.             rednet.send(id, textutils.serialize({"DB", "files", fs.list(fileLoc)}))
  345.         end
  346.     end
  347.     print = oldPrint
  348. end
  349.  
  350. for i,v in ipairs(rs.getSides()) do
  351.     if peripheral.getType(v) == "modem" then
  352.         rednet.open(v)
  353.     end
  354. end
  355.  
  356. if cipher then
  357.     print("ARC4 cipher found.")
  358. else
  359.     print("No ARC4 cipher found.")
  360.     print("This means that passwords will be read as cleartext.")
  361.     print("Try enabling the HTTP API if possible.")
  362. end
  363.  
  364. local function recvThread(id, msg)
  365.     --print(id..": "..msg.." @ "..os.clock())
  366.     local packet = textutils.unserialize(msg)
  367.     if type(packet) == "table" then
  368.         if packet[1] == "login" then
  369.             local user = packet[2]
  370.             local pass = packet[3]
  371.             if fs.exists(usersLoc..user) then
  372.                 if cipher then
  373.                     local userHandle = fs.open(usersLoc..user, "rb")
  374.                     local cBytes = {}
  375.                     while true do
  376.                         local byte = userHandle.read()
  377.                         if byte then
  378.                             table.insert(cBytes, byte)
  379.                         else
  380.                             break
  381.                         end
  382.                     end
  383.                     local ciphertext = string.char(unpack(cBytes))
  384.                     local plaintext = cipher(pass, ciphertext)
  385.                     if plaintext == user then
  386.                         loggedIn[id] = {user, os.clock(), "master"} -- {user, time since last activity, current branch}
  387.                         print("Login sucessful. User: "..user)
  388.                         rednet.send(id, textutils.serialize({"DB", "successful"}))
  389.                     else
  390.                         print("Login failed: incorrect password")
  391.                         rednet.send(id, textutils.serialize({"DB", "error", "Incorrect password."}))
  392.                     end
  393.                 else
  394.                     local userHandle = fs.open(usersLoc..user, "r")
  395.                     local pw = userHandle.readAll()
  396.                     if pass == pw then
  397.                         loggedIn[id] = {user, os.clock(), "master"} -- {user, time since last activity, current branch}
  398.                         print("Login sucessful. User: "..user)
  399.                         rednet.send(id, textutils.serialize({"DB", "successful"}))
  400.                     else
  401.                         print("Login failed: incorrect password")
  402.                         rednet.send(id, textutils.serialize({"DB", "error", "Incorrect password."}))
  403.                     end
  404.                 end
  405.             else
  406.                 print("Login failed: unknown user")
  407.                 rednet.send(id, textutils.serialize({"DB", "error", "User not found."}))
  408.             end
  409.         elseif packet[1] == "logout" or packet[1] == "logoff" then
  410.             if loggedIn[id] ~= nil then
  411.                 if loggedIn[id][1] ~= nil then
  412.                     print(loggedIn[id][1].." logged out.")
  413.                     loggedIn[id] = nil
  414.                     rednet.send(id, textutils.serialize({"DB", "goodbye"}))
  415.                 else
  416.                     rednet.send(id, textutils.serialize({"DB", "error", "You have not logged in!"}))
  417.                 end
  418.             else
  419.                 rednet.send(id, textutils.serialize({"DB", "error", "You have not logged in!"}))
  420.             end
  421.         elseif loggedIn[id] then
  422.             if loggedIn[id] ~= nil then
  423.                 loggedIn[id][2] = os.clock()
  424.             end
  425.             handleMsg(id, msg)
  426.             return
  427.         elseif allowGuest and (packet[1] == "list" or packet[1] == "read") then
  428.             if packet[1] == "list" then
  429.                 local logHandle = fs.open("dbLog", "a")
  430.                 logHandle.writeLine("["..id.."] ".."Guest requested file listing.")
  431.                 logHandle.close()
  432.                 print("["..id.."] ".."Guest requested file listing. Path: "..fileLocBase.."master/")
  433.                 local files = fs.list(fileLocBase.."master/")
  434.                 rednet.send(id, textutils.serialize({"DB", "files", files}))
  435.             elseif packet[1] == "read" then
  436.                 local logHandle = fs.open("dbLog", "a")
  437.                 logHandle.writeLine("["..id.."] ".."Guest requested file: "..packet[2])
  438.                 logHandle.close()
  439.                 if fs.exists(fileLocBase.."master/"..makeFileSafe(packet[2])) then
  440.                     print("["..id.."] ".."Guest requested file: "..packet[2])
  441.                     local file = makeFileSafe(packet[2])
  442.                     local handle = fs.open(fileLocBase.."master/"..file, "r")
  443.                     local data = handle.readAll()
  444.                     handle.close()
  445.                     rednet.send(id, textutils.serialize({"DB", "file", packet[2], data}))
  446.                 else
  447.                     rednet.send(id, textutils.serialize({"DB", "error", "That file does not exist."}))
  448.                 end
  449.             end
  450.         else
  451.             rednet.send(id, textutils.serialize({"DB", "error", "You have not logged in!"}))
  452.             return
  453.         end
  454.     end
  455. end
  456.  
  457. local function loginTrackerThread()
  458.     while true do
  459.         local currentTime = os.clock()
  460.         for i=1, table.maxn(loggedIn) do
  461.             v = loggedIn[i]
  462.             if v then
  463.                 if currentTime - v[2] > inactivityTimeout then
  464.                     print("Logging "..v[1].." out due to inactivity...")
  465.                     loggedIn[i] = nil
  466.                 end
  467.             end
  468.         end
  469.         os.sleep(1)
  470.     end
  471. end
  472.  
  473. local newUserProgram = [[
  474. if fs.exists("arcfour") then
  475.     os.loadAPI("arcfour")
  476. end
  477.  
  478. local args = {...}
  479.  
  480. local user = args[1]
  481. local pw = args[2]
  482.  
  483. local function writePassword(file, password)
  484.     local handle = fs.open(file, "wb")
  485.     local bytes = {}
  486.     for i=1, #password do
  487.         handle.write(string.byte(password, i, i))
  488.     end
  489.     handle.close()
  490. end
  491.  
  492. if arcfour then
  493.     writePassword("users/"..user, arcfour.rc4_cipher(pw, user))
  494. else
  495.     local handle = fs.open("users/"..user, "w")
  496.     handle.write(pw)
  497.     handle.close()
  498. end
  499. ]]
  500.  
  501. if not fs.exists("newUser") then
  502.     local handle = fs.open("newUser", "w")
  503.     handle.write(newUserProgram)
  504.     handle.close()
  505. end
  506.  
  507. if allowGuest then
  508.     print("Guest restricted read-only access enabled.")
  509. end
  510. parallel.waitForAll(
  511. function()
  512.     while true do
  513.         local id, msg = rednet.receive()
  514.         recvThread(id, msg)
  515.     end
  516. end,
  517. loginTrackerThread
  518. )
  519. print = defaultPrint
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement