Class: Net::FTP
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
Protocol
|
|
Instance Chain:
self,
SSL,
OpenSSL,
MonitorMixin,
Protocol
|
|
Inherits: |
Protocol
|
Defined in: | lib/net/ftp.rb |
Overview
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.
Example
require 'net/ftp'
Example 1
ftp = Net::FTP.new('example.com')
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
Example 2
Net::FTP.open('example.com') 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
Major Methods
The following are the methods most likely to be useful to users:
Constant Summary
-
CASE_DEPENDENT_PARSER =
# File 'lib/net/ftp.rb', line 1044->(value) { value }
-
CASE_INDEPENDENT_PARSER =
# File 'lib/net/ftp.rb', line 1045->(value) { value.downcase }
-
CRLF =
Internal use only
# File 'lib/net/ftp.rb', line 89"\r\n"
-
DECIMAL_PARSER =
# File 'lib/net/ftp.rb', line 1046->(value) { value.to_i }
-
DEFAULT_BLOCKSIZE =
Internal use only
# File 'lib/net/ftp.rb', line 90BufferedIO::BUFSIZE
-
FACT_PARSERS =
# File 'lib/net/ftp.rb', line 1057Hash.new(CASE_DEPENDENT_PARSER)
-
FTP_PORT =
Internal use only
# File 'lib/net/ftp.rb', line 8821
-
OCTAL_PARSER =
# File 'lib/net/ftp.rb', line 1047->(value) { value.to_i(8) }
-
TIME_PARSER =
# File 'lib/net/ftp.rb', line 1048->(value, local = false) { unless /\A(?<year>\d{4})(?<month>\d{2})(?<day>\d{2}) (?<hour>\d{2})(?<min>\d{2})(?<sec>\d{2}) (\.(?<fractions>\d+))?/x =~ value raise FTPProtoError, "invalid time-val: #{value}" end usec = fractions.to_i * 10 ** (6 - fractions.to_s.size) Time.send(local ? :local : :utc, year, month, day, hour, min, sec, usec) }
Class Attribute Summary
-
.default_passive
rw
When
true
, connections are in passive mode per default. -
.default_passive=(value)
rw
When
true
, connections are in passive mode per default.
Class Method Summary
-
.new(host = nil, options = {}) ⇒ FTP
constructor
Creates and returns a new
FTP
object. -
.open(host, *args)
A synonym for .new, but with a mandatory host parameter.
Instance Attribute Summary
-
#binary
rw
When
true
, transfers are performed in binary mode. -
#binary=(newmode)
rw
A setter to toggle transfers in binary mode.
-
#closed? ⇒ Boolean
readonly
Returns
true
iff the connection is closed. -
#debug_mode
rw
When
true
, all traffic to and from the server is written to $stdout. -
#last_response
readonly
The server’s last response.
-
#last_response_code
(also: #lastresp)
readonly
The server’s last response code.
-
#lastresp
readonly
Alias for #last_response_code.
-
#open_timeout
rw
Number of seconds to wait for the connection to open.
-
#passive
rw
When
true
, the connection is in passive mode. -
#read_timeout
rw
Number of seconds to wait for one block to be read (via one read(2) call).
-
#read_timeout=(sec)
rw
Setter for the read_timeout attribute.
-
#resume
rw
Sets or retrieves the
resume
status, which decides whether incomplete transfers are resumed or restarted. -
#ssl_handshake_timeout
rw
Number of seconds to wait for the TLS handshake.
-
#use_pasv_ip
rw
When
true
, use the IP address in PASV responses. -
#welcome
readonly
The server’s welcome message.
-
#return_code
rw
Internal use only
Obsolete.
-
#return_code=(s)
rw
Internal use only
Obsolete.
Instance Method Summary
-
#abort
Aborts the previous command (ABOR command).
-
#acct(account)
Sends the ACCT command.
-
#chdir(dirname)
Changes the (remote) directory.
-
#close
Closes the connection.
-
#connect(host, port = FTP_PORT)
Establishes an
FTP
connection to host, optionally overriding the default port. -
#delete(filename)
Deletes a file on the server.
-
#dir(*args, &block)
Alias for #list.
-
#get(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block)
Retrieves
remotefile
in whatever mode the session is set (text or binary). -
#getbinaryfile(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block)
Retrieves
remotefile
in binary mode, storing the result inlocalfile
. -
#getdir
Alias for #pwd.
-
#gettextfile(remotefile, localfile = File.basename(remotefile), &block)
Retrieves
remotefile
in ASCII (text) mode, storing the result inlocalfile
. -
#help(arg = nil)
Issues the HELP command.
-
#list(*args, &block)
(also: #ls, #dir)
Returns an array of file information in the directory (the output is like ‘ls -l`).
-
#login(user = "anonymous", passwd = nil, acct = nil)
Logs in to the remote host.
-
#ls(*args, &block)
Alias for #list.
-
#mdtm(filename)
Returns the raw last modification time of the (remote) file in the format “YYYYMMDDhhmmss” (MDTM command).
-
#mkdir(dirname)
Creates a remote directory.
-
#mlsd(pathname = nil, &block)
Returns an array of the entries of the directory specified by
pathname
. -
#mlst(pathname = nil)
Returns data (e.g., size, last modification time, entry type, etc.) about the file or directory specified by
pathname
. -
#mtime(filename, local = false)
Returns the last modification time of the (remote) file.
-
#nlst(dir = nil)
Returns an array of filenames in the remote directory.
-
#noop
Issues a NOOP command.
-
#put(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block)
Transfers
localfile
to the server in whatever mode the session is set (text or binary). -
#putbinaryfile(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block)
Transfers
localfile
to the server in binary mode, storing the result inremotefile
. -
#puttextfile(localfile, remotefile = File.basename(localfile), &block)
Transfers
localfile
to the server in ASCII (text) mode, storing the result inremotefile
. -
#pwd
(also: #getdir)
Returns the current remote directory.
-
#quit
Exits the
FTP
session. -
#rename(fromname, toname)
Renames a file on the server.
-
#retrbinary(cmd, blocksize, rest_offset = nil)
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. -
#retrlines(cmd)
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.
-
#rmdir(dirname)
Removes a remote directory.
-
#sendcmd(cmd)
Sends a command and returns the response.
-
#set_socket(sock, get_greeting = true)
Set the socket used to connect to the
FTP
server. -
#site(arg)
Issues a SITE command.
-
#size(filename)
Returns the size of the given (remote) filename.
-
#status(pathname = nil)
Returns the status (STAT command).
-
#storbinary(cmd, file, blocksize, rest_offset = nil)
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. -
#storlines(cmd, file)
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. -
#system
Returns system information.
-
#voidcmd(cmd)
Sends a command and expect a response beginning with ‘2’.
- #parse_mlsx_entry(entry) private
- #parse_pasv_ipv4_host(s) private
- #parse_pasv_ipv6_host(s) private
- #parse_pasv_port(s) private
- #start_tls_session(sock) private
- #get_body(resp) private Internal use only
-
#getline
private
Internal use only
Reads a line from the sock.
-
#getmultiline
private
Internal use only
Receive a section of lines until the response code’s match.
-
#getresp
private
Internal use only
Receives a response from the destination host.
-
#makepasv
private
Internal use only
sends the appropriate command to enable a passive connection.
-
#makeport
private
Internal use only
Constructs a TCPServer socket.
-
#open_socket(host, port)
private
Internal use only
Constructs a socket with
host
andport
. -
#parse227(resp)
private
Internal use only
handler for response code 227 (Entering Passive Mode (h1,h2,h3,h4,p1,p2)).
-
#parse228(resp)
private
Internal use only
handler for response code 228 (Entering Long Passive Mode).
-
#parse229(resp)
private
Internal use only
handler for response code 229 (Extended Passive Mode Entered).
-
#parse257(resp)
private
Internal use only
handler for response code 257 (“PATHNAME” created).
-
#putline(line)
private
Internal use only
Ensures that
line
has a control return / line feed (CRLF) and writes it to the socket. -
#sanitize(s)
private
Internal use only
If string
s
includes the PASS command (password), then the contents of the password are cleaned from the string using “*”. -
#send_type_command
private
Internal use only
Sends a command to destination host, with the current binary sendmode type.
-
#sendport(host, port)
private
Internal use only
Constructs and send the appropriate PORT (or EPRT) command.
-
#transfercmd(cmd, rest_offset = nil)
private
Internal use only
Constructs a connection for transferring data.
-
#voidresp
private
Internal use only
Receives a response.
-
#with_binary(newmode)
private
Internal use only
Toggles transfers in binary mode and yields to a block.
Constructor Details
.new(host = nil, options = {}) ⇒ FTP
Creates and returns a new FTP
object. If a host
is given, a connection is made.
options
is an option hash, each key of which is a symbol.
The available options are:
- port
-
Port number (default value is 21)
- ssl
-
If options is true, then an attempt will be made to use SSL (now TLS) to connect to the server. For this to work OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] extensions need to be installed. If options is a hash, it’s passed to OpenSSL::SSL::SSLContext#set_params as parameters.
- private_data_connection
-
If true, TLS is used for data connections. Default:
true
when options is true. - username
-
Username for login. If options is the string “anonymous” and the options is
nil
, “anonymous@” is used as a password. - password
-
Password for login.
- account
-
Account information for ACCT.
- passive
-
When
true
, the connection is in passive mode. Default:true
. - open_timeout
-
Number of seconds to wait for the connection to open. See Net::FTP#open_timeout for details. Default:
nil
. - read_timeout
-
Number of seconds to wait for one block to be read. See Net::FTP#read_timeout for details. Default:
60
. - ssl_handshake_timeout
-
Number of seconds to wait for the TLS handshake. See Net::FTP#ssl_handshake_timeout for details. Default:
nil
. - use_pasv_ip
-
When
true
, use the IP address in PASV responses. Otherwise, it uses the same IP address for the control connection. Default:false
. - debug_mode
-
When
true
, all traffic to and from the server is written to $stdout. Default:false
.
# File 'lib/net/ftp.rb', line 218
def initialize(host = nil, = {}, passwd = nil, acct = nil) super() begin = .to_hash rescue NoMethodError # for backward compatibility = {} [:username] = [:password] = passwd [:account] = acct end @host = nil if [:ssl] unless defined?(OpenSSL::SSL) raise "SSL extension not installed" end ssl_params = [:ssl] == true ? {} : [:ssl] @ssl_context = SSLContext.new @ssl_context.set_params(ssl_params) if defined?(VerifyCallbackProc) @ssl_context.verify_callback = VerifyCallbackProc end @ssl_context.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT | OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess } @ssl_session = nil if [:private_data_connection].nil? @private_data_connection = true else @private_data_connection = [:private_data_connection] end else @ssl_context = nil if [:private_data_connection] raise ArgumentError, "private_data_connection can be set to true only when ssl is enabled" end @private_data_connection = false end @binary = true if [:passive].nil? @passive = @@default_passive else @passive = [:passive] end if [:debug_mode].nil? @debug_mode = false else @debug_mode = [:debug_mode] end @resume = false @bare_sock = @sock = NullSocket.new @logged_in = false @open_timeout = [:open_timeout] @ssl_handshake_timeout = [:ssl_handshake_timeout] @read_timeout = [:read_timeout] || 60 @use_pasv_ip = [:use_pasv_ip] || false if host connect(host, [:port] || FTP_PORT) if [:username] login( [:username], [:password], [:account]) end end end
Class Attribute Details
.default_passive (rw)
When true
, connections are in passive mode per default. Default: true
.
# File 'lib/net/ftp.rb', line 155
def self.default_passive @@default_passive end
.default_passive=(value) (rw)
When true
, connections are in passive mode per default. Default: true
.
# File 'lib/net/ftp.rb', line 149
def self.default_passive=(value) @@default_passive = value end
Class Method Details
.open(host, *args)
A synonym for .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.
Instance Attribute Details
#binary (rw)
When true
, transfers are performed in binary mode. Default: true
.
# File 'lib/net/ftp.rb', line 95
attr_reader :binary
#binary=(newmode) (rw)
A setter to toggle transfers in binary mode. newmode
is either true
or false
# File 'lib/net/ftp.rb', line 286
def binary=(newmode) if newmode != @binary @binary = newmode send_type_command if @logged_in end end
#closed? ⇒ Boolean
(readonly)
Returns true
iff the connection is closed.
# File 'lib/net/ftp.rb', line 1328
def closed? @sock == nil or @sock.closed? end
#debug_mode (rw)
When true
, all traffic to and from the server is written to $stdout. Default: false
.
# File 'lib/net/ftp.rb', line 106
attr_accessor :debug_mode
#last_response (readonly)
The server’s last response.
# File 'lib/net/ftp.rb', line 145
attr_reader :last_response
#last_response_code (readonly) Also known as: #lastresp
The server’s last response code.
# File 'lib/net/ftp.rb', line 141
attr_reader :last_response_code
#lastresp (readonly)
Alias for #last_response_code.
# File 'lib/net/ftp.rb', line 142
alias lastresp last_response_code
#open_timeout (rw)
Number of seconds to wait for the connection to open. Any number may be used, including Floats for fractional seconds. If the FTP
object cannot open a connection in this many seconds, it raises a Net::OpenTimeout
exception. The default value is nil
.
# File 'lib/net/ftp.rb', line 116
attr_accessor :open_timeout
#passive (rw)
When true
, the connection is in passive mode. Default: true
.
# File 'lib/net/ftp.rb', line 98
attr_accessor :passive
#read_timeout (rw)
Number of seconds to wait for one block to be read (via one read(2) call). Any number may be used, including Floats for fractional seconds. If the FTP
object cannot read data in this many seconds, it raises a Timeout::Error
exception. The default value is 60 seconds.
# File 'lib/net/ftp.rb', line 129
attr_reader :read_timeout
#read_timeout=(sec) (rw)
Setter for the read_timeout attribute.
# File 'lib/net/ftp.rb', line 132
def read_timeout=(sec) @sock.read_timeout = sec @read_timeout = sec end
#resume (rw)
Sets or retrieves the resume
status, which decides whether incomplete transfers are resumed or restarted. Default: false
.
# File 'lib/net/ftp.rb', line 110
attr_accessor :resume
#return_code (rw)
Obsolete
# File 'lib/net/ftp.rb', line 324
def return_code # :nodoc: warn("Net::FTP#return_code is obsolete and do nothing", uplevel: 1) return "\n" end
#return_code=(s) (rw)
Obsolete
# File 'lib/net/ftp.rb', line 330
def return_code=(s) # :nodoc: warn("Net::FTP#return_code= is obsolete and do nothing", uplevel: 1) end
#ssl_handshake_timeout (rw)
Number of seconds to wait for the TLS handshake. Any number may be used, including Floats for fractional seconds. If the FTP
object cannot complete the TLS handshake in this many seconds, it raises a Net::OpenTimeout
exception. The default value is nil
. If ssl_handshake_timeout
is nil
, #open_timeout is used instead.
# File 'lib/net/ftp.rb', line 123
attr_accessor :ssl_handshake_timeout
#use_pasv_ip (rw)
When true
, use the IP address in PASV responses. Otherwise, it uses the same IP address for the control connection. Default: false
.
# File 'lib/net/ftp.rb', line 102
attr_accessor :use_pasv_ip
#welcome (readonly)
The server’s welcome message.
# File 'lib/net/ftp.rb', line 138
attr_reader :welcome
Instance Method Details
#abort
Aborts the previous command (ABOR command).
# File 'lib/net/ftp.rb', line 1234
def abort line = "ABOR" + CRLF print "put: ABOR\n" if @debug_mode @sock.send(line, Socket::MSG_OOB) resp = getmultiline unless ["426", "226", "225"].include?(resp[0, 3]) raise FTPProtoError, resp end return resp end
#acct(account)
Sends the ACCT command.
This is a less common FTP
command, to send account information if the destination host requires it.
# File 'lib/net/ftp.rb', line 887
def acct(account) cmd = "ACCT " + account voidcmd(cmd) end
#chdir(dirname)
Changes the (remote) directory.
# File 'lib/net/ftp.rb', line 1155
def chdir(dirname) if dirname == ".." begin voidcmd("CDUP") return rescue FTPPermError => e if e. [0, 3] != "500" raise e end end end cmd = "CWD #{dirname}" voidcmd(cmd) end
#close
Closes the connection. Further operations are impossible until you open a new connection with #connect.
# File 'lib/net/ftp.rb', line 1312
def close if @sock and not @sock.closed? begin @sock.shutdown(Socket::SHUT_WR) rescue nil orig, self.read_timeout = self.read_timeout, 3 @sock.read rescue nil ensure @sock.close self.read_timeout = orig end end end
#connect(host, port = FTP_PORT)
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 374
def connect(host, port = FTP_PORT) if @debug_mode print "connect: ", host, ", ", port, "\n" end synchronize do @host = host @bare_sock = open_socket(host, port) @sock = BufferedSocket.new(@bare_sock, read_timeout: @read_timeout) voidresp if @ssl_context begin voidcmd("AUTH TLS") ssl_sock = start_tls_session(@bare_sock) @sock = BufferedSSLSocket.new(ssl_sock, read_timeout: @read_timeout) if @private_data_connection voidcmd("PBSZ 0") voidcmd("PROT P") end rescue OpenSSL::SSL::SSLError, OpenTimeout @sock.close raise end end end end
#delete(filename)
Deletes a file on the server.
# File 'lib/net/ftp.rb', line 1141
def delete(filename) resp = sendcmd("DELE #{filename}") if resp.start_with?("250") return elsif resp.start_with?("5") raise FTPPermError, resp else raise FTPReplyError, resp end end
#dir(*args, &block)
Alias for #list.
# File 'lib/net/ftp.rb', line 926
alias dir list
#get(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block)
Retrieves remotefile
in whatever mode the session is set (text or binary). See #gettextfile and #getbinaryfile.
# File 'lib/net/ftp.rb', line 816
def get(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data if @binary getbinaryfile(remotefile, localfile, blocksize, &block) else gettextfile(remotefile, localfile, &block) end end
#get_body(resp) (private)
# File 'lib/net/ftp.rb', line 1170
def get_body(resp) # :nodoc: resp.slice(/\A[0-9a-zA-Z]{3} (.*)$/, 1) end
#getbinaryfile(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block)
Retrieves remotefile
in binary mode, storing the result in localfile
. If localfile
is nil, returns retrieved data. If a block is supplied, it is passed the retrieved data in blocksize
chunks.
# File 'lib/net/ftp.rb', line 755
def getbinaryfile(remotefile, localfile = File.basename(remotefile), blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data f = nil result = nil if localfile if @resume rest_offset = File.size?(localfile) f = File.open(localfile, "a") else rest_offset = nil f = File.open(localfile, "w") end elsif !block_given? result = String.new end begin f&.binmode retrbinary("RETR #{remotefile}", blocksize, rest_offset) do |data| f&.write(data) block&.(data) result&.concat(data) end return result ensure f&.close end end
#getdir
Alias for #pwd.
# File 'lib/net/ftp.rb', line 1218
alias getdir pwd
#getline (private)
Reads a line from the sock. If EOF, then it will raise EOFError
# File 'lib/net/ftp.rb', line 439
def getline # :nodoc: line = @sock.readline # if get EOF, raise EOFError line.sub!(/(\r\n|\n|\r)\z/n, "") if @debug_mode print "get: ", sanitize(line), "\n" end return line end
#getmultiline (private)
Receive a section of lines until the response code’s match.
#getresp (private)
Receives a response from the destination host.
Returns the response code or raises FTPTempError
, FTPPermError
, or FTPProtoError
# File 'lib/net/ftp.rb', line 468
def getresp # :nodoc: @last_response = getmultiline @last_response_code = @last_response[0, 3] case @last_response_code when /\A[123]/ return @last_response when /\A4/ raise FTPTempError, @last_response when /\A5/ raise FTPPermError, @last_response else raise FTPProtoError, @last_response end end
#gettextfile(remotefile, localfile = File.basename(remotefile), &block)
Retrieves remotefile
in ASCII (text) mode, storing the result in localfile
. If localfile
is nil, returns retrieved data. If a block is supplied, it is passed the retrieved data one line at a time.
# File 'lib/net/ftp.rb', line 790
def gettextfile(remotefile, localfile = File.basename(remotefile), &block) # :yield: line f = nil result = nil if localfile f = File.open(localfile, "w") elsif !block_given? result = String.new end begin retrlines("RETR #{remotefile}") do |line, newline| l = newline ? line + "\n" : line f&.print(l) block&.(line, newline) result&.concat(l) end return result ensure f&.close end end
#help(arg = nil)
Issues the HELP command.
# File 'lib/net/ftp.rb', line 1276
def help(arg = nil) cmd = "HELP" if arg cmd = cmd + " " + arg end sendcmd(cmd) end
#list(*args, &block) Also known as: #ls, #dir
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 911
def list(*args, &block) # :yield: line cmd = "LIST" args.each do |arg| cmd = "#{cmd} #{arg}" end lines = [] retrlines(cmd) do |line| lines << line end if block lines.each(&block) end return lines end
#login(user = "anonymous", passwd = nil, acct = nil)
Logs in to the remote host. The session must have been previously connected. If user
is the string “anonymous” and the password
is nil
, “anonymous@” is used as a password. If the #acct parameter is not nil
, an FTP
ACCT command is sent following the successful login. Raises an exception on error (typically FTPPermError
).
# File 'lib/net/ftp.rb', line 606
def login(user = "anonymous", passwd = nil, acct = nil) if user == "anonymous" and passwd == nil passwd = "anonymous@" end resp = "" synchronize do resp = sendcmd('USER ' + user) if resp.start_with?("3") raise FTPReplyError, resp if passwd.nil? resp = sendcmd('PASS ' + passwd) end if resp.start_with?("3") raise FTPReplyError, resp if acct.nil? resp = sendcmd('ACCT ' + acct) end end if !resp.start_with?("2") raise FTPReplyError, resp end @welcome = resp send_type_command @logged_in = true end
#ls(*args, &block)
Alias for #list.
# File 'lib/net/ftp.rb', line 925
alias ls list
#makepasv (private)
sends the appropriate command to enable a passive connection
#makeport (private)
Constructs a TCPServer socket
# File 'lib/net/ftp.rb', line 531
def makeport # :nodoc: Addrinfo.tcp(@bare_sock.local_address.ip_address, 0).listen end
#mdtm(filename)
Returns the raw last modification time of the (remote) file in the format “YYYYMMDDhhmmss” (MDTM command).
Use #mtime if you want a parsed Time instance.
#mkdir(dirname)
Creates a remote directory.
#mlsd(pathname = nil, &block)
Returns an array of the entries of the directory specified by pathname
. Each entry has the facts (e.g., size, last modification time, etc.) and the pathname. If a block is given, it iterates through the listing. If pathname
is omitted, the current directory is assumed.
# File 'lib/net/ftp.rb', line 1115
def mlsd(pathname = nil, &block) # :yield: entry cmd = pathname ? "MLSD #{pathname}" : "MLSD" entries = [] retrlines(cmd) do |line| entries << parse_mlsx_entry(line) end if block entries.each(&block) end return entries end
#mlst(pathname = nil)
Returns data (e.g., size, last modification time, entry type, etc.) about the file or directory specified by pathname
. If pathname
is omitted, the current directory is assumed.
# File 'lib/net/ftp.rb', line 1093
def mlst(pathname = nil) cmd = pathname ? "MLST #{pathname}" : "MLST" resp = sendcmd(cmd) if !resp.start_with?("250") raise FTPReplyError, resp end line = resp.lines[1] unless line raise FTPProtoError, resp end entry = line.sub(/\A(250-| *)/, "") return parse_mlsx_entry(entry) end
#mtime(filename, local = false)
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 1192
def mtime(filename, local = false) return TIME_PARSER.(mdtm(filename), local) end
#nlst(dir = nil)
Returns an array of filenames in the remote directory.
#noop
Issues a NOOP command.
Does nothing except return a response.
# File 'lib/net/ftp.rb', line 1296
def noop voidcmd("NOOP") end
#open_socket(host, port) (private)
Constructs a socket with host
and port
.
If SOCKSSocket is defined and the environment (ENV) defines SOCKS_SERVER, then a SOCKSSocket is returned, else a Socket is returned.
# File 'lib/net/ftp.rb', line 339
def open_socket(host, port) # :nodoc: return Timeout.timeout(@open_timeout, OpenTimeout) { if defined? SOCKSSocket and ENV["SOCKS_SERVER"] @passive = true SOCKSSocket.open(host, port) else Socket.tcp(host, port) end } end
#parse227(resp) (private)
handler for response code 227 (Entering Passive Mode (h1,h2,h3,h4,p1,p2))
Returns host and port.
# File 'lib/net/ftp.rb', line 1336
def parse227(resp) # :nodoc: if !resp.start_with?("227") raise FTPReplyError, resp end if m = /\((?<host>\d(,\d){3}),(?<port>\d,\d)\)/.match(resp) if @use_pasv_ip host = parse_pasv_ipv4_host(m["host"]) else host = @bare_sock.remote_address.ip_address end return host, parse_pasv_port(m["port"]) else raise FTPProtoError, resp end end
#parse228(resp) (private)
handler for response code 228 (Entering Long Passive Mode)
Returns host and port.
# File 'lib/net/ftp.rb', line 1357
def parse228(resp) # :nodoc: if !resp.start_with?("228") raise FTPReplyError, resp end if m = /\(4,4,(?<host>\d(,\d){3}),2,(?<port>\d,\d)\)/.match(resp) return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"]) elsif m = /\(6,16,(?<host>\d(,(\d)){15}),2,(?<port>\d,\d)\)/.match(resp) return parse_pasv_ipv6_host(m["host"]), parse_pasv_port(m["port"]) else raise FTPProtoError, resp end end
#parse229(resp) (private)
handler for response code 229 (Extended Passive Mode Entered)
Returns host and port.
# File 'lib/net/ftp.rb', line 1394
def parse229(resp) # :nodoc: if !resp.start_with?("229") raise FTPReplyError, resp end if m = /\((?<d>[!-~])\k<d>\k<d>(?<port>\d+)\k<d>\)/.match(resp) return @bare_sock.remote_address.ip_address, m["port"].to_i else raise FTPProtoError, resp end end
#parse257(resp) (private)
handler for response code 257 (“PATHNAME” created)
Returns host and port.
# File 'lib/net/ftp.rb', line 1410
def parse257(resp) # :nodoc: if !resp.start_with?("257") raise FTPReplyError, resp end return resp.slice(/"(([^"]|"")*)"/, 1).to_s.gsub(/""/, '"') end
#parse_mlsx_entry(entry) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 1073
def parse_mlsx_entry(entry) facts, pathname = entry.chomp.split(/ /, 2) unless pathname raise FTPProtoError, entry end return MLSxEntry.new( facts.scan(/(.*?)=(.*?);/).each_with_object({}) { |(factname, value), h| name = factname.downcase h[name] = FACT_PARSERS[name].(value) }, pathname) end
#parse_pasv_ipv4_host(s) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 1371
def parse_pasv_ipv4_host(s) return s.tr(",", ".") end
#parse_pasv_ipv6_host(s) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 1376
def parse_pasv_ipv6_host(s) return s.split(/,/).map { |i| "%02x" % i.to_i }.each_slice(2).map(&:join).join(":") end
#parse_pasv_port(s) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 1383
def parse_pasv_port(s) return s.split(/,/).map(&:to_i).inject { |x, y| (x << 8) + y } end
#put(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block)
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 872
def put(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block) if @binary putbinaryfile(localfile, remotefile, blocksize, &block) else puttextfile(localfile, remotefile, &block) end end
#putbinaryfile(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block)
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 830
def putbinaryfile(localfile, remotefile = File.basename(localfile), blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data if @resume begin rest_offset = size(remotefile) rescue Net::FTPPermError rest_offset = nil end else rest_offset = nil end f = File.open(localfile) begin f.binmode if rest_offset storbinary("APPE #{remotefile}", f, blocksize, rest_offset, &block) else storbinary("STOR #{remotefile}", f, blocksize, rest_offset, &block) end ensure f.close end end
#putline(line) (private)
Ensures that line
has a control return / line feed (CRLF) and writes it to the socket.
#puttextfile(localfile, remotefile = File.basename(localfile), &block)
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.
#pwd Also known as: #getdir
Returns the current remote directory.
#quit
Exits the FTP
session.
# File 'lib/net/ftp.rb', line 1287
def quit voidcmd("QUIT") end
#rename(fromname, toname)
Renames a file on the server.
# File 'lib/net/ftp.rb', line 1130
def rename(fromname, toname) resp = sendcmd("RNFR #{fromname}") if !resp.start_with?("3") raise FTPReplyError, resp end voidcmd("RNTO #{toname}") end
#retrbinary(cmd, blocksize, rest_offset = nil)
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 637
def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data synchronize do with_binary(true) do begin conn = transfercmd(cmd, rest_offset) loop do data = conn.read(blocksize) break if data == nil yield(data) end conn.shutdown(Socket::SHUT_WR) conn.read_timeout = 1 conn.read ensure conn.close if conn end voidresp end end end
#retrlines(cmd)
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 664
def retrlines(cmd) # :yield: line synchronize do with_binary(false) do begin conn = transfercmd(cmd) loop do line = conn.gets break if line == nil yield(line.sub(/\r?\n\z/, ""), !line.match(/\n\z/).nil?) end conn.shutdown(Socket::SHUT_WR) conn.read_timeout = 1 conn.read ensure conn.close if conn end voidresp end end end
#rmdir(dirname)
Removes a remote directory.
# File 'lib/net/ftp.rb', line 1207
def rmdir(dirname) voidcmd("RMD #{dirname}") end
#sanitize(s) (private)
If string s
includes the PASS command (password), then the contents of the password are cleaned from the string using “*”
# File 'lib/net/ftp.rb', line 415
def sanitize(s) # :nodoc: if s =~ /^PASS /i return s[0, 5] + "*" * (s.length - 5) else return s end end
#send_type_command (private)
Sends a command to destination host, with the current binary sendmode type.
If binary mode is true
, then “TYPE I” (image) is sent, otherwise “TYPE A” (ascii) is sent.
#sendcmd(cmd)
Sends a command and returns the response.
#sendport(host, port) (private)
Constructs and send the appropriate PORT (or EPRT) command
# File 'lib/net/ftp.rb', line 517
def sendport(host, port) # :nodoc: remote_address = @bare_sock.remote_address if remote_address.ipv4? cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",") elsif remote_address.ipv6? cmd = sprintf("EPRT |2|%s|%d|", host, port) else raise FTPProtoError, host end voidcmd(cmd) end
#set_socket(sock, get_greeting = true)
Set the socket used to connect to the FTP
server.
May raise FTPReplyError
if get_greeting
is false.
# File 'lib/net/ftp.rb', line 404
def set_socket(sock, get_greeting = true) synchronize do @sock = sock if get_greeting voidresp end end end
#site(arg)
Issues a SITE command.
# File 'lib/net/ftp.rb', line 1303
def site(arg) cmd = "SITE " + arg voidcmd(cmd) end
#size(filename)
Returns the size of the given (remote) filename.
# File 'lib/net/ftp.rb', line 1178
def size(filename) with_binary(true) do resp = sendcmd("SIZE #{filename}") if !resp.start_with?("213") raise FTPReplyError, resp end return get_body(resp).to_i end end
#start_tls_session(sock) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 351
def start_tls_session(sock) ssl_sock = SSLSocket.new(sock, @ssl_context) ssl_sock.sync_close = true ssl_sock.hostname = @host if ssl_sock.respond_to? :hostname= if @ssl_session && Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout # ProFTPD returns 425 for data connections if session is not reused. ssl_sock.session = @ssl_session end ssl_socket_connect(ssl_sock, @ssl_handshake_timeout || @open_timeout) if @ssl_context.verify_mode != VERIFY_NONE ssl_sock.post_connection_check(@host) end return ssl_sock end
#status(pathname = nil)
Returns the status (STAT command). pathname - when stat is invoked with pathname as a parameter it acts like
list but alot faster and over the same tcp session.
#storbinary(cmd, file, blocksize, rest_offset = nil)
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 691
def storbinary(cmd, file, blocksize, rest_offset = nil) # :yield: data if rest_offset file.seek(rest_offset, IO::SEEK_SET) end synchronize do with_binary(true) do conn = transfercmd(cmd) loop do buf = file.read(blocksize) break if buf == nil conn.write(buf) yield(buf) if block_given? end conn.close voidresp end end rescue Errno::EPIPE # EPIPE, in this case, means that the data connection was unexpectedly # terminated. Rather than just raising EPIPE to the caller, check the # response on the control connection. If getresp doesn't raise a more # appropriate exception, re-raise the original exception. getresp raise end
#storlines(cmd, file)
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 723
def storlines(cmd, file) # :yield: line synchronize do with_binary(false) do conn = transfercmd(cmd) loop do buf = file.gets break if buf == nil if buf[-2, 2] != CRLF buf = buf.chomp + CRLF end conn.write(buf) yield(buf) if block_given? end conn.close voidresp end end rescue Errno::EPIPE # EPIPE, in this case, means that the data connection was unexpectedly # terminated. Rather than just raising EPIPE to the caller, check the # response on the control connection. If getresp doesn't raise a more # appropriate exception, re-raise the original exception. getresp raise end
#system
Returns system information.
# File 'lib/net/ftp.rb', line 1223
def system resp = sendcmd("SYST") if !resp.start_with?("215") raise FTPReplyError, resp end return get_body(resp) end
#transfercmd(cmd, rest_offset = nil) (private)
Constructs a connection for transferring data
# File 'lib/net/ftp.rb', line 549
def transfercmd(cmd, rest_offset = nil) # :nodoc: if @passive host, port = makepasv conn = open_socket(host, port) if @resume and rest_offset resp = sendcmd("REST " + rest_offset.to_s) if !resp.start_with?("3") raise FTPReplyError, resp end end resp = sendcmd(cmd) # skip 2XX for some ftp servers resp = getresp if resp.start_with?("2") if !resp.start_with?("1") raise FTPReplyError, resp end else sock = makeport begin addr = sock.local_address sendport(addr.ip_address, addr.ip_port) if @resume and rest_offset resp = sendcmd("REST " + rest_offset.to_s) if !resp.start_with?("3") raise FTPReplyError, resp end end resp = sendcmd(cmd) # skip 2XX for some ftp servers resp = getresp if resp.start_with?("2") if !resp.start_with?("1") raise FTPReplyError, resp end conn, = sock.accept sock.shutdown(Socket::SHUT_WR) rescue nil sock.read rescue nil ensure sock.close end end if @private_data_connection return BufferedSSLSocket.new(start_tls_session(conn), read_timeout: @read_timeout) else return BufferedSocket.new(conn, read_timeout: @read_timeout) end end
#voidcmd(cmd)
Sends a command and expect a response beginning with ‘2’.
#voidresp (private)
Receives a response.
Raises FTPReplyError if the first position of the response code is not equal 2.
# File 'lib/net/ftp.rb', line 488
def voidresp # :nodoc: resp = getresp if !resp.start_with?("2") raise FTPReplyError, resp end end
#with_binary(newmode) (private)
Toggles transfers in binary mode and yields to a block. This preserves your current binary send mode, but allows a temporary transaction with binary sendmode of newmode
.
newmode
is either true
or false