aboutsummaryrefslogtreecommitdiff
path: root/src/ftp.lua
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2001-06-06 20:55:45 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2001-06-06 20:55:45 +0000
commitc53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea (patch)
tree07d34dbcd03c4ac81353fb716ea4e7c914e0e633 /src/ftp.lua
parent77090c53fe558c48680516410a928bece08dd0d5 (diff)
downloadluasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.tar.gz
luasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.tar.bz2
luasocket-c53ad62b00b9dbf2a54214c2c6bf3f06d7c43eea.zip
Streaming by callbacks implemented.
Diffstat (limited to 'src/ftp.lua')
-rw-r--r--src/ftp.lua173
1 files changed, 135 insertions, 38 deletions
diff --git a/src/ftp.lua b/src/ftp.lua
index a9dac71..229bbc6 100644
--- a/src/ftp.lua
+++ b/src/ftp.lua
@@ -15,6 +15,8 @@ local PORT = 21
15-- this is the default anonymous password. used when no password is 15-- this is the default anonymous password. used when no password is
16-- provided in url. should be changed for your e-mail. 16-- provided in url. should be changed for your e-mail.
17local EMAIL = "anonymous@anonymous.org" 17local EMAIL = "anonymous@anonymous.org"
18-- block size used in transfers
19local BLOCKSIZE = 4096
18 20
19----------------------------------------------------------------------------- 21-----------------------------------------------------------------------------
20-- Parses a url and returns its scheme, user, password, host, port 22-- Parses a url and returns its scheme, user, password, host, port
@@ -68,7 +70,7 @@ local get_pasv = function(pasv)
68 local ip, port 70 local ip, port
69 _,_, a, b, c, d, p1, p2 = 71 _,_, a, b, c, d, p1, p2 =
70 strfind(pasv, "(%d*),(%d*),(%d*),(%d*),(%d*),(%d*)") 72 strfind(pasv, "(%d*),(%d*),(%d*),(%d*),(%d*),(%d*)")
71 if not a or not b or not c or not d or not p1 or not p2 then 73 if not (a and b and c and d and p1 and p2) then
72 return nil, nil 74 return nil, nil
73 end 75 end
74 ip = format("%d.%d.%d.%d", a, b, c, d) 76 ip = format("%d.%d.%d.%d", a, b, c, d)
@@ -82,6 +84,8 @@ end
82-- control: control connection socket 84-- control: control connection socket
83-- cmd: command 85-- cmd: command
84-- arg: command argument if any 86-- arg: command argument if any
87-- Returns
88-- error message in case of error, nil otherwise
85----------------------------------------------------------------------------- 89-----------------------------------------------------------------------------
86local send_command = function(control, cmd, arg) 90local send_command = function(control, cmd, arg)
87 local line, err 91 local line, err
@@ -284,35 +288,84 @@ local logout = function(control)
284end 288end
285 289
286----------------------------------------------------------------------------- 290-----------------------------------------------------------------------------
291-- Receives data and send it to a callback
292-- Input
293-- data: data connection
294-- callback: callback to return file contents
295-- Returns
296-- nil if successfull, or an error message in case of error
297-----------------------------------------------------------------------------
298local receive_indirect = function(data, callback)
299 local chunk, err, res
300 while not err do
301 chunk, err = data:receive(%BLOCKSIZE)
302 if err == "closed" then err = "done" end
303 res = callback(chunk, err)
304 if not res then break end
305 end
306end
307
308-----------------------------------------------------------------------------
287-- Retrieves file or directory listing 309-- Retrieves file or directory listing
288-- Input 310-- Input
289-- control: control connection with server 311-- control: control connection with server
290-- server: server socket bound to local address 312-- server: server socket bound to local address
291-- file: file name under current directory 313-- file: file name under current directory
292-- isdir: is file a directory name? 314-- isdir: is file a directory name?
315-- callback: callback to receive file contents
293-- Returns 316-- Returns
294-- file: string with file contents, nil if error 317-- err: error message in case of error, nil otherwise
295-- answer: server answer or error message
296----------------------------------------------------------------------------- 318-----------------------------------------------------------------------------
297local retrieve_file = function(control, server, file, isdir) 319local retrieve = function(control, server, file, isdir, callback)
320 local code, answer
298 local data 321 local data
299 -- ask server for file or directory listing accordingly 322 -- ask server for file or directory listing accordingly
300 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125}) 323 if isdir then code, answer = %try_command(control, "nlst", file, {150, 125})
301 else code, answer = %try_command(control, "retr", file, {150, 125}) end 324 else code, answer = %try_command(control, "retr", file, {150, 125}) end
302 data, answer = server:accept() 325 data, answer = server:accept()
303 server:close() 326 server:close()
304 if not data then return nil, answer end 327 if not data then
305 -- download whole file
306 file, err = data:receive("*a")
307 data:close()
308 if err then
309 control:close() 328 control:close()
310 return nil, err 329 return answer
311 end 330 end
331 answer = %receive_indirect(data, callback)
332 if answer then
333 control:close()
334 return answer
335 end
336 data:close()
312 -- make sure file transfered ok 337 -- make sure file transfered ok
313 code, answer = %check_answer(control, {226, 250}) 338 code, answer = %check_answer(control, {226, 250})
314 if not code then return nil, answer 339 if not code then return answer end
315 else return file, answer end 340end
341
342-----------------------------------------------------------------------------
343-- Sends data comming from a callback
344-- Input
345-- data: data connection
346-- send_cb: callback to produce file contents
347-- chunk, size: first callback results
348-- Returns
349-- nil if successfull, or an error message in case of error
350-----------------------------------------------------------------------------
351local try_sendindirect = function(data, send_cb, chunk, size)
352 local sent, err
353 sent = 0
354 while 1 do
355 if type(chunk) ~= "string" or type(size) ~= "number" then
356 data:close()
357 if not chunk and type(size) == "string" then return size
358 else return "invalid callback return" end
359 end
360 err = data:send(chunk)
361 if err then
362 data:close()
363 return err
364 end
365 sent = sent + strlen(chunk)
366 if sent >= size then break end
367 chunk, size = send_cb()
368 end
316end 369end
317 370
318----------------------------------------------------------------------------- 371-----------------------------------------------------------------------------
@@ -321,29 +374,34 @@ end
321-- control: control connection with server 374-- control: control connection with server
322-- server: server socket bound to local address 375-- server: server socket bound to local address
323-- file: file name under current directory 376-- file: file name under current directory
324-- bytes: file contents in string 377-- send_cb: callback to produce the file contents
325-- Returns 378-- Returns
326-- code: return code, nil if error 379-- code: return code, nil if error
327-- answer: server answer or error message 380-- answer: server answer or error message
328----------------------------------------------------------------------------- 381-----------------------------------------------------------------------------
329local store_file = function (control, server, file, bytes) 382local store = function(control, server, file, send_cb)
330 local data 383 local data
331 local code, answer = %try_command(control, "stor", file, {150, 125}) 384 local code, answer = %try_command(control, "stor", file, {150, 125})
332 if not code then 385 if not code then
333 data:close() 386 control:close()
334 return nil, answer 387 return nil, answer
335 end 388 end
389 -- start data connection
336 data, answer = server:accept() 390 data, answer = server:accept()
337 server:close() 391 server:close()
338 if not data then return nil, answer end 392 if not data then
339 -- send whole file and close connection to mark file end
340 answer = data:send(bytes)
341 data:close()
342 if answer then
343 control:close() 393 control:close()
344 return nil, answer 394 return nil, answer
395 end
396 -- send whole file
397 err = %try_sendindirect(data, send_cb, send_cb())
398 if err then
399 control:close()
400 return nil, err
345 end 401 end
346 -- check if file was received right 402 -- close connection to inform that file transmission is complete
403 data:close()
404 -- check if file was received correctly
347 return %check_answer(control, {226, 250}) 405 return %check_answer(control, {226, 250})
348end 406end
349 407
@@ -365,55 +423,53 @@ end
365-- Retrieve a file from a ftp server 423-- Retrieve a file from a ftp server
366-- Input 424-- Input
367-- url: file location 425-- url: file location
426-- receive_cb: callback to receive file contents
368-- type: "binary" or "ascii" 427-- type: "binary" or "ascii"
369-- Returns 428-- Returns
370-- file: downloaded file or nil in case of error
371-- err: error message if any 429-- err: error message if any
372----------------------------------------------------------------------------- 430-----------------------------------------------------------------------------
373function ftp_get(url, type) 431function ftp_getindirect(url, receive_cb, type)
374 local control, server, data, err 432 local control, server, data, err
375 local answer, code, server, pfile, file 433 local answer, code, server, pfile, file
376 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 434 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
377 -- start control connection 435 -- start control connection
378 control, err = connect(parsed.host, parsed.port) 436 control, err = connect(parsed.host, parsed.port)
379 if not control then return nil, err end 437 if not control then return err end
380 control:timeout(%TIMEOUT) 438 control:timeout(%TIMEOUT)
381 -- get and check greeting 439 -- get and check greeting
382 code, answer = %check_greeting(control) 440 code, answer = %check_greeting(control)
383 if not code then return nil, answer end 441 if not code then return answer end
384 -- try to log in 442 -- try to log in
385 code, answer = %login(control, parsed.user, parsed.pass) 443 code, answer = %login(control, parsed.user, parsed.pass)
386 if not code then return nil, answer end 444 if not code then return answer end
387 -- go to directory 445 -- go to directory
388 pfile = %split_path(parsed.path) 446 pfile = %split_path(parsed.path)
389 if not pfile then return nil, "invalid path" end 447 if not pfile then return "invalid path" end
390 code, answer = %cwd(control, pfile.path) 448 code, answer = %cwd(control, pfile.path)
391 if not code then return nil, answer end 449 if not code then return answer end
392 -- change to binary type? 450 -- change to binary type?
393 code, answer = %change_type(control, type) 451 code, answer = %change_type(control, type)
394 if not code then return nil, answer end 452 if not code then return answer end
395 -- setup passive connection 453 -- setup passive connection
396 server, answer = %port(control) 454 server, answer = %port(control)
397 if not server then return nil, answer end 455 if not server then return answer end
398 -- ask server to send file or directory listing 456 -- ask server to send file or directory listing
399 file, answer = %retrieve_file(control, server, pfile.name, pfile.isdir) 457 err = %retrieve(control, server, pfile.name, pfile.isdir, receive_cb)
400 if not file then return nil, answer end 458 if err then return err end
401 -- disconnect 459 -- disconnect
402 %logout(control) 460 %logout(control)
403 -- return whatever file we received plus a possible error message
404 return file, answer
405end 461end
406 462
407----------------------------------------------------------------------------- 463-----------------------------------------------------------------------------
408-- Uploads a file to a FTP server 464-- Uploads a file to a FTP server
409-- Input 465-- Input
410-- url: file location 466-- url: file location
411-- bytes: file contents 467-- send_cb: callback to produce the file contents
412-- type: "binary" or "ascii" 468-- type: "binary" or "ascii"
413-- Returns 469-- Returns
414-- err: error message if any 470-- err: error message if any
415----------------------------------------------------------------------------- 471-----------------------------------------------------------------------------
416function ftp_put(url, bytes, type) 472function ftp_putindirect(url, send_cb, type)
417 local control, data 473 local control, data
418 local answer, code, server, file, pfile 474 local answer, code, server, file, pfile
419 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL}) 475 parsed = %split_url(url, {user = "anonymous", port = 21, pass = %EMAIL})
@@ -439,10 +495,51 @@ function ftp_put(url, bytes, type)
439 server, answer = %port(control) 495 server, answer = %port(control)
440 if not server then return answer end 496 if not server then return answer end
441 -- ask server to send file 497 -- ask server to send file
442 code, answer = %store_file(control, server, pfile.name, bytes) 498 code, answer = %store(control, server, pfile.name, send_cb)
443 if not code then return answer end 499 if not code then return answer end
444 -- disconnect 500 -- disconnect
445 %logout(control) 501 %logout(control)
446 -- no errors 502 -- no errors
447 return nil 503 return nil
448end 504end
505
506-----------------------------------------------------------------------------
507-- Uploads a file to a FTP server
508-- Input
509-- url: file location
510-- bytes: file contents
511-- type: "binary" or "ascii"
512-- Returns
513-- err: error message if any
514-----------------------------------------------------------------------------
515function ftp_put(url, bytes, type)
516 local send_cb = function()
517 return %bytes, strlen(%bytes)
518 end
519 return ftp_putindirect(url, send_cb, type)
520end
521
522-----------------------------------------------------------------------------
523-- We need fast concatenation routines for direct requests
524-----------------------------------------------------------------------------
525dofile("buffer.lua")
526
527-----------------------------------------------------------------------------
528-- Retrieve a file from a ftp server
529-- Input
530-- url: file location
531-- type: "binary" or "ascii"
532-- Returns
533-- data: file contents as a string
534-- err: error message in case of error, nil otherwise
535-----------------------------------------------------------------------------
536function ftp_get(url, type)
537 local bytes = { buf = buf_create() }
538 local receive_cb = function(chunk, err)
539 if not chunk then %bytes.buf = nil end
540 buf_addstring(%bytes.buf, chunk)
541 return 1
542 end
543 err = ftp_getindirect(url, receive_cb, type)
544 return buf_getresult(bytes.buf), err
545end