SHOW:
|
|
- or go back to the newest paste.
| 1 | local ftp_client = [[ | |
| 2 | -- socket_client class(client) | |
| 3 | local socket_client = {
| |
| 4 | _modem = false; | |
| 5 | _channel = 0; | |
| 6 | _reply_channel = 0; | |
| 7 | _side = "top"; | |
| 8 | _stack = {};
| |
| 9 | } | |
| 10 | socket_client.__index = socket_client | |
| 11 | ||
| 12 | socket_client.__call = function(self, ch, rep_ch) | |
| 13 | local sides = {
| |
| 14 | "right", "left", | |
| 15 | "top", "bottom", | |
| 16 | "front", "back" | |
| 17 | } | |
| 18 | ||
| 19 | local m = nil | |
| 20 | local side = "none" | |
| 21 | for k, v in ipairs(sides) do | |
| 22 | if peripheral.getType(v) == "modem" then | |
| 23 | side = v | |
| 24 | break | |
| 25 | end | |
| 26 | end | |
| 27 | ||
| 28 | if side == "none" then | |
| 29 | return -1 | |
| 30 | end | |
| 31 | ||
| 32 | m = peripheral.wrap(side) | |
| 33 | ||
| 34 | local t = {
| |
| 35 | _channel = ch; | |
| 36 | _reply_channel = rep_ch; | |
| 37 | _side = s or "top"; | |
| 38 | _modem = m; | |
| 39 | } | |
| 40 | setmetatable(t, socket_client) | |
| 41 | ||
| 42 | return t | |
| 43 | end | |
| 44 | ||
| 45 | function socket_client:connect(timeout) | |
| 46 | if not self._modem then return end | |
| 47 | ||
| 48 | self:write("request_connect")
| |
| 49 | local timer_id = 0 | |
| 50 | if timeout then timer_id = os.startTimer(timeout) end | |
| 51 | ||
| 52 | while true do | |
| 53 | local ev, p1, p2, p3, p4 = os.pullEvent() | |
| 54 | ||
| 55 | if timeout and ev == "timer" and p1 == timer_id then | |
| 56 | return false | |
| 57 | elseif ev == "modem_message" then | |
| 58 | if p4:find("success") ~= nil then
| |
| 59 | return true | |
| 60 | end | |
| 61 | end | |
| 62 | end | |
| 63 | ||
| 64 | self._modem.closeAll() | |
| 65 | ||
| 66 | return false | |
| 67 | end | |
| 68 | ||
| 69 | function socket_client:listen() | |
| 70 | if not self._modem then return end | |
| 71 | ||
| 72 | self._modem.open(self._channel) | |
| 73 | self._modem.open(self._reply_channel) | |
| 74 | ||
| 75 | while true do | |
| 76 | local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
| |
| 77 | if p2 == self._reply_channel then | |
| 78 | return p4 | |
| 79 | end | |
| 80 | end | |
| 81 | ||
| 82 | self._modem.closeAll() | |
| 83 | ||
| 84 | return "" | |
| 85 | end | |
| 86 | ||
| 87 | function socket_client:read() | |
| 88 | return self:listen() | |
| 89 | end | |
| 90 | ||
| 91 | function socket_client:write(msg) | |
| 92 | if not self._modem then return end | |
| 93 | ||
| 94 | self._modem.open(self._channel) | |
| 95 | self._modem.transmit(self._channel, self._reply_channel, msg) | |
| 96 | self._modem.closeAll() | |
| 97 | end | |
| 98 | setmetatable(socket_client, socket_client) | |
| 99 | ||
| 100 | local text_utils = {}
| |
| 101 | text_utils.last = function(str, tok) | |
| 102 | local pos = -1 | |
| 103 | ||
| 104 | while str:find(tok, pos + 1) ~= nil do | |
| 105 | pos = str:find(tok, pos + 1) | |
| 106 | end | |
| 107 | ||
| 108 | return (pos < 0 and 0 or pos - 1) | |
| 109 | end | |
| 110 | ||
| 111 | function string:split(sep) | |
| 112 | local sep, fields = sep or ":", {}
| |
| 113 | local pattern = string.format("([^%s]+)", sep)
| |
| 114 | self:gsub(pattern, function(c) fields[#fields+1] = c end) | |
| 115 | return fields | |
| 116 | end | |
| 117 | ||
| 118 | local arg = {...}
| |
| 119 | ||
| 120 | if arg[1] ~= nil and arg[1]:lower() == "help" then | |
| 121 | print("Usage:\nFTP_client [server channel] [client channel]")
| |
| 122 | return | |
| 123 | end | |
| 124 | ||
| 125 | local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1])) | |
| 126 | local reply_ch = arg[2] == nil and 13 or (tonumber(arg[2]) == nil and 13 or tonumber(arg[2])) | |
| 127 | local socket = socket_client(ch, reply_ch) | |
| 128 | ||
| 129 | if type(socket) == "number" and socket == -1 then | |
| 130 | print("No modem found.")
| |
| 131 | return | |
| 132 | end | |
| 133 | ||
| 134 | local remote_dir = "/" | |
| 135 | local local_dir = shell.getRunningProgram():sub(0, text_utils.last(shell.getRunningProgram(), "/")) | |
| 136 | local history = {}
| |
| 137 | ||
| 138 | local function cmd_cd(c, str) | |
| 139 | socket:write("cmd:" .. str)
| |
| 140 | local d = socket:read(1) | |
| 141 | if d == -1 then | |
| 142 | print("Error reading from server")
| |
| 143 | else | |
| 144 | remote_dir = d | |
| 145 | end | |
| 146 | ||
| 147 | return socket:read(1) | |
| 148 | end | |
| 149 | ||
| 150 | local function cmd_ls(c, str) | |
| 151 | socket:write("cmd:" .. str)
| |
| 152 | local l = socket:read(1) | |
| 153 | if l == -1 then | |
| 154 | print("Error reading from server.")
| |
| 155 | else | |
| 156 | if l ~= "" then | |
| 157 | print(l) | |
| 158 | end | |
| 159 | end | |
| 160 | end | |
| 161 | ||
| 162 | local function cmd_lls(c, str) | |
| 163 | local p = c[2] | |
| 164 | if not fs.exists(local_dir .. (p and "/" .. p or "")) then | |
| 165 | return "no_such_file" | |
| 166 | end | |
| 167 | ||
| 168 | local l = fs.list(local_dir .. (p and "/" .. p or "")) | |
| 169 | local s = "" | |
| 170 | for k, v in ipairs(l) do | |
| 171 | s = s .. v .. "\n" | |
| 172 | end | |
| 173 | s = s:sub(1, #s - 1) | |
| 174 | print(s) | |
| 175 | end | |
| 176 | ||
| 177 | local function cmd_mkdir(c, str) | |
| 178 | socket:write("cmd:" .. str)
| |
| 179 | local l = socket:read(1) | |
| 180 | if l == -1 then | |
| 181 | print("Error reading from server.")
| |
| 182 | else | |
| 183 | return l | |
| 184 | end | |
| 185 | end | |
| 186 | ||
| 187 | local function cmd_lcd(c, str) | |
| 188 | local p = c[2] | |
| 189 | if p == ".." then | |
| 190 | local_dir = local_dir:sub(1, text_utils.last(local_dir, "/")) | |
| 191 | elseif p ~= "." then | |
| 192 | if fs.isDir(local_dir .. "/" .. p) then | |
| 193 | local_dir = local_dir .. "/" .. p | |
| 194 | else | |
| 195 | return "no_such_file" | |
| 196 | end | |
| 197 | end | |
| 198 | ||
| 199 | return local_dir .. "/" | |
| 200 | end | |
| 201 | ||
| 202 | local function cmd_delete(c, str) | |
| 203 | socket:write("cmd:" .. str)
| |
| 204 | local l = socket:read(1) | |
| 205 | if l == -1 then | |
| 206 | print("Error reading from server.")
| |
| 207 | else | |
| 208 | return l | |
| 209 | end | |
| 210 | end | |
| 211 | ||
| 212 | local function cmd_rmdir(c, str) | |
| 213 | socket:write("cmd:" .. str)
| |
| 214 | local l = socket:read(1) | |
| 215 | if l == -1 then | |
| 216 | print("Error reading from server.")
| |
| 217 | else | |
| 218 | return l | |
| 219 | end | |
| 220 | end | |
| 221 | ||
| 222 | local function cmd_put(c, str) | |
| 223 | local t = os.time() | |
| 224 | if not c[2] then | |
| 225 | return "wrong_args" | |
| 226 | end | |
| 227 | ||
| 228 | if fs.isDir(local_dir .. "/" .. c[2]) then | |
| 229 | return "custom_err Cannot upload directory. Use 'rput' or 'mput' instead." | |
| 230 | end | |
| 231 | ||
| 232 | if not fs.exists(local_dir .. "/" .. c[2]) then | |
| 233 | return "no_such_file" | |
| 234 | end | |
| 235 | ||
| 236 | socket:write("cmd:" .. str)
| |
| 237 | local l = socket:read(1) | |
| 238 | if l == -1 then | |
| 239 | print("Error reading from server.")
| |
| 240 | return "unknown_error" | |
| 241 | elseif l == "ready" then | |
| 242 | local f = io.open(local_dir .. "/" .. c[2], "r") | |
| 243 | local data = f:read("*a")
| |
| 244 | f:close() | |
| 245 | ||
| 246 | socket:write(data) | |
| 247 | ||
| 248 | local res = socket:read(1) | |
| 249 | if res == -1 then | |
| 250 | return "unknown_error" | |
| 251 | else | |
| 252 | return (res == "" and "custom_err Wrote " .. #data .. " bytes in " .. (os.time() - t) * 0.0138 .. " seconds." or res) | |
| 253 | end | |
| 254 | else | |
| 255 | return l | |
| 256 | end | |
| 257 | end | |
| 258 | ||
| 259 | local function putfiles(dir, abs) | |
| 260 | local ret = cmd_mkdir({}, "mkdir " .. abs)
| |
| 261 | if ret ~= "" then return ret end | |
| 262 | ||
| 263 | local l = fs.list(dir) | |
| 264 | ||
| 265 | for k, v in ipairs(l) do | |
| 266 | if fs.isDir(local_dir .. "/" .. dir .. "/" .. v) then | |
| 267 | putfiles(local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v) | |
| 268 | else | |
| 269 | local ret = cmd_put({"put", local_dir .. "/" .. dir .. "/" .. v, abs .. "/" .. v}, "put asd " .. abs .. "/" .. v)
| |
| 270 | if ret ~= "" then | |
| 271 | return ret | |
| 272 | end | |
| 273 | end | |
| 274 | end | |
| 275 | end | |
| 276 | ||
| 277 | local function cmd_rput(c, str) | |
| 278 | if not c[2] then | |
| 279 | return "wrong_args" | |
| 280 | end | |
| 281 | ||
| 282 | if not fs.exists(local_dir .. "/" .. c[2]) then | |
| 283 | return "no_such_file" | |
| 284 | end | |
| 285 | ||
| 286 | if not fs.isDir(local_dir .. "/" .. c[2]) then | |
| 287 | return "custom_err Given path is not a directory. Use 'put' instead." | |
| 288 | end | |
| 289 | ||
| 290 | ret = putfiles(local_dir .. "/" .. c[2], c[3] or c[2]) | |
| 291 | return ret | |
| 292 | end | |
| 293 | ||
| 294 | local function cmd_get(c, str) | |
| 295 | if not c[2] then | |
| 296 | return "wrong_args" | |
| 297 | end | |
| 298 | ||
| 299 | if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then | |
| 300 | return "custom_err Cannot overwrite directory." | |
| 301 | end | |
| 302 | ||
| 303 | if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then | |
| 304 | io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
| |
| 305 | local ans = io.read() | |
| 306 | ||
| 307 | if ans:lower() ~= "y" and ans:lower() ~= "yes" then | |
| 308 | return "" | |
| 309 | end | |
| 310 | end | |
| 311 | ||
| 312 | socket:write("cmd:" .. str)
| |
| 313 | ||
| 314 | local ret = socket:read() | |
| 315 | if ret == "ok" then | |
| 316 | socket:write("ready")
| |
| 317 | local data = socket:read() | |
| 318 | ||
| 319 | local f = io.open(local_dir .. "/" .. (c[3] or c[2]), "w") | |
| 320 | f:write(data) | |
| 321 | f:close() | |
| 322 | ||
| 323 | ret = socket:read() | |
| 324 | return ret | |
| 325 | else | |
| 326 | return ret | |
| 327 | end | |
| 328 | end | |
| 329 | ||
| 330 | local function cmd_rget(c, str) | |
| 331 | if not c[2] then | |
| 332 | return "wrong_args" | |
| 333 | end | |
| 334 | ||
| 335 | if fs.isDir(local_dir .. "/" .. (c[3] or c[2])) then | |
| 336 | return "custom_err Cannot overwrite directory." | |
| 337 | end | |
| 338 | ||
| 339 | if fs.exists(local_dir .. "/" .. (c[3] or c[2])) then | |
| 340 | io.write("Overwrite file '" .. (c[3] or c[2]) .. "? (y/n) ")
| |
| 341 | local ans = io.read() | |
| 342 | ||
| 343 | if ans:lower() ~= "y" and ans:lower() ~= "yes" then | |
| 344 | return "" | |
| 345 | end | |
| 346 | end | |
| 347 | ||
| 348 | socket:write("cmd:" .. str)
| |
| 349 | ||
| 350 | local ret = socket:read() | |
| 351 | ||
| 352 | if ret == "ok" then | |
| 353 | socket:write("ready")
| |
| 354 | local p = socket:read() | |
| 355 | while p ~= "__getfiles_done__" do | |
| 356 | if p:sub(1, 1) == "d" then | |
| 357 | fs.makeDir(local_dir .. "/" .. p:sub(2)) | |
| 358 | else | |
| 359 | local data = socket:read() | |
| 360 | local f = io.open(local_dir .. "/" .. p:sub(2), "w") | |
| 361 | f:write(data) | |
| 362 | f:close() | |
| 363 | end | |
| 364 | ||
| 365 | p = socket:read() | |
| 366 | end | |
| 367 | ||
| 368 | ret = socket:read() | |
| 369 | ||
| 370 | return ret | |
| 371 | else | |
| 372 | return ret | |
| 373 | end | |
| 374 | end | |
| 375 | ||
| 376 | local help_text = {
| |
| 377 | ["cd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on remote\nmachine."; | |
| 378 | ||
| 379 | ["lcd"] = "Usage:\ncd <dir>\n\nChanges directory to <dir> on local\nmachine."; | |
| 380 | ||
| 381 | ["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."; | |
| 382 | ||
| 383 | ["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."; | |
| 384 | ||
| 385 | ["quit"] = "Usage:\nquit|bye\n\nCloses the connection and exists the program."; | |
| 386 | ||
| 387 | ["pwd"] = "Usage:\npwd\n\nPrints the current directory on the remote\nmachine."; | |
| 388 | ||
| 389 | ["mkdir"] = "Usage:\nmkdir <dir>\n\nCreates the directory <dir> on the remote\nmachine."; | |
| 390 | ||
| 391 | ["delete"] = "Usage:\ndelete <file>\n\nDeletes the file <file> on the remote machine.\nCannot delete directories."; | |
| 392 | ||
| 393 | ["rmdir"] = "Usage:\nrmdir <dir>\n\nRemoves the directory <dir> on the remote\nmachine. Cannot delete files."; | |
| 394 | ||
| 395 | ["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."; | |
| 396 | ||
| 397 | ["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."; | |
| 398 | ||
| 399 | ["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'."; | |
| 400 | ||
| 401 | ["rget"] = "Usage:\nrget|mget <remote/path>\n\nDownloads the directory <remote/path> from the\nremote machine. The directory is saved as\n<remote/path>"; | |
| 402 | } | |
| 403 | help_text["bye"] = help_text["quit"] | |
| 404 | help_text["mput"] = help_text["rput"] | |
| 405 | help_text["mget"] = help_text["rget"] | |
| 406 | ||
| 407 | local function cmd_help(c, str) | |
| 408 | if c[2] ~= nil then | |
| 409 | local text = help_text[c[2] ] | |
| 410 | if text == nil then | |
| 411 | return "custom_err No help available for '" .. c[2] .. "'" | |
| 412 | end | |
| 413 | ||
| 414 | print(text .. "\n") | |
| 415 | ||
| 416 | return "" | |
| 417 | end | |
| 418 | ||
| 419 | 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")
| |
| 420 | return "" | |
| 421 | end | |
| 422 | ||
| 423 | local function main() | |
| 424 | print("Connecting...")
| |
| 425 | if socket:connect(2) == false then | |
| 426 | return false | |
| 427 | end | |
| 428 | ||
| 429 | while true do | |
| 430 | io.write(local_dir .. "> ") | |
| 431 | local cmd = read(nil, history) | |
| 432 | table.insert(history, cmd) | |
| 433 | ||
| 434 | local w = string.split(cmd, " ") | |
| 435 | ||
| 436 | ||
| 437 | local ret = "" | |
| 438 | ||
| 439 | if w[1] ~= nil then | |
| 440 | if w[1]:lower() == "cd" then | |
| 441 | ret = cmd_cd(w, cmd) | |
| 442 | elseif w[1]:lower() == "ls" then | |
| 443 | ret = cmd_ls(w, cmd) | |
| 444 | elseif w[1]:lower() == "lls" then | |
| 445 | ret = cmd_lls(w, cmd) | |
| 446 | elseif w[1]:lower() == "lcd" then | |
| 447 | ret = cmd_lcd(w, cmd) | |
| 448 | elseif w[1]:lower() == "quit" or w[1]:lower() == "bye" then | |
| 449 | socket:write("request_disconnect")
| |
| 450 | return true | |
| 451 | elseif w[1]:lower() == "pwd" then | |
| 452 | print(remote_dir) | |
| 453 | elseif w[1]:lower() == "mkdir" then | |
| 454 | ret = cmd_mkdir(w, cmd) | |
| 455 | elseif w[1]:lower() == "delete" then | |
| 456 | ret = cmd_delete(w, cmd) | |
| 457 | elseif w[1]:lower() == "rmdir" then | |
| 458 | ret = cmd_rmdir(w, cmd) | |
| 459 | elseif w[1]:lower() == "put" then | |
| 460 | ret = cmd_put(w, cmd) | |
| 461 | elseif w[1]:lower() == "mput" or w[1]:lower() == "rput" then | |
| 462 | ret = cmd_rput(w, cmd) | |
| 463 | elseif w[1]:lower() == "get" then | |
| 464 | ret = cmd_get(w, cmd) | |
| 465 | elseif w[1]:lower() == "rget" or w[1]:lower() == "mget" then | |
| 466 | ret = cmd_rget(w, cmd) | |
| 467 | elseif w[1]:lower() == "help" or w[1] == "?" then | |
| 468 | ret = cmd_help(w, cmd) | |
| 469 | else | |
| 470 | ret = "unknown_command" | |
| 471 | end | |
| 472 | end | |
| 473 | ||
| 474 | if ret == "unknown_error" then | |
| 475 | print("Unknown error.")
| |
| 476 | elseif ret == "unknown_command" then | |
| 477 | print("Unknown command. Try 'help'")
| |
| 478 | elseif ret == "wrong_args" then | |
| 479 | print("Insufficient arguments. Try 'help'")
| |
| 480 | elseif ret == "no_such_file" then | |
| 481 | print("No such file or directory.")
| |
| 482 | elseif ret == "invalid_path" then | |
| 483 | print("Invalid path.")
| |
| 484 | elseif ret == "already_exists" then | |
| 485 | print("File or directory already exists.")
| |
| 486 | elseif ret == "not_allowed" then | |
| 487 | print("Insufficient permissions.")
| |
| 488 | elseif type(ret) == "string" and ret:sub(0, string.len("custom_err")) == "custom_err" then
| |
| 489 | print(ret:sub(string.len("custom_err") + 2))
| |
| 490 | end | |
| 491 | end | |
| 492 | ||
| 493 | return true | |
| 494 | end | |
| 495 | ||
| 496 | term.clear() | |
| 497 | term.setCursorPos(1, 1) | |
| 498 | print("Welcome to FTP!\nFor a list of commands, run 'help'")
| |
| 499 | if main() == true then | |
| 500 | term.clear() | |
| 501 | term.setCursorPos(1, 1) | |
| 502 | print("Thanks for using FTP!")
| |
| 503 | else | |
| 504 | print("Unable to connect.")
| |
| 505 | end | |
| 506 | ]] | |
| 507 | ||
| 508 | local ftp_server = [[ | |
| 509 | local text_utils = {}
| |
| 510 | text_utils.last = function(str, tok) | |
| 511 | local pos = -1 | |
| 512 | ||
| 513 | while str:find(tok, pos + 1) ~= nil do | |
| 514 | pos = str:find(tok, pos + 1) | |
| 515 | end | |
| 516 | ||
| 517 | return (pos < 0 and 0 or pos - 1) | |
| 518 | end | |
| 519 | ||
| 520 | function string:split(sep) | |
| 521 | local sep, fields = sep or ":", {}
| |
| 522 | local pattern = string.format("([^%s]+)", sep)
| |
| 523 | self:gsub(pattern, function(c) fields[#fields+1] = c end) | |
| 524 | return fields | |
| 525 | end | |
| 526 | ||
| 527 | -- Socket server class | |
| 528 | local socket_server = {
| |
| 529 | _modem = false; | |
| 530 | _channel = -1; | |
| 531 | _reply_channel = 0; | |
| 532 | _side = "top"; | |
| 533 | _connected = false; | |
| 534 | } | |
| 535 | socket_server.__index = socket_server | |
| 536 | ||
| 537 | socket_server.__call = function(self, rep_ch) | |
| 538 | local sides = {
| |
| 539 | "right", "left", | |
| 540 | "top", "bottom", | |
| 541 | "front", "back" | |
| 542 | } | |
| 543 | ||
| 544 | local m = nil | |
| 545 | local side = "none" | |
| 546 | for k, v in ipairs(sides) do | |
| 547 | if peripheral.getType(v) == "modem" then | |
| 548 | side = v | |
| 549 | break | |
| 550 | end | |
| 551 | end | |
| 552 | ||
| 553 | if side == "none" then | |
| 554 | return -1 | |
| 555 | end | |
| 556 | ||
| 557 | m = peripheral.wrap(side) | |
| 558 | ||
| 559 | local t = {
| |
| 560 | _side = s or "top"; | |
| 561 | _reply_channel = rep_ch; | |
| 562 | _modem = m; | |
| 563 | } | |
| 564 | setmetatable(t, socket_server) | |
| 565 | ||
| 566 | return t | |
| 567 | end | |
| 568 | ||
| 569 | function socket_server:accept(ch, timeout) | |
| 570 | if self._connected then return end | |
| 571 | if not self._modem then return end | |
| 572 | ||
| 573 | self._modem.open(ch) | |
| 574 | ||
| 575 | local timer_id = 0 | |
| 576 | if timeout then timer_id = os.startTimer(timeout) end | |
| 577 | ||
| 578 | while true do | |
| 579 | local ev, p1, p2, p3, p4 = os.pullEvent() | |
| 580 | ||
| 581 | if timeout and ev == "timer" and p1 == timer_id then | |
| 582 | return false | |
| 583 | elseif ev == "modem_message" then | |
| 584 | if p4:find("request_connect") ~= nil then
| |
| 585 | print("Client connected on channel " .. p2)
| |
| 586 | self._channel = p2 | |
| 587 | self._connected = true | |
| 588 | self._modem.transmit(self._channel, self._reply_channel, "success") | |
| 589 | return true | |
| 590 | end | |
| 591 | end | |
| 592 | end | |
| 593 | ||
| 594 | self._modem.closeAll() | |
| 595 | ||
| 596 | return false | |
| 597 | end | |
| 598 | ||
| 599 | function socket_server:accept_any(timeout) | |
| 600 | if self._connected then return end | |
| 601 | if not self._modem then return end | |
| 602 | ||
| 603 | for i = 0, 1000 do | |
| 604 | self._modem.open(i) | |
| 605 | end | |
| 606 | ||
| 607 | local timer_id = 0 | |
| 608 | if timeout then timer_id = os.startTimer(timeout) end | |
| 609 | ||
| 610 | while true do | |
| 611 | local ev, p1, p2, p3, p4 = os.pullEvent() | |
| 612 | ||
| 613 | if timeout and ev == "timer" and p1 == timer_id then | |
| 614 | return false | |
| 615 | elseif ev == "modem_message" then | |
| 616 | if p4:find("request_connect") ~= nil then
| |
| 617 | print("Client connected on channel " .. p3)
| |
| 618 | self._channel = p3 | |
| 619 | self._connected = true | |
| 620 | self._modem.transmit(self._channel, self._reply_channel, "success") | |
| 621 | return true | |
| 622 | end | |
| 623 | end | |
| 624 | end | |
| 625 | ||
| 626 | self._modem.closeAll() | |
| 627 | ||
| 628 | return false | |
| 629 | end | |
| 630 | ||
| 631 | function socket_server:listen() | |
| 632 | if not self._modem then return end | |
| 633 | ||
| 634 | while true do | |
| 635 | if self._channel ~= -1 then | |
| 636 | self._modem.open(self._channel) | |
| 637 | self._modem.open(self._reply_channel) | |
| 638 | ||
| 639 | local ev, p1, p2, p3, p4 = os.pullEvent("modem_message")
| |
| 640 | if p2 == self._reply_channel then | |
| 641 | return p4 | |
| 642 | end | |
| 643 | ||
| 644 | self._modem.closeAll() | |
| 645 | end | |
| 646 | end | |
| 647 | ||
| 648 | return "" | |
| 649 | end | |
| 650 | ||
| 651 | function socket_server:read() | |
| 652 | local r = self:listen() | |
| 653 | if r == "request_disconnect" then | |
| 654 | print("Client on channel ".. self._channel .. " disconnected.")
| |
| 655 | self._connected = false | |
| 656 | end | |
| 657 | return r | |
| 658 | end | |
| 659 | ||
| 660 | function socket_server:write(msg) | |
| 661 | if not self._modem then return end | |
| 662 | ||
| 663 | self._modem.open(self._channel) | |
| 664 | self._modem.transmit(self._channel, self._reply_channel, msg) | |
| 665 | self._modem.closeAll() | |
| 666 | end | |
| 667 | setmetatable(socket_server, socket_server) | |
| 668 | ||
| 669 | local arg = {...}
| |
| 670 | ||
| 671 | _print = function(...) | |
| 672 | if not bg then print(...) end | |
| 673 | end | |
| 674 | ||
| 675 | if arg[2] == nil and arg[1] ~= nil and (arg[1]:lower() == "-b" or arg[1]:lower() == "--background") then | |
| 676 | arg[3] = arg[1] | |
| 677 | end | |
| 678 | ||
| 679 | if arg[1] ~= nil and arg[1]:lower() == "help" then | |
| 680 | _print("Usage:\nFTP_server [channel] [options]\nIf the option -b is provided, the server will run in the background")
| |
| 681 | return | |
| 682 | end | |
| 683 | ||
| 684 | local ch = arg[1] == nil and 12 or (tonumber(arg[1]) == nil and 12 or tonumber(arg[1])) | |
| 685 | ||
| 686 | local server = socket_server(ch) | |
| 687 | ||
| 688 | if type(server) == "number" and server == -1 then | |
| 689 | _print("No modem found.")
| |
| 690 | return | |
| 691 | end | |
| 692 | ||
| 693 | local dir = "" | |
| 694 | local bg = false | |
| 695 | ||
| 696 | local function cd(p) | |
| 697 | if p == ".." then | |
| 698 | dir = dir:sub(1, text_utils.last(dir, "/")) | |
| 699 | elseif p ~= "." then | |
| 700 | if fs.isDir(dir .. "/" .. p) then | |
| 701 | dir = dir .. "/" .. p | |
| 702 | else | |
| 703 | server:write(dir .. "/") | |
| 704 | return "no_such_file" | |
| 705 | end | |
| 706 | end | |
| 707 | ||
| 708 | server:write(dir .. "/") | |
| 709 | return dir .. "/" | |
| 710 | end | |
| 711 | ||
| 712 | local function ls(p) | |
| 713 | if not fs.exists(dir .. (p and "/" .. p or "")) then | |
| 714 | server:write("")
| |
| 715 | return "no_such_file" | |
| 716 | end | |
| 717 | ||
| 718 | local l = fs.list(dir .. (p and "/" .. p or "")) | |
| 719 | local s = "" | |
| 720 | for k, v in ipairs(l) do | |
| 721 | s = s .. v .. "\n" | |
| 722 | end | |
| 723 | s = s:sub(1, #s - 1) | |
| 724 | server:write(s) | |
| 725 | ||
| 726 | return "" | |
| 727 | end | |
| 728 | ||
| 729 | local function mkdir(p) | |
| 730 | if fs.exists(dir .. "/" .. p) then | |
| 731 | return "already_exists" | |
| 732 | end | |
| 733 | ||
| 734 | fs.makeDir(dir .. "/" .. p) | |
| 735 | ||
| 736 | return "" | |
| 737 | end | |
| 738 | ||
| 739 | local function delete(p) | |
| 740 | if not fs.exists(dir .. "/" .. p) then | |
| 741 | return "no_such_file" | |
| 742 | elseif fs.isReadOnly(dir .. "/" .. p) then | |
| 743 | return "not_allowed" | |
| 744 | elseif fs.isDir(dir .. "/" .. p) then | |
| 745 | return "custom_err Cannot remove a directory. Use 'rmdir' instead." | |
| 746 | end | |
| 747 | ||
| 748 | fs.delete(dir .. "/" .. p) | |
| 749 | ||
| 750 | return "" | |
| 751 | end | |
| 752 | ||
| 753 | local function rmdir(p) | |
| 754 | if not fs.exists(dir .. "/" .. p) then | |
| 755 | return "no_such_file" | |
| 756 | elseif fs.isReadOnly(dir .. "/" .. p) then | |
| 757 | return "not_allowed" | |
| 758 | elseif not fs.isDir(dir .. "/" .. p) then | |
| 759 | return "custom_err Cannot remove a file. Use 'delete' instead." | |
| 760 | end | |
| 761 | ||
| 762 | fs.delete(dir .. "/" .. p) | |
| 763 | ||
| 764 | return "" | |
| 765 | end | |
| 766 | ||
| 767 | local function put(p) | |
| 768 | if fs.isDir(dir .. "/" .. p) then | |
| 769 | return "custom_err Cannot overwrite directory." | |
| 770 | end | |
| 771 | ||
| 772 | server:write("ready")
| |
| 773 | local data = server:read() | |
| 774 | local f = io.open(dir .. "/" .. p, "w") | |
| 775 | f:write(data) | |
| 776 | f:close() | |
| 777 | ||
| 778 | return "" | |
| 779 | end | |
| 780 | ||
| 781 | local function get(p) | |
| 782 | if not fs.exists(dir .. "/" .. p) then | |
| 783 | return "no_such_file" | |
| 784 | end | |
| 785 | ||
| 786 | if fs.isDir(dir .. "/" .. p) then | |
| 787 | return "custom_err Cannot download directory. Use 'rget' or 'mget' instead." | |
| 788 | end | |
| 789 | ||
| 790 | server:write("ok")
| |
| 791 | local ret = server:read() | |
| 792 | if ret ~= "ready" then | |
| 793 | return "unknown_err" | |
| 794 | end | |
| 795 | ||
| 796 | local f = io.open(dir .. "/" .. p, "r") | |
| 797 | local data = f:read("*a")
| |
| 798 | f:close() | |
| 799 | server:write(data) | |
| 800 | ||
| 801 | return "" | |
| 802 | end | |
| 803 | ||
| 804 | local function rget(p) | |
| 805 | if not fs.exists(dir .. "/" .. p) then | |
| 806 | return "no_such_file" | |
| 807 | end | |
| 808 | ||
| 809 | if not fs.isDir(dir .. "/" .. p) then | |
| 810 | return "custom_err Cannot download single files.\nUse 'get' instead." | |
| 811 | end | |
| 812 | ||
| 813 | server:write("ok")
| |
| 814 | ||
| 815 | local function getfiles(path) | |
| 816 | server:write("d" .. path)
| |
| 817 | local l = fs.list(dir .. "/" .. path) | |
| 818 | ||
| 819 | for k, v in ipairs(l) do | |
| 820 | if fs.isDir(dir .. "/" .. path .. "/" .. v) then | |
| 821 | getfiles(dir .. "/" .. path .. "/" .. v) | |
| 822 | else | |
| 823 | server:write("f" .. path .. "/" .. v)
| |
| 824 | ||
| 825 | local ret = server:read() | |
| 826 | if ret ~= "ready" then | |
| 827 | return ret | |
| 828 | end | |
| 829 | ||
| 830 | local f = io.open(dir .. "/" .. path .. "/" .. v, "r") | |
| 831 | local data = f:read("*a")
| |
| 832 | f:close() | |
| 833 | ||
| 834 | server:write(data) | |
| 835 | end | |
| 836 | end | |
| 837 | end | |
| 838 | ||
| 839 | getfiles(p) | |
| 840 | server:write("__getfiles_done__")
| |
| 841 | end | |
| 842 | ||
| 843 | local function main() | |
| 844 | server:accept_any() | |
| 845 | ||
| 846 | while true do | |
| 847 | if not server._connected then | |
| 848 | server:accept_any() | |
| 849 | end | |
| 850 | ||
| 851 | local msg = server:read() | |
| 852 | ||
| 853 | if msg:sub(1, 4) == "cmd:" then | |
| 854 | local cmd = msg:sub(5) | |
| 855 | _print("Command: " .. cmd)
| |
| 856 | cmd = string.split(cmd, " ") | |
| 857 | ||
| 858 | if cmd[1]:lower() == "cd" then | |
| 859 | if not cmd[2] then | |
| 860 | server:write("wrong_args")
| |
| 861 | else | |
| 862 | local ret = cd(cmd[2]) | |
| 863 | server:write(ret) | |
| 864 | end | |
| 865 | elseif cmd[1]:lower() == "ls" then | |
| 866 | local ret = ls(cmd[2] or "") | |
| 867 | server:write(ret) | |
| 868 | elseif cmd[1]:lower() == "mkdir" then | |
| 869 | if not cmd[2] then | |
| 870 | server:write("wrong_args")
| |
| 871 | else | |
| 872 | local ret = mkdir(cmd[2]) | |
| 873 | server:write(ret) | |
| 874 | end | |
| 875 | elseif cmd[1]:lower() == "delete" then | |
| 876 | if not cmd[2] then | |
| 877 | server:write("wrong_args")
| |
| 878 | else | |
| 879 | local ret = delete(cmd[2]) | |
| 880 | server:write(ret) | |
| 881 | end | |
| 882 | elseif cmd[1]:lower() == "rmdir" then | |
| 883 | if not cmd[2] then | |
| 884 | server:write("wrong_args")
| |
| 885 | else | |
| 886 | local ret = rmdir(cmd[2]) | |
| 887 | server:write(ret) | |
| 888 | end | |
| 889 | elseif cmd[1]:lower() == "put" then | |
| 890 | if not cmd[2] then | |
| 891 | server:write("wrong_args")
| |
| 892 | else | |
| 893 | local ret = put((cmd[3] ~= nil and cmd[3] or cmd[2])) | |
| 894 | server:write(ret) | |
| 895 | end | |
| 896 | elseif cmd[1]:lower() == "get" then | |
| 897 | if not cmd[2] then | |
| 898 | server:write("wrong_args")
| |
| 899 | else | |
| 900 | local ret = get(cmd[2]) | |
| 901 | server:write(ret) | |
| 902 | end | |
| 903 | elseif cmd[1]:lower() == "rget" or cmd[1]:lower() == "mget" then | |
| 904 | if not cmd[2] then | |
| 905 | server:write("wrong_args")
| |
| 906 | else | |
| 907 | local ret = rget((cmd[3] ~= nil and cmd[3] or cmd[2])) | |
| 908 | server:write(ret) | |
| 909 | end | |
| 910 | else | |
| 911 | server:write("unknown_command")
| |
| 912 | end | |
| 913 | end | |
| 914 | end | |
| 915 | ||
| 916 | return "" | |
| 917 | end | |
| 918 | ||
| 919 | if arg[3] ~= nil and (arg[3]:lower() == "-b" or arg[3] == "--background") then | |
| 920 | bg = true | |
| 921 | term.clear() | |
| 922 | term.setCursorPos(1, 1) | |
| 923 | parallel.waitForAll(main, function() shell.run("shell") end)
| |
| 924 | else | |
| 925 | main() | |
| 926 | end | |
| 927 | ]] | |
| 928 | ||
| 929 | local arg = {...}
| |
| 930 | ||
| 931 | if not arg[1] then print("Usage:\ninstall server|client") else
| |
| 932 | if arg[1]:lower() == "c" or arg[1]:lower() == "client" then | |
| 933 | local f = io.open("ftp_client", "w")
| |
| 934 | f:write(ftp_client) | |
| 935 | f:close() | |
| 936 | print("Installed to 'ftp_client'.\nRun 'ftp_client help' for information about using it.")
| |
| 937 | elseif arg[1]:lower() == "s" or arg[1]:lower() == "server" then | |
| 938 | local f = io.open("ftp_server", "w")
| |
| 939 | f:write(ftp_server) | |
| 940 | f:close() | |
| 941 | - | print("Installed to 'ftp_server'.\nRun 'ftp_server' help' for information about using it.")
|
| 941 | + | print("Installed to 'ftp_server'.\nRun 'ftp_server help' for information about using it.")
|
| 942 | end | |
| 943 | end |