aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ftp.lua67
-rw-r--r--src/http.lua17
-rw-r--r--src/tp.lua10
-rw-r--r--test/ftptest.lua34
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
51function metat.__index:pasvconnect() 51function 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))
55end 55end
56 56
57function metat.__index:login(user, password) 57function 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
82end 82end
83 83
84function metat.__index:port(ip, port) 84function 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
99end
100
101
102function 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
98end 116end
99 117
118function 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
130end
131
132
100function metat.__index:send(sendt) 133function 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
222local function parse(u) 255local 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
233end 266end
234 267
268_M.genericform = genericform
269
235local function sput(u, body) 270local 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)
239end 274end
@@ -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()
257end 292end
258 293
259local function sget(u) 294local 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
347end 347end
348 348
349local function srequest(u, b) 349-- turns an url and a body into a generic request
350local 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 366end
367
368_M.genericform = genericform
369
370local 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
365end 374end
366 375
367_M.request = socket.protect(function(reqt, body) 376_M.request = socket.protect(function(reqt, body)
diff --git a/src/tp.lua b/src/tp.lua
index 328cbab..b8ebc56 100644
--- a/src/tp.lua
+++ b/src/tp.lua
@@ -46,6 +46,14 @@ end
46-- metatable for sock object 46-- metatable for sock object
47local metat = { __index = {} } 47local metat = { __index = {} }
48 48
49function metat.__index:getpeername()
50 return self.c:getpeername()
51end
52
53function metat.__index:getsockname()
54 return self.c:getpeername()
55end
56
49function metat.__index:check(ok) 57function 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)
124end 132end
125 133
126return _M \ No newline at end of file 134return _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")
3local url = require("socket.url") 3local url = require("socket.url")
4local ltn12 = require("ltn12") 4local 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
9dofile("testsupport.lua") 22dofile("testsupport.lua")
10 23
11local host, port, index_file, index, back, err, ret 24local host = host or "localhost"
25local port, index_file, index, back, err, ret
12 26
13local t = socket.gettime() 27local t = socket.gettime()
14 28
15host = host or "localhost"
16index_file = "index.html" 29index_file = "index.html"
17 30
18
19-- a function that returns a directory listing 31-- a function that returns a directory listing
20local function nlst(u) 32local function nlst(u)
21 local t = {} 33 local t = {}
@@ -55,27 +67,27 @@ assert(not err and back == index, err)
55print("ok") 67print("ok")
56 68
57io.write("erasing before upload: ") 69io.write("erasing before upload: ")
58ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") 70ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
59if not ret then print(err) 71if not ret then print(err)
60else print("ok") end 72else print("ok") end
61 73
62io.write("testing upload: ") 74io.write("testing upload: ")
63ret, err = ftp.put("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i", index) 75ret, err = ftp.put("ftp://luasocket:password@" .. host .. "/index.up.html;type=i", index)
64assert(ret and not err, err) 76assert(ret and not err, err)
65print("ok") 77print("ok")
66 78
67io.write("downloading uploaded file: ") 79io.write("downloading uploaded file: ")
68back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/index.up.html;type=i") 80back, err = ftp.get("ftp://luasocket:password@" .. host .. "/index.up.html;type=i")
69assert(ret and not err and index == back, err) 81assert(ret and not err and index == back, err)
70print("ok") 82print("ok")
71 83
72io.write("erasing after upload/download: ") 84io.write("erasing after upload/download: ")
73ret, err = dele("ftp://luasocket:pedrovian@" .. host .. "/index.up.html") 85ret, err = dele("ftp://luasocket:password@" .. host .. "/index.up.html")
74assert(ret and not err, err) 86assert(ret and not err, err)
75print("ok") 87print("ok")
76 88
77io.write("testing weird-character translation: ") 89io.write("testing weird-character translation: ")
78back, err = ftp.get("ftp://luasocket:pedrovian@" .. host .. "/%23%3f;type=i") 90back, err = ftp.get("ftp://luasocket:password@" .. host .. "/%23%3f;type=i")
79assert(not err and back == index, err) 91assert(not err and back == index, err)
80print("ok") 92print("ok")
81 93
@@ -84,7 +96,7 @@ local back = {}
84ret, err = ftp.get{ 96ret, 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}