Update Network.lua and installer to use new FTP library

This commit is contained in:
smok1e 2025-09-06 15:29:50 +03:00
parent 3f34c05a7b
commit 9054359b4e
2 changed files with 172 additions and 329 deletions

View File

@ -118,6 +118,10 @@
path = "Libraries/BigLetters.lua", path = "Libraries/BigLetters.lua",
id = 391 id = 391
}, },
{
path = "Libraries/FTP.lua",
id = 2529
}
-- Scripts -- Scripts
{ {

View File

@ -1,4 +1,5 @@
local GUI = require("GUI") local GUI = require("GUI")
local FTP = require("FTP")
local event = require("Event") local event = require("Event")
local filesystem = require("Filesystem") local filesystem = require("Filesystem")
local system = require("System") local system = require("System")
@ -64,338 +65,176 @@ function network.getFTPProxyName(address, port, user)
return user .. "@" .. address .. ":" .. port return user .. "@" .. address .. ":" .. port
end end
local function FTPSocketWrite(socketHandle, data)
local success, result = pcall(socketHandle.write, data .. "\r\n")
if success then
return true, result
else
return false, result
end
end
local function FTPSocketRead(socketHandle)
event.sleep(network.internetDelay)
local deadline, data, success, result = computer.uptime() + network.internetTimeout, ""
while computer.uptime() < deadline do
success, result = pcall(socketHandle.read, math.huge)
if success then
if not result or #result == 0 then
if #data > 0 then
return true, data
end
else
data, deadline = data .. result, computer.uptime() + network.internetTimeout
end
else
return false, result
end
end
return false, "Socket read time out"
end
local function FTPParseLines(data)
local lines = {}
for line in data:gmatch("[^\r\n]+") do
table.insert(lines, line)
end
return lines
end
local function FTPLogin(socketHandle, user, password)
FTPSocketWrite(socketHandle, "USER " .. user)
FTPSocketWrite(socketHandle, "PASS " .. password)
FTPSocketWrite(socketHandle, "TYPE I")
local success, result = FTPSocketRead(socketHandle)
if success then
if result:match("TYPE okay") or result:match("200") then
return true
else
return false, "Authentication failed"
end
else
return false, result
end
end
local function FTPEnterPassiveModeAndRunCommand(commandSocketHandle, command, dataToWrite)
FTPSocketWrite(commandSocketHandle, "PASV")
local success, result = FTPSocketRead(commandSocketHandle)
if not success then
return false, result
end
local digits = {result:match("Entering Passive Mode %((%d+),(%d+),(%d+),(%d+),(%d+),(%d+)%)")}
if #digits ~= 6 then
return false, "Entering passive mode failed: wrong address byte array. Socket response message was: " .. tostring(result)
end
local address, port = table.concat(digits, ".", 1, 4), tonumber(digits[5]) * 256 + tonumber(digits[6])
FTPSocketWrite(commandSocketHandle, command)
local dataSocketHandle = network.internetProxy.connect(address, port)
if dataToWrite then
event.sleep(network.internetDelay)
dataSocketHandle.read(1)
dataSocketHandle.write(dataToWrite)
dataSocketHandle.close()
return true
else
local success, result = FTPSocketRead(dataSocketHandle)
dataSocketHandle.close()
return not not success, result
end
end
local function FTPParseFileInfo(result)
local info = {}
for token in result:gmatch("[^; ]+") do
local key, value = token:match("(.+)=(.+)")
if key then
key = key:lower()
if key == "size" or key == "sizd" then
info["size"] = tonumber(value)
elseif key == "modify" then
info["modify"] = tonumber(value)
elseif key == "type" then
info["isdir"] = not not value:match("dir")
else
info[key] = value
end
else
info["filename"] = token
end
end
if not info["filename"] or info["isdir"] == nil then
return false, "File not exists"
end
return true, info["filename"], info["isdir"], info["size"] or 0, info["modify"] or 0
end
local function FTPFileInfo(socketHandle, path, field)
FTPSocketWrite(socketHandle, "MLST " .. path)
local success, result = FTPSocketRead(socketHandle)
if success then
local success, name, isDirectory, size, lastModified = FTPParseFileInfo(result)
if success then
if field == "isDirectory" then
return true, isDirectory
elseif field == "lastModified" then
return true, lastModified
else
return true, size
end
else
return true, false
end
else
return false, result
end
end
local function check(...)
local result = {...}
if not result[1] then
GUI.alert(table.unpack(result, 2))
end
return table.unpack(result)
end
function network.connectToFTP(address, port, user, password) function network.connectToFTP(address, port, user, password)
if network.internetProxy then if not network.internetProxy then
local socketHandle, reason = network.internetProxy.connect(address, port) return false, "Internet component is not available"
if socketHandle then end
FTPSocketRead(socketHandle)
local result, reason = FTPLogin(socketHandle, user, password) local client, reason = FTP.connect(address, port)
if result then if not client then
return false, reason
end
local proxy, fileHandles, label = {}, {}, network.getFTPProxyName(address, port, user) local result, reason = client:login(user, password)
if not result then
return false, reason
end
local result, reason = client:setMode("I")
if not result then
return false, reason
end
local result, reason = client:changeWorkingDirectory("/")
if not result then
return false, reason
end
local function getFileField(path, field)
local result, reason = client:getFileInfo(path, true)
if not result then
error(reason)
end
return result[field]
end
local label = network.getFTPProxyName(address, port, user)
local proxy, fileHandles = {}, {}
proxy.type = "filesystem" proxy.type = "filesystem"
proxy.slot = 0 proxy.slot = 0
proxy.address = label proxy.address = label
proxy.networkFTP = true proxy.networkFTP = true
proxy.getLabel = function() -- Send command every 30 seconds so server wont suddenly drop connection
local timerHandler = event.addHandler(
function()
client:keepAlive()
end,
30
)
function proxy.getLabel()
return label return label
end end
proxy.spaceUsed = function() function proxy.spaceUsed()
return network.proxySpaceUsed return network.proxySpaceUsed
end end
proxy.spaceTotal = function() function proxy.spaceTotal()
return network.proxySpaceTotal return network.proxySpaceTotal
end end
proxy.setLabel = function(text) function proxy.setLabel(text)
label = text label = text
return true return true
end end
proxy.isReadOnly = function() function proxy.isReadOnly()
return false return false
end end
proxy.closeSocketHandle = function() function proxy.closeSocketHandle()
filesystem.unmount(proxy) filesystem.unmount(proxy)
return socketHandle.close() event.removeHandler(timerHandler)
return client:close()
end end
proxy.list = function(path) function proxy.list(path)
local success, result = FTPEnterPassiveModeAndRunCommand(socketHandle, "MLSD " .. path) local result, reason = client:listDirectory(path)
if not success then if not result then
return nil, result error(reason)
end end
local list = FTPParseLines(result) local list = {}
for i = 1, #list do for _, entry in pairs(result) do
local success, name, isDirectory = FTPParseFileInfo(list[i]) table.insert(list, entry.isdir and (entry.name .. "/") or entry.name)
if success then
list[i] = name .. (isDirectory and "/" or "")
end
end end
return list return list
end end
proxy.isDirectory = function(path) function proxy.isDirectory(path)
local success, result = check(FTPFileInfo(socketHandle, path, "isDirectory")) return getFileField(path, "isdir")
if success then
return result
else
return false
end
end end
proxy.lastModified = function(path) function proxy.lastModified(path)
local success, result = check(FTPFileInfo(socketHandle, path, "lastModified")) return getFileField(path, "modify")
if success and result ~= false then
return result
else
return 0
end
end end
proxy.size = function(path) function proxy.size(path)
local success, result = check(FTPFileInfo(socketHandle, path, "size")) return getFileField(path, "size")
if success then
return result
else
return 0
end
end end
proxy.exists = function(path) function proxy.exists(path)
local success, result = check(FTPFileInfo(socketHandle, path)) return client:fileExists(path, true)
if success then
return result
else
return false
end
end end
proxy.open = function(path, mode) function proxy.open(path, mode)
local temporaryPath = system.getTemporaryPath() local tmp = system.getTemporaryPath()
if mode == "r" or mode == "rb" or mode == "a" or mode == "ab" then if mode == "r" or mode == "rb" or mode == "a" or mode == "ab" then
local success, result = FTPEnterPassiveModeAndRunCommand(socketHandle, "RETR " .. path) local success, reason = client:readFileToFilesystem(path, tmp)
if not success then
filesystem.write(temporaryPath, success and result or "") error(reason)
end
end end
local fileHandle, reason = filesystemProxy.open(temporaryPath, mode) local handle, reason = filesystemProxy.open(tmp, mode)
if fileHandle then if not handle then
fileHandles[fileHandle] = { return nil, reason
temporaryPath = temporaryPath, end
fileHandles[handle] = {
temporaryPath = tmp,
path = path, path = path,
needUpload = mode ~= "r" and mode ~= "rb", needUpload = mode ~= "r" and mode ~= "rb"
} }
return handle
end end
return fileHandle, reason function proxy.close(handle)
end if not fileHandles[handle] then
proxy.close = function(fileHandle)
if not fileHandles[fileHandle] then
return return
end end
filesystemProxy.close(fileHandle) filesystemProxy.close(handle)
if fileHandles[fileHandle].needUpload then if fileHandles[handle].needUpload then
local data = filesystem.read(fileHandles[fileHandle].temporaryPath) client:writeFileFromFilesystem(fileHandles[handle].path, fileHandles[handle].temporaryPath)
end
check(FTPEnterPassiveModeAndRunCommand(socketHandle, "STOR " .. fileHandles[fileHandle].path, data))
end end
filesystem.remove(fileHandles[fileHandle].temporaryPath) function proxy.write(...)
fileHandles[fileHandle] = nil
end
proxy.write = function(...)
return filesystemProxy.write(...) return filesystemProxy.write(...)
end end
proxy.read = function(...) function proxy.read(...)
return filesystemProxy.read(...) return filesystemProxy.read(...)
end end
proxy.seek = function(...) function proxy.seek(...)
return filesystemProxy.seek(...) return filesystemProxy.seek(...)
end end
proxy.remove = function(path) function proxy.remove(path)
if proxy.isDirectory(path) then return client:removeFile(path)
local list = proxy.list(path)
for i = 1, #list do
proxy.remove((path .. "/" .. list[i]):gsub("/+", "/"))
end end
FTPSocketWrite(socketHandle, "RMD " .. path) function proxy.makeDirectory(path)
else return client:makeDirectory(path)
FTPSocketWrite(socketHandle, "DELE " .. path)
end
end end
proxy.makeDirectory = function(path) function proxy.rename(from, to)
FTPSocketWrite(socketHandle, "MKD " .. path) return client:renameFile(from, to)
return true
end
proxy.rename = function(oldPath, newPath)
FTPSocketWrite(socketHandle, "RNFR " .. oldPath)
FTPSocketWrite(socketHandle, "RNTO " .. newPath)
return true
end end
return proxy return proxy
else
return false, reason
end
else
return false, reason
end
else
return false, "Internet component is not available"
end
end end
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------