Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local ftp_client = [[
- -- socket_client class(client)
- local socket_client = {
- _modem = false;
- _channel = 0;
- _reply_channel = 0;
- _side = "top";
- _stack = {};
- }
- socket_client.__index = socket_client
- socket_client.__call = function(self, ch, rep_ch)
- local sides = {
- "right", "left",
- "top", "bottom",
- "front", "back"
- }
- local m = nil
- local side = "none"
- for k, v in ipairs(sides) do
- if peripheral.getType(v) == "modem" then
- side = v
- break
- end
- end
- if side == "none" then
- return -1
- end
- m = peripheral.wrap(side)
- local t = {
- _channel = ch;
- _reply_channel = rep_ch;
- _side = s or "top";
- _modem = m;
- }
- setmetatable(t, socket_client)
- return t
- end
- function socket_client:connect(timeout)
- if not self._modem then return end
- self:write("request_connect")
- local timer_id = 0
- if timeout then timer_id = os.startTimer(timeout) end
- while true do
- local ev, p1, p2, p3, p4 = os.pullEvent()
- if timeout and ev == "timer" and p1 == timer_id then
- return false
- elseif ev == "modem_message" then
- if p4:find("success") ~= nil then
- return true
- end
- end
- end
- self._modem.closeAll()
- return false
- end
- function socket_client:listen()
- if not self._modem then return end
- self._modem.open(self._channel)
- self._modem.open(self._reply_channel)
- while true do
- local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
- if p2 == self._reply_channel then
- return p4
- end
- end
- self._modem.closeAll()
- return ""
- end
- function socket_client:read()
- return self:listen()
- end
- function socket_client:write(msg)
- if not self._modem then return end
- self._modem.open(self._channel)
- self._modem.transmit(self._channel, self._reply_channel, msg)
- self._modem.closeAll()
- end
- setmetatable(socket_client, socket_client)
- local text_utils = {}
- text_utils.last = function(str, tok)
- local pos = -1
- while str:find(tok, pos + 1) ~= nil do
- pos = str:find(tok, pos + 1)
- end
- return (pos < 0 and 0 or pos - 1)
- end
- function string:split(sep)
- local sep, fields = sep or ":", {}
- local pattern = string.format("([^%s]+)", sep)
- self:gsub(pattern, function(c) fields[#fields+1] = c end)
- return fields
- end
- local arg = {...}
- if arg[1] ~= nil and arg[1]:lower() == "help" then
- print("Usage:\nFTP_client [server channel] [client channel]")
- return
- end
- local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1]))
- local reply_ch = arg[2] == nil and 13 or (tonumber(arg[2]) == nil and 13 or tonumber(arg[2]))
- local socket = socket_client(ch, reply_ch)
- if type(socket) == "number" and socket == -1 then
- print("No modem found.")
- return
- end
- local remote_dir = "/"
- local local_dir = shell.getRunningProgram():sub(0, text_utils.last(shell.getRunningProgram(), "/"))
- local history = {}
- local function cmd_cd(c, str)
- socket:write("cmd:" .. str)
- local d = socket:read(1)
- if d == -1 then
- print("Error reading from server")
- else
- remote_dir = d
- end
- return socket:read(1)
- end
- local function cmd_ls(c, str)
- socket:write("cmd:" .. str)
- local l = socket:read(1)
- if l == -1 then
- print("Error reading from server.")
- else
- if l ~= "" then
- print(l)
- end
- end
- end
- local function cmd_lls(c, str)
- local p = c[2]
- if not fs.exists(local_dir .. (p and "/" .. p or "")) then
- return "no_such_file"
- end
- local l = fs.list(local_dir .. (p and "/" .. p or ""))
- local s = ""
- for k, v in ipairs(l) do
- s = s .. v .. "\n"
- end
- s = s:sub(1, #s - 1)
- print(s)
- end
- local function cmd_mkdir(c, str)
- socket:write("cmd:" .. str)
- local l = socket:read(1)
- if l == -1 then
- print("Error reading from server.")
- else
- return l
- end
- end
- local function cmd_lcd(c, str)
- local p = c[2]
- if p == ".." then
- local_dir = local_dir:sub(1, text_utils.last(local_dir, "/"))
- elseif p ~= "." then
- if fs.isDir(local_dir .. "/" .. p) then
- local_dir = local_dir .. "/" .. p
- else
- return "no_such_file"
- end
- end
- return local_dir .. "/"
- end
- local function cmd_delete(c, str)
- socket:write("cmd:" .. str)
- local l = socket:read(1)
- if l == -1 then
- print("Error reading from server.")
- else
- return l
- end
- end
- local function cmd_rmdir(c, str)
- socket:write("cmd:" .. str)
- local l = socket:read(1)
- if l == -1 then
- print("Error reading from server.")
- else
- return l
- end
- end
- local function cmd_put(c, str)
- local t = os.time()
- if not c[2] then
- return "wrong_args"
- end
- if fs.isDir(local_dir .. "/" .. c[2]) then
- return "custom_err Cannot upload directory. Use 'rput' or 'mput' instead."
- end
- if not fs.exists(local_dir .. "/" .. c[2]) then
- return "no_such_file"
- end
- socket:write("cmd:" .. str)
- local l = socket:read(1)
- if l == -1 then
- print("Error reading from server.")
- return "unknown_error"
- elseif l == "ready" then
- local f = io.open(local_dir .. "/" .. c[2], "r")
- local data = f:read("*a")
- f:close()
- socket:write(data)
- local res = socket:read(1)
- if res == -1 then
- return "unknown_error"
- else
- return (res == "" and "custom_err Wrote " .. #data .. " bytes in " .. (os.time() - t) * 0.0138 .. " seconds." or res)
- end
- else
- return l
- end
- end
- local function putfiles(dir, abs)
- local ret = cmd_mkdir({}, "mkdir " .. abs)
- if ret ~= "" then return ret end
- local l = fs.list(dir)
- for k, v in ipairs(l) do
- if fs.isDir(local_dir .. "/" .. dir .. "/" .. v) then
- putfiles(local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v)
- else
- local ret = cmd_put({"put", local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v}, "put asd " .. abs .. "/" .. v)
- if ret ~= "" then
- return ret
- end
- end
- end
- end
- local function cmd_rput(c, str)
- if not c[2] then
- return "wrong_args"
- end
- if not fs.exists(local_dir .. "/" .. c[2]) then
- return "no_such_file"
- end
- if not fs.isDir(local_dir .. "/" .. c[2]) then
- return "custom_err Given path is not a directory. Use 'put' instead."
- end
- ret = putfiles(local_dir .. "/" .. c[2], c[3] or c[2])
- return ret
- end
- local function cmd_get(c, str)
- if not c[2] then
- return "wrong_args"
- end
- if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then
- return "custom_err Cannot overwrite directory."
- end
- if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then
- io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
- local ans = io.read()
- if ans:lower() ~= "y" and ans:lower() ~= "yes" then
- return ""
- end
- end
- socket:write("cmd:" .. str)
- local ret = socket:read()
- if ret == "ok" then
- socket:write("ready")
- local data = socket:read()
- local f = io.open(local_dir .. "/" .. (c[3] or c[2]), "w")
- f:write(data)
- f:close()
- ret = socket:read()
- return ret
- else
- return ret
- end
- end
- local function cmd_rget(c, str)
- if not c[2] then
- return "wrong_args"
- end
- if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then
- return "custom_err Cannot overwrite directory."
- end
- if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then
- io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
- local ans = io.read()
- if ans:lower() ~= "y" and ans:lower() ~= "yes" then
- return ""
- end
- end
- socket:write("cmd:" .. str)
- local ret = socket:read()
- if ret == "ok" then
- socket:write("ready")
- local p = socket:read()
- while p ~= "__getfiles_done__" do
- if p:sub(1, 1) == "d" then
- fs.makeDir(local_dir .. "/" .. p:sub(2))
- else
- local data = socket:read()
- local f = io.open(local_dir .. "/" .. p:sub(2), "w")
- f:write(data)
- f:close()
- end
- p = socket:read()
- end
- ret = socket:read()
- return ret
- else
- return ret
- end
- end
- local help_text = {
- ["cd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on remote\nmachine.";
- ["lcd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on local\nmachine.";
- ["ls"] = "Usage:\nls [dir]\n\nLists files in current directory on remote\nmachine.\nIf [dir] is provided, files in that directory\nwill be listed instead.";
- ["lls"] = "Usage:\nls [dir]\n\nLists files in current directory on local\nmachine.\nIf [dir] is provided, files in that directory\nwill be listed instead.";
- ["quit"] = "Usage:\nquit|bye\n\nCloses the connection and exists the program.";
- ["pwd"] = "Usage:\npwd\n\nPrints the current directory on the remote\nmachine.";
- ["mkdir"] = "Usage:\nmkdir <dir>\n\nCreates the directory <dir> on the remote\nmachine.";
- ["delete"] = "Usage:\ndelete <file>\n\nDeletes the file <file> on the remote machine.\nCannot delete directories.";
- ["rmdir"] = "Usage:\nrmdir <dir>\n\nRemoves the directory <dir> on the remote\nmachine. Cannot delete files.";
- ["put"] = "Usage:\nput <local/path> [remote/path]\n\nUploads a file to the remote machine.\nIf [remote/path] is specified, it will upload\nto that filename, otherwise it will upload to\nas <local/path> on remote machine.\nCannot upload directories.";
- ["rput"] = "Usage:\nrput|mput <local/path> [remote/path]\n\nUploads a directory to the remote machine.\nIf [remote/path] is specified, it will upload\nto that filename, otherwise it will upload to\nas <local/path> on remote machine.\nCannot upload single files.";
- ["get"] = "Usage:\nget <remote/path> [local/path]\n\nDownloads a single file from <remote/path> on\nthe remote machine. If [local/path] is specified,\nit will save there, otherwise in <remote/path>.\nCannot download directories. Use 'rget' or 'mget'.";
- ["rget"] = "Usage:\nrget|mget <remote/path>\n\nDownloads the directory <remote/path> from the\nremote machine. The directory is saved as\n<remote/path>";
- }
- help_text["bye"] = help_text["quit"]
- help_text["mput"] = help_text["rput"]
- help_text["mget"] = help_text["rget"]
- local function cmd_help(c, str)
- if c[2] ~= nil then
- local text = help_text[c[2] ]
- if text == nil then
- return "custom_err No help available for '" .. c[2] .. "'"
- end
- print(text .. "\n")
- return ""
- end
- print("Help for FTP:\ncd <dir> - change directory on remote\nlcd <dir> - change directory locally\nls [dir] - list files on remote\nlls [dir] - list files locally\nquit/bye - close connection\npwd - print current remote dir\nmkdir <dir> - create directory on remote\ndelete <file> - remove file on remote\nrmdir <dir> - remove directory on remote\nput <file> [remote_file] - upload file\nrput/mput <dir> [remote_dir] - upload dir\nget <remote_file> [file] - download file\nhelp - show this screen\nhelp <cmd> - help for specific command\n")
- return ""
- end
- local function main()
- print("Connecting...")
- if socket:connect(2) == false then
- return false
- end
- while true do
- io.write(local_dir .. "> ")
- local cmd = read(nil, history)
- table.insert(history, cmd)
- local w = string.split(cmd, " ")
- local ret = ""
- if w[1] ~= nil then
- if w[1]:lower() == "cd" then
- ret = cmd_cd(w, cmd)
- elseif w[1]:lower() == "ls" then
- ret = cmd_ls(w, cmd)
- elseif w[1]:lower() == "lls" then
- ret = cmd_lls(w, cmd)
- elseif w[1]:lower() == "lcd" then
- ret = cmd_lcd(w, cmd)
- elseif w[1]:lower() == "quit" or w[1]:lower() == "bye" then
- socket:write("request_disconnect")
- return true
- elseif w[1]:lower() == "pwd" then
- print(remote_dir)
- elseif w[1]:lower() == "mkdir" then
- ret = cmd_mkdir(w, cmd)
- elseif w[1]:lower() == "delete" then
- ret = cmd_delete(w, cmd)
- elseif w[1]:lower() == "rmdir" then
- ret = cmd_rmdir(w, cmd)
- elseif w[1]:lower() == "put" then
- ret = cmd_put(w, cmd)
- elseif w[1]:lower() == "mput" or w[1]:lower() == "rput" then
- ret = cmd_rput(w, cmd)
- elseif w[1]:lower() == "get" then
- ret = cmd_get(w, cmd)
- elseif w[1]:lower() == "rget" or w[1]:lower() == "mget" then
- ret = cmd_rget(w, cmd)
- elseif w[1]:lower() == "help" or w[1] == "?" then
- ret = cmd_help(w, cmd)
- else
- ret = "unknown_command"
- end
- end
- if ret == "unknown_error" then
- print("Unknown error.")
- elseif ret == "unknown_command" then
- print("Unknown command. Try 'help'")
- elseif ret == "wrong_args" then
- print("Insufficient arguments. Try 'help'")
- elseif ret == "no_such_file" then
- print("No such file or directory.")
- elseif ret == "invalid_path" then
- print("Invalid path.")
- elseif ret == "already_exists" then
- print("File or directory already exists.")
- elseif ret == "not_allowed" then
- print("Insufficient permissions.")
- elseif type(ret) == "string" and ret:sub(0, string.len("custom_err")) == "custom_err" then
- print(ret:sub(string.len("custom_err") + 2))
- end
- end
- return true
- end
- term.clear()
- term.setCursorPos(1, 1)
- print("Welcome to FTP!\nFor a list of commands, run 'help'")
- if main() == true then
- term.clear()
- term.setCursorPos(1, 1)
- print("Thanks for using FTP!")
- else
- print("Unable to connect.")
- end
- ]]
- local ftp_server = [[
- local text_utils = {}
- text_utils.last = function(str, tok)
- local pos = -1
- while str:find(tok, pos + 1) ~= nil do
- pos = str:find(tok, pos + 1)
- end
- return (pos < 0 and 0 or pos - 1)
- end
- function string:split(sep)
- local sep, fields = sep or ":", {}
- local pattern = string.format("([^%s]+)", sep)
- self:gsub(pattern, function(c) fields[#fields+1] = c end)
- return fields
- end
- -- Socket server class
- local socket_server = {
- _modem = false;
- _channel = -1;
- _reply_channel = 0;
- _side = "top";
- _connected = false;
- }
- socket_server.__index = socket_server
- socket_server.__call = function(self, rep_ch)
- local sides = {
- "right", "left",
- "top", "bottom",
- "front", "back"
- }
- local m = nil
- local side = "none"
- for k, v in ipairs(sides) do
- if peripheral.getType(v) == "modem" then
- side = v
- break
- end
- end
- if side == "none" then
- return -1
- end
- m = peripheral.wrap(side)
- local t = {
- _side = s or "top";
- _reply_channel = rep_ch;
- _modem = m;
- }
- setmetatable(t, socket_server)
- return t
- end
- function socket_server:accept(ch, timeout)
- if self._connected then return end
- if not self._modem then return end
- self._modem.open(ch)
- local timer_id = 0
- if timeout then timer_id = os.startTimer(timeout) end
- while true do
- local ev, p1, p2, p3, p4 = os.pullEvent()
- if timeout and ev == "timer" and p1 == timer_id then
- return false
- elseif ev == "modem_message" then
- if p4:find("request_connect") ~= nil then
- print("Client connected on channel " .. p2)
- self._channel = p2
- self._connected = true
- self._modem.transmit(self._channel, self._reply_channel, "success")
- return true
- end
- end
- end
- self._modem.closeAll()
- return false
- end
- function socket_server:accept_any(timeout)
- if self._connected then return end
- if not self._modem then return end
- for i = 0, 1000 do
- self._modem.open(i)
- end
- local timer_id = 0
- if timeout then timer_id = os.startTimer(timeout) end
- while true do
- local ev, p1, p2, p3, p4 = os.pullEvent()
- if timeout and ev == "timer" and p1 == timer_id then
- return false
- elseif ev == "modem_message" then
- if p4:find("request_connect") ~= nil then
- print("Client connected on channel " .. p3)
- self._channel = p3
- self._connected = true
- self._modem.transmit(self._channel, self._reply_channel, "success")
- return true
- end
- end
- end
- self._modem.closeAll()
- return false
- end
- function socket_server:listen()
- if not self._modem then return end
- while true do
- if self._channel ~= -1 then
- self._modem.open(self._channel)
- self._modem.open(self._reply_channel)
- local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
- if p2 == self._reply_channel then
- return p4
- end
- self._modem.closeAll()
- end
- end
- return ""
- end
- function socket_server:read()
- local r = self:listen()
- if r == "request_disconnect" then
- print("Client on channel ".. self._channel .. " disconnected.")
- self._connected = false
- end
- return r
- end
- function socket_server:write(msg)
- if not self._modem then return end
- self._modem.open(self._channel)
- self._modem.transmit(self._channel, self._reply_channel, msg)
- self._modem.closeAll()
- end
- setmetatable(socket_server, socket_server)
- local arg = {...}
- _print = function(...)
- if not bg then print(...) end
- end
- if arg[2] == nil and arg[1] ~= nil and (arg[1]:lower() == "-b" or arg[1]:lower() == "--background") then
- arg[3] = arg[1]
- end
- if arg[1] ~= nil and arg[1]:lower() == "help" then
- _print("Usage:\nFTP_server [channel] [options]\nIf the option -b is provided, the server will run in the background")
- return
- end
- local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1]))
- local server = socket_server(ch)
- if type(server) == "number" and server == -1 then
- _print("No modem found.")
- return
- end
- local dir = ""
- local bg = false
- local function cd(p)
- if p == ".." then
- dir = dir:sub(1, text_utils.last(dir, "/"))
- elseif p ~= "." then
- if fs.isDir(dir .. "/" .. p) then
- dir = dir .. "/" .. p
- else
- server:write(dir .. "/")
- return "no_such_file"
- end
- end
- server:write(dir .. "/")
- return dir .. "/"
- end
- local function ls(p)
- if not fs.exists(dir .. (p and "/" .. p or "")) then
- server:write("")
- return "no_such_file"
- end
- local l = fs.list(dir .. (p and "/" .. p or ""))
- local s = ""
- for k, v in ipairs(l) do
- s = s .. v .. "\n"
- end
- s = s:sub(1, #s - 1)
- server:write(s)
- return ""
- end
- local function mkdir(p)
- if fs.exists(dir .. "/" .. p) then
- return "already_exists"
- end
- fs.makeDir(dir .. "/" .. p)
- return ""
- end
- local function delete(p)
- if not fs.exists(dir .. "/" .. p) then
- return "no_such_file"
- elseif fs.isReadOnly(dir .. "/" .. p) then
- return "not_allowed"
- elseif fs.isDir(dir .. "/" .. p) then
- return "custom_err Cannot remove a directory. Use 'rmdir' instead."
- end
- fs.delete(dir .. "/" .. p)
- return ""
- end
- local function rmdir(p)
- if not fs.exists(dir .. "/" .. p) then
- return "no_such_file"
- elseif fs.isReadOnly(dir .. "/" .. p) then
- return "not_allowed"
- elseif not fs.isDir(dir .. "/" .. p) then
- return "custom_err Cannot remove a file. Use 'delete' instead."
- end
- fs.delete(dir .. "/" .. p)
- return ""
- end
- local function put(p)
- if fs.isDir(dir .. "/" .. p) then
- return "custom_err Cannot overwrite directory."
- end
- server:write("ready")
- local data = server:read()
- local f = io.open(dir .. "/" .. p, "w")
- f:write(data)
- f:close()
- return ""
- end
- local function get(p)
- if not fs.exists(dir .. "/" .. p) then
- return "no_such_file"
- end
- if fs.isDir(dir .. "/" .. p) then
- return "custom_err Cannot download directory. Use 'rget' or 'mget' instead."
- end
- server:write("ok")
- local ret = server:read()
- if ret ~= "ready" then
- return "unknown_err"
- end
- local f = io.open(dir .. "/" .. p, "r")
- local data = f:read("*a")
- f:close()
- server:write(data)
- return ""
- end
- local function rget(p)
- if not fs.exists(dir .. "/" .. p) then
- return "no_such_file"
- end
- if not fs.isDir(dir .. "/" .. p) then
- return "custom_err Cannot download single files.\nUse 'get' instead."
- end
- server:write("ok")
- local function getfiles(path)
- server:write("d" .. path)
- local l = fs.list(dir .. "/" .. path)
- for k, v in ipairs(l) do
- if fs.isDir(dir .. "/" .. path .. "/" .. v) then
- getfiles(dir .. "/" .. path .. "/" .. v)
- else
- server:write("f" .. path .. "/" .. v)
- local ret = server:read()
- if ret ~= "ready" then
- return ret
- end
- local f = io.open(dir .. "/" .. path .. "/" .. v, "r")
- local data = f:read("*a")
- f:close()
- server:write(data)
- end
- end
- end
- getfiles(p)
- server:write("__getfiles_done__")
- end
- local function main()
- server:accept_any()
- while true do
- if not server._connected then
- server:accept_any()
- end
- local msg = server:read()
- if msg:sub(1, 4) == "cmd:" then
- local cmd = msg:sub(5)
- _print("Command: " .. cmd)
- cmd = string.split(cmd, " ")
- if cmd[1]:lower() == "cd" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = cd(cmd[2])
- server:write(ret)
- end
- elseif cmd[1]:lower() == "ls" then
- local ret = ls(cmd[2] or "")
- server:write(ret)
- elseif cmd[1]:lower() == "mkdir" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = mkdir(cmd[2])
- server:write(ret)
- end
- elseif cmd[1]:lower() == "delete" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = delete(cmd[2])
- server:write(ret)
- end
- elseif cmd[1]:lower() == "rmdir" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = rmdir(cmd[2])
- server:write(ret)
- end
- elseif cmd[1]:lower() == "put" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = put((cmd[3] ~= nil and cmd[3] or cmd[2]))
- server:write(ret)
- end
- elseif cmd[1]:lower() == "get" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = get(cmd[2])
- server:write(ret)
- end
- elseif cmd[1]:lower() == "rget" or cmd[1]:lower() == "mget" then
- if not cmd[2] then
- server:write("wrong_args")
- else
- local ret = rget((cmd[3] ~= nil and cmd[3] or cmd[2]))
- server:write(ret)
- end
- else
- server:write("unknown_command")
- end
- end
- end
- return ""
- end
- if arg[3] ~= nil and (arg[3]:lower() == "-b" or arg[3] == "--background") then
- bg = true
- term.clear()
- term.setCursorPos(1, 1)
- parallel.waitForAll(main, function() shell.run("shell") end)
- else
- main()
- end
- ]]
- local arg = {...}
- if not arg[1] then print("Usage:\ninstall server|client") else
- if arg[1]:lower() == "c" or arg[1]:lower() == "client" then
- local f = io.open("ftp_client", "w")
- f:write(ftp_client)
- f:close()
- print("Installed to 'ftp_client'.\nRun 'ftp_client help' for information about using it.")
- elseif arg[1]:lower() == "s" or arg[1]:lower() == "server" then
- local f = io.open("ftp_server", "w")
- f:write(ftp_server)
- f:close()
- print("Installed to 'ftp_server'.\nRun 'ftp_server' help' for information about using it.")
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment