Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --Simple FTP client
- --Originally by gamax92
- --'Improved' by ShadowKatStudios
- local computer = require("computer")
- local component = require("component")
- local internet = require("internet")
- local fs = require("filesystem")
- local shell = require("shell")
- local term = require("term")
- local function errprint(msg)
- io.stderr:write(msg .. "\n")
- end
- local args, opts = shell.parse(...)
- local alias = {
- quit = "bye",
- dir = "ls",
- disconnect = "close",
- }
- setmetatable(alias,{
- __index = function(t,k)
- return k
- end
- })
- local help = {
- bye = "terminate ftp session and exit",
- ls = "list contents of remote directory",
- close = "terminate ftp session",
- help = "print local help information",
- user = "send new user information",
- system = "show remote system type",
- quote = "send arbitrary ftp command",
- read = "read a line from server",
- open = "connect to remote ftp",
- pwd = "print working directory on remote machine",
- cdup = "change remote working directory to parent directory",
- cd = "change remote working directory",
- get = "receive file",
- put = "send one file",
- }
- setmetatable(help,{
- __index = function(t,k)
- return "no help avaliable"
- end
- })
- local connection, datasock
- local function readftpline()
- local resX,resY = component.gpu.getResolution()
- local line
- while true do
- line = connection:read("*l")
- if line == nil then
- errprint("No response from server")
- return "", -1
- end
- line = line:gsub("\r","")
- io.stdout:write(line .. (resX ~= #line and "\n" or ""))
- if tonumber(line:sub(1,3)) ~= nil and (line:sub(4,4) == " " or #line == 3) then
- break
- end
- end
- return line, tonumber(line:sub(1,3))
- end
- local function writeftpline(line)
- if opts.d then
- print("---> " .. line)
- end
- connection:write(line .. "\r\n")
- end
- local history,startline = {}
- if #args > 0 then
- startline = "open " .. args[1] .. (#args >= 2 and " " .. args[2] or "")
- end
- while true do
- local line
- if startline ~= nil then
- line = startline
- startline = nil
- else
- io.stdout:write("ftp> ")
- line = (term.read(history) or ""):gsub("\n","")
- end
- local param = {}
- for part in (line .. " "):gmatch("(.-) ") do
- if part ~= "" then
- param[#param + 1] = part
- end
- end
- for i = 1,1 do if #param ~= 0 then -- Wrap command parser for break
- realcmd = alias[param[1]]
- if realcmd == "help" then
- if #param > 1 then
- print(param[2] .. " - " .. help[alias[param[2]]])
- else
- local cmdlist = {}
- for k,v in pairs(help) do
- cmdlist[#cmdlist + 1] = k
- end
- for k,v in pairs(alias) do
- cmdlist[#cmdlist + 1] = k
- end
- table.sort(cmdlist)
- local maxsize = 0
- for i = 1, #cmdlist do
- if #cmdlist[i] > maxsize then
- maxsize = #cmdlist[i]
- end
- end
- for i = 1, #cmdlist do
- cmdlist[i] = cmdlist[i] .. string.rep(" ", maxsize - #cmdlist[i] + 1)
- end
- print(table.concat(cmdlist,""))
- end
- elseif realcmd == "quote" then
- if #param > 1 then
- writeftpline(table.concat({table.unpack(param,2)}," "))
- readftpline()
- else
- errprint("Need something to send")
- end
- elseif realcmd == "read" then
- readftpline()
- elseif realcmd == "open" then
- if connection ~= nil then
- print("Already connected, use close first.")
- break
- end
- param[3] = param[3] or "21"
- if tonumber(param[3]) == nil then
- print("Bad argument #2: Non numerical value")
- break
- end
- local err
- connection, err = internet.open(param[2], tonumber(param[3]))
- if connection == nil then
- errprint("Connection failed: " .. err)
- break
- end
- print("Connected to " .. param[2])
- local line, code = readftpline()
- if code < 200 or code > 299 then
- errprint("Server gave odd response")
- print("Closing connection ...")
- writeftpline("QUIT")
- connection:close()
- connection = nil
- end
- elseif realcmd == "ls" then
- writeftpline("PASV")
- local line, code = readftpline()
- if code == 227 then
- local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
- local port = tonumber(hport)*256 + tonumber(lport)
- datasock = internet.open(ip:gsub(",","."), port)
- if datasock == nil then
- print("Passive connection failed")
- end
- writeftpline("LIST")
- local line, code = readftpline()
- if code >= 100 and code <= 199 then
- local data = datasock:read("*a"):gsub("\r","")
- datasock:close()
- local line, code = readftpline()
- if code == 226 then
- io.stdout:write(data)
- else
- errprint("Transfer failed")
- end
- else
- print("LIST request refused")
- datasock:close()
- end
- else
- print("Passive mode refused")
- end
- elseif realcmd == "user" then
- local name
- if #param > 1 then
- name = param[2]
- else
- io.stdout:write("username> ")
- name = io.read() or ""
- if name == "" then
- errprint("Username required")
- break
- end
- end
- writeftpline("USER " .. name)
- local line, code = readftpline()
- if code >= 200 and code <= 299 then
- elseif code >= 300 and code <= 399 then
- local pass
- if #param > 2 then
- pass = param[3]
- else
- io.stdout:write("password> ")
- pass = io.read() or ""
- end
- writeftpline("PASS " .. pass)
- local line, code = readftpline()
- if code ~= 230 then
- errprint("Login failed")
- end
- else
- errprint("Login failed")
- end
- elseif realcmd == "close" then
- if connection ~= nil then
- writeftpline("QUIT")
- local line, code = readftpline()
- connection:close()
- connection = nil
- else
- errprint("Not connected")
- end
- elseif realcmd == "bye" then
- if connection ~= nil then
- writeftpline("QUIT")
- local line, code = readftpline()
- connection:close()
- connection = nil
- end
- return
- elseif realcmd == "system" then
- writeftpline("SYST")
- readftpline()
- elseif realcmd == "pwd" then
- writeftpline("PWD")
- readftpline()
- elseif realcmd == "cdup" then
- writeftpline("CDUP")
- readftpline()
- elseif realcmd == "cd" then
- local location
- if #param > 1 then
- location = param[2]
- else
- io.stdout:write("location> ")
- location = io.read() or ""
- if location == "" then
- errprint("Location required")
- break
- end
- end
- writeftpline("CWD " .. location)
- readftpline()
- elseif realcmd == "mkdir" then
- local location
- if #param > 1 then
- location = param[2]
- else
- io.stdout:write("name> ")
- location = io.read() or ""
- if location == "" then
- errprint("Location required")
- break
- end
- end
- writeftpline("MKD " .. location)
- readftpline()
- elseif realcmd == "mv" then
- local file
- local newname
- if #param > 1 then
- file = param[2]
- newname = param[3] or param[2]
- else
- io.stdout:write("filename> ")
- file = io.read() or ""
- if file == "" then
- errprint("Filename required")
- break
- end
- io.stdout:write("newname> ")
- newname = io.read() or ""
- if newname == "" then
- errprint("New filename required")
- break
- end
- end
- writeftpline("RNFR "..file)
- local line, code = readftpline()
- writeftpline("RNTO "..newname)
- local line, code = readftpline()
- elseif realcmd == "get" then
- local file
- local localname
- if #param > 1 then
- file = param[2]
- localname = param[3] or param[2]
- else
- io.stdout:write("filename> ")
- file = io.read() or ""
- if file == "" then
- errprint("Filename required")
- break
- end
- io.stdout:write("localname> ")
- localname = io.read() or ""
- if localname == "" then
- errprint("Local filename required")
- break
- end
- end
- writeftpline("PASV")
- local line, code = readftpline()
- if code == 227 then
- local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
- local port = tonumber(hport)*256 + tonumber(lport)
- datasock = internet.open(ip:gsub(",","."), port)
- if datasock == nil then
- errprint("Passive connection failed")
- break
- end
- writeftpline("RETR " .. file)
- local line, code = readftpline()
- if code >= 100 and code <= 199 then
- local fileout = fs.open(shell.resolve(localname),"wb")
- if fileout == nil then
- errprint("Could not open local file")
- break
- end
- local start = computer.uptime()
- local bcount = 0
- while true do
- local data = datasock:read(8192)
- if data == nil then break end
- fileout:write(data)
- bcount = bcount + #data
- end
- local elapsed = computer.uptime() - start
- datasock:close()
- fileout:close()
- local line, code = readftpline()
- if code == 226 then
- print("Received " .. bcount .. " bytes in " .. math.floor(elapsed * 100) / 100 .. " seconds. " .. math.floor((bcount/1024/elapsed) * 10) / 10 .. "kB/s")
- else
- errprint("Transfer failed")
- end
- else
- errprint("file request failed")
- datasock:close()
- end
- else
- errprint("Passive mode refused")
- end
- elseif realcmd == "put" then
- local file
- local localname
- if #param > 1 then
- localname = param[2]
- file = param[3] or param[2]
- else
- io.stdout:write("localname> ")
- localname = io.read() or ""
- if localname == "" then
- errprint("Local filename required")
- break
- end
- io.stdout:write("filename> ")
- file = io.read() or ""
- if file == "" then
- errprint("Filename required")
- break
- end
- end
- if not fs.exists(shell.resolve(localname)) then
- errprint("No such file")
- end
- writeftpline("PASV")
- local line, code = readftpline()
- if code == 227 then
- local ip,hport,lport = line:match(".-(%d*,%d*,%d*,%d*),(%d*),(%d*)")
- local port = tonumber(hport)*256 + tonumber(lport)
- datasock = internet.open(ip:gsub(",","."), port)
- if datasock == nil then
- errprint("Passive connection failed")
- break
- end
- writeftpline("STOR " .. file)
- local line, code = readftpline()
- if code >= 100 and code <= 199 then
- local filein = fs.open(shell.resolve(localname),"rb")
- if filein == nil then
- errprint("Could not open local file")
- break
- end
- local start = computer.uptime()
- local bcount = 0
- local filesize = fs.size(shell.resolve(localname))
- while bcount < filesize do
- local data = filein:read(math.min(8192,filesize - bcount))
- datasock:write(data)
- bcount = bcount + #data
- end
- local elapsed = computer.uptime() - start
- datasock:close()
- filein:close()
- local line, code = readftpline()
- if code == 226 then
- print("Sent " .. bcount .. " bytes in " .. math.floor(elapsed * 100) / 100 .. " seconds. " .. math.floor((bcount/1024/elapsed) * 10) / 10 .. "kB/s")
- else
- errprint("Transfer failed")
- end
- else
- errprint("file transfer failed")
- datasock:close()
- end
- else
- errprint("Passive mode refused")
- end
- else
- errprint("Invalid command")
- end
- end end
- end
Add Comment
Please, Sign In to add comment