Advertisement
Guest User

ftp.lua

a guest
Jul 22nd, 2016
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.83 KB | None | 0 0
  1. local component = require("component")
  2.  
  3. if not component.isAvailable("internet") then
  4. io.stderr:write("OpenFTP requires an Internet Card to run!\n")
  5. return
  6. end
  7.  
  8. local internet = require("internet")
  9. local computer = require("computer")
  10. local unicode = require("unicode")
  11. local shell = require("shell")
  12. local event = require("event")
  13. local term = require("term")
  14. local text = require("text")
  15. local fs = require("filesystem")
  16. local inetc = component.internet
  17. local gpu = component.gpu
  18.  
  19. -- Variables -------------------------------------------------------------------
  20.  
  21. local isColored, isVerbose
  22. local args, options
  23.  
  24. local sock, host, port, timer
  25. local w = gpu.getResolution()
  26.  
  27. local history = {}
  28. local commands = {}
  29.  
  30. local chsize = 102400
  31. local isRunning = true
  32.  
  33. -- Functions -------------------------------------------------------------------
  34.  
  35. local function setFG(fg)
  36. if isColored then
  37. gpu.setForeground(fg)
  38. end
  39. end
  40.  
  41. local function nop() end
  42.  
  43. local function help()
  44. print("Usage: ftp [--colors=<always|never|auto>] <host> [port]")
  45. print()
  46. print("Options: ")
  47. print(" --colors=<always|never|auto> Specify whether to use color or not.")
  48.  
  49. os.exit(0)
  50. end
  51.  
  52. local function init(...)
  53. args, options = shell.parse(...)
  54.  
  55. local oColors = options["colors"] or "auto"
  56. oColors = (oColors == "always" or oColors == "never") and oColors or "auto"
  57.  
  58. if oColors == "always" then
  59. isColored = true
  60. elseif oColors == "never" then
  61. isColored = false
  62. elseif oColors == "auto" then
  63. isColored = gpu.getDepth() > 1
  64. end
  65.  
  66. isVerbose = options["verbose"] == true
  67. host, port = args[1] or nil, args[2] or 21
  68.  
  69. if #args < 1 then help() end
  70. end
  71.  
  72. local function connect()
  73. local lSock, reason = internet.open(host .. ":" .. port)
  74. if not lSock then
  75. io.stderr:write(("ftp: %s: %s\n"):format(host .. ":" .. port,
  76. reason or "unknown error"))
  77. os.exit(1)
  78. return
  79. end
  80.  
  81. sock = lSock
  82. sock:setTimeout(0.2)
  83. end
  84.  
  85. local read
  86.  
  87. local function lost()
  88. read(trace, true)
  89. setFG(0xFF0000)
  90. print("Connection lost.")
  91. setFG(0xFFFFFF)
  92. sock:close()
  93. os.exit(0)
  94. end
  95.  
  96. local function readLine()
  97. local ok, line = pcall(sock.read, sock)
  98. if ok and line == "" then lost() end
  99.  
  100. return ok and line or false
  101. end
  102.  
  103. function read(f, nwait)
  104. local was, lastRet, out = false, nil, computer.uptime() + 2
  105.  
  106. repeat
  107. local line = readLine()
  108.  
  109. if line then
  110. lastRet = f(line)
  111. was = true
  112. out = computer.uptime() + 2
  113. end
  114.  
  115. if computer.uptime() >= out then
  116. lost()
  117. end
  118. until not line and (was or nwait)
  119.  
  120. return was, lastRet
  121. end
  122.  
  123. local function parseOutput(str)
  124. local match = {str:match("^(%d%d%d)([ -])(.*)$")}
  125.  
  126. if #match < 1 then return false end
  127.  
  128. local code = tonumber(match[1])
  129. local codeColor do
  130. if code >= 100 and code < 200 then
  131. codeColor = 0x0000FF
  132. elseif code >= 200 and code < 300 then
  133. codeColor = 0x00FF00
  134. elseif code >= 300 and code < 400 then
  135. codeColor = 0xFFFF00
  136. else
  137. codeColor = 0xFF0000
  138. end
  139. end
  140. local isLast = match[2] == " "
  141. local text = match[3]
  142.  
  143. return {
  144. code = code,
  145. codeColor = codeColor,
  146. isLast = isLast,
  147. text = text
  148. }
  149. end
  150.  
  151. local function traceLine(data)
  152. setFG(data.codeColor)
  153. io.write(data.code)
  154. setFG(0x666999)
  155. io.write(data.isLast and " " or "-")
  156. setFG(0xFFFFFF)
  157. io.write(data.text .. "\n")
  158. end
  159.  
  160. local function trace(str)
  161. local data = parseOutput(str)
  162. if data then
  163. traceLine(data)
  164. return data
  165. else
  166. print(str)
  167. end
  168. end
  169.  
  170. local function exit()
  171. if sock then
  172. sock:write("QUIT\r\n")
  173. sock:flush()
  174. read(trace, true)
  175. sock:close()
  176. end
  177.  
  178. setFG(0xFFFFFF)
  179.  
  180. os.exit(0)
  181. end
  182.  
  183. local function auth()
  184. read(trace)
  185.  
  186. local got = false
  187. while true do
  188. local user repeat
  189. io.write("Name: ")
  190. user = term.read()
  191. if not user then
  192. print()
  193. end
  194. until user and #user > 0
  195. sock:write("USER " .. user .. "\r\n")
  196. sock:flush()
  197.  
  198. local got = false
  199.  
  200. read(function (str)
  201. local data = trace(str)
  202. if not got then
  203. got = data and data.code == 331 and data.isLast
  204. end
  205. end)
  206.  
  207. if got then break end
  208. end
  209.  
  210. io.write("Password: ")
  211. pass = term.read(nil, nil, nil, "*"):sub(1, -2)
  212. if not pass then
  213. print()
  214. end
  215. sock:write("PASS " .. (pass or "") .. "\r\n")
  216. sock:flush()
  217.  
  218. local logined = false
  219.  
  220. while true do
  221. local was = read(function (str)
  222. local data = trace(str)
  223. logined = data and data.code == 230 and data.isLast
  224. end)
  225. if was then break end
  226. end
  227.  
  228. if not logined then
  229. exit()
  230. end
  231.  
  232. print("Using binary mode to transfer files.")
  233. sock:write("TYPE I\r\n")
  234. sock:flush()
  235. read(nop)
  236. end
  237.  
  238. local function pasv()
  239. sock:write("PASV\r\n")
  240. local ip, port
  241.  
  242. local was, ret = read(function (str)
  243. local data = trace(str)
  244. if data and data.code == 227 and data.isLast then
  245. local match = {data.text:match("(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)")}
  246. if match then
  247. ip = table.concat({match[1], match[2], match[3], match[4]}, ".")
  248. port = tonumber(match[5]) * 256 + tonumber(match[6])
  249. end
  250. end
  251. end)
  252.  
  253. if not ip or not port then
  254. return false
  255. end
  256.  
  257. return inetc.connect(ip, port)
  258. end
  259.  
  260. local function readPasv(pasvSock, f)
  261. os.sleep(0.2)
  262.  
  263. local buf = {}
  264. local bufLen = 0
  265. local written = false
  266.  
  267. while true do
  268. local chunk = pasvSock.read(chsize)
  269.  
  270. if bufLen >= chsize and written then
  271. buf = {}
  272. bufLen = 0
  273. end
  274.  
  275. if chunk then
  276. table.insert(buf, chunk)
  277. bufLen = bufLen + #chunk
  278. written = false
  279. end
  280.  
  281. if not written and (bufLen >= chsize or not chunk) then
  282. f(table.concat(buf), bufLen)
  283. written = true
  284. end
  285.  
  286. if not chunk and written then break end
  287. end
  288.  
  289. pasvSock.close()
  290. end
  291.  
  292. local function writePasv(pasvSock, f)
  293. repeat
  294. local chunk, len = f()
  295. if chunk then
  296. len = len or 0
  297. local written = 0
  298. repeat
  299. written = written + pasvSock.write(chunk)
  300. until written >= len
  301. end
  302. until not chunk
  303.  
  304. pasvSock.write("")
  305. pasvSock.close()
  306.  
  307. os.sleep(0.2)
  308. end
  309.  
  310. local function handleInput(str)
  311. str = text.trim(str)
  312.  
  313. if str:sub(1, 1) == "!" then
  314. if str:sub(2, -1) == "" then
  315. shell.execute("sh")
  316. else
  317. shell.execute(str:sub(2, -1))
  318. end
  319.  
  320. return
  321. end
  322.  
  323. local cmd, args do
  324. local tokens = text.tokenize(str)
  325. if #tokens < 1 then return end
  326.  
  327. cmd = tokens[1]
  328. table.remove(tokens, 1)
  329. args = tokens
  330. end
  331.  
  332. if commands[cmd] then
  333. commands[cmd](args)
  334. else
  335. setFG(0xFF0000)
  336. print("Invalid command.")
  337. setFG(0xFFFFFF)
  338. end
  339. end
  340.  
  341. local function main()
  342. connect()
  343. auth()
  344.  
  345. repeat
  346. setFG(0x999999)
  347. io.write("ftp> ")
  348. setFG(0xFFFFFF)
  349. local input = term.read(history)
  350.  
  351. if input and text.trim(input) ~= "" then
  352. handleInput(input)
  353.  
  354. if input:sub(1, 1) ~= " " then
  355. table.insert(history, input)
  356. end
  357. end
  358. until not input
  359. print()
  360.  
  361. isRunning = false
  362. end
  363.  
  364. local progress do
  365. local units = {"B", "k", "M", "G", "T"}
  366. local chars = {[0] = ""}
  367. for i = 0x258f, 0x2588, -1 do
  368. table.insert(chars, unicode.char(i))
  369. end
  370.  
  371. local function formatFSize(fsize)
  372. local result = ""
  373. if fsize < 10e3 then
  374. result = ("%.4f"):format(fsize):sub(1,5)
  375. result = result .. (" "):rep(6 - #result) .. units[1]
  376. elseif fsize < 10e13 then
  377. local digits = #("%d"):format(fsize)
  378. local unit = units[math.floor((digits - 1) / 3) + 1]
  379. result = ("%.13f"):format(fsize / (10 ^ (math.floor((digits - 1) / 3) * 3))):sub(1, 5)
  380. result = result .. (" "):rep(6 - #result) .. unit
  381. else
  382. result = ("%.1e"):format(fsize)
  383. end
  384. return result
  385. end
  386.  
  387. function progress(fgot, ftotal, tstart, width)
  388. local perc = 0
  389. if tonumber(tostring(fgot / ftotal)) then
  390. perc = fgot / ftotal * 100
  391. if perc > 100 then
  392. perc = 100
  393. end
  394. else
  395. error("The programmer has derped! fgot = 0, ftotal = 0, perc = -nan, undefined behaviour!")
  396. end
  397. local pstr = ("%.2f"):format(perc)
  398.  
  399. local delta = computer.uptime() - tstart
  400. local perperc = delta / perc
  401. local t = (100 - perc) * perperc
  402. local time = "n/a"
  403. local cspeed = ""
  404. if tonumber(tostring(t)) then
  405. cspeed = tonumber(tostring(fgot / delta))
  406. cspeed = cspeed and (formatFSize(cspeed) .. "/s") or "n/a"
  407. local days = math.floor(t / 86400)
  408. t = t - days * 86400
  409. local hours = math.floor(t / 3600)
  410. t = t - hours * 3600
  411. local minutes = math.floor(t / 60)
  412. local seconds = t - minutes * 60
  413. time = ("%02d"):format(seconds)
  414. time = ("%02d"):format(minutes) .. ":" .. time
  415. if hours ~= 0 or days ~= 0 then
  416. time = ("%02d"):format(hours) .. ":" .. time
  417. end
  418. if days ~= 0 then
  419. time = tostring(days) .. "d, " .. time
  420. end
  421. end
  422.  
  423. local lpart = formatFSize(fgot) .. " / " .. formatFSize(ftotal) .. "▕"
  424. local rpart = chars[1] .. (" "):rep(6 - #pstr) .. pstr .. "% " .. cspeed .. " -- " .. time
  425. local pwidth = width - unicode.len(lpart) - unicode.len(rpart)
  426.  
  427. local cwidth = pwidth / 100
  428. local fr = tonumber(select(2, math.modf(perc * cwidth)))
  429. local lastblockindex = math.floor(fr * 8)
  430. if lastblockindex == 8 then
  431. lastblockindex = 7
  432. end
  433. local lastblock = chars[lastblockindex]
  434. local blocks = math.floor(perc * cwidth)
  435.  
  436. local result = lpart .. chars[8]:rep(blocks) .. lastblock
  437. result = result .. (" "):rep(width - unicode.len(result) - unicode.len(rpart)) .. rpart
  438. return result
  439. end
  440. end
  441.  
  442. -- Сommands --------------------------------------------------------------------
  443.  
  444. function commands.quit()
  445. exit()
  446. end
  447.  
  448. function commands.pwd(args)
  449. local _, copts = shell.parse(table.unpack(args))
  450. if copts.help or copts.h then
  451. print("pwd - print working directory.")
  452. print()
  453. print("Usage: pwd [-h|--help]")
  454. print()
  455. print("Options:")
  456. print(" -h|--help Print this message")
  457.  
  458. return
  459. end
  460.  
  461. sock:write("PWD\r\n")
  462. sock:flush()
  463. read(trace)
  464. end
  465.  
  466. function commands.system(args)
  467. local _, copts = shell.parse(table.unpack(args))
  468. if copts.help or copts.h then
  469. print("system - print system running on server.")
  470. print()
  471. print("Usage: system [-h|--help]")
  472. print()
  473. print("Options:")
  474. print(" -h|--help Print this message")
  475.  
  476. return
  477. end
  478.  
  479. sock:write("SYST\r\n")
  480. sock:flush()
  481. read(trace)
  482. end
  483.  
  484. function commands.nop(args)
  485. local _, copts = shell.parse(table.unpack(args))
  486. if copts.help or copts.h then
  487. print("nop - do nothing.")
  488. print()
  489. print("Usage: system [-h|--help]")
  490. print()
  491. print("Options:")
  492. print(" -h|--help Print this message")
  493.  
  494. return
  495. end
  496.  
  497. sock:write("NOOP\r\n")
  498. sock:flush()
  499. read(trace)
  500. end
  501.  
  502. commands[".."] = function (args)
  503. local _, copts = shell.parse(table.unpack(args))
  504. if copts.help or copts.h then
  505. print(".. - go to the parent directory.")
  506. print()
  507. print("Usage: .. [-h|--help]")
  508. print("Options:")
  509. print(" -h|--help Print this message")
  510.  
  511. return
  512. end
  513.  
  514. sock:write("CDUP\r\n")
  515. sock:flush()
  516. read(trace)
  517. end
  518.  
  519. function commands.size(args)
  520. local _, copts = shell.parse(table.unpack(args))
  521. if copts.help or copts.h then
  522. print("size - print file size.")
  523. print()
  524. print("Usage: size [-h|--help] <path>")
  525. print()
  526. print("Options:")
  527. print(" -h|--help Print this message")
  528. print()
  529. print("Arguments:")
  530. print(" path Path to file.")
  531.  
  532. return
  533. end
  534.  
  535. if #args < 1 then
  536. print("Usage: size <file>")
  537. return
  538. end
  539.  
  540. sock:write("SIZE " .. args[1] .. "\r\n")
  541. sock:flush()
  542. read(trace)
  543. end
  544.  
  545. function commands.shelp(args)
  546. local _, copts = shell.parse(table.unpack(args))
  547. if copts.help or copts.h then
  548. print("shelp - print server help.")
  549. print()
  550. print("Usage: shelp [-h|--help]")
  551. print()
  552. print("Options:")
  553. print(" -h|--help Print this message")
  554.  
  555. return
  556. end
  557.  
  558. sock:write("HELP\r\n")
  559. sock:flush()
  560. read(trace)
  561. end
  562.  
  563. function commands.stat(args)
  564. local _, copts = shell.parse(table.unpack(args))
  565. if copts.help or copts.h then
  566. print("stat - print server statistics.")
  567. print()
  568. print("Usage: stat [-h|--help]")
  569. print()
  570. print("Options:")
  571. print(" -h|--help Print this message")
  572.  
  573. return
  574. end
  575.  
  576. sock:write("STAT\r\n")
  577. sock:flush()
  578. read(trace)
  579. end
  580.  
  581. function commands.binary(args)
  582. local _, copts = shell.parse(table.unpack(args))
  583. if copts.help or copts.h then
  584. print("binary - switch to binary mode.")
  585. print()
  586. print("Usage: binary [-h|--help]")
  587. print()
  588. print("Options:")
  589. print(" -h|--help Print this message")
  590.  
  591. return
  592. end
  593.  
  594. sock:write("TYPE I\r\n")
  595. sock:flush()
  596. read(trace)
  597. end
  598.  
  599. function commands.ascii()
  600. local _, copts = shell.parse(table.unpack(args))
  601. if copts.help or copts.h then
  602. print("ascii - switch to ASCII mode.")
  603. print()
  604. print("Usage: ascii [-h|--help]")
  605. print()
  606. print("Options:")
  607. print(" -h|--help Print this message")
  608.  
  609. return
  610. end
  611.  
  612. sock:write("TYPE A\r\n")
  613. sock:flush()
  614. read(trace)
  615. end
  616.  
  617. function commands.cd(args)
  618. local cargs, copts = shell.parse(table.unpack(args))
  619. if copts.help or copts.h or #cargs < 1 then
  620. print("ascii - change working directory.")
  621. print()
  622. print("Usage: ascii [-h|--help] <path>")
  623. print()
  624. print("Options:")
  625. print(" -h|--help Print this message")
  626. print()
  627. print("Arguments:")
  628. print(" path Path to new working directory")
  629.  
  630. return
  631. end
  632.  
  633. sock:write("CWD " .. cargs[1] .. "\r\n")
  634. sock:flush()
  635. read(trace)
  636. end
  637.  
  638. function commands.ls(args)
  639. local cargs, copts = shell.parse(table.unpack(args))
  640. if copts.help or copts.h then
  641. print("ls - print list files.")
  642. print()
  643. print("Usage: ls [-h|--help] [path]")
  644. print()
  645. print("Options:")
  646. print(" -h|--help Print this message")
  647. print(" -s Use short listing format")
  648. print()
  649. print("Arguments:")
  650. print(" path Path to the directory (the current")
  651. print(" directory by default) to list its files")
  652.  
  653. return
  654. end
  655.  
  656. local pasvSock = pasv()
  657. if not pasvSock then
  658. return
  659. end
  660.  
  661. sock:write((copts.s and "NLST" or "LIST")
  662. .. (cargs[1] and " " .. cargs[1] or "") .. "\r\n")
  663. sock:flush()
  664. read(function (str)
  665. local data = trace(str)
  666. if data and data.code == 150 and data.isLast then
  667. readPasv(pasvSock, function (str) print(str) end)
  668. end
  669. end)
  670. end
  671.  
  672. function commands.rename(args)
  673. local cargs, copts = shell.parse(table.unpack(args))
  674. if copts.help or copts.h or #cargs < 2 then
  675. print("rename - rename file or directory.")
  676. print()
  677. print("Usage: rename [-h|--help] <source> <dest>")
  678. print()
  679. print("Options:")
  680. print(" -h|--help Print this message")
  681. print()
  682. print("Arguments:")
  683. print(" source A path to file to rename")
  684. print(" dest A name the source should be renamed to")
  685. print()
  686. print("Aliases: rn")
  687.  
  688. return
  689. end
  690.  
  691. sock:write("RNFR " .. cargs[1] .. "\r\n")
  692. sock:flush()
  693. read(function (str)
  694. local data = trace(str)
  695. if data and data.code == 350 and data.isLast then
  696. sock:write("RNTO " .. cargs[2] .. "\r\n")
  697. sock:flush()
  698. read(trace)
  699. end
  700. end)
  701. end
  702.  
  703. commands.rn = commands.rename
  704.  
  705. function commands.rm(args)
  706. local cargs, copts = shell.parse(table.unpack(args))
  707. if copts.help or copts.h or #cargs < 1 then
  708. print("rm - remove a file or directory.")
  709. print()
  710. print("Usage: rm [-h|--help] [-d] <path>")
  711. print()
  712. print("Options:")
  713. print(" -h|--help Print this message")
  714. print(" -d Remove a directory")
  715. print()
  716. print("Arguments:")
  717. print(" path A path to the file or directory to remove")
  718.  
  719. return
  720. end
  721.  
  722. sock:write((copts.d and "RMD " or "DELE ") .. cargs[1] .. "\r\n")
  723. sock:flush()
  724. read(trace)
  725. end
  726.  
  727. function commands.mkdir(args)
  728. local cargs, copts = shell.parse(table.unpack(args))
  729. if copts.help or copts.h or #cargs < 1 then
  730. print("mkdir - create a directory.")
  731. print()
  732. print("Usage: mkdir [-h|--help] <path>")
  733. print()
  734. print("Options:")
  735. print(" -h|--help Print this message")
  736. print()
  737. print("Arguments:")
  738. print(" path A path to new directory")
  739.  
  740. return
  741. end
  742.  
  743. sock:write("MKD " .. cargs[1] .. "\r\n")
  744. sock:flush()
  745. read(trace)
  746. end
  747.  
  748. function commands.raw(args)
  749. local cargs, copts = shell.parse(table.unpack(args))
  750. if copts.help or copts.h or #cargs < 1 then
  751. print("raw - send a message to the server.")
  752. print("Sent message will end with CRLF (\\r\\n)")
  753. print()
  754. print("Usage: raw [-h|--help] <message>")
  755. print()
  756. print("Options:")
  757. print(" -h|--help Print this message")
  758. print()
  759. print("Arguments:")
  760. print(" message Message that should be sent to the server")
  761.  
  762. return
  763. end
  764.  
  765. sock:write(cargs[1] .. "\r\n")
  766. sock:flush()
  767. os.sleep(0.5)
  768. read(trace, true)
  769. end
  770.  
  771. function commands.get(args)
  772. local cargs, copts = shell.parse(table.unpack(args))
  773. if copts.help or copts.h or #cargs < 1 then
  774. print("get - get a file from the server.")
  775. print()
  776. print("Usage: get [-h|--help] <remote-path> [local-path]")
  777. print()
  778. print("Options:")
  779. print(" -h|--help Print this message")
  780. print()
  781. print("Arguments:")
  782. print(" remote-path A path to file that should be got from the server")
  783. print(" local-path A path to local file (remote-path by default)")
  784.  
  785. return
  786. end
  787.  
  788. local file, reason = io.open(cargs[2] or cargs[1], "w")
  789.  
  790. if not file then
  791. setFG(0xFF0000)
  792. print("Error opening file for writing: "
  793. .. tostring(reason or "unknown error"))
  794. setFG(0x000000)
  795. return
  796. end
  797.  
  798. sock:write("SIZE " .. cargs[1] .. "\r\n")
  799. sock:flush()
  800.  
  801. local size = 0
  802. read(function (str)
  803. local data = trace(str)
  804. if data and data.code == 213 and data.isLast then
  805. size = data.text:match("(%d+)")
  806. end
  807. end)
  808. size = tonumber(size)
  809.  
  810. local pasvSock = pasv()
  811. if not pasvSock then
  812. return
  813. end
  814.  
  815. sock:write("RETR " .. cargs[1] .. "\r\n")
  816. sock:flush()
  817. read(function (str)
  818. local data = trace(str)
  819. if data and data.code == 150 and data.isLast then
  820. local start = computer.uptime()
  821. local show = size and size > 0
  822. local now = 0
  823.  
  824. io.write(progress(now, size, start, w))
  825.  
  826. readPasv(pasvSock, function (chunk, len)
  827. file:write(chunk)
  828. if show then
  829. now = now + len
  830. term.clearLine()
  831. io.write(progress(now, size, start, w))
  832. end
  833. end)
  834.  
  835. if show then
  836. print()
  837. end
  838.  
  839. file:flush()
  840. file:close()
  841. end
  842. end)
  843. end
  844.  
  845. function commands.put(args)
  846. local cargs, copts = shell.parse(table.unpack(args))
  847. if copts.help or copts.h or #cargs < 1 then
  848. print("put - put a file to the server.")
  849. print()
  850. print("Usage: put [-h|--help] <local-path> [remote-path]")
  851. print()
  852. print("Options:")
  853. print(" -h|--help Print this message")
  854. print()
  855. print("Arguments:")
  856. print(" local-path A path to local file.")
  857. print(" remote-path A path to file that should be put to the server")
  858. print(" (local-path by default)")
  859.  
  860. return
  861. end
  862.  
  863. local file, reason = io.open(cargs[1], "rb")
  864.  
  865. if not file then
  866. setFG(0xFF0000)
  867. print("Error opening file for reading: "
  868. .. tostring(reason or "unknown error"))
  869. setFG(0x000000)
  870. return
  871. end
  872.  
  873. local pasvSock = pasv()
  874. if not pasvSock then
  875. return
  876. end
  877.  
  878. sock:write("STOR " .. (cargs[2] or cargs[1]) .. "\r\n")
  879. sock:flush()
  880. read(function (str)
  881. local data = trace(str)
  882. if data and data.code == 150 and data.isLast then
  883. os.sleep(0.3)
  884.  
  885. local start = computer.uptime()
  886. local size = fs.size(os.getenv("PWD") .. "/" .. cargs[1])
  887. local now = 0
  888.  
  889. io.write(progress(now, size, start, w))
  890.  
  891. writePasv(pasvSock, function ()
  892. local chunk = file:read(chsize)
  893. if not chunk then
  894. return
  895. end
  896.  
  897. local len = #chunk
  898.  
  899. now = now + len
  900. term.clearLine()
  901. io.write(progress(now, size, start, w))
  902.  
  903. return chunk, len
  904. end)
  905.  
  906. print()
  907.  
  908. file:close()
  909. end
  910. end)
  911. end
  912.  
  913.  
  914. -- Main ------------------------------------------------------------------------
  915.  
  916. init(...)
  917. main()
  918. exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement