Class | Net::FTP |
In: |
lib/net/ftp.rb
|
Parent: | Object |
This class implements the File Transfer Protocol. If you have used a command-line FTP program, and are familiar with the commands, you will be able to use this class easily. Some extra features are included to take advantage of Ruby‘s style and strengths.
require 'net/ftp'
ftp = Net::FTP.new('ftp.netlab.co.jp') ftp.login files = ftp.chdir('pub/lang/ruby/contrib') files = ftp.list('n*') ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) ftp.close
Net::FTP.open('ftp.netlab.co.jp') do |ftp| ftp.login files = ftp.chdir('pub/lang/ruby/contrib') files = ftp.list('n*') ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) end
The following are the methods most likely to be useful to users:
last_response_code | -> | lastresp |
binary | [RW] | When true, transfers are performed in binary mode. Default: true. |
debug_mode | [RW] | When true, all traffic to and from the server is written to +$stdout+. Default: false. |
last_response | [R] | The server‘s last response. |
last_response_code | [R] | The server‘s last response code. |
passive | [RW] | When true, the connection is in passive mode. Default: false. |
resume | [RW] | Sets or retrieves the resume status, which decides whether incomplete transfers are resumed or restarted. Default: false. |
welcome | [R] | The server‘s welcome message. |
Creates and returns a new FTP object. If a host is given, a connection is made. Additionally, if the user is given, the given user name, password, and (optionally) account are used to log in. See login.
# File lib/net/ftp.rb, line 129 129: def initialize(host = nil, user = nil, passwd = nil, acct = nil) 130: super() 131: @binary = true 132: @passive = false 133: @debug_mode = false 134: @resume = false 135: if host 136: connect(host) 137: if user 138: login(user, passwd, acct) 139: end 140: end 141: end
A synonym for FTP.new, but with a mandatory host parameter.
If a block is given, it is passed the FTP object, which will be closed when the block finishes, or when an exception is raised.
# File lib/net/ftp.rb, line 111 111: def FTP.open(host, user = nil, passwd = nil, acct = nil) 112: if block_given? 113: ftp = new(host, user, passwd, acct) 114: begin 115: yield ftp 116: ensure 117: ftp.close 118: end 119: else 120: new(host, user, passwd, acct) 121: end 122: end
Aborts the previous command (ABOR command).
# File lib/net/ftp.rb, line 745 745: def abort 746: line = "ABOR" + CRLF 747: print "put: ABOR\n" if @debug_mode 748: @sock.send(line, Socket::MSG_OOB) 749: resp = getmultiline 750: unless ["426", "226", "225"].include?(resp[0, 3]) 751: raise FTPProtoError, resp 752: end 753: return resp 754: end
Sends the ACCT command. TODO: more info.
# File lib/net/ftp.rb, line 598 598: def acct(account) 599: cmd = "ACCT " + account 600: voidcmd(cmd) 601: end
Changes the (remote) directory.
# File lib/net/ftp.rb, line 668 668: def chdir(dirname) 669: if dirname == ".." 670: begin 671: voidcmd("CDUP") 672: return 673: rescue FTPPermError => e 674: if e.message[0, 3] != "500" 675: raise e 676: end 677: end 678: end 679: cmd = "CWD " + dirname 680: voidcmd(cmd) 681: end
Returns true iff the connection is closed.
# File lib/net/ftp.rb, line 820 820: def closed? 821: @sock == nil or @sock.closed? 822: end
Establishes an FTP connection to host, optionally overriding the default port. If the environment variable SOCKS_SERVER is set, sets up the connection through a SOCKS proxy. Raises an exception (typically Errno::ECONNREFUSED) if the connection cannot be established.
# File lib/net/ftp.rb, line 170 170: def connect(host, port = FTP_PORT) 171: if @debug_mode 172: print "connect: ", host, ", ", port, "\n" 173: end 174: synchronize do 175: @sock = open_socket(host, port) 176: voidresp 177: end 178: end
Deletes a file on the server.
# File lib/net/ftp.rb, line 654 654: def delete(filename) 655: resp = sendcmd("DELE " + filename) 656: if resp[0, 3] == "250" 657: return 658: elsif resp[0] == ?5 659: raise FTPPermError, resp 660: else 661: raise FTPReplyError, resp 662: end 663: end
Retrieves remotefile in whatever mode the session is set (text or binary). See gettextfile and getbinaryfile.
# File lib/net/ftp.rb, line 534 534: def get(remotefile, localfile = File.basename(remotefile), 535: blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data 536: unless @binary 537: gettextfile(remotefile, localfile, &block) 538: else 539: getbinaryfile(remotefile, localfile, blocksize, &block) 540: end 541: end
Retrieves remotefile in binary mode, storing the result in localfile. If a block is supplied, it is passed the retrieved data in blocksize chunks.
# File lib/net/ftp.rb, line 493 493: def getbinaryfile(remotefile, localfile = File.basename(remotefile), 494: blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data 495: if @resume 496: rest_offset = File.size?(localfile) 497: f = open(localfile, "a") 498: else 499: rest_offset = nil 500: f = open(localfile, "w") 501: end 502: begin 503: f.binmode 504: retrbinary("RETR " + remotefile, blocksize, rest_offset) do |data| 505: f.write(data) 506: yield(data) if block 507: end 508: ensure 509: f.close 510: end 511: end
Retrieves remotefile in ASCII (text) mode, storing the result in localfile. If a block is supplied, it is passed the retrieved data one line at a time.
# File lib/net/ftp.rb, line 518 518: def gettextfile(remotefile, localfile = File.basename(remotefile), &block) # :yield: line 519: f = open(localfile, "w") 520: begin 521: retrlines("RETR " + remotefile) do |line| 522: f.puts(line) 523: yield(line) if block 524: end 525: ensure 526: f.close 527: end 528: end
Issues the HELP command.
# File lib/net/ftp.rb, line 779 779: def help(arg = nil) 780: cmd = "HELP" 781: if arg 782: cmd = cmd + " " + arg 783: end 784: sendcmd(cmd) 785: end
Returns an array of file information in the directory (the output is like `ls -l`). If a block is given, it iterates through the listing.
# File lib/net/ftp.rb, line 622 622: def list(*args, &block) # :yield: line 623: cmd = "LIST" 624: args.each do |arg| 625: cmd = cmd + " " + arg 626: end 627: if block 628: retrlines(cmd, &block) 629: else 630: lines = [] 631: retrlines(cmd) do |line| 632: lines << line 633: end 634: return lines 635: end 636: end
Logs in to the remote host. The session must have been previously connected. If user is the string "anonymous" and the password is nil, a password of user@host is synthesized. If the acct parameter is not nil, an FTP ACCT command is sent following the successful login. Raises an exception on error (typically Net::FTPPermError).
# File lib/net/ftp.rb, line 372 372: def login(user = "anonymous", passwd = nil, acct = nil) 373: if user == "anonymous" and passwd == nil 374: passwd = getaddress 375: end 376: 377: resp = "" 378: synchronize do 379: resp = sendcmd('USER ' + user) 380: if resp[0] == ?3 381: raise FTPReplyError, resp if passwd.nil? 382: resp = sendcmd('PASS ' + passwd) 383: end 384: if resp[0] == ?3 385: raise FTPReplyError, resp if acct.nil? 386: resp = sendcmd('ACCT ' + acct) 387: end 388: end 389: if resp[0] != ?2 390: raise FTPReplyError, resp 391: end 392: @welcome = resp 393: end
Issues the MDTM command. TODO: more info.
# File lib/net/ftp.rb, line 769 769: def mdtm(filename) 770: resp = sendcmd("MDTM " + filename) 771: if resp[0, 3] == "213" 772: return resp[3 .. -1].strip 773: end 774: end
Creates a remote directory.
# File lib/net/ftp.rb, line 710 710: def mkdir(dirname) 711: resp = sendcmd("MKD " + dirname) 712: return parse257(resp) 713: end
Returns the last modification time of the (remote) file. If local is true, it is returned as a local time, otherwise it‘s a UTC time.
# File lib/net/ftp.rb, line 701 701: def mtime(filename, local = false) 702: str = mdtm(filename) 703: ary = str.scan(MDTM_REGEXP)[0].collect {|i| i.to_i} 704: return local ? Time.local(*ary) : Time.gm(*ary) 705: end
Returns an array of filenames in the remote directory.
# File lib/net/ftp.rb, line 606 606: def nlst(dir = nil) 607: cmd = "NLST" 608: if dir 609: cmd = cmd + " " + dir 610: end 611: files = [] 612: retrlines(cmd) do |line| 613: files.push(line) 614: end 615: return files 616: end
Transfers localfile to the server in whatever mode the session is set (text or binary). See puttextfile and putbinaryfile.
# File lib/net/ftp.rb, line 586 586: def put(localfile, remotefile = File.basename(localfile), 587: blocksize = DEFAULT_BLOCKSIZE, &block) 588: unless @binary 589: puttextfile(localfile, remotefile, &block) 590: else 591: putbinaryfile(localfile, remotefile, blocksize, &block) 592: end 593: end
Transfers localfile to the server in binary mode, storing the result in remotefile. If a block is supplied, calls it, passing in the transmitted data in blocksize chunks.
# File lib/net/ftp.rb, line 548 548: def putbinaryfile(localfile, remotefile = File.basename(localfile), 549: blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data 550: if @resume 551: begin 552: rest_offset = size(remotefile) 553: rescue Net::FTPPermError 554: rest_offset = nil 555: end 556: else 557: rest_offset = nil 558: end 559: f = open(localfile) 560: begin 561: f.binmode 562: storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block) 563: ensure 564: f.close 565: end 566: end
Transfers localfile to the server in ASCII (text) mode, storing the result in remotefile. If callback or an associated block is supplied, calls it, passing in the transmitted data one line at a time.
# File lib/net/ftp.rb, line 573 573: def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line 574: f = open(localfile) 575: begin 576: storlines("STOR " + remotefile, f, &block) 577: ensure 578: f.close 579: end 580: end
Returns the current remote directory.
# File lib/net/ftp.rb, line 725 725: def pwd 726: resp = sendcmd("PWD") 727: return parse257(resp) 728: end
Renames a file on the server.
# File lib/net/ftp.rb, line 643 643: def rename(fromname, toname) 644: resp = sendcmd("RNFR " + fromname) 645: if resp[0] != ?3 646: raise FTPReplyError, resp 647: end 648: voidcmd("RNTO " + toname) 649: end
Puts the connection into binary (image) mode, issues the given command, and fetches the data returned, passing it to the associated block in chunks of blocksize characters. Note that cmd is a server command (such as "RETR myfile").
# File lib/net/ftp.rb, line 401 401: def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data 402: synchronize do 403: voidcmd("TYPE I") 404: conn = transfercmd(cmd, rest_offset) 405: loop do 406: data = conn.read(blocksize) 407: break if data == nil 408: yield(data) 409: end 410: conn.close 411: voidresp 412: end 413: end
Puts the connection into ASCII (text) mode, issues the given command, and passes the resulting data, one line at a time, to the associated block. If no block is given, prints the lines. Note that cmd is a server command (such as "RETR myfile").
# File lib/net/ftp.rb, line 421 421: def retrlines(cmd) # :yield: line 422: synchronize do 423: voidcmd("TYPE A") 424: conn = transfercmd(cmd) 425: loop do 426: line = conn.gets 427: break if line == nil 428: if line[-2, 2] == CRLF 429: line = line[0 .. -3] 430: elsif line[-1] == ?\n 431: line = line[0 .. -2] 432: end 433: yield(line) 434: end 435: conn.close 436: voidresp 437: end 438: end
Obsolete
# File lib/net/ftp.rb, line 144 144: def return_code 145: $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing") 146: return "\n" 147: end
Obsolete
# File lib/net/ftp.rb, line 150 150: def return_code=(s) 151: $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing") 152: end
Removes a remote directory.
# File lib/net/ftp.rb, line 718 718: def rmdir(dirname) 719: voidcmd("RMD " + dirname) 720: end
Sends a command and returns the response.
# File lib/net/ftp.rb, line 261 261: def sendcmd(cmd) 262: synchronize do 263: putline(cmd) 264: return getresp 265: end 266: end
WRITEME or make private
# File lib/net/ftp.rb, line 183 183: def set_socket(sock, get_greeting = true) 184: synchronize do 185: @sock = sock 186: if get_greeting 187: voidresp 188: end 189: end 190: end
Issues a SITE command.
# File lib/net/ftp.rb, line 804 804: def site(arg) 805: cmd = "SITE " + arg 806: voidcmd(cmd) 807: end
Puts the connection into binary (image) mode, issues the given server-side command (such as "STOR myfile"), and sends the contents of the file named file to the server. If the optional block is given, it also passes it the data, in chunks of blocksize characters.
# File lib/net/ftp.rb, line 446 446: def storbinary(cmd, file, blocksize, rest_offset = nil, &block) # :yield: data 447: if rest_offset 448: file.seek(rest_offset, IO::SEEK_SET) 449: end 450: synchronize do 451: voidcmd("TYPE I") 452: conn = transfercmd(cmd, rest_offset) 453: loop do 454: buf = file.read(blocksize) 455: break if buf == nil 456: conn.write(buf) 457: yield(buf) if block 458: end 459: conn.close 460: voidresp 461: end 462: end
Puts the connection into ASCII (text) mode, issues the given server-side command (such as "STOR myfile"), and sends the contents of the file named file to the server, one line at a time. If the optional block is given, it also passes it the lines.
# File lib/net/ftp.rb, line 470 470: def storlines(cmd, file, &block) # :yield: line 471: synchronize do 472: voidcmd("TYPE A") 473: conn = transfercmd(cmd) 474: loop do 475: buf = file.gets 476: break if buf == nil 477: if buf[-2, 2] != CRLF 478: buf = buf.chomp + CRLF 479: end 480: conn.write(buf) 481: yield(buf) if block 482: end 483: conn.close 484: voidresp 485: end 486: end
Sends a command and expect a response beginning with ‘2’.
# File lib/net/ftp.rb, line 271 271: def voidcmd(cmd) 272: synchronize do 273: putline(cmd) 274: voidresp 275: end 276: end
# File lib/net/ftp.rb, line 348 348: def getaddress 349: thishost = Socket.gethostname rescue "" 350: if not thishost.index(".") 351: thishost = Socket.gethostbyname(thishost)[0] rescue "" 352: end 353: if ENV.has_key?("LOGNAME") 354: realuser = ENV["LOGNAME"] 355: elsif ENV.has_key?("USER") 356: realuser = ENV["USER"] 357: else 358: realuser = "anonymous" 359: end 360: return realuser + "@" + thishost 361: end
# File lib/net/ftp.rb, line 210 210: def getline 211: line = @sock.readline # if get EOF, raise EOFError 212: line.sub!(/(\r\n|\n|\r)\z/n, "") 213: if @debug_mode 214: print "get: ", sanitize(line), "\n" 215: end 216: return line 217: end
# File lib/net/ftp.rb, line 220 220: def getmultiline 221: line = getline 222: buff = line 223: if line[3] == ?- 224: code = line[0, 3] 225: begin 226: line = getline 227: buff << "\n" << line 228: end until line[0, 3] == code and line[3] != ?- 229: end 230: return buff << "\n" 231: end
# File lib/net/ftp.rb, line 234 234: def getresp 235: @last_response = getmultiline 236: @last_response_code = @last_response[0, 3] 237: case @last_response_code 238: when /\A[123]/ 239: return @last_response 240: when /\A4/ 241: raise FTPTempError, @last_response 242: when /\A5/ 243: raise FTPPermError, @last_response 244: else 245: raise FTPProtoError, @last_response 246: end 247: end
# File lib/net/ftp.rb, line 300 300: def makepasv 301: if @sock.peeraddr[0] == "AF_INET" 302: host, port = parse227(sendcmd("PASV")) 303: else 304: host, port = parse229(sendcmd("EPSV")) 305: # host, port = parse228(sendcmd("LPSV")) 306: end 307: return host, port 308: end
# File lib/net/ftp.rb, line 291 291: def makeport 292: sock = TCPServer.open(@sock.addr[3], 0) 293: port = sock.addr[1] 294: host = sock.addr[3] 295: resp = sendport(host, port) 296: return sock 297: end
# File lib/net/ftp.rb, line 154 154: def open_socket(host, port) 155: if defined? SOCKSSocket and ENV["SOCKS_SERVER"] 156: @passive = true 157: return SOCKSSocket.open(host, port) 158: else 159: return TCPSocket.open(host, port) 160: end 161: end
# File lib/net/ftp.rb, line 824 824: def parse227(resp) 825: if resp[0, 3] != "227" 826: raise FTPReplyError, resp 827: end 828: left = resp.index("(") 829: right = resp.index(")") 830: if left == nil or right == nil 831: raise FTPProtoError, resp 832: end 833: numbers = resp[left + 1 .. right - 1].split(",") 834: if numbers.length != 6 835: raise FTPProtoError, resp 836: end 837: host = numbers[0, 4].join(".") 838: port = (numbers[4].to_i << 8) + numbers[5].to_i 839: return host, port 840: end
# File lib/net/ftp.rb, line 843 843: def parse228(resp) 844: if resp[0, 3] != "228" 845: raise FTPReplyError, resp 846: end 847: left = resp.index("(") 848: right = resp.index(")") 849: if left == nil or right == nil 850: raise FTPProtoError, resp 851: end 852: numbers = resp[left + 1 .. right - 1].split(",") 853: if numbers[0] == "4" 854: if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2" 855: raise FTPProtoError, resp 856: end 857: host = numbers[2, 4].join(".") 858: port = (numbers[7].to_i << 8) + numbers[8].to_i 859: elsif numbers[0] == "6" 860: if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2" 861: raise FTPProtoError, resp 862: end 863: v6 = ["", "", "", "", "", "", "", ""] 864: for i in 0 .. 7 865: v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i, 866: numbers[(i * 2) + 3].to_i) 867: end 868: host = v6[0, 8].join(":") 869: port = (numbers[19].to_i << 8) + numbers[20].to_i 870: end 871: return host, port 872: end
# File lib/net/ftp.rb, line 875 875: def parse229(resp) 876: if resp[0, 3] != "229" 877: raise FTPReplyError, resp 878: end 879: left = resp.index("(") 880: right = resp.index(")") 881: if left == nil or right == nil 882: raise FTPProtoError, resp 883: end 884: numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1]) 885: if numbers.length != 4 886: raise FTPProtoError, resp 887: end 888: port = numbers[3].to_i 889: host = (@sock.peeraddr())[3] 890: return host, port 891: end
# File lib/net/ftp.rb, line 894 894: def parse257(resp) 895: if resp[0, 3] != "257" 896: raise FTPReplyError, resp 897: end 898: if resp[3, 2] != ' "' 899: return "" 900: end 901: dirname = "" 902: i = 5 903: n = resp.length 904: while i < n 905: c = resp[i, 1] 906: i = i + 1 907: if c == '"' 908: if i > n or resp[i, 1] != '"' 909: break 910: end 911: i = i + 1 912: end 913: dirname = dirname + c 914: end 915: return dirname 916: end
# File lib/net/ftp.rb, line 201 201: def putline(line) 202: if @debug_mode 203: print "put: ", sanitize(line), "\n" 204: end 205: line = line + CRLF 206: @sock.write(line) 207: end
# File lib/net/ftp.rb, line 192 192: def sanitize(s) 193: if s =~ /^PASS /i 194: return s[0, 5] + "*" * (s.length - 5) 195: else 196: return s 197: end 198: end
# File lib/net/ftp.rb, line 278 278: def sendport(host, port) 279: af = (@sock.peeraddr)[0] 280: if af == "AF_INET" 281: cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",") 282: elsif af == "AF_INET6" 283: cmd = sprintf("EPRT |2|%s|%d|", host, port) 284: else 285: raise FTPProtoError, host 286: end 287: voidcmd(cmd) 288: end
# File lib/net/ftp.rb, line 311 311: def transfercmd(cmd, rest_offset = nil) 312: if @passive 313: host, port = makepasv 314: conn = open_socket(host, port) 315: if @resume and rest_offset 316: resp = sendcmd("REST " + rest_offset.to_s) 317: if resp[0] != ?3 318: raise FTPReplyError, resp 319: end 320: end 321: resp = sendcmd(cmd) 322: # skip 2XX for some ftp servers 323: resp = getresp if resp[0] == ?2 324: if resp[0] != ?1 325: raise FTPReplyError, resp 326: end 327: else 328: sock = makeport 329: if @resume and rest_offset 330: resp = sendcmd("REST " + rest_offset.to_s) 331: if resp[0] != ?3 332: raise FTPReplyError, resp 333: end 334: end 335: resp = sendcmd(cmd) 336: # skip 2XX for some ftp servers 337: resp = getresp if resp[0] == ?2 338: if resp[0] != ?1 339: raise FTPReplyError, resp 340: end 341: conn = sock.accept 342: sock.close 343: end 344: return conn 345: end