diff options
-rw-r--r-- | src/ftp.lua | 67 | ||||
-rw-r--r-- | src/http.lua | 17 | ||||
-rw-r--r-- | src/tp.lua | 10 | ||||
-rw-r--r-- | test/ftptest.lua | 34 |
4 files changed, 96 insertions, 32 deletions
diff --git a/src/ftp.lua b/src/ftp.lua index e0c3cae..11798ad 100644 --- a/src/ftp.lua +++ b/src/ftp.lua | |||
@@ -51,7 +51,7 @@ end | |||
51 | function metat.__index:pasvconnect() | 51 | function metat.__index:pasvconnect() |
52 | self.data = self.try(socket.tcp()) | 52 | self.data = self.try(socket.tcp()) |
53 | self.try(self.data:settimeout(_M.TIMEOUT)) | 53 | self.try(self.data:settimeout(_M.TIMEOUT)) |
54 | self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) | 54 | self.try(self.data:connect(self.pasvt.address, self.pasvt.port)) |
55 | end | 55 | end |
56 | 56 | ||
57 | function metat.__index:login(user, password) | 57 | function metat.__index:login(user, password) |
@@ -71,32 +71,65 @@ function metat.__index:pasv() | |||
71 | local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) | 71 | local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) |
72 | self.try(a and b and c and d and p1 and p2, reply) | 72 | self.try(a and b and c and d and p1 and p2, reply) |
73 | self.pasvt = { | 73 | self.pasvt = { |
74 | ip = string.format("%d.%d.%d.%d", a, b, c, d), | 74 | address = string.format("%d.%d.%d.%d", a, b, c, d), |
75 | port = p1*256 + p2 | 75 | port = p1*256 + p2 |
76 | } | 76 | } |
77 | if self.server then | 77 | if self.server then |
78 | self.server:close() | 78 | self.server:close() |
79 | self.server = nil | 79 | self.server = nil |
80 | end | 80 | end |
81 | return self.pasvt.ip, self.pasvt.port | 81 | return self.pasvt.address, self.pasvt.port |
82 | end | 82 | end |
83 | 83 | ||
84 | function metat.__index:port(ip, port) | 84 | function metat.__index:epsv() |
85 | self.try(self.tp:command("epsv")) | ||
86 | local code, reply = self.try(self.tp:check("229")) | ||
87 | local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" | ||
88 | local d, prt, address, port = string.match(reply, pattern) | ||
89 | self.try(port, "invalid epsv response") | ||
90 | self.pasvt = { | ||
91 | address = self.tp:getpeername(), | ||
92 | port = port | ||
93 | } | ||
94 | if self.server then | ||
95 | self.server:close() | ||
96 | self.server = nil | ||
97 | end | ||
98 | return self.pasvt.address, self.pasvt.port | ||
99 | end | ||
100 | |||
101 | |||
102 | function metat.__index:port(address, port) | ||
85 | self.pasvt = nil | 103 | self.pasvt = nil |
86 | if not ip then | 104 | if not address then |
87 | ip, port = self.try(self.tp:getcontrol():getsockname()) | 105 | address, port = self.try(self.tp:getsockname()) |
88 | self.server = self.try(socket.bind(ip, 0)) | 106 | self.server = self.try(socket.bind(address, 0)) |
89 | ip, port = self.try(self.server:getsockname()) | 107 | address, port = self.try(self.server:getsockname()) |
90 | self.try(self.server:settimeout(_M.TIMEOUT)) | 108 | self.try(self.server:settimeout(_M.TIMEOUT)) |
91 | end | 109 | end |
92 | local pl = math.mod(port, 256) | 110 | local pl = math.mod(port, 256) |
93 | local ph = (port - pl)/256 | 111 | local ph = (port - pl)/256 |
94 | local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") | 112 | local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",") |
95 | self.try(self.tp:command("port", arg)) | 113 | self.try(self.tp:command("port", arg)) |
96 | self.try(self.tp:check("2..")) | 114 | self.try(self.tp:check("2..")) |
97 | return 1 | 115 | return 1 |
98 | end | 116 | end |
99 | 117 | ||
118 | function metat.__index:eprt(family, address, port) | ||
119 | self.pasvt = nil | ||
120 | if not address then | ||
121 | address, port = self.try(self.tp:getsockname()) | ||
122 | self.server = self.try(socket.bind(address, 0)) | ||
123 | address, port = self.try(self.server:getsockname()) | ||
124 | self.try(self.server:settimeout(_M.TIMEOUT)) | ||
125 | end | ||
126 | local arg = string.format("|%s|%s|%d|", family, address, port) | ||
127 | self.try(self.tp:command("eprt", arg)) | ||
128 | self.try(self.tp:check("2..")) | ||
129 | return 1 | ||
130 | end | ||
131 | |||
132 | |||
100 | function metat.__index:send(sendt) | 133 | function metat.__index:send(sendt) |
101 | self.try(self.pasvt or self.server, "need port or pasv first") | 134 | self.try(self.pasvt or self.server, "need port or pasv first") |
102 | -- if there is a pasvt table, we already sent a PASV command | 135 | -- if there is a pasvt table, we already sent a PASV command |
@@ -110,12 +143,12 @@ function metat.__index:send(sendt) | |||
110 | -- send the transfer command and check the reply | 143 | -- send the transfer command and check the reply |
111 | self.try(self.tp:command(command, argument)) | 144 | self.try(self.tp:command(command, argument)) |
112 | local code, reply = self.try(self.tp:check{"2..", "1.."}) | 145 | local code, reply = self.try(self.tp:check{"2..", "1.."}) |
113 | -- if there is not a a pasvt table, then there is a server | 146 | -- if there is not a pasvt table, then there is a server |
114 | -- and we already sent a PORT command | 147 | -- and we already sent a PORT command |
115 | if not self.pasvt then self:portconnect() end | 148 | if not self.pasvt then self:portconnect() end |
116 | -- get the sink, source and step for the transfer | 149 | -- get the sink, source and step for the transfer |
117 | local step = sendt.step or ltn12.pump.step | 150 | local step = sendt.step or ltn12.pump.step |
118 | local readt = {self.tp.c} | 151 | local readt = { self.tp } |
119 | local checkstep = function(src, snk) | 152 | local checkstep = function(src, snk) |
120 | -- check status in control connection while downloading | 153 | -- check status in control connection while downloading |
121 | local readyt = socket.select(readt, nil, 0) | 154 | local readyt = socket.select(readt, nil, 0) |
@@ -207,7 +240,7 @@ local function tput(putt) | |||
207 | f:greet() | 240 | f:greet() |
208 | f:login(putt.user, putt.password) | 241 | f:login(putt.user, putt.password) |
209 | if putt.type then f:type(putt.type) end | 242 | if putt.type then f:type(putt.type) end |
210 | f:pasv() | 243 | f:epsv() |
211 | local sent = f:send(putt) | 244 | local sent = f:send(putt) |
212 | f:quit() | 245 | f:quit() |
213 | f:close() | 246 | f:close() |
@@ -219,7 +252,7 @@ local default = { | |||
219 | scheme = "ftp" | 252 | scheme = "ftp" |
220 | } | 253 | } |
221 | 254 | ||
222 | local function parse(u) | 255 | local function genericform(u) |
223 | local t = socket.try(url.parse(u, default)) | 256 | local t = socket.try(url.parse(u, default)) |
224 | socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") | 257 | socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") |
225 | socket.try(t.host, "missing hostname") | 258 | socket.try(t.host, "missing hostname") |
@@ -232,8 +265,10 @@ local function parse(u) | |||
232 | return t | 265 | return t |
233 | end | 266 | end |
234 | 267 | ||
268 | _M.genericform = genericform | ||
269 | |||
235 | local function sput(u, body) | 270 | local function sput(u, body) |
236 | local putt = parse(u) | 271 | local putt = genericform(u) |
237 | putt.source = ltn12.source.string(body) | 272 | putt.source = ltn12.source.string(body) |
238 | return tput(putt) | 273 | return tput(putt) |
239 | end | 274 | end |
@@ -250,14 +285,14 @@ local function tget(gett) | |||
250 | f:greet() | 285 | f:greet() |
251 | f:login(gett.user, gett.password) | 286 | f:login(gett.user, gett.password) |
252 | if gett.type then f:type(gett.type) end | 287 | if gett.type then f:type(gett.type) end |
253 | f:pasv() | 288 | f:epsv() |
254 | f:receive(gett) | 289 | f:receive(gett) |
255 | f:quit() | 290 | f:quit() |
256 | return f:close() | 291 | return f:close() |
257 | end | 292 | end |
258 | 293 | ||
259 | local function sget(u) | 294 | local function sget(u) |
260 | local gett = parse(u) | 295 | local gett = genericform(u) |
261 | local t = {} | 296 | local t = {} |
262 | gett.sink = ltn12.sink.table(t) | 297 | gett.sink = ltn12.sink.table(t) |
263 | tget(gett) | 298 | tget(gett) |
diff --git a/src/http.lua b/src/http.lua index d6bcc91..f2fff01 100644 --- a/src/http.lua +++ b/src/http.lua | |||
@@ -346,11 +346,13 @@ end | |||
346 | return 1, code, headers, status | 346 | return 1, code, headers, status |
347 | end | 347 | end |
348 | 348 | ||
349 | local function srequest(u, b) | 349 | -- turns an url and a body into a generic request |
350 | local function genericform(u, b) | ||
350 | local t = {} | 351 | local t = {} |
351 | local reqt = { | 352 | local reqt = { |
352 | url = u, | 353 | url = u, |
353 | sink = ltn12.sink.table(t) | 354 | sink = ltn12.sink.table(t), |
355 | target = t | ||
354 | } | 356 | } |
355 | if b then | 357 | if b then |
356 | reqt.source = ltn12.source.string(b) | 358 | reqt.source = ltn12.source.string(b) |
@@ -360,8 +362,15 @@ local function srequest(u, b) | |||
360 | } | 362 | } |
361 | reqt.method = "POST" | 363 | reqt.method = "POST" |
362 | end | 364 | end |
363 | local code, headers, status = socket.skip(1, trequest(reqt)) | 365 | return reqt |
364 | return table.concat(t), code, headers, status | 366 | end |
367 | |||
368 | _M.genericform = genericform | ||
369 | |||
370 | local function srequest(u, b) | ||
371 | local reqt = genericform(u, b) | ||
372 | local _, code, headers, status = trequest(reqt) | ||
373 | return table.concat(reqt.target), code, headers, status | ||
365 | end | 374 | end |
366 | 375 | ||
367 | _M.request = socket.protect(function(reqt, body) | 376 | _M.request = socket.protect(function(reqt, body) |
@@ -46,6 +46,14 @@ end | |||
46 | -- metatable for sock object | 46 | -- metatable for sock object |
47 | local metat = { __index = {} } | 47 | local metat = { __index = {} } |
48 | 48 | ||
49 | function metat.__index:getpeername() | ||
50 | return self.c:getpeername() | ||
51 | end | ||
52 | |||
53 | function metat.__index:getsockname() | ||
54 | return self.c:getpeername() | ||
55 | end | ||
56 | |||
49 | function metat.__index:check(ok) | 57 | function metat.__index:check(ok) |
50 | local code, reply = get_reply(self.c) | 58 | local code, reply = get_reply(self.c) |
51 | if not code then return nil, reply end | 59 | if not code then return nil, reply end |
@@ -123,4 +131,4 @@ function _M.connect(host, port, timeout, create) | |||
123 | return base.setmetatable({c = c}, metat) | 131 | return base.setmetatable({c = c}, metat) |
124 | end | 132 | end |
125 | 133 | ||
126 | return _M \ No newline at end of file | 134 | return _M |
diff --git a/test/ftptest.lua b/test/ftptest.lua index fb13326..3ea0d39 100644 --- a/test/ftptest.lua +++ b/test/ftptest.lua | |||
@@ -3,19 +3,31 @@ local ftp = require("socket.ftp") | |||
3 | local url = require("socket.url") | 3 | local url = require("socket.url") |
4 | local ltn12 = require("ltn12") | 4 | local ltn12 = require("ltn12") |
5 | 5 | ||
6 | -- use dscl to create user "luasocket" with password "password" | ||
7 | -- with home in /Users/diego/luasocket/test/ftp | ||
8 | -- with group com.apple.access_ftp | ||
9 | -- with shell set to /sbin/nologin | ||
10 | -- set /etc/ftpchroot to chroot luasocket | ||
11 | -- must set group com.apple.access_ftp on user _ftp (for anonymous access) | ||
12 | -- copy index.html to /var/empty/pub (home of user ftp) | ||
13 | -- start ftp server with | ||
14 | -- sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist | ||
15 | -- copy index.html to /Users/diego/luasocket/test/ftp | ||
16 | -- stop with | ||
17 | -- sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist | ||
18 | |||
6 | -- override protection to make sure we see all errors | 19 | -- override protection to make sure we see all errors |
7 | --socket.protect = function(s) return s end | 20 | --socket.protect = function(s) return s end |
8 | 21 | ||
9 | dofile("testsupport.lua") | 22 | dofile("testsupport.lua") |
10 | 23 | ||
11 | local host, port, index_file, index, back, err, ret | 24 | local host = host or "localhost" |
25 | local port, index_file, index, back, err, ret | ||
12 | 26 | ||
13 | local t = socket.gettime() | 27 | local t = socket.gettime() |
14 | 28 | ||
15 | host = host or "localhost" | ||
16 | index_file = "index.html" | 29 | index_file = "index.html" |
17 | 30 | ||
18 | |||
19 | -- a function that returns a directory listing | 31 | -- a function that returns a directory listing |
20 | local function nlst(u) | 32 | local function nlst(u) |
21 | local t = {} | 33 | local t = {} |
@@ -55,27 +67,27 @@ assert(not err and back == index, err) | |||
55 | print("ok") | 67 | print("ok") |
56 | 68 | ||
57 | io.write("erasing before upload: ") | 69 | io.write("erasing before upload: ") |
58 | ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") | 70 | ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") |
59 | if not ret then print(err) | 71 | if not ret then print(err) |
60 | else print("ok") end | 72 | else print("ok") end |
61 | 73 | ||
62 | io.write("testing upload: ") | 74 | io.write("testing upload: ") |
63 | ret, err = ftp.put("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i", index) | 75 | ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index) |
64 | assert(ret and not err, err) | 76 | assert(ret and not err, err) |
65 | print("ok") | 77 | print("ok") |
66 | 78 | ||
67 | io.write("downloading uploaded file: ") | 79 | io.write("downloading uploaded file: ") |
68 | back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i") | 80 | back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i") |
69 | assert(ret and not err and index == back, err) | 81 | assert(ret and not err and index == back, err) |
70 | print("ok") | 82 | print("ok") |
71 | 83 | ||
72 | io.write("erasing after upload/download: ") | 84 | io.write("erasing after upload/download: ") |
73 | ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") | 85 | ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html") |
74 | assert(ret and not err, err) | 86 | assert(ret and not err, err) |
75 | print("ok") | 87 | print("ok") |
76 | 88 | ||
77 | io.write("testing weird-character translation: ") | 89 | io.write("testing weird-character translation: ") |
78 | back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/%23%3f;type=i") | 90 | back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i") |
79 | assert(not err and back == index, err) | 91 | assert(not err and back == index, err) |
80 | print("ok") | 92 | print("ok") |
81 | 93 | ||
@@ -84,7 +96,7 @@ local back = {} | |||
84 | ret, err = ftp.get{ | 96 | ret, err = ftp.get{ |
85 | url = "//stupid:mistake@" .. host .. "/index.html", | 97 | url = "//stupid:mistake@" .. host .. "/index.html", |
86 | user = "luasocket", | 98 | user = "luasocket", |
87 | password = "pedrovian", | 99 | password = "password", |
88 | type = "i", | 100 | type = "i", |
89 | sink = ltn12.sink.table(back) | 101 | sink = ltn12.sink.table(back) |
90 | } | 102 | } |