summaryrefslogtreecommitdiff
path: root/spec/ruby/library/net-ftp/fixtures/server.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/library/net-ftp/fixtures/server.rb')
-rw-r--r--spec/ruby/library/net-ftp/fixtures/server.rb277
1 files changed, 277 insertions, 0 deletions
diff --git a/spec/ruby/library/net-ftp/fixtures/server.rb b/spec/ruby/library/net-ftp/fixtures/server.rb
new file mode 100644
index 0000000000..ecbed591d5
--- /dev/null
+++ b/spec/ruby/library/net-ftp/fixtures/server.rb
@@ -0,0 +1,277 @@
+module NetFTPSpecs
+ class DummyFTP
+ attr_accessor :connect_message
+ attr_reader :login_user, :login_pass, :login_acct
+
+ # hostname or IP address
+ attr_reader :hostname
+ # port number
+ attr_reader :server_port
+
+ def initialize
+ @hostname = "localhost"
+ @server = TCPServer.new(@hostname, 0)
+ @server_port = @server.addr[1]
+
+ @handlers = {}
+ @commands = []
+ @connect_message = nil
+ end
+
+ def serve_once
+ @thread = Thread.new do
+ @socket = @server.accept
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1)
+ begin
+ handle_request
+ ensure
+ @socket.close
+ end
+ end
+ end
+
+ def handle_request
+ # Send out the welcome message.
+ response @connect_message || "220 Dummy FTP Server ready!"
+
+ begin
+ while command = @socket.gets
+ command, argument = command.chomp.split(" ", 2)
+
+ if command == "QUIT"
+ self.response("221 OK, bye")
+ break
+ elsif proc_handler = @handlers[command.downcase.to_sym]
+ if argument.nil?
+ proc_handler.call(self)
+ else
+ proc_handler.call(self, argument)
+ end
+ else
+ if argument.nil?
+ self.send(command.downcase.to_sym)
+ else
+ self.send(command.downcase.to_sym, argument)
+ end
+ end
+ end
+ rescue => e
+ self.error_response("Exception: #{e} #{e.backtrace.inspect}")
+ end
+ end
+
+ def error_response(text)
+ self.response("451 #{text}")
+ end
+
+ def response(text)
+ @socket.puts(text) unless @socket.closed?
+ end
+
+ def stop
+ @datasocket.close unless @datasocket.nil? || @datasocket.closed?
+ @server.close
+ @thread.join
+ end
+
+
+ ##
+ def handle(sym, &block)
+ @handlers[sym] = block
+ end
+
+ def should_receive(method)
+ @handler_for = method
+ self
+ end
+
+ def and_respond(text)
+ @handlers[@handler_for] = -> s, *args { s.response(text) }
+ end
+
+ ##
+ # FTP methods
+ ##
+
+ def abor
+ self.response("226 Closing data connection. (ABOR)")
+ end
+
+ def acct(account)
+ @login_acct = account
+ self.response("230 User '#{account}' logged in, proceed. (ACCT)")
+ end
+
+ def cdup
+ self.response("200 Command okay. (CDUP)")
+ end
+
+ def cwd(dir)
+ self.response("200 Command okay. (CWD #{dir})")
+ end
+
+ def dele(file)
+ self.response("250 Requested file action okay, completed. (DELE #{file})")
+ end
+
+ def eprt(arg)
+ _, _, host, port = arg.split("|")
+
+ @datasocket = TCPSocket.new(host, port)
+ self.response("200 port opened")
+ end
+
+ def help(param = :default)
+ if param == :default
+ self.response("211 System status, or system help reply. (HELP)")
+ else
+ self.response("211 System status, or system help reply. (HELP #{param})")
+ end
+ end
+
+ def list(folder)
+ self.response("150 opening ASCII connection for file list")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb")
+ @datasocket.puts("-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb")
+ @datasocket.close()
+ self.response("226 transfer complete (LIST #{folder})")
+ end
+
+ def mdtm(filename)
+ self.response("213 19980705132316")
+ end
+
+ def mkd(foldername)
+ self.response(%Q{257 "#{foldername.gsub('"', '""')}" created.})
+ end
+
+ def nlst(folder = nil)
+ self.response("150 opening ASCII connection for file list")
+ @datasocket.puts("last_response_code.rb")
+ @datasocket.puts("list.rb")
+ @datasocket.puts("pwd.rb")
+ @datasocket.close()
+ self.response("226 transfer complete (NLST#{folder ? " #{folder}" : ""})")
+ end
+
+ def noop
+ self.response("200 Command okay. (NOOP)")
+ end
+
+ def pass(password)
+ @login_pass = password
+ self.response("230 User logged in, proceed. (PASS #{password})")
+ end
+
+ def port(arg)
+ nums = arg.split(",")
+
+ if nums[0] == "::1"
+ # IPv6
+ port = nums[1].to_i * 256 + nums[2].to_i
+ host = nums[0]
+ else
+ # IPv4
+ port = nums[4].to_i * 256 + nums[5].to_i
+ host = nums[0..3].join(".")
+ end
+
+ @datasocket = TCPSocket.new(host, port)
+ self.response("200 port opened")
+ end
+
+ def pwd
+ self.response('257 "/some/dir/" - current directory')
+ end
+
+ def retr(file)
+ self.response("125 Data transfer starting")
+ if @restart_at && @restart_at == 20
+ @datasocket.puts("of the file named '#{file}'.")
+ @restart_at = nil
+ else
+ @datasocket.puts("This is the content")
+ @datasocket.puts("of the file named '#{file}'.")
+ end
+ @datasocket.close()
+ self.response("226 Closing data connection. (RETR #{file})")
+ end
+
+ def rest(at_bytes)
+ @restart_at = at_bytes.to_i
+ self.response("350 Requested file action pending further information. (REST)")
+ end
+
+ def rmd(folder)
+ self.response("250 Requested file action okay, completed. (RMD #{folder})")
+ end
+
+ def rnfr(from)
+ @rename_from = from
+ self.response("350 Requested file action pending further information.")
+ end
+
+ def rnto(to)
+ self.response("250 Requested file action okay, completed. (Renamed #{@rename_from} to #{to})")
+ @rename_from = nil
+ end
+
+ def site(param)
+ self.response("200 Command okay. (SITE #{param})")
+ end
+
+ def size(filename)
+ if filename == "binary"
+ self.response("213 24")
+ else
+ self.response("213 1024")
+ end
+ end
+
+ def stat(param = :default)
+ if param == :default
+ self.response("211 System status, or system help reply. (STAT)")
+ else
+ self.response("211 System status, or system help reply. (STAT #{param})")
+ end
+ end
+
+ def stor(file)
+ tmp_file = tmp("#{file}file", false)
+
+ self.response("125 Data transfer starting.")
+
+ mode = @restart_at ? "a" : "w"
+
+ File.open(tmp_file, mode + "b") do |f|
+ loop do
+ data = @datasocket.recv(1024)
+ break if !data || data.empty?
+ f << data
+ end
+ end
+
+ @datasocket.close()
+ self.response("200 OK, Data received. (STOR #{file})")
+ end
+
+ def appe(file)
+ @restart_at = true
+ stor(file)
+ end
+
+ def syst
+ self.response("215 FTP Dummy Server (SYST)")
+ end
+
+ def type(type)
+ self.response("200 TYPE switched to #{type}")
+ end
+
+ def user(name)
+ @login_user = name
+ self.response("230 User logged in, proceed. (USER #{name})")
+ end
+ end
+end