aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2001-01-25 22:03:16 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2001-01-25 22:03:16 +0000
commita466bd5d4266047e3a54387a76ad5665055e583a (patch)
treeafe41b02beebe3b8f334cdc13299c111d24b853a /src
parent273fd0964e4d6fd3f5457d090633596feb6d582a (diff)
downloadluasocket-a466bd5d4266047e3a54387a76ad5665055e583a.tar.gz
luasocket-a466bd5d4266047e3a54387a76ad5665055e583a.tar.bz2
luasocket-a466bd5d4266047e3a54387a76ad5665055e583a.zip
Data connection is now passive. Even minimum FTP servers are usable.
Diffstat (limited to 'src')
-rw-r--r--src/ftp.lua135
1 files changed, 73 insertions, 62 deletions
diff --git a/src/ftp.lua b/src/ftp.lua
index b817356..a9dac71 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -1,5 +1,5 @@
1----------------------------------------------------------------------------- 1-----------------------------------------------------------------------------
2-- Simple FTP support for the Lua language using the LuaSocket toolkit. 2-- Simple FTP support for the Lua language using the LuaSocket 1.2 toolkit.
3-- Author: Diego Nehab 3-- Author: Diego Nehab
4-- Date: 26/12/2000 4-- Date: 26/12/2000
5-- Conforming to: RFC 959 5-- Conforming to: RFC 959
@@ -170,20 +170,22 @@ end
170-- Input 170-- Input
171-- file: abolute path to file 171-- file: abolute path to file
172-- Returns 172-- Returns
173-- file: filename 173-- a table with the following fields
174-- path: table with directories to reach filename 174-- name: filename
175-- isdir: is it a directory or a file 175-- path: directory to file
176-- isdir: is it a directory?
176----------------------------------------------------------------------------- 177-----------------------------------------------------------------------------
177local split_path = function(file) 178local split_path = function(file)
178 local path = {} 179 local parsed = {}
179 local isdir 180 file = gsub(file, "(/)$", function(i) %parsed.isdir = i end)
180 file = file or "/" 181 if not parsed.isdir then
181 -- directory ends with a '/' 182 file = gsub(file, "([^/]+)$", function(n) %parsed.name = n end)
182 _,_, isdir = strfind(file, "([/])$") 183 end
183 gsub(file, "([^/]+)", function (dir) tinsert(%path, dir) end) 184 file = gsub(file, "/$", "")
184 if not isdir then file = tremove(path) 185 file = gsub(file, "^/", "")
185 else file = nil end 186 if file == "" then file = nil end
186 return file, path, isdir 187 parsed.path = file
188 if parsed.path or parsed.name or parsed.isdir then return parsed end
187end 189end
188 190
189----------------------------------------------------------------------------- 191-----------------------------------------------------------------------------
@@ -226,40 +228,44 @@ end
226-- Change to target directory 228-- Change to target directory
227-- Input 229-- Input
228-- control: socket for control connection with server 230-- control: socket for control connection with server
229-- path: array with directories in order 231-- path: directory to change to
230-- Returns 232-- Returns
231-- code: nil if error 233-- code: nil if error
232-- answer: server answer or error message 234-- answer: server answer or error message
233----------------------------------------------------------------------------- 235-----------------------------------------------------------------------------
234local cwd = function(control, path) 236local cwd = function(control, path)
235 local code, answer = 250, "Home directory used" 237 local code, answer = 250, "Home directory used"
236 for i = 1, getn(path) do 238 if path then
237 code, answer = %try_command(control, "cwd", path[i], {250}) 239 code, answer = %try_command(control, "cwd", path, {250})
238 if not code then return nil, answer end
239 end 240 end
240 return code, answer 241 return code, answer
241end 242end
242 243
243----------------------------------------------------------------------------- 244-----------------------------------------------------------------------------
244-- Start data connection with server 245-- Change to target directory
245-- Input 246-- Input
246-- control: control connection with server 247-- control: socket for control connection with server
247-- Returns 248-- Returns
248-- data: socket for data connection with server, nil if error 249-- server: server socket bound to local address, nil if error
249-- answer: server answer or error message 250-- answer: error message if any
250----------------------------------------------------------------------------- 251-----------------------------------------------------------------------------
251local start_dataconnection = function(control) 252local port = function(control)
252 -- ask for passive data connection 253 local code, answer
253 local code, answer = %try_command(control, "pasv", nil, {227}) 254 local server, ctl_ip
254 if not code then return nil, answer end 255 ctl_ip, answer = control:getsockname()
255 -- get data connection parameters from server reply 256 server, answer = bind(ctl_ip, 0)
256 local host, port = %get_pasv(answer) 257 server:timeout(%TIMEOUT)
257 if not host or not port then return nil, answer end 258 local ip, p, ph, pl
258 -- start data connection with given parameters 259 ip, p = server:getsockname()
259 local data, err = connect(host, port) 260 pl = mod(p, 256)
260 if not data then return nil, err end 261 ph = (p - pl)/256
261 data:timeout(%TIMEOUT) 262 local arg = gsub(format("%s,%d,%d", ip, ph, pl), "%.", ",")
262 return data 263 code, answer = %try_command(control, "port", arg, {200})
264 if not code then
265 control:close()
266 server:close()
267 return nil, answer
268 else return server end
263end 269end
264 270
265----------------------------------------------------------------------------- 271-----------------------------------------------------------------------------
@@ -281,22 +287,21 @@ end
281-- Retrieves file or directory listing 287-- Retrieves file or directory listing
282-- Input 288-- Input
283-- control: control connection with server 289-- control: control connection with server
284-- data: data connection with server 290-- server: server socket bound to local address
285-- file: file name under current directory 291-- file: file name under current directory
286-- isdir: is file a directory name? 292-- isdir: is file a directory name?
287-- Returns 293-- Returns
288-- file: string with file contents, nil if error 294-- file: string with file contents, nil if error
289-- answer: server answer or error message 295-- answer: server answer or error message
290----------------------------------------------------------------------------- 296-----------------------------------------------------------------------------
291local retrieve_file = function(control, data, file, isdir) 297local retrieve_file = function(control, server, file, isdir)
298 local data
292 -- ask server for file or directory listing accordingly 299 -- ask server for file or directory listing accordingly
293 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125}) 300 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125})
294 else code, answer = %try_command(control, "retr", file, {150, 125}) end 301 else code, answer = %try_command(control, "retr", file, {150, 125}) end
295 if not code then 302 data, answer = server:accept()
296 control:close() 303 server:close()
297 data:close() 304 if not data then return nil, answer end
298 return nil, answer
299 end
300 -- download whole file 305 -- download whole file
301 file, err = data:receive("*a") 306 file, err = data:receive("*a")
302 data:close() 307 data:close()
@@ -314,19 +319,23 @@ end
314-- Stores a file 319-- Stores a file
315-- Input 320-- Input
316-- control: control connection with server 321-- control: control connection with server
317-- data: data connection with server 322-- server: server socket bound to local address
318-- file: file name under current directory 323-- file: file name under current directory
319-- bytes: file contents in string 324-- bytes: file contents in string
320-- Returns 325-- Returns
321-- file: string with file contents, nil if error 326-- code: return code, nil if error
322-- answer: server answer or error message 327-- answer: server answer or error message
323----------------------------------------------------------------------------- 328-----------------------------------------------------------------------------
324local store_file = function (control, data, file, bytes) 329local store_file = function (control, server, file, bytes)
330 local data
325 local code, answer = %try_command(control, "stor", file, {150, 125}) 331 local code, answer = %try_command(control, "stor", file, {150, 125})
326 if not code then 332 if not code then
327 data:close() 333 data:close()
328 return nil, answer 334 return nil, answer
329 end 335 end
336 data, answer = server:accept()
337 server:close()
338 if not data then return nil, answer end
330 -- send whole file and close connection to mark file end 339 -- send whole file and close connection to mark file end
331 answer = data:send(bytes) 340 answer = data:send(bytes)
332 data:close() 341 data:close()
@@ -362,8 +371,8 @@ end
362-- err: error message if any 371-- err: error message if any
363----------------------------------------------------------------------------- 372-----------------------------------------------------------------------------
364function ftp_get(url, type) 373function ftp_get(url, type)
365 local control, data, err 374 local control, server, data, err
366 local answer, code, server, file, path 375 local answer, code, server, pfile, file
367 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 376 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
368 -- start control connection 377 -- start control connection
369 control, err = connect(parsed.host, parsed.port) 378 control, err = connect(parsed.host, parsed.port)
@@ -376,21 +385,22 @@ function ftp_get(url, type)
376 code, answer = %login(control, parsed.user, parsed.pass) 385 code, answer = %login(control, parsed.user, parsed.pass)
377 if not code then return nil, answer end 386 if not code then return nil, answer end
378 -- go to directory 387 -- go to directory
379 file, path, isdir = %split_path(parsed.path) 388 pfile = %split_path(parsed.path)
380 code, answer = %cwd(control, path) 389 if not pfile then return nil, "invalid path" end
390 code, answer = %cwd(control, pfile.path)
381 if not code then return nil, answer end 391 if not code then return nil, answer end
382 -- change to binary type? 392 -- change to binary type?
383 code, answer = %change_type(control, type) 393 code, answer = %change_type(control, type)
384 if not code then return nil, answer end 394 if not code then return nil, answer end
385 -- start data connection 395 -- setup passive connection
386 data, answer = %start_dataconnection(control) 396 server, answer = %port(control)
387 if not data then return nil, answer end 397 if not server then return nil, answer end
388 -- ask server to send file or directory listing 398 -- ask server to send file or directory listing
389 file, answer = %retrieve_file(control, data, file, isdir) 399 file, answer = %retrieve_file(control, server, pfile.name, pfile.isdir)
390 if not file then return nil, answer end 400 if not file then return nil, answer end
391 -- disconnect 401 -- disconnect
392 %logout(control) 402 %logout(control)
393 -- return whatever file we received plus a possible error 403 -- return whatever file we received plus a possible error message
394 return file, answer 404 return file, answer
395end 405end
396 406
@@ -405,7 +415,7 @@ end
405----------------------------------------------------------------------------- 415-----------------------------------------------------------------------------
406function ftp_put(url, bytes, type) 416function ftp_put(url, bytes, type)
407 local control, data 417 local control, data
408 local answer, code, server, file, path 418 local answer, code, server, file, pfile
409 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 419 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
410 -- start control connection 420 -- start control connection
411 control, answer = connect(parsed.host, parsed.port) 421 control, answer = connect(parsed.host, parsed.port)
@@ -418,20 +428,21 @@ function ftp_put(url, bytes, type)
418 code, answer = %login(control, parsed.user, parsed.pass) 428 code, answer = %login(control, parsed.user, parsed.pass)
419 if not code then return answer end 429 if not code then return answer end
420 -- go to directory 430 -- go to directory
421 file, path, isdir = %split_path(parsed.path) 431 pfile = %split_path(parsed.path)
422 code, answer = %cwd(control, path) 432 if not pfile or pfile.isdir then return "invalid path" end
433 code, answer = %cwd(control, pfile.path)
423 if not code then return answer end 434 if not code then return answer end
424 -- change to binary type? 435 -- change to binary type?
425 code, answer = %change_type(control, type) 436 code, answer = %change_type(control, type)
426 if not code then return answer end 437 if not code then return answer end
427 -- start data connection 438 -- setup passive connection
428 data, answer = %start_dataconnection(control) 439 server, answer = %port(control)
429 if not data then return answer end 440 if not server then return answer end
430 -- ask server to send file or directory listing 441 -- ask server to send file
431 code, answer = %store_file(control, data, file, bytes) 442 code, answer = %store_file(control, server, pfile.name, bytes)
432 if not code then return answer end 443 if not code then return answer end
433 -- disconnect 444 -- disconnect
434 %logout(control) 445 %logout(control)
435 -- return whatever file we received plus a possible error 446 -- no errors
436 return nil 447 return nil
437end 448end