class DoubleBagFTPS

Constants

EXPLICIT
IMPLICIT
IMPLICIT_PORT

Attributes

ftps_mode[R]

The form of FTPS that should be used. Either EXPLICIT or IMPLICIT. Defaults to EXPLICIT.

ssl_context[RW]

The OpenSSL::SSL::SSLContext to use for creating all OpenSSL::SSL::SSLSocket objects.

Public Class Methods

create_ssl_context(params = {}) click to toggle source
# File lib/double_bag_ftps.rb, line 203
def DoubleBagFTPS.create_ssl_context(params = {})
  raise 'SSL extension not installed' unless defined?(OpenSSL)
  context = OpenSSL::SSL::SSLContext.new
  context.set_params(params)
  return context
end
new(host = nil, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {}) click to toggle source
Calls superclass method
# File lib/double_bag_ftps.rb, line 20
def initialize(host = nil, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
  raise ArgumentError unless valid_ftps_mode?(ftps_mode)
  @ftps_mode = ftps_mode
  @ssl_context = DoubleBagFTPS.create_ssl_context(ssl_context_params)
  super(host, user, passwd, acct)
end
open(host, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {}) { |ftps| ... } click to toggle source
# File lib/double_bag_ftps.rb, line 27
def DoubleBagFTPS.open(host, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
  if block_given?
    ftps = new(host, user, passwd, acct, ftps_mode, ssl_context_params)
    begin
      yield ftps
    ensure
      ftps.close
    end
  else
    new(host, user, passwd, acct, ftps_mode, ssl_context_params)
  end
end

Public Instance Methods

connect(host, port = ftps_implicit? ? IMPLICIT_PORT : FTP_PORT) click to toggle source

Establishes the command channel. Override parent to record host name for verification, and allow default implicit port.

Calls superclass method
# File lib/double_bag_ftps.rb, line 57
def connect(host, port = ftps_implicit? ? IMPLICIT_PORT : FTP_PORT)
  @hostname = host
  super
end
ftps_explicit?() click to toggle source
# File lib/double_bag_ftps.rb, line 147
def ftps_explicit?; @ftps_mode == EXPLICIT end
ftps_implicit?() click to toggle source
# File lib/double_bag_ftps.rb, line 148
def ftps_implicit?; @ftps_mode == IMPLICIT end
ftps_mode=(ftps_mode) click to toggle source

Allow @ftps_mode to be set when @sock is not connected

# File lib/double_bag_ftps.rb, line 43
def ftps_mode=(ftps_mode)
  # Ruby 1.8.7/1.9.2 compatible check
  if (defined?(NullSocket) && @sock.kind_of?(NullSocket)) || @sock.nil? || @sock.closed?
    raise ArgumentError unless valid_ftps_mode?(ftps_mode)
    @ftps_mode = ftps_mode
  else
    raise 'Cannot set ftps_mode while connected'
  end
end
login(user = 'anonymous', passwd = nil, acct = nil, auth = 'TLS') click to toggle source
Calls superclass method
# File lib/double_bag_ftps.rb, line 62
def login(user = 'anonymous', passwd = nil, acct = nil, auth = 'TLS')
  if ftps_explicit?
    synchronize do
      sendcmd('AUTH ' + auth) # Set the security mechanism
      @sock = ssl_socket(@sock)
    end
  end
  
  super(user, passwd, acct)
  voidcmd('PBSZ 0') # The expected value for Protection Buffer Size (PBSZ) is 0 for TLS/SSL
  voidcmd('PROT P') # Set data channel protection level to Private
end

Private Instance Methods

decorate_socket(sock) click to toggle source

Ruby 2.0's Ftp class closes sockets by first doing a shutdown, setting the read timeout, and doing a read. OpenSSL doesn't have those methods, so fake it.

Ftp calls close in an ensure block, so the socket will still get closed.

# File lib/double_bag_ftps.rb, line 183
def decorate_socket(sock)

  def sock.shutdown(how)
    @shutdown = true
  end

  def sock.read_timeout=(seconds)
  end

  # Skip read after shutdown.  Prevents 2.0 from hanging in
  # Ftp#close

  def sock.read(*args)
    return if @shutdown
    super(*args)
  end

end
open_socket(host, port, defer_implicit_ssl = false) click to toggle source

Override parent to allow an OpenSSL::SSL::SSLSocket to be returned when using implicit FTPS

# File lib/double_bag_ftps.rb, line 79
def open_socket(host, port, defer_implicit_ssl = false)
  if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
    @passive = true
    sock = SOCKSSocket.open(host, port)
  else
    sock = TCPSocket.open(host, port)
  end
  return (!defer_implicit_ssl && ftps_implicit?) ? ssl_socket(sock) : sock
end
sendport_needed?() click to toggle source

Before ruby-2.2.3, makeport called sendport automatically. After Ruby 2.3.0, makeport does not call sendport automatically, so do it ourselves. This change to Ruby's FTP lib has been backported to Ruby 2.1 since version 2.1.7.

# File lib/double_bag_ftps.rb, line 138
def sendport_needed?
  @sendport_needed ||= begin
    Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.3") ||
    Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.7") &&
    Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.0")
  end
end
ssl_socket(sock) click to toggle source

Returns a connected OpenSSL::SSL::SSLSocket

# File lib/double_bag_ftps.rb, line 158
def ssl_socket(sock)
  raise 'SSL extension not installed' unless defined?(OpenSSL)
  sock = OpenSSL::SSL::SSLSocket.new(sock, @ssl_context)
  if @ssl_session
    sock.session = @ssl_session
  end
  sock.sync_close = true
  sock.connect
  print "get: #{sock.peer_cert.to_text}" if @debug_mode
  unless @ssl_context.verify_mode == OpenSSL::SSL::VERIFY_NONE
    sock.post_connection_check(@hostname)
  end
  @ssl_session = sock.session
  decorate_socket sock
  return sock
end
transfercmd(cmd, rest_offset = nil) click to toggle source

Override parent to support ssl sockets

# File lib/double_bag_ftps.rb, line 93
def transfercmd(cmd, rest_offset = nil)
  if @passive
    host, port = makepasv

    if @resume and rest_offset
      resp = sendcmd('REST ' + rest_offset.to_s)
      if resp[0] != ?3
        raise FTPReplyError, resp
      end
    end
    conn = open_socket(host, port, true)
    resp = sendcmd(cmd)
    # skip 2XX for some ftp servers
    resp = getresp if resp[0] == ?2
    if resp[0] != ?1
      raise FTPReplyError, resp
    end
    conn = ssl_socket(conn) # SSL connection now possible after cmd sent
  else
    sock = makeport
    sendport(sock.addr[3], sock.addr[1]) if sendport_needed?
    if @resume and rest_offset
      resp = sendcmd('REST ' + rest_offset.to_s)
      if resp[0] != ?3
        raise FTPReplyError, resp
      end
    end
    resp = sendcmd(cmd)
    # skip 2XX for some ftp servers
    resp = getresp if resp[0] == ?2
    if resp[0] != ?1
      raise FTPReplyError, resp
    end
    conn = sock.accept
    conn = ssl_socket(conn)
    sock.close
  end
  return conn
end
valid_ftps_mode?(mode) click to toggle source
# File lib/double_bag_ftps.rb, line 150
def valid_ftps_mode?(mode)
  mode == EXPLICIT || mode == IMPLICIT
end