mirror of
https://github.com/lunarmodules/luasocket.git
synced 2024-12-25 03:58:21 +01:00
Family agostic FTP and expose HTTP/FTP url parsing
This commit is contained in:
parent
5b4b915879
commit
916b548240
67
src/ftp.lua
67
src/ftp.lua
@ -51,7 +51,7 @@ end
|
|||||||
function metat.__index:pasvconnect()
|
function metat.__index:pasvconnect()
|
||||||
self.data = self.try(socket.tcp())
|
self.data = self.try(socket.tcp())
|
||||||
self.try(self.data:settimeout(_M.TIMEOUT))
|
self.try(self.data:settimeout(_M.TIMEOUT))
|
||||||
self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
|
self.try(self.data:connect(self.pasvt.address, self.pasvt.port))
|
||||||
end
|
end
|
||||||
|
|
||||||
function metat.__index:login(user, password)
|
function metat.__index:login(user, password)
|
||||||
@ -71,32 +71,65 @@ function metat.__index:pasv()
|
|||||||
local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
|
local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
|
||||||
self.try(a and b and c and d and p1 and p2, reply)
|
self.try(a and b and c and d and p1 and p2, reply)
|
||||||
self.pasvt = {
|
self.pasvt = {
|
||||||
ip = string.format("%d.%d.%d.%d", a, b, c, d),
|
address = string.format("%d.%d.%d.%d", a, b, c, d),
|
||||||
port = p1*256 + p2
|
port = p1*256 + p2
|
||||||
}
|
}
|
||||||
if self.server then
|
if self.server then
|
||||||
self.server:close()
|
self.server:close()
|
||||||
self.server = nil
|
self.server = nil
|
||||||
end
|
end
|
||||||
return self.pasvt.ip, self.pasvt.port
|
return self.pasvt.address, self.pasvt.port
|
||||||
end
|
end
|
||||||
|
|
||||||
function metat.__index:port(ip, port)
|
function metat.__index:epsv()
|
||||||
|
self.try(self.tp:command("epsv"))
|
||||||
|
local code, reply = self.try(self.tp:check("229"))
|
||||||
|
local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)"
|
||||||
|
local d, prt, address, port = string.match(reply, pattern)
|
||||||
|
self.try(port, "invalid epsv response")
|
||||||
|
self.pasvt = {
|
||||||
|
address = self.tp:getpeername(),
|
||||||
|
port = port
|
||||||
|
}
|
||||||
|
if self.server then
|
||||||
|
self.server:close()
|
||||||
|
self.server = nil
|
||||||
|
end
|
||||||
|
return self.pasvt.address, self.pasvt.port
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function metat.__index:port(address, port)
|
||||||
self.pasvt = nil
|
self.pasvt = nil
|
||||||
if not ip then
|
if not address then
|
||||||
ip, port = self.try(self.tp:getcontrol():getsockname())
|
address, port = self.try(self.tp:getsockname())
|
||||||
self.server = self.try(socket.bind(ip, 0))
|
self.server = self.try(socket.bind(address, 0))
|
||||||
ip, port = self.try(self.server:getsockname())
|
address, port = self.try(self.server:getsockname())
|
||||||
self.try(self.server:settimeout(_M.TIMEOUT))
|
self.try(self.server:settimeout(_M.TIMEOUT))
|
||||||
end
|
end
|
||||||
local pl = math.mod(port, 256)
|
local pl = math.mod(port, 256)
|
||||||
local ph = (port - pl)/256
|
local ph = (port - pl)/256
|
||||||
local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
|
local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",")
|
||||||
self.try(self.tp:command("port", arg))
|
self.try(self.tp:command("port", arg))
|
||||||
self.try(self.tp:check("2.."))
|
self.try(self.tp:check("2.."))
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function metat.__index:eprt(family, address, port)
|
||||||
|
self.pasvt = nil
|
||||||
|
if not address then
|
||||||
|
address, port = self.try(self.tp:getsockname())
|
||||||
|
self.server = self.try(socket.bind(address, 0))
|
||||||
|
address, port = self.try(self.server:getsockname())
|
||||||
|
self.try(self.server:settimeout(_M.TIMEOUT))
|
||||||
|
end
|
||||||
|
local arg = string.format("|%s|%s|%d|", family, address, port)
|
||||||
|
self.try(self.tp:command("eprt", arg))
|
||||||
|
self.try(self.tp:check("2.."))
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function metat.__index:send(sendt)
|
function metat.__index:send(sendt)
|
||||||
self.try(self.pasvt or self.server, "need port or pasv first")
|
self.try(self.pasvt or self.server, "need port or pasv first")
|
||||||
-- if there is a pasvt table, we already sent a PASV command
|
-- if there is a pasvt table, we already sent a PASV command
|
||||||
@ -110,12 +143,12 @@ function metat.__index:send(sendt)
|
|||||||
-- send the transfer command and check the reply
|
-- send the transfer command and check the reply
|
||||||
self.try(self.tp:command(command, argument))
|
self.try(self.tp:command(command, argument))
|
||||||
local code, reply = self.try(self.tp:check{"2..", "1.."})
|
local code, reply = self.try(self.tp:check{"2..", "1.."})
|
||||||
-- if there is not a a pasvt table, then there is a server
|
-- if there is not a pasvt table, then there is a server
|
||||||
-- and we already sent a PORT command
|
-- and we already sent a PORT command
|
||||||
if not self.pasvt then self:portconnect() end
|
if not self.pasvt then self:portconnect() end
|
||||||
-- get the sink, source and step for the transfer
|
-- get the sink, source and step for the transfer
|
||||||
local step = sendt.step or ltn12.pump.step
|
local step = sendt.step or ltn12.pump.step
|
||||||
local readt = {self.tp.c}
|
local readt = { self.tp }
|
||||||
local checkstep = function(src, snk)
|
local checkstep = function(src, snk)
|
||||||
-- check status in control connection while downloading
|
-- check status in control connection while downloading
|
||||||
local readyt = socket.select(readt, nil, 0)
|
local readyt = socket.select(readt, nil, 0)
|
||||||
@ -207,7 +240,7 @@ local function tput(putt)
|
|||||||
f:greet()
|
f:greet()
|
||||||
f:login(putt.user, putt.password)
|
f:login(putt.user, putt.password)
|
||||||
if putt.type then f:type(putt.type) end
|
if putt.type then f:type(putt.type) end
|
||||||
f:pasv()
|
f:epsv()
|
||||||
local sent = f:send(putt)
|
local sent = f:send(putt)
|
||||||
f:quit()
|
f:quit()
|
||||||
f:close()
|
f:close()
|
||||||
@ -219,7 +252,7 @@ local default = {
|
|||||||
scheme = "ftp"
|
scheme = "ftp"
|
||||||
}
|
}
|
||||||
|
|
||||||
local function parse(u)
|
local function genericform(u)
|
||||||
local t = socket.try(url.parse(u, default))
|
local t = socket.try(url.parse(u, default))
|
||||||
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
|
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
|
||||||
socket.try(t.host, "missing hostname")
|
socket.try(t.host, "missing hostname")
|
||||||
@ -232,8 +265,10 @@ local function parse(u)
|
|||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_M.genericform = genericform
|
||||||
|
|
||||||
local function sput(u, body)
|
local function sput(u, body)
|
||||||
local putt = parse(u)
|
local putt = genericform(u)
|
||||||
putt.source = ltn12.source.string(body)
|
putt.source = ltn12.source.string(body)
|
||||||
return tput(putt)
|
return tput(putt)
|
||||||
end
|
end
|
||||||
@ -250,14 +285,14 @@ local function tget(gett)
|
|||||||
f:greet()
|
f:greet()
|
||||||
f:login(gett.user, gett.password)
|
f:login(gett.user, gett.password)
|
||||||
if gett.type then f:type(gett.type) end
|
if gett.type then f:type(gett.type) end
|
||||||
f:pasv()
|
f:epsv()
|
||||||
f:receive(gett)
|
f:receive(gett)
|
||||||
f:quit()
|
f:quit()
|
||||||
return f:close()
|
return f:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function sget(u)
|
local function sget(u)
|
||||||
local gett = parse(u)
|
local gett = genericform(u)
|
||||||
local t = {}
|
local t = {}
|
||||||
gett.sink = ltn12.sink.table(t)
|
gett.sink = ltn12.sink.table(t)
|
||||||
tget(gett)
|
tget(gett)
|
||||||
|
17
src/http.lua
17
src/http.lua
@ -346,11 +346,13 @@ end
|
|||||||
return 1, code, headers, status
|
return 1, code, headers, status
|
||||||
end
|
end
|
||||||
|
|
||||||
local function srequest(u, b)
|
-- turns an url and a body into a generic request
|
||||||
|
local function genericform(u, b)
|
||||||
local t = {}
|
local t = {}
|
||||||
local reqt = {
|
local reqt = {
|
||||||
url = u,
|
url = u,
|
||||||
sink = ltn12.sink.table(t)
|
sink = ltn12.sink.table(t),
|
||||||
|
target = t
|
||||||
}
|
}
|
||||||
if b then
|
if b then
|
||||||
reqt.source = ltn12.source.string(b)
|
reqt.source = ltn12.source.string(b)
|
||||||
@ -360,8 +362,15 @@ local function srequest(u, b)
|
|||||||
}
|
}
|
||||||
reqt.method = "POST"
|
reqt.method = "POST"
|
||||||
end
|
end
|
||||||
local code, headers, status = socket.skip(1, trequest(reqt))
|
return reqt
|
||||||
return table.concat(t), code, headers, status
|
end
|
||||||
|
|
||||||
|
_M.genericform = genericform
|
||||||
|
|
||||||
|
local function srequest(u, b)
|
||||||
|
local reqt = genericform(u, b)
|
||||||
|
local _, code, headers, status = trequest(reqt)
|
||||||
|
return table.concat(reqt.target), code, headers, status
|
||||||
end
|
end
|
||||||
|
|
||||||
_M.request = socket.protect(function(reqt, body)
|
_M.request = socket.protect(function(reqt, body)
|
||||||
|
10
src/tp.lua
10
src/tp.lua
@ -46,6 +46,14 @@ end
|
|||||||
-- metatable for sock object
|
-- metatable for sock object
|
||||||
local metat = { __index = {} }
|
local metat = { __index = {} }
|
||||||
|
|
||||||
|
function metat.__index:getpeername()
|
||||||
|
return self.c:getpeername()
|
||||||
|
end
|
||||||
|
|
||||||
|
function metat.__index:getsockname()
|
||||||
|
return self.c:getpeername()
|
||||||
|
end
|
||||||
|
|
||||||
function metat.__index:check(ok)
|
function metat.__index:check(ok)
|
||||||
local code, reply = get_reply(self.c)
|
local code, reply = get_reply(self.c)
|
||||||
if not code then return nil, reply end
|
if not code then return nil, reply end
|
||||||
@ -123,4 +131,4 @@ function _M.connect(host, port, timeout, create)
|
|||||||
return base.setmetatable({c = c}, metat)
|
return base.setmetatable({c = c}, metat)
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
@ -3,19 +3,31 @@ local ftp = require("socket.ftp")
|
|||||||
local url = require("socket.url")
|
local url = require("socket.url")
|
||||||
local ltn12 = require("ltn12")
|
local ltn12 = require("ltn12")
|
||||||
|
|
||||||
|
-- use dscl to create user "luasocket" with password "password"
|
||||||
|
-- with home in /Users/diego/luasocket/test/ftp
|
||||||
|
-- with group com.apple.access_ftp
|
||||||
|
-- with shell set to /sbin/nologin
|
||||||
|
-- set /etc/ftpchroot to chroot luasocket
|
||||||
|
-- must set group com.apple.access_ftp on user _ftp (for anonymous access)
|
||||||
|
-- copy index.html to /var/empty/pub (home of user ftp)
|
||||||
|
-- start ftp server with
|
||||||
|
-- sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist
|
||||||
|
-- copy index.html to /Users/diego/luasocket/test/ftp
|
||||||
|
-- stop with
|
||||||
|
-- sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist
|
||||||
|
|
||||||
-- override protection to make sure we see all errors
|
-- override protection to make sure we see all errors
|
||||||
--socket.protect = function(s) return s end
|
--socket.protect = function(s) return s end
|
||||||
|
|
||||||
dofile("testsupport.lua")
|
dofile("testsupport.lua")
|
||||||
|
|
||||||
local host, port, index_file, index, back, err, ret
|
local host = host or "localhost"
|
||||||
|
local port, index_file, index, back, err, ret
|
||||||
|
|
||||||
local t = socket.gettime()
|
local t = socket.gettime()
|
||||||
|
|
||||||
host = host or "localhost"
|
|
||||||
index_file = "index.html"
|
index_file = "index.html"
|
||||||
|
|
||||||
|
|
||||||
-- a function that returns a directory listing
|
-- a function that returns a directory listing
|
||||||
local function nlst(u)
|
local function nlst(u)
|
||||||
local t = {}
|
local t = {}
|
||||||
@ -55,27 +67,27 @@ assert(not err and back == index, err)
|
|||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
io.write("erasing before upload: ")
|
io.write("erasing before upload: ")
|
||||||
ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html")
|
ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
|
||||||
if not ret then print(err)
|
if not ret then print(err)
|
||||||
else print("ok") end
|
else print("ok") end
|
||||||
|
|
||||||
io.write("testing upload: ")
|
io.write("testing upload: ")
|
||||||
ret, err = ftp.put("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i", index)
|
ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index)
|
||||||
assert(ret and not err, err)
|
assert(ret and not err, err)
|
||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
io.write("downloading uploaded file: ")
|
io.write("downloading uploaded file: ")
|
||||||
back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i")
|
back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i")
|
||||||
assert(ret and not err and index == back, err)
|
assert(ret and not err and index == back, err)
|
||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
io.write("erasing after upload/download: ")
|
io.write("erasing after upload/download: ")
|
||||||
ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html")
|
ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
|
||||||
assert(ret and not err, err)
|
assert(ret and not err, err)
|
||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
io.write("testing weird-character translation: ")
|
io.write("testing weird-character translation: ")
|
||||||
back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/%23%3f;type=i")
|
back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i")
|
||||||
assert(not err and back == index, err)
|
assert(not err and back == index, err)
|
||||||
print("ok")
|
print("ok")
|
||||||
|
|
||||||
@ -84,7 +96,7 @@ local back = {}
|
|||||||
ret, err = ftp.get{
|
ret, err = ftp.get{
|
||||||
url = "//stupid:mistake@" .. host .. "/index.html",
|
url = "//stupid:mistake@" .. host .. "/index.html",
|
||||||
user = "luasocket",
|
user = "luasocket",
|
||||||
password = "pedrovian",
|
password = "password",
|
||||||
type = "i",
|
type = "i",
|
||||||
sink = ltn12.sink.table(back)
|
sink = ltn12.sink.table(back)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user