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:
Mainframe User Support
Constant Summary
-
CASE_DEPENDENT_PARSER =
# File 'lib/net/ftp.rb', line 1082->(value) { value }
-
CASE_INDEPENDENT_PARSER =
# File 'lib/net/ftp.rb', line 1083->(value) { value.downcase }
-
CRLF =
Internal use only
# File 'lib/net/ftp.rb', line 94"\r\n"
-
DECIMAL_PARSER =
# File 'lib/net/ftp.rb', line 1084->(value) { value.to_i }
-
DEFAULT_BLOCKSIZE =
Internal use only
# File 'lib/net/ftp.rb', line 95BufferedIO::BUFSIZE
-
FACT_PARSERS =
# File 'lib/net/ftp.rb', line 1096Hash.new(CASE_DEPENDENT_PARSER)
-
FTP_PORT =
Internal use only
# File 'lib/net/ftp.rb', line 9321
-
OCTAL_PARSER =
# File 'lib/net/ftp.rb', line 1085->(value) { value.to_i(8) }
-
TIME_PARSER =
# File 'lib/net/ftp.rb', line 1086->(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{1,17}))?/x =~ value value = value[0, 97] + "..." if value.size > 100 raise FTPProtoError, "invalid time-val: #{value}" end usec = ".#{fractions}".to_r * 1_000_000 if fractions Time.public_send(local ? :local : :utc, year, month, day, hour, min, sec, usec) }
-
VERSION =
Internal use only
# File 'lib/net/ftp.rb', line 92"0.3.8"
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
if and only if the connection is closed. -
#debug_mode
rw
When
true
, all traffic to and from the server is written to$stdout
. -
#debug_output
rw
Sets or retrieves the output stream for debugging.
-
#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. -
#debug_print(msg)
Writes debug message to the debug output stream.
-
#delete(filename)
Deletes a file on the server.
-
#dir(*args, &block)
Alias for #list.
-
#features
Issues a FEAT command.
-
#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`).
-
#literal(arguments)
Alias for #quote.
-
#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.
-
#option(name, params = nil)
Issues an OPTS command - name Should be the name of the option to set - params is any optional parameters to supply with the option.
-
#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. -
#quote(arguments)
(also: #literal)
The “quote” subcommand sends arguments verbatim to the remote ftp server.
-
#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)).
-
#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
[:ssl] 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
whenoptions
[:ssl] is true. - implicit_ftps
-
If true, TLS is established on initial connection. Default:
false
- username
-
Username for login. If options is the string “anonymous” and the
options
[:password] isnil
, “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 230
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 # jruby-openssl does not support session caching unless RUBY_ENGINE == "jruby" @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 } end @ssl_session = nil if [:private_data_connection].nil? @private_data_connection = true else @private_data_connection = [:private_data_connection] end if [:implicit_ftps].nil? @implicit_ftps = false else @implicit_ftps = [:implicit_ftps] 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 if [:implicit_ftps] raise ArgumentError, "implicit_ftps can be set to true only when ssl is enabled" end @implicit_ftps = 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 @debug_output = $stdout @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 165
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 159
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 100
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 314
def binary=(newmode) if newmode != @binary @binary = newmode send_type_command if @logged_in end end
#closed? ⇒ Boolean
(readonly)
Returns true
if and only if the connection is closed.
# File 'lib/net/ftp.rb', line 1413
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 111
attr_accessor :debug_mode
#debug_output (rw)
Sets or retrieves the output stream for debugging. Output stream will be used only when #debug_mode is set to true. The default value is $stdout
.
# File 'lib/net/ftp.rb', line 116
attr_accessor :debug_output
#last_response (readonly)
The server’s last response.
# File 'lib/net/ftp.rb', line 155
attr_reader :last_response
#last_response_code (readonly) Also known as: #lastresp
The server’s last response code.
# File 'lib/net/ftp.rb', line 151
attr_reader :last_response_code
#lastresp (readonly)
Alias for #last_response_code.
# File 'lib/net/ftp.rb', line 152
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 126
attr_accessor :open_timeout
#passive (rw)
When true
, the connection is in passive mode. Default: true
.
# File 'lib/net/ftp.rb', line 103
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 139
attr_reader :read_timeout
#read_timeout=(sec) (rw)
Setter for the read_timeout attribute.
# File 'lib/net/ftp.rb', line 142
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 120
attr_accessor :resume
#return_code (rw)
Obsolete
# File 'lib/net/ftp.rb', line 352
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 358
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 133
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 107
attr_accessor :use_pasv_ip
#welcome (readonly)
The server’s welcome message.
# File 'lib/net/ftp.rb', line 148
attr_reader :welcome
Instance Method Details
#abort
Aborts the previous command (ABOR command).
# File 'lib/net/ftp.rb', line 1283
def abort line = "ABOR" + CRLF debug_print "put: ABOR" @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 925
def acct(account) cmd = "ACCT " + account voidcmd(cmd) end
#chdir(dirname)
Changes the (remote) directory.
# File 'lib/net/ftp.rb', line 1194
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 1397
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 402
def connect(host, port = FTP_PORT) debug_print "connect: #{host}:#{port}" synchronize do @host = host @bare_sock = open_socket(host, port) if @ssl_context begin unless @implicit_ftps set_socket(BufferedSocket.new(@bare_sock, read_timeout: @read_timeout)) voidcmd("AUTH TLS") end set_socket(BufferedSSLSocket.new(start_tls_session(@bare_sock), read_timeout: @read_timeout), @implicit_ftps) if @private_data_connection voidcmd("PBSZ 0") voidcmd("PROT P") end rescue OpenSSL::SSL::SSLError, OpenTimeout @sock.close raise end else set_socket(BufferedSocket.new(@bare_sock, read_timeout: @read_timeout)) end end end
#debug_print(msg)
Writes debug message to the debug output stream
# File 'lib/net/ftp.rb', line 1488
def debug_print(msg) @debug_output << msg + "\n" if @debug_mode && @debug_output end
#delete(filename)
Deletes a file on the server.
# File 'lib/net/ftp.rb', line 1180
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 964
alias dir list
#features
Issues a FEAT command
Returns an array of supported optional features
# File 'lib/net/ftp.rb', line 1363
def features resp = sendcmd("FEAT") if !resp.start_with?("211") raise FTPReplyError, resp end feats = [] resp.each_line do |line| next if !line.start_with?(' ') # skip status lines feats << line.strip end return feats end
#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 849
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 1209
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 788
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 1267
alias getdir pwd
#getline (private)
Reads a line from the sock. If EOF, then it will raise EOFError
# File 'lib/net/ftp.rb', line 465
def getline # :nodoc: line = @sock.readline # if get EOF, raise EOFError line.sub!(/(\r\n|\n|\r)\z/n, "") debug_print "get: #{sanitize(line)}" 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 492
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 823
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 1326
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 949
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
#literal(arguments)
Alias for #quote.
# File 'lib/net/ftp.rb', line 1251
alias literal quote
#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 635
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 963
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 555
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 1154
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 1132
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 1231
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 1346
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 367
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
#option(name, params = nil)
Issues an OPTS command
-
name Should be the name of the option to set
-
params is any optional parameters to supply with the option
example: option(‘UTF8’, ‘ON’) => ‘OPTS UTF8 ON’
# File 'lib/net/ftp.rb', line 1386
def option(name, params = nil) cmd = "OPTS #{name}" cmd += " #{params}" if params voidcmd(cmd) 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 1421
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
#parse229(resp) (private)
handler for response code 229 (Extended Passive Mode Entered)
Returns host and port.
# File 'lib/net/ftp.rb', line 1461
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 1477
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 1112
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 1438
def parse_pasv_ipv4_host(s) return s.tr(",", ".") end
#parse_pasv_ipv6_host(s) (private)
[ GitHub ]# File 'lib/net/ftp.rb', line 1443
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 1450
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 910
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 863
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.
# File 'lib/net/ftp.rb', line 454
def putline(line) # :nodoc: debug_print "put: #{sanitize(line)}" if /[\r\n]/ =~ line raise ArgumentError, "A line must not contain CR or LF" end line = line + CRLF @sock.write(line) end
#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.
Returns the response which will contain a job number if the user was communicating with a mainframe in ASCII mode after issuing ‘quote site filetype=jes’
#pwd Also known as: #getdir
Returns the current remote directory.
#quit
Exits the FTP
session.
# File 'lib/net/ftp.rb', line 1337
def quit voidcmd("QUIT") end
#quote(arguments) Also known as: #literal
The “quote” subcommand sends arguments verbatim to the remote ftp server. The “literal” subcommand is an alias for “quote”.
# File 'lib/net/ftp.rb', line 1248
def quote(arguments) sendcmd(arguments) end
#rename(fromname, toname)
Renames a file on the server.
# File 'lib/net/ftp.rb', line 1169
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 666
def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data synchronize do with_binary(true) do begin conn = transfercmd(cmd, rest_offset) while data = conn.read(blocksize) yield(data) end conn.shutdown(Socket::SHUT_WR) rescue nil conn.read_timeout = 1 conn.read rescue nil 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 691
def retrlines(cmd) # :yield: line synchronize do with_binary(false) do begin conn = transfercmd(cmd) while line = conn.gets yield(line.sub(/\r?\n\z/, ""), !line.match(/\n\z/).nil?) end conn.shutdown(Socket::SHUT_WR) rescue nil conn.read_timeout = 1 conn.read rescue nil ensure conn.close if conn end voidresp end end end
#rmdir(dirname)
Removes a remote directory.
# File 'lib/net/ftp.rb', line 1256
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 443
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 541
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 432
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 1353
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 1217
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 379
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 a lot faster and over the same tcp session.
# File 'lib/net/ftp.rb', line 1300
def status(pathname = nil) line = pathname ? "STAT #{pathname}" : "STAT" if /[\r\n]/ =~ line raise ArgumentError, "A line must not contain CR or LF" end debug_print "put: #{line}" @sock.send(line + CRLF, Socket::MSG_OOB) return getresp end
#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 716
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 begin conn = transfercmd(cmd) while buf = file.read(blocksize) conn.write(buf) yield(buf) if block_given? end conn.shutdown(Socket::SHUT_WR) rescue nil conn.read_timeout = 1 conn.read rescue nil ensure conn.close if conn end 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 752
def storlines(cmd, file) # :yield: line synchronize do with_binary(false) do begin conn = transfercmd(cmd) while buf = file.gets if buf[-2, 2] != CRLF buf = buf.chomp + CRLF end conn.write(buf) yield(buf) if block_given? end conn.shutdown(Socket::SHUT_WR) rescue nil conn.read_timeout = 1 conn.read rescue nil ensure conn.close if conn end getresp # The response might be important when connected to a mainframe 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 1272
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 572
def transfercmd(cmd, rest_offset = nil) # :nodoc: if @passive host, port = makepasv succeeded = false begin 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 succeeded = true ensure conn&.close if !succeeded 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 512
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