shadowkat1010

ftp.lua

May 24th, 2014
266
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
Add Comment
Please, Sign In to add comment