class FakeFtp::Server
Constants
- CMDS
- LNBK
Attributes
mode[R]
passive_port[RW]
path[R]
port[RW]
Public Class Methods
new(control_port = 21, data_port = nil, options = {})
click to toggle source
# File lib/fake_ftp/server.rb, line 34 def initialize(control_port = 21, data_port = nil, options = {}) self.port = control_port self.passive_port = data_port raise(Errno::EADDRINUSE, "#{port}") if !control_port.zero? && is_running? raise(Errno::EADDRINUSE, "#{passive_port}") if passive_port && !passive_port.zero? && is_running?(passive_port) @connection = nil @options = options @files = [] @mode = :active @path = "/pub" end
Public Instance Methods
add_file(filename, data, last_modified_time = Time.now)
click to toggle source
# File lib/fake_ftp/server.rb, line 58 def add_file(filename, data, last_modified_time = Time.now) @files << FakeFtp::File.new(::File.basename(filename.to_s), data, @mode, last_modified_time) end
file(name)
click to toggle source
# File lib/fake_ftp/server.rb, line 50 def file(name) @files.detect { |file| file.name == name } end
files()
click to toggle source
# File lib/fake_ftp/server.rb, line 46 def files @files.map(&:name) end
is_running?(tcp_port = nil)
click to toggle source
# File lib/fake_ftp/server.rb, line 104 def is_running?(tcp_port = nil) tcp_port.nil? ? port_is_open?(port) : port_is_open?(tcp_port) end
reset()
click to toggle source
# File lib/fake_ftp/server.rb, line 54 def reset @files.clear end
start()
click to toggle source
# File lib/fake_ftp/server.rb, line 62 def start @started = true @server = ::TCPServer.new('127.0.0.1', port) @port = @server.addr[1] @thread = Thread.new do while @started @client = @server.accept rescue nil if @client respond_with('220 Can has FTP?') @connection = Thread.new(@client) do |socket| while @started && !socket.nil? && !socket.closed? input = socket.gets rescue nil respond_with parse(input) if input end unless @client.nil? @client.close unless @client.closed? @client = nil end end end end unless @server.nil? @server.close unless @server.closed? @server = nil end end if passive_port @data_server = ::TCPServer.new('127.0.0.1', passive_port) @passive_port = @data_server.addr[1] end end
stop()
click to toggle source
# File lib/fake_ftp/server.rb, line 95 def stop @started = false @client.close if @client @server.close if @server @server = nil @data_server.close if @data_server @data_server = nil end
Private Instance Methods
_acct(*args)
click to toggle source
FTP commands
Methods are prefixed with an underscore to avoid conflicts with internal server methods. Methods map 1:1 to FTP command words.
# File lib/fake_ftp/server.rb, line 134 def _acct(*args) '230 WHATEVER!' end
_cdup(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 144 def _cdup(*args) '250 OK!' end
_cwd(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 138 def _cwd(*args) @path = args[0] @path = "/#{path}" if path[0].chr != "/" '250 OK!' end
_dele(filename = '')
click to toggle source
# File lib/fake_ftp/server.rb, line 289 def _dele(filename = '') files_to_delete = @files.select{ |file| file.name == filename } return '550 Delete operation failed.' if files_to_delete.count == 0 @files = @files - files_to_delete '250 Delete operation successful.' end
_list(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 148 def _list(*args) wildcards = [] args.each do |arg| next unless arg.include? '*' wildcards << arg.gsub('*', '.*') end respond_with('425 Ain\'t no data port!') && return if active? && @active_connection.nil? respond_with('150 Listing status ok, about to open data connection') data_client = active? ? @active_connection : @data_server.accept files = @files if not wildcards.empty? files = files.select do |f| wildcards.any? { |wildcard| f.name =~ /#{wildcard}/ } end end files = files.map do |f| "-rw-r--r--\t1\towner\tgroup\t#{f.bytes}\t#{f.created.strftime('%b %d %H:%M')}\t#{f.name}\n" end data_client.write(files.join) data_client.close @active_connection = nil '226 List information transferred' end
_mdtm(filename = '', local = false)
click to toggle source
# File lib/fake_ftp/server.rb, line 176 def _mdtm(filename = '', local = false) respond_with('501 No filename given') && return if filename.empty? server_file = file(filename) respond_with('550 File not found') && return if server_file.nil? respond_with("213 #{server_file.last_modified_time.strftime("%Y%m%d%H%M%S")}") end
_mkd(directory)
click to toggle source
# File lib/fake_ftp/server.rb, line 313 def _mkd(directory) "257 OK!" end
_nlst(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 184 def _nlst(*args) respond_with('425 Ain\'t no data port!') && return if active? && @active_connection.nil? respond_with('150 Listing status ok, about to open data connection') data_client = active? ? @active_connection : @data_server.accept data_client.write(files.join("\n")) data_client.close @active_connection = nil '226 List information transferred' end
_pass(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 197 def _pass(*args) '230 logged in' end
_pasv(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 201 def _pasv(*args) if passive_port @mode = :passive p1 = (passive_port / 256).to_i p2 = passive_port % 256 "227 Entering Passive Mode (127,0,0,1,#{p1},#{p2})" else '502 Aww hell no, use Active' end end
_port(remote = '')
click to toggle source
# File lib/fake_ftp/server.rb, line 212 def _port(remote = '') remote = remote.split(',') remote_port = remote[4].to_i * 256 + remote[5].to_i unless @active_connection.nil? @active_connection.close @active_connection = nil end @mode = :active @active_connection = ::TCPSocket.open('127.0.0.1', remote_port) '200 Okay' end
_pwd(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 224 def _pwd(*args) "257 \"#{path}\" is current directory" end
_quit(*args)
click to toggle source
# File lib/fake_ftp/server.rb, line 228 def _quit(*args) respond_with '221 OMG bye!' @client.close if @client @client = nil end
_retr(filename = '')
click to toggle source
# File lib/fake_ftp/server.rb, line 234 def _retr(filename = '') respond_with('501 No filename given') if filename.empty? file = file(::File.basename(filename.to_s)) return respond_with('550 File not found') if file.nil? respond_with('425 Ain\'t no data port!') && return if active? && @active_connection.nil? respond_with('150 File status ok, about to open data connection') data_client = active? ? @active_connection : @data_server.accept data_client.write(file.data) data_client.close @active_connection = nil '226 File transferred' end
_rnfr(rename_from='')
click to toggle source
# File lib/fake_ftp/server.rb, line 252 def _rnfr(rename_from='') return '501 Send path name.' if rename_from.nil? || rename_from.size < 1 @rename_from = rename_from '350 Send RNTO to complete rename.' end
_rnto(rename_to='')
click to toggle source
# File lib/fake_ftp/server.rb, line 259 def _rnto(rename_to='') return '501 Send path name.' if rename_to.nil? || rename_to.size < 1 return '503 Send RNFR first.' unless @rename_from if file = file(@rename_from) file.name = rename_to @rename_from = nil '250 Path renamed.' else @rename_from = nil '550 File not found.' end end
_stor(filename = '')
click to toggle source
# File lib/fake_ftp/server.rb, line 274 def _stor(filename = '') respond_with('425 Ain\'t no data port!') && return if active? && @active_connection.nil? respond_with('125 Do it!') data_client = active? ? @active_connection : @data_server.accept data = data_client.read(nil).chomp file = FakeFtp::File.new(::File.basename(filename.to_s), data, @mode) @files << file data_client.close @active_connection = nil '226 Did it!' end
_type(type = 'A')
click to toggle source
# File lib/fake_ftp/server.rb, line 298 def _type(type = 'A') case type.to_s when 'A' '200 Type set to A.' when 'I' '200 Type set to I.' else '504 We don\'t allow those' end end
_user(name = '')
click to toggle source
# File lib/fake_ftp/server.rb, line 309 def _user(name = '') (name.to_s == 'anonymous') ? '230 logged in' : '331 send your password' end
active?()
click to toggle source
# File lib/fake_ftp/server.rb, line 317 def active? @mode == :active end
parse(request)
click to toggle source
# File lib/fake_ftp/server.rb, line 114 def parse(request) return if request.nil? puts request if @options[:debug] command = request[0, 4].downcase.strip contents = request.split message = contents[1..contents.length] case command when *CMDS __send__ "_#{command}", *message else '500 Unknown command' end end
port_is_open?(port)
click to toggle source
# File lib/fake_ftp/server.rb, line 323 def port_is_open?(port) begin Timeout::timeout(1) do begin s = TCPSocket.new("127.0.0.1", port) s.close return true rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH return false end end rescue Timeout::Error end return false end
respond_with(stuff)
click to toggle source
# File lib/fake_ftp/server.rb, line 110 def respond_with(stuff) @client.print stuff << LNBK unless stuff.nil? or @client.nil? or @client.closed? end