Advertisement
Guest User

ftp.lua

a guest
May 25th, 2014
259
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.03 KB | None | 0 0
  1. --Simple FTP client
  2. --Originally by gamax92
  3. --'Improved' by ShadowKatStudios
  4. local computer = require("computer")
  5. local component = require("component")
  6. local internet = require("internet")
  7. local fs = require("filesystem")
  8. local shell = require("shell")
  9. local term = require("term")
  10.  
  11. local function errprint(msg)
  12.     io.stderr:write(msg .. "\n")
  13. end
  14.  
  15. local args, opts = shell.parse(...)
  16.  
  17. local alias = {
  18.     quit = "bye",
  19.     dir = "ls",
  20.     disconnect = "close",
  21. }
  22. setmetatable(alias,{
  23.     __index = function(t,k)
  24.         return k
  25.     end
  26. })
  27.  
  28. local help = {
  29.     bye = "terminate ftp session and exit",
  30.     ls = "list contents of remote directory",
  31.     close = "terminate ftp session",
  32.     help = "print local help information",
  33.     user = "send new user information",
  34.     system = "show remote system type",
  35.     quote = "send arbitrary ftp command",
  36.     read = "read a line from server",
  37.     open = "connect to remote ftp",
  38.     pwd = "print working directory on remote machine",
  39.     cdup = "change remote working directory to parent directory",
  40.     cd = "change remote working directory",
  41.     get = "receive file",
  42.     put = "send one file",
  43. }
  44. setmetatable(help,{
  45.     __index = function(t,k)
  46.         return "no help avaliable"
  47.     end
  48. })
  49.  
  50. local connection, datasock
  51.  
  52. local function readftpline()
  53.     local resX,resY = component.gpu.getResolution()
  54.     local line
  55.     while true do
  56.         line = connection:read("*l")
  57.         if line == nil then
  58.             errprint("No response from server")
  59.             return "", -1
  60.         end
  61.         line = line:gsub("\r","")
  62.         io.stdout:write(line .. (resX ~= #line and "\n" or ""))
  63.         if tonumber(line:sub(1,3)) ~= nil and (line:sub(4,4) == " " or #line == 3) then
  64.             break
  65.         end
  66.     end
  67.     return line, tonumber(line:sub(1,3))
  68. end
  69.  
  70. local function writeftpline(line)
  71.     if opts.d then
  72.         print("---> " .. line)
  73.     end
  74.     connection:write(line .. "\r\n")
  75. end
  76.  
  77. local history,startline = {}
  78. if #args > 0 then
  79.     startline = "open " .. args[1] .. (#args >= 2 and " " .. args[2] or "")
  80. end
  81. while true do
  82.     local line
  83.     if startline ~= nil then
  84.         line = startline
  85.         startline = nil
  86.     else
  87.         io.stdout:write("ftp> ")
  88.         line = (term.read(history) or ""):gsub("\n","")
  89.     end
  90.     local param = {}
  91.     for part in (line .. " "):gmatch("(.-) ") do
  92.         if part ~= "" then
  93.             param[#param + 1] = part
  94.         end
  95.     end
  96.     for i = 1,1 do if #param ~= 0 then -- Wrap command parser for break
  97.         realcmd = alias[param[1]]
  98.         if realcmd == "help" then
  99.             if #param > 1 then
  100.                 print(param[2] .. " - " .. help[alias[param[2]]])
  101.             else
  102.                 local cmdlist = {}
  103.                 for k,v in pairs(help) do
  104.                     cmdlist[#cmdlist + 1] = k
  105.                 end
  106.                 for k,v in pairs(alias) do
  107.                     cmdlist[#cmdlist + 1] = k
  108.                 end
  109.                 table.sort(cmdlist)
  110.                 local maxsize = 0
  111.                 for i = 1, #cmdlist do
  112.                     if #cmdlist[i] > maxsize then
  113.                         maxsize = #cmdlist[i]
  114.                     end
  115.                 end
  116.                 for i = 1, #cmdlist do
  117.                     cmdlist[i] = cmdlist[i] .. string.rep(" ", maxsize - #cmdlist[i] + 1)
  118.                 end
  119.                 print(table.concat(cmdlist,""))
  120.             end
  121.         elseif realcmd == "quote" then
  122.             if #param > 1 then
  123.                 writeftpline(table.concat({table.unpack(param,2)}," "))
  124.                 readftpline()
  125.             else
  126.                 errprint("Need something to send")
  127.             end
  128.         elseif realcmd == "read" then
  129.             readftpline()
  130.         elseif realcmd == "open" then
  131.             if connection ~= nil then
  132.                 print("Already connected, use close first.")
  133.                 break          
  134.             end
  135.             param[3] = param[3] or "21"
  136.             if tonumber(param[3]) == nil then
  137.                 print("Bad argument #2: Non numerical value")
  138.                 break
  139.             end
  140.             local err
  141.             connection, err = internet.open(param[2], tonumber(param[3]))
  142.             if connection == nil then
  143.                 errprint("Connection failed: " .. err)
  144.                 break
  145.             end
  146.             print("Connected to " .. param[2])
  147.             local line, code = readftpline()
  148.             if code < 200 or code > 299 then
  149.                 errprint("Server gave odd response")
  150.                 print("Closing connection ...")
  151.                 writeftpline("QUIT")
  152.                 connection:close()
  153.                 connection = nil
  154.             end
  155.         elseif realcmd == "ls" then
  156.             writeftpline("PASV")
  157.             local line, code = readftpline()
  158.             if code == 227 then
  159.                 local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
  160.                 local port = tonumber(hport)*256 + tonumber(lport)
  161.                 datasock = internet.open(ip:gsub(",","."), port)
  162.                 if datasock == nil then
  163.                     print("Passive connection failed")
  164.                 end
  165.                 writeftpline("LIST")
  166.                 local line, code = readftpline()
  167.                 if code >= 100 and code <= 199 then
  168.                     local data = datasock:read("*a"):gsub("\r","")
  169.                     datasock:close()
  170.                     local line, code = readftpline()
  171.                     if code == 226 then
  172.                         io.stdout:write(data)
  173.                     else
  174.                         errprint("Transfer failed")
  175.                     end
  176.                 else
  177.                     print("LIST request refused")
  178.                     datasock:close()
  179.                 end
  180.             else
  181.                 print("Passive mode refused")
  182.             end
  183.         elseif realcmd == "user" then
  184.             local name
  185.             if #param > 1 then
  186.                 name = param[2]
  187.             else
  188.                 io.stdout:write("username> ")
  189.                 name = io.read() or ""
  190.                 if name == "" then
  191.                     errprint("Username required")
  192.                     break
  193.                 end
  194.             end
  195.             writeftpline("USER " .. name)
  196.             local line, code = readftpline()
  197.             if code >= 200 and code <= 299 then
  198.             elseif code >= 300 and code <= 399 then
  199.                 local pass
  200.                 if #param > 2 then
  201.                     pass = param[3]
  202.                 else
  203.                     io.stdout:write("password> ")
  204.                     pass = io.read() or ""
  205.                 end
  206.                 writeftpline("PASS " .. pass)
  207.                 local line, code = readftpline()
  208.                 if code ~= 230 then
  209.                     errprint("Login failed")
  210.                 end
  211.             else
  212.                 errprint("Login failed")
  213.             end
  214.         elseif realcmd == "close" then
  215.             if connection ~= nil then
  216.                 writeftpline("QUIT")
  217.                 local line, code = readftpline()
  218.                 connection:close()
  219.                 connection = nil
  220.             else
  221.                 errprint("Not connected")
  222.             end
  223.         elseif realcmd == "bye" then
  224.             if connection ~= nil then
  225.                 writeftpline("QUIT")
  226.                 local line, code = readftpline()
  227.                 connection:close()
  228.                 connection = nil
  229.             end
  230.             return
  231.         elseif realcmd == "system" then
  232.             writeftpline("SYST")
  233.             readftpline()
  234.         elseif realcmd == "pwd" then
  235.             writeftpline("PWD")
  236.             readftpline()
  237.         elseif realcmd == "cdup" then
  238.             writeftpline("CDUP")
  239.             readftpline()
  240.         elseif realcmd == "cd" then
  241.             local location
  242.             if #param > 1 then
  243.                 location = param[2]
  244.             else
  245.                 io.stdout:write("location> ")
  246.                 location = io.read() or ""
  247.                 if location == "" then
  248.                     errprint("Location required")
  249.                     break
  250.                 end
  251.             end
  252.             writeftpline("CWD " .. location)
  253.             readftpline()
  254.         elseif realcmd == "mkdir" then
  255.             local location
  256.             if #param > 1 then
  257.                 location = param[2]
  258.             else
  259.                 io.stdout:write("name> ")
  260.                 location = io.read() or ""
  261.                 if location == "" then
  262.                     errprint("Location required")
  263.                     break
  264.                 end
  265.             end
  266.             writeftpline("MKD " .. location)
  267.             readftpline()
  268.         elseif realcmd == "mv" then
  269.             local file
  270.             local newname
  271.             if #param > 1 then
  272.                 file = param[2]
  273.                 newname = param[3] or param[2]
  274.             else
  275.                 io.stdout:write("filename> ")
  276.                 file = io.read() or ""
  277.                 if file == "" then
  278.                     errprint("Filename required")
  279.                     break
  280.                 end
  281.                 io.stdout:write("newname> ")
  282.                 newname = io.read() or ""
  283.                 if newname == "" then
  284.                     errprint("New filename required")
  285.                     break
  286.                 end
  287.             end
  288.             writeftpline("RNFR "..file)
  289.             local line, code = readftpline()
  290.             writeftpline("RNTO "..newname)
  291.             local line, code = readftpline()
  292.         elseif realcmd == "get" then
  293.             local file
  294.             local localname
  295.             if #param > 1 then
  296.                 file = param[2]
  297.                 localname = param[3] or param[2]
  298.             else
  299.                 io.stdout:write("filename> ")
  300.                 file = io.read() or ""
  301.                 if file == "" then
  302.                     errprint("Filename required")
  303.                     break
  304.                 end
  305.                 io.stdout:write("localname> ")
  306.                 localname = io.read() or ""
  307.                 if localname == "" then
  308.                     errprint("Local filename required")
  309.                     break
  310.                 end
  311.             end
  312.             writeftpline("PASV")
  313.             local line, code = readftpline()
  314.             if code == 227 then
  315.                 local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
  316.                 local port = tonumber(hport)*256 + tonumber(lport)
  317.                 datasock = internet.open(ip:gsub(",","."), port)
  318.                 if datasock == nil then
  319.                     errprint("Passive connection failed")
  320.                     break
  321.                 end
  322.                 writeftpline("RETR " .. file)
  323.                 local line, code = readftpline()
  324.                 if code >= 100 and code <= 199 then
  325.                     local fileout = fs.open(shell.resolve(localname),"wb")
  326.                     if fileout == nil then
  327.                         errprint("Could not open local file")
  328.                         break
  329.                     end
  330.                     local start = computer.uptime()
  331.                     local bcount = 0
  332.                     while true do
  333.                         local data = datasock:read(8192)
  334.                         if data == nil then break end
  335.                         fileout:write(data)
  336.                         bcount = bcount + #data
  337.                     end
  338.                     local elapsed = computer.uptime() - start
  339.                     datasock:close()
  340.                     fileout:close()
  341.                     local line, code = readftpline()
  342.                     if code == 226 then
  343.                         print("Received " .. bcount .. " bytes in " .. math.floor(elapsed * 100) / 100 .. " seconds. " .. math.floor((bcount/1024/elapsed) * 10) / 10 .. "kB/s")
  344.                     else
  345.                         errprint("Transfer failed")
  346.                     end
  347.                 else
  348.                     errprint("file request failed")
  349.                     datasock:close()
  350.                 end
  351.             else
  352.                 errprint("Passive mode refused")
  353.             end
  354.         elseif realcmd == "put" then
  355.             local file
  356.             local localname
  357.             if #param > 1 then
  358.                 localname = param[2]
  359.                 file = param[3] or param[2]
  360.             else
  361.                 io.stdout:write("localname> ")
  362.                 localname = io.read() or ""
  363.                 if localname == "" then
  364.                     errprint("Local filename required")
  365.                     break
  366.                 end
  367.                 io.stdout:write("filename> ")
  368.                 file = io.read() or ""
  369.                 if file == "" then
  370.                     errprint("Filename required")
  371.                     break
  372.                 end
  373.             end
  374.             if not fs.exists(shell.resolve(localname)) then
  375.                 errprint("No such file")
  376.             end
  377.             writeftpline("PASV")
  378.             local line, code = readftpline()
  379.             if code == 227 then
  380.                 local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
  381.                 local port = tonumber(hport)*256 + tonumber(lport)
  382.                 datasock = internet.open(ip:gsub(",","."), port)
  383.                 if datasock == nil then
  384.                     errprint("Passive connection failed")
  385.                     break
  386.                 end
  387.                 writeftpline("STOR " .. file)
  388.                 local line, code = readftpline()
  389.                 if code >= 100 and code <= 199 then
  390.                     local filein = fs.open(shell.resolve(localname),"rb")
  391.                     if filein == nil then
  392.                         errprint("Could not open local file")
  393.                         break
  394.                     end
  395.                     local start = computer.uptime()
  396.                     local bcount = 0
  397.                     local filesize = fs.size(shell.resolve(localname))
  398.                     while bcount < filesize do
  399.                         local data = filein:read(math.min(8192,filesize - bcount))
  400.                         datasock:write(data)
  401.                         bcount = bcount + #data
  402.                     end
  403.                     local elapsed = computer.uptime() - start
  404.                     datasock:close()
  405.                     filein:close()
  406.                     local line, code = readftpline()
  407.                     if code == 226 then
  408.                         print("Sent " .. bcount .. " bytes in " .. math.floor(elapsed * 100) / 100 .. " seconds. " .. math.floor((bcount/1024/elapsed) * 10) / 10 .. "kB/s")
  409.                     else
  410.                         errprint("Transfer failed")
  411.                     end
  412.                 else
  413.                     errprint("file transfer failed")
  414.                     datasock:close()
  415.                 end
  416.             else
  417.                 errprint("Passive mode refused")
  418.             end
  419.         else
  420.             errprint("Invalid command")
  421.         end
  422.     end end
  423. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement