"Fossies" - the Fresh Open Source Software Archive

Member "ruby-2.7.4/test/net/ftp/test_ftp.rb" (7 Jul 2021, 87697 Bytes) of package /linux/misc/ruby-2.7.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Ruby source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_ftp.rb": 2.7.3_vs_2.7.4.

    1 # frozen_string_literal: true
    2 
    3 require "net/ftp"
    4 require "test/unit"
    5 require "ostruct"
    6 require "stringio"
    7 require "tempfile"
    8 require "tmpdir"
    9 
   10 class FTPTest < Test::Unit::TestCase
   11   SERVER_NAME = "localhost"
   12   SERVER_ADDR =
   13     begin
   14       Addrinfo.getaddrinfo(SERVER_NAME, 0, nil, :STREAM)[0].ip_address
   15     rescue SocketError
   16       "127.0.0.1"
   17     end
   18   CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
   19   SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
   20   SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
   21 
   22   def setup
   23     @thread = nil
   24     @default_passive = Net::FTP.default_passive
   25     Net::FTP.default_passive = false
   26   end
   27 
   28   def teardown
   29     Net::FTP.default_passive = @default_passive
   30     if @thread
   31       @thread.join
   32     end
   33   end
   34 
   35   def test_not_connected
   36     ftp = Net::FTP.new
   37     assert_raise(Net::FTPConnectionError) do
   38       ftp.quit
   39     end
   40   end
   41 
   42   def test_closed_when_not_connected
   43     ftp = Net::FTP.new
   44     assert_equal(true, ftp.closed?)
   45     assert_nothing_raised(Net::FTPConnectionError) do
   46       ftp.close
   47     end
   48   end
   49 
   50   def test_connect_fail
   51     server = create_ftp_server { |sock|
   52       sock.print("421 Service not available, closing control connection.\r\n")
   53     }
   54     begin
   55       ftp = Net::FTP.new
   56       assert_raise(Net::FTPTempError){ ftp.connect(SERVER_ADDR, server.port) }
   57     ensure
   58       ftp.close if ftp
   59       server.close
   60     end
   61   end
   62 
   63   def test_parse227
   64     ftp = Net::FTP.new(nil, use_pasv_ip: true)
   65     host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
   66     assert_equal("192.168.0.1", host)
   67     assert_equal(3106, port)
   68     assert_raise(Net::FTPReplyError) do
   69       ftp.send(:parse227, "500 Syntax error")
   70     end
   71     assert_raise(Net::FTPProtoError) do
   72       ftp.send(:parse227, "227 Entering Passive Mode")
   73     end
   74     assert_raise(Net::FTPProtoError) do
   75       ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34,56)")
   76     end
   77     assert_raise(Net::FTPProtoError) do
   78       ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1)")
   79     end
   80     assert_raise(Net::FTPProtoError) do
   81       ftp.send(:parse227, "227 ) foo bar (")
   82     end
   83 
   84     ftp = Net::FTP.new
   85     sock = OpenStruct.new
   86     sock.remote_address = OpenStruct.new
   87     sock.remote_address.ip_address = "10.0.0.1"
   88     ftp.instance_variable_set(:@bare_sock, sock)
   89     host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
   90     assert_equal("10.0.0.1", host)
   91   end
   92 
   93   def test_parse228
   94     ftp = Net::FTP.new
   95     host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,2,12,34)")
   96     assert_equal("192.168.0.1", host)
   97     assert_equal(3106, port)
   98     host, port = ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
   99     assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
  100     assert_equal(3106, port)
  101     assert_raise(Net::FTPReplyError) do
  102       ftp.send(:parse228, "500 Syntax error")
  103     end
  104     assert_raise(Net::FTPProtoError) do
  105       ftp.send(:parse228, "228 Entering Passive Mode")
  106     end
  107     assert_raise(Net::FTPProtoError) do
  108       ftp.send(:parse228, "228 Entering Long Passive Mode (6,4,192,168,0,1,2,12,34)")
  109     end
  110     assert_raise(Net::FTPProtoError) do
  111       ftp.send(:parse228, "228 Entering Long Passive Mode (4,4,192,168,0,1,3,12,34,56)")
  112     end
  113     assert_raise(Net::FTPProtoError) do
  114       ftp.send(:parse228, "228 Entering Long Passive Mode (4,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34)")
  115     end
  116     assert_raise(Net::FTPProtoError) do
  117       ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,3,12,34,56)")
  118     end
  119     assert_raise(Net::FTPProtoError) do
  120       ftp.send(:parse228, "228 Entering Long Passive Mode (6,16,16,128,0,0,0,0,0,0,0,8,8,0,32,12,65,122,2,12,34,56)")
  121     end
  122     assert_raise(Net::FTPProtoError) do
  123       ftp.send(:parse227, "227 ) foo bar (")
  124     end
  125   end
  126 
  127   def test_parse229
  128     ftp = Net::FTP.new
  129     sock = OpenStruct.new
  130     sock.remote_address = OpenStruct.new
  131     sock.remote_address.ip_address = "1080:0000:0000:0000:0008:0800:200c:417a"
  132     ftp.instance_variable_set(:@bare_sock, sock)
  133     host, port = ftp.send(:parse229, "229 Entering Passive Mode (|||3106|)")
  134     assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
  135     assert_equal(3106, port)
  136     host, port = ftp.send(:parse229, "229 Entering Passive Mode (!!!3106!)")
  137     assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
  138     assert_equal(3106, port)
  139     host, port = ftp.send(:parse229, "229 Entering Passive Mode (~~~3106~)")
  140     assert_equal("1080:0000:0000:0000:0008:0800:200c:417a", host)
  141     assert_equal(3106, port)
  142     assert_raise(Net::FTPReplyError) do
  143       ftp.send(:parse229, "500 Syntax error")
  144     end
  145     assert_raise(Net::FTPProtoError) do
  146       ftp.send(:parse229, "229 Entering Passive Mode")
  147     end
  148     assert_raise(Net::FTPProtoError) do
  149       ftp.send(:parse229, "229 Entering Passive Mode (|!!3106!)")
  150     end
  151     assert_raise(Net::FTPProtoError) do
  152       ftp.send(:parse229, "229 Entering Passive Mode (   3106 )")
  153     end
  154     assert_raise(Net::FTPProtoError) do
  155       ftp.send(:parse229, "229 Entering Passive Mode (\x7f\x7f\x7f3106\x7f)")
  156     end
  157     assert_raise(Net::FTPProtoError) do
  158       ftp.send(:parse229, "229 ) foo bar (")
  159     end
  160   end
  161 
  162   def test_parse_pasv_port
  163     ftp = Net::FTP.new
  164     assert_equal(12, ftp.send(:parse_pasv_port, "12"))
  165     assert_equal(3106, ftp.send(:parse_pasv_port, "12,34"))
  166     assert_equal(795192, ftp.send(:parse_pasv_port, "12,34,56"))
  167     assert_equal(203569230, ftp.send(:parse_pasv_port, "12,34,56,78"))
  168   end
  169 
  170   def test_login
  171     commands = []
  172     server = create_ftp_server { |sock|
  173       sock.print("220 (test_ftp).\r\n")
  174       commands.push(sock.gets)
  175       sock.print("331 Please specify the password.\r\n")
  176       commands.push(sock.gets)
  177       sock.print("230 Login successful.\r\n")
  178       commands.push(sock.gets)
  179       sock.print("200 Switching to Binary mode.\r\n")
  180     }
  181     begin
  182       begin
  183         ftp = Net::FTP.new
  184         ftp.connect(SERVER_ADDR, server.port)
  185         ftp.login
  186         assert_match(/\AUSER /, commands.shift)
  187         assert_match(/\APASS /, commands.shift)
  188         assert_equal("TYPE I\r\n", commands.shift)
  189         assert_equal(nil, commands.shift)
  190       ensure
  191         ftp.close if ftp
  192       end
  193     ensure
  194       server.close
  195     end
  196   end
  197 
  198   def test_login_fail1
  199     commands = []
  200     server = create_ftp_server { |sock|
  201       sock.print("220 (test_ftp).\r\n")
  202       commands.push(sock.gets)
  203       sock.print("502 Command not implemented.\r\n")
  204     }
  205     begin
  206       begin
  207         ftp = Net::FTP.new
  208         ftp.connect(SERVER_ADDR, server.port)
  209         assert_raise(Net::FTPPermError){ ftp.login }
  210       ensure
  211         ftp.close if ftp
  212       end
  213     ensure
  214       server.close
  215     end
  216   end
  217 
  218   def test_login_fail2
  219     commands = []
  220     server = create_ftp_server { |sock|
  221       sock.print("220 (test_ftp).\r\n")
  222       commands.push(sock.gets)
  223       sock.print("331 Please specify the password.\r\n")
  224       commands.push(sock.gets)
  225       sock.print("530 Not logged in.\r\n")
  226     }
  227     begin
  228       begin
  229         ftp = Net::FTP.new
  230         ftp.connect(SERVER_ADDR, server.port)
  231         assert_raise(Net::FTPPermError){ ftp.login }
  232       ensure
  233         ftp.close if ftp
  234       end
  235     ensure
  236       server.close
  237     end
  238   end
  239 
  240   def test_implicit_login
  241     commands = []
  242     server = create_ftp_server { |sock|
  243       sock.print("220 (test_ftp).\r\n")
  244       commands.push(sock.gets)
  245       sock.print("331 Please specify the password.\r\n")
  246       commands.push(sock.gets)
  247       sock.print("332 Need account for login.\r\n")
  248       commands.push(sock.gets)
  249       sock.print("230 Login successful.\r\n")
  250       commands.push(sock.gets)
  251       sock.print("200 Switching to Binary mode.\r\n")
  252     }
  253     begin
  254       begin
  255         ftp = Net::FTP.new(SERVER_ADDR,
  256                            port: server.port,
  257                            username: "foo",
  258                            password: "bar",
  259                            account: "baz")
  260         assert_equal("USER foo\r\n", commands.shift)
  261         assert_equal("PASS bar\r\n", commands.shift)
  262         assert_equal("ACCT baz\r\n", commands.shift)
  263         assert_equal("TYPE I\r\n", commands.shift)
  264         assert_equal(nil, commands.shift)
  265       ensure
  266         ftp.close if ftp
  267       end
  268     ensure
  269       server.close
  270     end
  271   end
  272 
  273   def test_s_open
  274     commands = []
  275     server = create_ftp_server { |sock|
  276       sock.print("220 (test_ftp).\r\n")
  277       commands.push(sock.gets)
  278       sock.print("331 Please specify the password.\r\n")
  279       commands.push(sock.gets)
  280       sock.print("230 Login successful.\r\n")
  281       commands.push(sock.gets)
  282       sock.print("200 Switching to Binary mode.\r\n")
  283     }
  284     begin
  285       Net::FTP.open(SERVER_ADDR, port: server.port, username: "anonymous") do
  286       end
  287       assert_equal("USER anonymous\r\n", commands.shift)
  288       assert_equal("PASS anonymous@\r\n", commands.shift)
  289       assert_equal("TYPE I\r\n", commands.shift)
  290       assert_equal(nil, commands.shift)
  291     ensure
  292       server.close
  293     end
  294   end
  295 
  296   def test_s_new_timeout_options
  297     ftp = Net::FTP.new
  298     assert_equal(nil, ftp.open_timeout)
  299     assert_equal(60, ftp.read_timeout)
  300 
  301     ftp = Net::FTP.new(nil, open_timeout: 123, read_timeout: 234)
  302     assert_equal(123, ftp.open_timeout)
  303     assert_equal(234, ftp.read_timeout)
  304   end
  305 
  306   # TODO: How can we test open_timeout?  sleep before accept cannot delay
  307   # connections.
  308   def _test_open_timeout_exceeded
  309     commands = []
  310     server = create_ftp_server(0.2) { |sock|
  311       sock.print("220 (test_ftp).\r\n")
  312       commands.push(sock.gets)
  313       sock.print("331 Please specify the password.\r\n")
  314       commands.push(sock.gets)
  315       sock.print("230 Login successful.\r\n")
  316       commands.push(sock.gets)
  317       sock.print("200 Switching to Binary mode.\r\n")
  318     }
  319     begin
  320       begin
  321         ftp = Net::FTP.new
  322         ftp.open_timeout = 0.1
  323         ftp.connect(SERVER_ADDR, server.port)
  324         assert_raise(Net::OpenTimeout) do
  325           ftp.login
  326         end
  327         assert_match(/\AUSER /, commands.shift)
  328         assert_match(/\APASS /, commands.shift)
  329         assert_equal(nil, commands.shift)
  330       ensure
  331         ftp.close if ftp
  332       end
  333     ensure
  334       server.close
  335     end
  336   end
  337 
  338   def test_read_timeout_exceeded
  339     commands = []
  340     server = create_ftp_server { |sock|
  341       sock.print("220 (test_ftp).\r\n")
  342       commands.push(sock.gets)
  343       sleep(0.1)
  344       sock.print("331 Please specify the password.\r\n")
  345       commands.push(sock.gets)
  346       sleep(2.0) # Net::ReadTimeout
  347       sock.print("230 Login successful.\r\n")
  348       commands.push(sock.gets)
  349       sleep(0.1)
  350       sock.print("200 Switching to Binary mode.\r\n")
  351     }
  352     begin
  353       begin
  354         ftp = Net::FTP.new
  355         ftp.read_timeout = 0.4
  356         ftp.connect(SERVER_ADDR, server.port)
  357         assert_raise(Net::ReadTimeout) do
  358           ftp.login
  359         end
  360         assert_match(/\AUSER /, commands.shift)
  361         assert_match(/\APASS /, commands.shift)
  362         assert_equal(nil, commands.shift)
  363       ensure
  364         ftp.close if ftp
  365       end
  366     ensure
  367       server.close
  368     end
  369   end
  370 
  371   def test_read_timeout_not_exceeded
  372     commands = []
  373     server = create_ftp_server { |sock|
  374       sock.print("220 (test_ftp).\r\n")
  375       commands.push(sock.gets)
  376       sleep(0.1)
  377       sock.print("331 Please specify the password.\r\n")
  378       commands.push(sock.gets)
  379       sleep(0.1)
  380       sock.print("230 Login successful.\r\n")
  381       commands.push(sock.gets)
  382       sleep(0.1)
  383       sock.print("200 Switching to Binary mode.\r\n")
  384     }
  385     begin
  386       begin
  387         ftp = Net::FTP.new
  388         ftp.read_timeout = 1.0
  389         ftp.connect(SERVER_ADDR, server.port)
  390         ftp.login
  391         assert_match(/\AUSER /, commands.shift)
  392         assert_match(/\APASS /, commands.shift)
  393         assert_equal("TYPE I\r\n", commands.shift)
  394         assert_equal(nil, commands.shift)
  395       ensure
  396         ftp.close
  397         assert_equal(1.0, ftp.read_timeout)
  398       end
  399     ensure
  400       server.close
  401     end
  402   end
  403 
  404   def test_list_read_timeout_exceeded
  405     commands = []
  406     list_lines = [
  407       "-rw-r--r--    1 0        0               0 Mar 30 11:22 foo.txt",
  408       "-rw-r--r--    1 0        0               0 Mar 30 11:22 bar.txt",
  409       "-rw-r--r--    1 0        0               0 Mar 30 11:22 baz.txt"
  410     ]
  411     server = create_ftp_server { |sock|
  412       sock.print("220 (test_ftp).\r\n")
  413       commands.push(sock.gets)
  414       sock.print("331 Please specify the password.\r\n")
  415       commands.push(sock.gets)
  416       sock.print("230 Login successful.\r\n")
  417       commands.push(sock.gets)
  418       sock.print("200 Switching to Binary mode.\r\n")
  419       commands.push(sock.gets)
  420       sock.print("200 Switching to ASCII mode.\r\n")
  421       line = sock.gets
  422       commands.push(line)
  423       host, port = process_port_or_eprt(sock, line)
  424       commands.push(sock.gets)
  425       sock.print("150 Here comes the directory listing.\r\n")
  426       begin
  427         conn = TCPSocket.new(host, port)
  428         list_lines.each_with_index do |l, i|
  429           if i == 1
  430             sleep(0.5)
  431           else
  432             sleep(0.1)
  433           end
  434           conn.print(l, "\r\n")
  435         end
  436       rescue Errno::EPIPE
  437       ensure
  438         assert_nil($!)
  439         conn.close
  440       end
  441       sock.print("226 Directory send OK.\r\n")
  442     }
  443     begin
  444       begin
  445         ftp = Net::FTP.new
  446         ftp.read_timeout = 0.2
  447         ftp.connect(SERVER_ADDR, server.port)
  448         ftp.login
  449         assert_match(/\AUSER /, commands.shift)
  450         assert_match(/\APASS /, commands.shift)
  451         assert_equal("TYPE I\r\n", commands.shift)
  452         assert_raise(Net::ReadTimeout) do
  453           ftp.list
  454         end
  455         assert_equal("TYPE A\r\n", commands.shift)
  456         assert_match(/\A(PORT|EPRT) /, commands.shift)
  457         assert_equal("LIST\r\n", commands.shift)
  458         assert_equal(nil, commands.shift)
  459       ensure
  460         ftp.close if ftp
  461       end
  462     ensure
  463       server.close
  464     end
  465   end
  466 
  467   def test_list_read_timeout_not_exceeded
  468     commands = []
  469     list_lines = [
  470       "-rw-r--r--    1 0        0               0 Mar 30 11:22 foo.txt",
  471       "-rw-r--r--    1 0        0               0 Mar 30 11:22 bar.txt",
  472       "-rw-r--r--    1 0        0               0 Mar 30 11:22 baz.txt"
  473     ]
  474     server = create_ftp_server { |sock|
  475       sock.print("220 (test_ftp).\r\n")
  476       commands.push(sock.gets)
  477       sock.print("331 Please specify the password.\r\n")
  478       commands.push(sock.gets)
  479       sock.print("230 Login successful.\r\n")
  480       commands.push(sock.gets)
  481       sock.print("200 Switching to Binary mode.\r\n")
  482       commands.push(sock.gets)
  483       sock.print("200 Switching to ASCII mode.\r\n")
  484       line = sock.gets
  485       commands.push(line)
  486       host, port = process_port_or_eprt(sock, line)
  487       commands.push(sock.gets)
  488       sock.print("150 Here comes the directory listing.\r\n")
  489       conn = TCPSocket.new(host, port)
  490       list_lines.each do |l|
  491         sleep(0.1)
  492         conn.print(l, "\r\n")
  493       end
  494       conn.close
  495       sock.print("226 Directory send OK.\r\n")
  496       commands.push(sock.gets)
  497       sock.print("200 Switching to Binary mode.\r\n")
  498     }
  499     begin
  500       begin
  501         ftp = Net::FTP.new
  502         ftp.read_timeout = 1.0
  503         ftp.connect(SERVER_ADDR, server.port)
  504         ftp.login
  505         assert_match(/\AUSER /, commands.shift)
  506         assert_match(/\APASS /, commands.shift)
  507         assert_equal("TYPE I\r\n", commands.shift)
  508         assert_equal(list_lines, ftp.list)
  509         assert_equal("TYPE A\r\n", commands.shift)
  510         assert_match(/\A(PORT|EPRT) /, commands.shift)
  511         assert_equal("LIST\r\n", commands.shift)
  512         assert_equal("TYPE I\r\n", commands.shift)
  513         assert_equal(nil, commands.shift)
  514       ensure
  515         ftp.close if ftp
  516       end
  517     ensure
  518       server.close
  519     end
  520   end
  521 
  522   def test_list_fail
  523     commands = []
  524     server = create_ftp_server { |sock|
  525       sock.print("220 (test_ftp).\r\n")
  526       commands.push(sock.gets)
  527       sock.print("331 Please specify the password.\r\n")
  528       commands.push(sock.gets)
  529       sock.print("230 Login successful.\r\n")
  530       commands.push(sock.gets)
  531       sock.print("200 Switching to Binary mode.\r\n")
  532       commands.push(sock.gets)
  533       sock.print("200 Switching to ASCII mode.\r\n")
  534       line = sock.gets
  535       commands.push(line)
  536       host, port = process_port_or_eprt(sock, line)
  537       commands.push(sock.gets)
  538       sock.print("553 Requested action not taken.\r\n")
  539       commands.push(sock.gets)
  540       sock.print("200 Switching to Binary mode.\r\n")
  541       [host, port]
  542     }
  543     begin
  544       begin
  545         ftp = Net::FTP.new
  546         ftp.connect(SERVER_ADDR, server.port)
  547         ftp.login
  548         assert_match(/\AUSER /, commands.shift)
  549         assert_match(/\APASS /, commands.shift)
  550         assert_equal("TYPE I\r\n", commands.shift)
  551         assert_raise(Net::FTPPermError){ ftp.list }
  552         assert_equal("TYPE A\r\n", commands.shift)
  553         assert_match(/\A(PORT|EPRT) /, commands.shift)
  554         assert_equal("LIST\r\n", commands.shift)
  555         assert_equal("TYPE I\r\n", commands.shift)
  556         assert_equal(nil, commands.shift)
  557       ensure
  558         ftp.close if ftp
  559       end
  560     ensure
  561       server.close
  562     end
  563   end
  564 
  565   def test_open_data_port_fail_no_leak
  566     commands = []
  567     server = create_ftp_server { |sock|
  568       sock.print("220 (test_ftp).\r\n")
  569       commands.push(sock.gets)
  570       sock.print("331 Please specify the password.\r\n")
  571       commands.push(sock.gets)
  572       sock.print("230 Login successful.\r\n")
  573       commands.push(sock.gets)
  574       sock.print("200 Switching to Binary mode.\r\n")
  575       commands.push(sock.gets)
  576       sock.print("200 Switching to ASCII mode.\r\n")
  577       line = sock.gets
  578       commands.push(line)
  579       sock.print("421 Service not available, closing control connection.\r\n")
  580       commands.push(sock.gets)
  581       sock.print("200 Switching to Binary mode.\r\n")
  582     }
  583     begin
  584       begin
  585         ftp = Net::FTP.new
  586         ftp.connect(SERVER_ADDR, server.port)
  587         ftp.login
  588         assert_match(/\AUSER /, commands.shift)
  589         assert_match(/\APASS /, commands.shift)
  590         assert_equal("TYPE I\r\n", commands.shift)
  591         assert_raise(Net::FTPTempError){ ftp.list }
  592         assert_equal("TYPE A\r\n", commands.shift)
  593         assert_match(/\A(PORT|EPRT) /, commands.shift)
  594         assert_equal("TYPE I\r\n", commands.shift)
  595         assert_equal(nil, commands.shift)
  596       ensure
  597         ftp.close if ftp
  598       end
  599     ensure
  600       server.close
  601     end
  602   end
  603 
  604   def test_retrbinary_read_timeout_exceeded
  605     commands = []
  606     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  607     server = create_ftp_server { |sock|
  608       sock.print("220 (test_ftp).\r\n")
  609       commands.push(sock.gets)
  610       sock.print("331 Please specify the password.\r\n")
  611       commands.push(sock.gets)
  612       sock.print("230 Login successful.\r\n")
  613       commands.push(sock.gets)
  614       sock.print("200 Switching to Binary mode.\r\n")
  615       line = sock.gets
  616       commands.push(line)
  617       host, port = process_port_or_eprt(sock, line)
  618       commands.push(sock.gets)
  619       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
  620       conn = TCPSocket.new(host, port)
  621       sleep(0.1)
  622       conn.print(binary_data[0,1024])
  623       sleep(1.0)
  624       conn.print(binary_data[1024, 1024]) rescue nil # may raise EPIPE or something
  625       conn.close
  626       sock.print("226 Transfer complete.\r\n")
  627     }
  628     begin
  629       begin
  630         ftp = Net::FTP.new
  631         ftp.read_timeout = 0.5
  632         ftp.connect(SERVER_ADDR, server.port)
  633         ftp.login
  634         assert_match(/\AUSER /, commands.shift)
  635         assert_match(/\APASS /, commands.shift)
  636         assert_equal("TYPE I\r\n", commands.shift)
  637         buf = String.new
  638         assert_raise(Net::ReadTimeout) do
  639           ftp.retrbinary("RETR foo", 1024) do |s|
  640             buf << s
  641           end
  642         end
  643         assert_equal(1024, buf.bytesize)
  644         assert_equal(binary_data[0, 1024], buf)
  645         assert_match(/\A(PORT|EPRT) /, commands.shift)
  646         assert_equal("RETR foo\r\n", commands.shift)
  647         assert_equal(nil, commands.shift)
  648       ensure
  649         ftp.close unless ftp.closed?
  650       end
  651     ensure
  652       server.close
  653     end
  654   end
  655 
  656   def test_retrbinary_read_timeout_not_exceeded
  657     commands = []
  658     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  659     server = create_ftp_server { |sock|
  660       sock.print("220 (test_ftp).\r\n")
  661       commands.push(sock.gets)
  662       sock.print("331 Please specify the password.\r\n")
  663       commands.push(sock.gets)
  664       sock.print("230 Login successful.\r\n")
  665       commands.push(sock.gets)
  666       sock.print("200 Switching to Binary mode.\r\n")
  667       line = sock.gets
  668       commands.push(line)
  669       host, port = process_port_or_eprt(sock, line)
  670       commands.push(sock.gets)
  671       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
  672       conn = TCPSocket.new(host, port)
  673       binary_data.scan(/.{1,1024}/nm) do |s|
  674         sleep(0.2)
  675         conn.print(s)
  676       end
  677       conn.shutdown(Socket::SHUT_WR)
  678       conn.read
  679       conn.close
  680       sock.print("226 Transfer complete.\r\n")
  681     }
  682     begin
  683       begin
  684         ftp = Net::FTP.new
  685         ftp.read_timeout = 1.0
  686         ftp.connect(SERVER_ADDR, server.port)
  687         ftp.login
  688         assert_match(/\AUSER /, commands.shift)
  689         assert_match(/\APASS /, commands.shift)
  690         assert_equal("TYPE I\r\n", commands.shift)
  691         buf = String.new
  692         ftp.retrbinary("RETR foo", 1024) do |s|
  693           buf << s
  694         end
  695         assert_equal(binary_data.bytesize, buf.bytesize)
  696         assert_equal(binary_data, buf)
  697         assert_match(/\A(PORT|EPRT) /, commands.shift)
  698         assert_equal("RETR foo\r\n", commands.shift)
  699         assert_equal(nil, commands.shift)
  700       ensure
  701         ftp.close if ftp
  702       end
  703     ensure
  704       server.close
  705     end
  706   end
  707 
  708   def test_retrbinary_fail
  709     commands = []
  710     server = create_ftp_server { |sock|
  711       sock.print("220 (test_ftp).\r\n")
  712       commands.push(sock.gets)
  713       sock.print("331 Please specify the password.\r\n")
  714       commands.push(sock.gets)
  715       sock.print("230 Login successful.\r\n")
  716       commands.push(sock.gets)
  717       sock.print("200 Switching to Binary mode.\r\n")
  718       line = sock.gets
  719       commands.push(line)
  720       host, port = process_port_or_eprt(sock, line)
  721       commands.push(sock.gets)
  722       sock.print("550 Requested action not taken.\r\n")
  723       [host, port]
  724     }
  725     begin
  726       begin
  727         ftp = Net::FTP.new
  728         ftp.connect(SERVER_ADDR, server.port)
  729         ftp.login
  730         assert_match(/\AUSER /, commands.shift)
  731         assert_match(/\APASS /, commands.shift)
  732         assert_equal("TYPE I\r\n", commands.shift)
  733         assert_raise(Net::FTPPermError){ ftp.retrbinary("RETR foo", 1024) }
  734         assert_match(/\A(PORT|EPRT) /, commands.shift)
  735         assert_equal("RETR foo\r\n", commands.shift)
  736         assert_equal(nil, commands.shift)
  737       ensure
  738         ftp.close if ftp
  739       end
  740     ensure
  741       server.close
  742     end
  743   end
  744 
  745   def test_getbinaryfile
  746     commands = []
  747     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  748     server = create_ftp_server { |sock|
  749       sock.print("220 (test_ftp).\r\n")
  750       commands.push(sock.gets)
  751       sock.print("331 Please specify the password.\r\n")
  752       commands.push(sock.gets)
  753       sock.print("230 Login successful.\r\n")
  754       commands.push(sock.gets)
  755       sock.print("200 Switching to Binary mode.\r\n")
  756       line = sock.gets
  757       commands.push(line)
  758       host, port = process_port_or_eprt(sock, line)
  759       commands.push(sock.gets)
  760       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
  761       conn = TCPSocket.new(host, port)
  762       binary_data.scan(/.{1,1024}/nm) do |s|
  763         conn.print(s)
  764       end
  765       conn.shutdown(Socket::SHUT_WR)
  766       conn.read
  767       conn.close
  768       sock.print("226 Transfer complete.\r\n")
  769     }
  770     begin
  771       begin
  772         ftp = Net::FTP.new
  773         ftp.connect(SERVER_ADDR, server.port)
  774         ftp.login
  775         assert_match(/\AUSER /, commands.shift)
  776         assert_match(/\APASS /, commands.shift)
  777         assert_equal("TYPE I\r\n", commands.shift)
  778         buf = ftp.getbinaryfile("foo", nil)
  779         assert_equal(binary_data, buf)
  780         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
  781         assert_match(/\A(PORT|EPRT) /, commands.shift)
  782         assert_equal("RETR foo\r\n", commands.shift)
  783         assert_equal(nil, commands.shift)
  784       ensure
  785         ftp.close if ftp
  786       end
  787     ensure
  788       server.close
  789     end
  790   end
  791 
  792   def test_getbinaryfile_empty
  793     commands = []
  794     binary_data = ""
  795     server = create_ftp_server { |sock|
  796       sock.print("220 (test_ftp).\r\n")
  797       commands.push(sock.gets)
  798       sock.print("331 Please specify the password.\r\n")
  799       commands.push(sock.gets)
  800       sock.print("230 Login successful.\r\n")
  801       commands.push(sock.gets)
  802       sock.print("200 Switching to Binary mode.\r\n")
  803       line = sock.gets
  804       commands.push(line)
  805       host, port = process_port_or_eprt(sock, line)
  806       commands.push(sock.gets)
  807       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
  808       conn = TCPSocket.new(host, port)
  809       conn.shutdown(Socket::SHUT_WR)
  810       conn.read
  811       conn.close
  812       sock.print("226 Transfer complete.\r\n")
  813     }
  814     begin
  815       begin
  816         ftp = Net::FTP.new
  817         ftp.connect(SERVER_ADDR, server.port)
  818         ftp.login
  819         assert_match(/\AUSER /, commands.shift)
  820         assert_match(/\APASS /, commands.shift)
  821         assert_equal("TYPE I\r\n", commands.shift)
  822         buf = ftp.getbinaryfile("foo", nil)
  823         assert_equal(binary_data, buf)
  824         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
  825         assert_match(/\A(PORT|EPRT) /, commands.shift)
  826         assert_equal("RETR foo\r\n", commands.shift)
  827         assert_equal(nil, commands.shift)
  828       ensure
  829         ftp.close if ftp
  830       end
  831     ensure
  832       server.close
  833     end
  834   end
  835 
  836   def test_getbinaryfile_with_filename_and_block
  837     commands = []
  838     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  839     server = create_ftp_server { |sock|
  840       sock.print("220 (test_ftp).\r\n")
  841       commands.push(sock.gets)
  842       sock.print("331 Please specify the password.\r\n")
  843       commands.push(sock.gets)
  844       sock.print("230 Login successful.\r\n")
  845       commands.push(sock.gets)
  846       sock.print("200 Switching to Binary mode.\r\n")
  847       line = sock.gets
  848       commands.push(line)
  849       host, port = process_port_or_eprt(sock, line)
  850       commands.push(sock.gets)
  851       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
  852       conn = TCPSocket.new(host, port)
  853       binary_data.scan(/.{1,1024}/nm) do |s|
  854         conn.print(s)
  855       end
  856       conn.shutdown(Socket::SHUT_WR)
  857       conn.read
  858       conn.close
  859       sock.print("226 Transfer complete.\r\n")
  860     }
  861     begin
  862       begin
  863         ftp = Net::FTP.new
  864         ftp.connect(SERVER_ADDR, server.port)
  865         ftp.login
  866         assert_match(/\AUSER /, commands.shift)
  867         assert_match(/\APASS /, commands.shift)
  868         assert_equal("TYPE I\r\n", commands.shift)
  869         Tempfile.create("foo", external_encoding: "ASCII-8BIT") do |f|
  870           f.binmode
  871           buf = String.new
  872           res = ftp.getbinaryfile("foo", f.path) { |s|
  873             buf << s
  874           }
  875           assert_equal(nil, res)
  876           assert_equal(binary_data, buf)
  877           assert_equal(Encoding::ASCII_8BIT, buf.encoding)
  878           assert_equal(binary_data, f.read)
  879         end
  880         assert_match(/\A(PORT|EPRT) /, commands.shift)
  881         assert_equal("RETR foo\r\n", commands.shift)
  882         assert_equal(nil, commands.shift)
  883       ensure
  884         ftp.close if ftp
  885       end
  886     ensure
  887       server.close
  888     end
  889   end
  890 
  891   def test_storbinary
  892     commands = []
  893     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  894     stored_data = nil
  895     server = create_ftp_server { |sock|
  896       sock.print("220 (test_ftp).\r\n")
  897       commands.push(sock.gets)
  898       sock.print("331 Please specify the password.\r\n")
  899       commands.push(sock.gets)
  900       sock.print("230 Login successful.\r\n")
  901       commands.push(sock.gets)
  902       sock.print("200 Switching to Binary mode.\r\n")
  903       line = sock.gets
  904       commands.push(line)
  905       host, port = process_port_or_eprt(sock, line)
  906       commands.push(sock.gets)
  907       sock.print("150 Opening BINARY mode data connection for foo\r\n")
  908       conn = TCPSocket.new(host, port)
  909       stored_data = conn.read
  910       conn.close
  911       sock.print("226 Transfer complete.\r\n")
  912     }
  913     begin
  914       begin
  915         ftp = Net::FTP.new
  916         ftp.connect(SERVER_ADDR, server.port)
  917         ftp.login
  918         assert_match(/\AUSER /, commands.shift)
  919         assert_match(/\APASS /, commands.shift)
  920         assert_equal("TYPE I\r\n", commands.shift)
  921         ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024)
  922         assert_equal(binary_data, stored_data)
  923         assert_match(/\A(PORT|EPRT) /, commands.shift)
  924         assert_equal("STOR foo\r\n", commands.shift)
  925         assert_equal(nil, commands.shift)
  926       ensure
  927         ftp.close if ftp
  928       end
  929     ensure
  930       server.close
  931     end
  932   end
  933 
  934   def test_storbinary_fail
  935     commands = []
  936     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
  937     server = create_ftp_server { |sock|
  938       sock.print("220 (test_ftp).\r\n")
  939       commands.push(sock.gets)
  940       sock.print("331 Please specify the password.\r\n")
  941       commands.push(sock.gets)
  942       sock.print("230 Login successful.\r\n")
  943       commands.push(sock.gets)
  944       sock.print("200 Switching to Binary mode.\r\n")
  945       line = sock.gets
  946       commands.push(line)
  947       host, port = process_port_or_eprt(sock, line)
  948       commands.push(sock.gets)
  949       sock.print("452 Requested file action aborted.\r\n")
  950       [host, port]
  951     }
  952     begin
  953       begin
  954         ftp = Net::FTP.new
  955         ftp.connect(SERVER_ADDR, server.port)
  956         ftp.login
  957         assert_match(/\AUSER /, commands.shift)
  958         assert_match(/\APASS /, commands.shift)
  959         assert_equal("TYPE I\r\n", commands.shift)
  960         assert_raise(Net::FTPTempError){ ftp.storbinary("STOR foo", StringIO.new(binary_data), 1024) }
  961         assert_match(/\A(PORT|EPRT) /, commands.shift)
  962         assert_equal("STOR foo\r\n", commands.shift)
  963         assert_equal(nil, commands.shift)
  964       ensure
  965         ftp.close if ftp
  966       end
  967     ensure
  968       server.close
  969     end
  970   end
  971 
  972   def test_retrlines
  973     commands = []
  974     text_data = <<EOF.gsub(/\n/, "\r\n")
  975 foo
  976 bar
  977 baz
  978 EOF
  979     server = create_ftp_server { |sock|
  980       sock.print("220 (test_ftp).\r\n")
  981       commands.push(sock.gets)
  982       sock.print("331 Please specify the password.\r\n")
  983       commands.push(sock.gets)
  984       sock.print("230 Login successful.\r\n")
  985       commands.push(sock.gets)
  986       sock.print("200 Switching to Binary mode.\r\n")
  987       commands.push(sock.gets)
  988       sock.print("200 Switching to ASCII mode.\r\n")
  989       line = sock.gets
  990       commands.push(line)
  991       host, port = process_port_or_eprt(sock, line)
  992       commands.push(sock.gets)
  993       sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
  994       conn = TCPSocket.new(host, port)
  995       text_data.each_line do |l|
  996         conn.print(l)
  997       end
  998       conn.shutdown(Socket::SHUT_WR)
  999       conn.read
 1000       conn.close
 1001       sock.print("226 Transfer complete.\r\n")
 1002       commands.push(sock.gets)
 1003       sock.print("200 Switching to Binary mode.\r\n")
 1004     }
 1005     begin
 1006       begin
 1007         ftp = Net::FTP.new
 1008         ftp.connect(SERVER_ADDR, server.port)
 1009         ftp.login
 1010         assert_match(/\AUSER /, commands.shift)
 1011         assert_match(/\APASS /, commands.shift)
 1012         assert_equal("TYPE I\r\n", commands.shift)
 1013         buf = String.new
 1014         ftp.retrlines("RETR foo") do |line|
 1015           buf << line + "\r\n"
 1016         end
 1017         assert_equal(text_data.bytesize, buf.bytesize)
 1018         assert_equal(text_data, buf)
 1019         assert_equal("TYPE A\r\n", commands.shift)
 1020         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1021         assert_equal("RETR foo\r\n", commands.shift)
 1022         assert_equal("TYPE I\r\n", commands.shift)
 1023         assert_equal(nil, commands.shift)
 1024       ensure
 1025         ftp.close if ftp
 1026       end
 1027     ensure
 1028       server.close
 1029     end
 1030   end
 1031 
 1032   def test_gettextfile
 1033     commands = []
 1034     text_data = <<EOF.gsub(/\n/, "\r\n")
 1035 foo
 1036 bar
 1037 baz
 1038 EOF
 1039     server = create_ftp_server { |sock|
 1040       sock.print("220 (test_ftp).\r\n")
 1041       commands.push(sock.gets)
 1042       sock.print("331 Please specify the password.\r\n")
 1043       commands.push(sock.gets)
 1044       sock.print("230 Login successful.\r\n")
 1045       commands.push(sock.gets)
 1046       sock.print("200 Switching to Binary mode.\r\n")
 1047       commands.push(sock.gets)
 1048       sock.print("200 Switching to ASCII mode.\r\n")
 1049       line = sock.gets
 1050       commands.push(line)
 1051       host, port = process_port_or_eprt(sock, line)
 1052       commands.push(sock.gets)
 1053       sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
 1054       conn = TCPSocket.new(host, port)
 1055       text_data.each_line do |l|
 1056         conn.print(l)
 1057       end
 1058       conn.shutdown(Socket::SHUT_WR)
 1059       conn.read
 1060       conn.close
 1061       sock.print("226 Transfer complete.\r\n")
 1062       commands.push(sock.gets)
 1063       sock.print("200 Switching to Binary mode.\r\n")
 1064     }
 1065     begin
 1066       begin
 1067         ftp = Net::FTP.new
 1068         ftp.connect(SERVER_ADDR, server.port)
 1069         ftp.login
 1070         assert_match(/\AUSER /, commands.shift)
 1071         assert_match(/\APASS /, commands.shift)
 1072         assert_equal("TYPE I\r\n", commands.shift)
 1073         buf = ftp.gettextfile("foo", nil)
 1074         assert_equal(text_data.gsub(/\r\n/, "\n"), buf)
 1075         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 1076         assert_equal("TYPE A\r\n", commands.shift)
 1077         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1078         assert_equal("RETR foo\r\n", commands.shift)
 1079         assert_equal("TYPE I\r\n", commands.shift)
 1080         assert_equal(nil, commands.shift)
 1081       ensure
 1082         ftp.close if ftp
 1083       end
 1084     ensure
 1085       server.close
 1086     end
 1087   end
 1088 
 1089   def test_gettextfile_with_filename_and_block
 1090     commands = []
 1091     text_data = <<EOF.gsub(/\n/, "\r\n")
 1092 foo
 1093 bar
 1094 baz
 1095 EOF
 1096     server = create_ftp_server { |sock|
 1097       sock.print("220 (test_ftp).\r\n")
 1098       commands.push(sock.gets)
 1099       sock.print("331 Please specify the password.\r\n")
 1100       commands.push(sock.gets)
 1101       sock.print("230 Login successful.\r\n")
 1102       commands.push(sock.gets)
 1103       sock.print("200 Switching to Binary mode.\r\n")
 1104       commands.push(sock.gets)
 1105       sock.print("200 Switching to ASCII mode.\r\n")
 1106       line = sock.gets
 1107       commands.push(line)
 1108       host, port = process_port_or_eprt(sock, line)
 1109       commands.push(sock.gets)
 1110       sock.print("150 Opening TEXT mode data connection for foo (#{text_data.size} bytes)\r\n")
 1111       conn = TCPSocket.new(host, port)
 1112       text_data.each_line do |l|
 1113         conn.print(l)
 1114       end
 1115       conn.shutdown(Socket::SHUT_WR)
 1116       conn.read
 1117       conn.close
 1118       sock.print("226 Transfer complete.\r\n")
 1119       commands.push(sock.gets)
 1120       sock.print("200 Switching to Binary mode.\r\n")
 1121     }
 1122     begin
 1123       begin
 1124         ftp = Net::FTP.new
 1125         ftp.connect(SERVER_ADDR, server.port)
 1126         ftp.login
 1127         assert_match(/\AUSER /, commands.shift)
 1128         assert_match(/\APASS /, commands.shift)
 1129         assert_equal("TYPE I\r\n", commands.shift)
 1130         Tempfile.create("foo", external_encoding: "ascii-8bit") do |f|
 1131           buf = String.new
 1132           res = ftp.gettextfile("foo", f.path) { |s|
 1133             buf << s << "\n"
 1134           }
 1135           assert_equal(nil, res)
 1136           assert_equal(text_data.gsub(/\r\n/, "\n"), buf)
 1137           assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 1138           assert_equal(buf, f.read)
 1139         end
 1140         assert_equal("TYPE A\r\n", commands.shift)
 1141         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1142         assert_equal("RETR foo\r\n", commands.shift)
 1143         assert_equal("TYPE I\r\n", commands.shift)
 1144         assert_equal(nil, commands.shift)
 1145       ensure
 1146         ftp.close if ftp
 1147       end
 1148     ensure
 1149       server.close
 1150     end
 1151   end
 1152 
 1153   def test_getbinaryfile_in_list
 1154     commands = []
 1155     binary_data = (0..0xff).map {|i| i.chr}.join
 1156     list_lines = [
 1157       "-rw-r--r--    1 0        0               0 Mar 30 11:22 foo.txt",
 1158       "-rw-r--r--    1 0        0               0 Mar 30 11:22 bar.txt",
 1159       "-rw-r--r--    1 0        0               0 Mar 30 11:22 baz.bin"
 1160     ]
 1161     server = create_ftp_server { |sock|
 1162       sock.print("220 (test_ftp).\r\n")
 1163       commands.push(sock.gets)
 1164       sock.print("331 Please specify the password.\r\n")
 1165       commands.push(sock.gets)
 1166       sock.print("230 Login successful.\r\n")
 1167       commands.push(sock.gets)
 1168       sock.print("200 Switching to Binary mode.\r\n")
 1169       commands.push(sock.gets)
 1170       sock.print("200 Switching to ASCII mode.\r\n")
 1171       line = sock.gets
 1172       commands.push(line)
 1173       host, port = process_port_or_eprt(sock, line)
 1174       commands.push(sock.gets)
 1175       sock.print("150 Here comes the directory listing.\r\n")
 1176       conn = TCPSocket.new(host, port)
 1177       list_lines.each_with_index do |l, i|
 1178         conn.print(l, "\r\n")
 1179       end
 1180       conn.close
 1181       sock.print("226 Directory send OK.\r\n")
 1182       commands.push(sock.gets)
 1183       sock.print("200 Switching to Binary mode.\r\n")
 1184       line = sock.gets
 1185       commands.push(line)
 1186       host, port = process_port_or_eprt(sock, line)
 1187       commands.push(sock.gets)
 1188       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 1189       conn = TCPSocket.new(host, port)
 1190       conn.print(binary_data)
 1191       conn.close
 1192       sock.print("226 Transfer complete.\r\n")
 1193     }
 1194     begin
 1195       begin
 1196         ftp = Net::FTP.new
 1197         ftp.connect(SERVER_ADDR, server.port)
 1198         ftp.login
 1199         assert_match(/\AUSER /, commands.shift)
 1200         assert_match(/\APASS /, commands.shift)
 1201         assert_equal("TYPE I\r\n", commands.shift)
 1202         ftp.list do |line|
 1203           file = line.slice(/(\S*\.bin)\z/)
 1204           if file
 1205             data = ftp.getbinaryfile(file, nil)
 1206             assert_equal(binary_data, data)
 1207           end
 1208         end
 1209         assert_equal("TYPE A\r\n", commands.shift)
 1210         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1211         assert_equal("LIST\r\n", commands.shift)
 1212         assert_equal("TYPE I\r\n", commands.shift)
 1213         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1214         assert_equal("RETR baz.bin\r\n", commands.shift)
 1215         assert_equal(nil, commands.shift)
 1216       ensure
 1217         ftp.close if ftp
 1218       end
 1219     ensure
 1220       server.close
 1221     end
 1222   end
 1223 
 1224   def test_abort
 1225     commands = []
 1226     server = create_ftp_server { |sock|
 1227       sock.print("220 (test_ftp).\r\n")
 1228       commands.push(sock.gets)
 1229       sock.print("331 Please specify the password.\r\n")
 1230       commands.push(sock.gets)
 1231       sock.print("230 Login successful.\r\n")
 1232       commands.push(sock.gets)
 1233       sock.print("200 Switching to Binary mode.\r\n")
 1234       commands.push(sock.gets)
 1235       sock.print("225 No transfer to ABOR.\r\n")
 1236     }
 1237     begin
 1238       begin
 1239         ftp = Net::FTP.new
 1240         ftp.connect(SERVER_ADDR, server.port)
 1241         ftp.login
 1242         assert_match(/\AUSER /, commands.shift)
 1243         assert_match(/\APASS /, commands.shift)
 1244         assert_equal("TYPE I\r\n", commands.shift)
 1245         ftp.abort
 1246         assert_equal("ABOR\r\n", commands.shift)
 1247         assert_equal(nil, commands.shift)
 1248       ensure
 1249         ftp.close if ftp
 1250       end
 1251     ensure
 1252       server.close
 1253     end
 1254   end
 1255 
 1256   def test_status
 1257     commands = []
 1258     server = create_ftp_server { |sock|
 1259       sock.print("220 (test_ftp).\r\n")
 1260       commands.push(sock.gets)
 1261       sock.print("331 Please specify the password.\r\n")
 1262       commands.push(sock.gets)
 1263       sock.print("230 Login successful.\r\n")
 1264       commands.push(sock.gets)
 1265       sock.print("200 Switching to Binary mode.\r\n")
 1266       commands.push(sock.gets)
 1267       sock.print("211 End of status\r\n")
 1268     }
 1269     begin
 1270       begin
 1271         ftp = Net::FTP.new
 1272         ftp.connect(SERVER_ADDR, server.port)
 1273         ftp.login
 1274         assert_match(/\AUSER /, commands.shift)
 1275         assert_match(/\APASS /, commands.shift)
 1276         assert_equal("TYPE I\r\n", commands.shift)
 1277         ftp.status
 1278         assert_equal("STAT\r\n", commands.shift)
 1279         assert_equal(nil, commands.shift)
 1280       ensure
 1281         ftp.close if ftp
 1282       end
 1283     ensure
 1284       server.close
 1285     end
 1286   end
 1287 
 1288   def test_status_path
 1289     commands = []
 1290     server = create_ftp_server { |sock|
 1291       sock.print("220 (test_ftp).\r\n")
 1292       commands.push(sock.gets)
 1293       sock.print("331 Please specify the password.\r\n")
 1294       commands.push(sock.gets)
 1295       sock.print("230 Login successful.\r\n")
 1296       commands.push(sock.gets)
 1297       sock.print("200 Switching to Binary mode.\r\n")
 1298       commands.push(sock.gets)
 1299       sock.print("213 End of status\r\n")
 1300     }
 1301     begin
 1302       begin
 1303         ftp = Net::FTP.new
 1304         ftp.connect(SERVER_ADDR, server.port)
 1305         ftp.login
 1306         assert_match(/\AUSER /, commands.shift)
 1307         assert_match(/\APASS /, commands.shift)
 1308         assert_equal("TYPE I\r\n", commands.shift)
 1309         ftp.status "/"
 1310         assert_equal("STAT /\r\n", commands.shift)
 1311         assert_equal(nil, commands.shift)
 1312       ensure
 1313         ftp.close if ftp
 1314       end
 1315     ensure
 1316       server.close
 1317     end
 1318   end
 1319 
 1320   def test_pathnames
 1321     require 'pathname'
 1322 
 1323     commands = []
 1324     server = create_ftp_server(0.2) { |sock|
 1325       sock.print("220 (test_ftp).\r\n")
 1326       commands.push(sock.gets)
 1327       sock.print("331 Please specify the password.\r\n")
 1328       commands.push(sock.gets)
 1329       sock.print("230 Login successful.\r\n")
 1330       commands.push(sock.gets)
 1331       sock.print("200 Switching to Binary mode.\r\n")
 1332       commands.push(sock.gets)
 1333       sock.print("257 'foo' directory created.\r\n")
 1334       commands.push(sock.gets)
 1335       sock.print("250 CWD command successful.\r\n")
 1336       commands.push(sock.gets)
 1337       sock.print("250 CWD command successful.\r\n")
 1338       commands.push(sock.gets)
 1339       sock.print("250 RMD command successful.\r\n")
 1340       commands.push(sock.gets)
 1341       sock.print("213 test.txt  Fri, 11 Jan 2013 11:20:41 -0500.\r\n")
 1342       commands.push(sock.gets)
 1343       sock.print("213 test.txt  16.\r\n")
 1344       commands.push(sock.gets)
 1345       sock.print("350 File exists, ready for destination name\r\n")
 1346       commands.push(sock.gets)
 1347       sock.print("250 RNTO command successful.\r\n")
 1348       commands.push(sock.gets)
 1349       sock.print("250 DELE command successful.\r\n")
 1350     }
 1351 
 1352     begin
 1353       begin
 1354         dir   = Pathname.new("foo")
 1355         file  = Pathname.new("test.txt")
 1356         file2 = Pathname.new("test2.txt")
 1357         ftp   = Net::FTP.new
 1358         ftp.connect(SERVER_ADDR, server.port)
 1359         ftp.login
 1360         ftp.mkdir(dir)
 1361         ftp.chdir(dir)
 1362         ftp.chdir("..")
 1363         ftp.rmdir(dir)
 1364         ftp.mdtm(file)
 1365         ftp.size(file)
 1366         ftp.rename(file, file2)
 1367         ftp.delete(file)
 1368 
 1369         # TODO: These commented tests below expose the error but don't test anything:
 1370         #   TypeError: no implicit conversion of Pathname into String
 1371         # ftp.nlst(dir)
 1372         # ftp.putbinaryfile(Pathname.new("/etc/hosts"), file2)
 1373         # ftp.puttextfile(Pathname.new("/etc/hosts"), file2)
 1374         # ftp.gettextfile(Pathname.new("/etc/hosts"), file2)
 1375         # ftp.getbinaryfile(Pathname.new("/etc/hosts"), file2)
 1376         # ftp.list(dir, dir, dir)
 1377 
 1378         assert_match(/\AUSER /, commands.shift)
 1379         assert_match(/\APASS /, commands.shift)
 1380         assert_match(/\ATYPE /, commands.shift)
 1381         assert_match(/\AMKD /, commands.shift)
 1382         assert_match(/\ACWD /, commands.shift)
 1383         assert_match(/\ACDUP/, commands.shift)
 1384         assert_match(/\ARMD /, commands.shift)
 1385         assert_match(/\AMDTM /, commands.shift)
 1386         assert_match(/\ASIZE /, commands.shift)
 1387         assert_match(/\ARNFR /, commands.shift)
 1388         assert_match(/\ARNTO /, commands.shift)
 1389         assert_match(/\ADELE /, commands.shift)
 1390       ensure
 1391         ftp.close if ftp
 1392       end
 1393     ensure
 1394       server.close
 1395     end
 1396   end
 1397 
 1398   def test_getmultiline
 1399     server = create_ftp_server { |sock|
 1400       sock.print("220 (test_ftp).\r\n")
 1401       sock.print("123- foo\r\n")
 1402       sock.print("bar\r\n")
 1403       sock.print(" 123 baz\r\n")
 1404       sock.print("123 quux\r\n")
 1405       sock.print("123 foo\r\n")
 1406       sock.print("foo\r\n")
 1407       sock.print("\r\n")
 1408     }
 1409     begin
 1410       begin
 1411         ftp = Net::FTP.new
 1412         ftp.connect(SERVER_ADDR, server.port)
 1413         assert_equal("123- foo\nbar\n 123 baz\n123 quux\n",
 1414                      ftp.send(:getmultiline))
 1415         assert_equal("123 foo\n", ftp.send(:getmultiline))
 1416         assert_equal("foo\n", ftp.send(:getmultiline))
 1417         assert_equal("\n", ftp.send(:getmultiline))
 1418       ensure
 1419         ftp.close if ftp
 1420       end
 1421     ensure
 1422       server.close
 1423     end
 1424   end
 1425 
 1426   def test_size
 1427     commands = []
 1428     server = create_ftp_server { |sock|
 1429       sock.print("220 (test_ftp).\r\n")
 1430       commands.push(sock.gets)
 1431       sock.print("213 12345\r\n")
 1432     }
 1433     begin
 1434       begin
 1435         ftp = Net::FTP.new
 1436         ftp.connect(SERVER_ADDR, server.port)
 1437         assert_equal(12345, ftp.size("foo.txt"))
 1438         assert_match("SIZE foo.txt\r\n", commands.shift)
 1439         assert_equal(nil, commands.shift)
 1440       ensure
 1441         ftp.close if ftp
 1442       end
 1443     ensure
 1444       server.close
 1445     end
 1446   end
 1447 
 1448   def test_mdtm
 1449     commands = []
 1450     server = create_ftp_server { |sock|
 1451       sock.print("220 (test_ftp).\r\n")
 1452       commands.push(sock.gets)
 1453       sock.print("213 20150910161739\r\n")
 1454     }
 1455     begin
 1456       begin
 1457         ftp = Net::FTP.new
 1458         ftp.connect(SERVER_ADDR, server.port)
 1459         assert_equal("20150910161739", ftp.mdtm("foo.txt"))
 1460         assert_match("MDTM foo.txt\r\n", commands.shift)
 1461         assert_equal(nil, commands.shift)
 1462       ensure
 1463         ftp.close if ftp
 1464       end
 1465     ensure
 1466       server.close
 1467     end
 1468   end
 1469 
 1470   def test_mtime
 1471     commands = []
 1472     server = create_ftp_server { |sock|
 1473       sock.print("220 (test_ftp).\r\n")
 1474       commands.push(sock.gets)
 1475       sock.print("213 20150910161739\r\n")
 1476       commands.push(sock.gets)
 1477       sock.print("213 20150910161739\r\n")
 1478       commands.push(sock.gets)
 1479       sock.print("213 20150910161739.123456\r\n")
 1480       commands.push(sock.gets)
 1481       sock.print("213 20150910161739.123\r\n")
 1482       commands.push(sock.gets)
 1483       sock.print("213 20150910161739.123456789\r\n")
 1484       commands.push(sock.gets)
 1485       sock.print("213 2015091016173\r\n")
 1486     }
 1487     begin
 1488       begin
 1489         ftp = Net::FTP.new
 1490         ftp.connect(SERVER_ADDR, server.port)
 1491         assert_equal(Time.utc(2015, 9, 10, 16, 17, 39), ftp.mtime("foo.txt"))
 1492         assert_equal(Time.local(2015, 9, 10, 16, 17, 39),
 1493                      ftp.mtime("foo.txt", true))
 1494         assert_equal(Time.utc(2015, 9, 10, 16, 17, 39, 123456),
 1495                      ftp.mtime("bar.txt"))
 1496         assert_equal(Time.utc(2015, 9, 10, 16, 17, 39, 123000),
 1497                      ftp.mtime("bar.txt"))
 1498         assert_equal(Time.utc(2015, 9, 10, 16, 17, 39,
 1499                               Rational(123456789, 1000)),
 1500                      ftp.mtime("bar.txt"))
 1501         assert_raise(Net::FTPProtoError) do
 1502           ftp.mtime("quux.txt")
 1503         end
 1504         assert_match("MDTM foo.txt\r\n", commands.shift)
 1505         assert_match("MDTM foo.txt\r\n", commands.shift)
 1506         assert_match("MDTM bar.txt\r\n", commands.shift)
 1507         assert_match("MDTM bar.txt\r\n", commands.shift)
 1508         assert_match("MDTM bar.txt\r\n", commands.shift)
 1509         assert_match("MDTM quux.txt\r\n", commands.shift)
 1510         assert_equal(nil, commands.shift)
 1511       ensure
 1512         ftp.close if ftp
 1513       end
 1514     ensure
 1515       server.close
 1516     end
 1517   end
 1518 
 1519   def test_system
 1520     commands = []
 1521     server = create_ftp_server { |sock|
 1522       sock.print("220 (test_ftp).\r\n")
 1523       commands.push(sock.gets)
 1524       sock.print("215 UNIX Type: L8\r\n")
 1525     }
 1526     begin
 1527       begin
 1528         ftp = Net::FTP.new
 1529         ftp.connect(SERVER_ADDR, server.port)
 1530         assert_equal("UNIX Type: L8", ftp.system)
 1531         assert_match("SYST\r\n", commands.shift)
 1532         assert_equal(nil, commands.shift)
 1533       ensure
 1534         ftp.close if ftp
 1535       end
 1536     ensure
 1537       server.close
 1538     end
 1539   end
 1540 
 1541   def test_features
 1542     commands = []
 1543     server = create_ftp_server { |sock|
 1544       sock.print("220 (test_ftp).\r\n")
 1545       commands.push(sock.gets)
 1546       sock.print("211-Features\r\n")
 1547       sock.print(" LANG EN*\r\n")
 1548       sock.print(" UTF8\r\n")
 1549       sock.print("211 End\r\n")
 1550     }
 1551     begin
 1552       begin
 1553         ftp = Net::FTP.new
 1554         ftp.connect(SERVER_ADDR, server.port)
 1555         assert_equal(['LANG EN*', 'UTF8'], ftp.features)
 1556         assert_equal("FEAT\r\n", commands.shift)
 1557         assert_equal(nil, commands.shift)
 1558       ensure
 1559         ftp.close if ftp
 1560       end
 1561     ensure
 1562       server.close
 1563     end
 1564   end
 1565 
 1566   def test_features_not_implemented
 1567     commands = []
 1568     server = create_ftp_server { |sock|
 1569       sock.print("220 (test_ftp).\r\n")
 1570       commands.push(sock.gets)
 1571       sock.print("502 Not Implemented\r\n")
 1572     }
 1573     begin
 1574       begin
 1575         ftp = Net::FTP.new
 1576         ftp.connect(SERVER_ADDR, server.port)
 1577         assert_raise(Net::FTPPermError) do
 1578           ftp.features
 1579         end
 1580         assert_equal("FEAT\r\n", commands.shift)
 1581         assert_equal(nil, commands.shift)
 1582       ensure
 1583         ftp.close if ftp
 1584       end
 1585     ensure
 1586       server.close
 1587     end
 1588 
 1589   end
 1590 
 1591   def test_option
 1592     commands = []
 1593     server = create_ftp_server { |sock|
 1594       sock.print("220 (test_ftp)\r\n")
 1595       commands.push(sock.gets)
 1596       sock.print("200 OPTS UTF8 command successful\r\n")
 1597     }
 1598     begin
 1599       begin
 1600         ftp = Net::FTP.new
 1601         ftp.connect(SERVER_ADDR, server.port)
 1602         ftp.option("UTF8", "ON")
 1603         assert_equal("OPTS UTF8 ON\r\n", commands.shift)
 1604         assert_equal(nil, commands.shift)
 1605       ensure
 1606         ftp.close if ftp
 1607       end
 1608     ensure
 1609       server.close
 1610     end
 1611   end
 1612 
 1613   def test_option_not_implemented
 1614     commands = []
 1615     server = create_ftp_server { |sock|
 1616       sock.print("220 (test_ftp)\r\n")
 1617       commands.push(sock.gets)
 1618       sock.print("502 Not implemented\r\n")
 1619     }
 1620     begin
 1621       begin
 1622         ftp = Net::FTP.new
 1623         ftp.connect(SERVER_ADDR, server.port)
 1624         assert_raise(Net::FTPPermError) do
 1625           ftp.option("UTF8", "ON")
 1626         end
 1627         assert_equal("OPTS UTF8 ON\r\n", commands.shift)
 1628         assert_equal(nil, commands.shift)
 1629       ensure
 1630         ftp.close if ftp
 1631       end
 1632     ensure
 1633       server.close
 1634     end
 1635   end
 1636 
 1637   def test_mlst
 1638     commands = []
 1639     server = create_ftp_server { |sock|
 1640       sock.print("220 (test_ftp).\r\n")
 1641       commands.push(sock.gets)
 1642       sock.print("250- Listing foo\r\n")
 1643       sock.print(" Type=file;Unique=FC00U1E554A;Size=1234567;Modify=20131220035929;Perm=r;Unix.mode=0644;Unix.owner=122;Unix.group=0;Unix.ctime=20131220120140;Unix.atime=20131220131139; /foo\r\n")
 1644       sock.print("250 End\r\n")
 1645       commands.push(sock.gets)
 1646       sock.print("250 Malformed response\r\n")
 1647       commands.push(sock.gets)
 1648       sock.print("250- Listing foo\r\n")
 1649       sock.print("\r\n")
 1650       sock.print("250 End\r\n")
 1651       commands.push(sock.gets)
 1652       sock.print("250- Listing foo\r\n")
 1653       sock.print(" abc /foo\r\n")
 1654       sock.print("250 End\r\n")
 1655     }
 1656     begin
 1657       begin
 1658         ftp = Net::FTP.new
 1659         ftp.connect(SERVER_ADDR, server.port)
 1660         entry = ftp.mlst("foo")
 1661         assert_equal("/foo", entry.pathname)
 1662         assert_equal("file", entry.facts["type"])
 1663         assert_equal("FC00U1E554A", entry.facts["unique"])
 1664         assert_equal(1234567, entry.facts["size"])
 1665         assert_equal("r", entry.facts["perm"])
 1666         assert_equal(0644, entry.facts["unix.mode"])
 1667         assert_equal(122, entry.facts["unix.owner"])
 1668         assert_equal(0, entry.facts["unix.group"])
 1669         modify = entry.facts["modify"]
 1670         assert_equal(2013, modify.year)
 1671         assert_equal(12, modify.month)
 1672         assert_equal(20, modify.day)
 1673         assert_equal(3, modify.hour)
 1674         assert_equal(59, modify.min)
 1675         assert_equal(29, modify.sec)
 1676         assert_equal(true, modify.utc?)
 1677         ctime = entry.facts["unix.ctime"]
 1678         assert_equal(12, ctime.hour)
 1679         assert_equal(1, ctime.min)
 1680         assert_equal(40, ctime.sec)
 1681         atime = entry.facts["unix.atime"]
 1682         assert_equal(13, atime.hour)
 1683         assert_equal(11, atime.min)
 1684         assert_equal(39, atime.sec)
 1685         assert_match("MLST foo\r\n", commands.shift)
 1686         assert_raise(Net::FTPProtoError) do
 1687           ftp.mlst("foo")
 1688         end
 1689         assert_match("MLST foo\r\n", commands.shift)
 1690         assert_raise(Net::FTPProtoError) do
 1691           ftp.mlst("foo")
 1692         end
 1693         assert_match("MLST foo\r\n", commands.shift)
 1694         entry = ftp.mlst("foo")
 1695         assert_equal("/foo", entry.pathname)
 1696         assert_match("MLST foo\r\n", commands.shift)
 1697         assert_equal(nil, commands.shift)
 1698       ensure
 1699         ftp.close if ftp
 1700       end
 1701     ensure
 1702       server.close
 1703     end
 1704   end
 1705 
 1706   def test_mlsd
 1707     commands = []
 1708     entry_lines = [
 1709       "Type=file;Unique=FC00U1E554A;Size=1234567;Modify=20131220035929.123456;Perm=r; foo bar",
 1710       "Type=cdir;Unique=FC00U1E554B;Modify=20131220035929;Perm=flcdmpe; .",
 1711       "Type=pdir;Unique=FC00U1E554C;Modify=20131220035929;Perm=flcdmpe; ..",
 1712     ]
 1713     server = create_ftp_server { |sock|
 1714       sock.print("220 (test_ftp).\r\n")
 1715       commands.push(sock.gets)
 1716       sock.print("331 Please specify the password.\r\n")
 1717       commands.push(sock.gets)
 1718       sock.print("230 Login successful.\r\n")
 1719       commands.push(sock.gets)
 1720       sock.print("200 Switching to Binary mode.\r\n")
 1721       commands.push(sock.gets)
 1722       sock.print("200 Switching to ASCII mode.\r\n")
 1723       line = sock.gets
 1724       commands.push(line)
 1725       host, port = process_port_or_eprt(sock, line)
 1726       commands.push(sock.gets)
 1727       sock.print("150 Here comes the directory listing.\r\n")
 1728       begin
 1729         conn = TCPSocket.new(host, port)
 1730         entry_lines.each do |l|
 1731           conn.print(l, "\r\n")
 1732         end
 1733       rescue Errno::EPIPE
 1734       ensure
 1735         assert_nil($!)
 1736         conn.close
 1737       end
 1738       sock.print("226 Directory send OK.\r\n")
 1739       commands.push(sock.gets)
 1740       sock.print("200 Switching to Binary mode.\r\n")
 1741     }
 1742     begin
 1743       begin
 1744         ftp = Net::FTP.new
 1745         ftp.connect(SERVER_ADDR, server.port)
 1746         ftp.login
 1747         assert_match(/\AUSER /, commands.shift)
 1748         assert_match(/\APASS /, commands.shift)
 1749         assert_equal("TYPE I\r\n", commands.shift)
 1750         entries = ftp.mlsd("/")
 1751         assert_equal(3, entries.size)
 1752         assert_equal("foo bar", entries[0].pathname)
 1753         assert_equal(".", entries[1].pathname)
 1754         assert_equal("..", entries[2].pathname)
 1755         assert_equal("file", entries[0].facts["type"])
 1756         assert_equal("cdir", entries[1].facts["type"])
 1757         assert_equal("pdir", entries[2].facts["type"])
 1758         assert_equal("flcdmpe", entries[1].facts["perm"])
 1759         modify = entries[0].facts["modify"]
 1760         assert_equal(2013, modify.year)
 1761         assert_equal(12, modify.month)
 1762         assert_equal(20, modify.day)
 1763         assert_equal(3, modify.hour)
 1764         assert_equal(59, modify.min)
 1765         assert_equal(29, modify.sec)
 1766         assert_equal(123456, modify.usec)
 1767         assert_equal(true, modify.utc?)
 1768         assert_equal("TYPE A\r\n", commands.shift)
 1769         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1770         assert_match("MLSD /\r\n", commands.shift)
 1771         assert_equal("TYPE I\r\n", commands.shift)
 1772         assert_equal(nil, commands.shift)
 1773       ensure
 1774         ftp.close if ftp
 1775       end
 1776     ensure
 1777       server.close
 1778     end
 1779   end
 1780 
 1781   def test_parse257
 1782     ftp = Net::FTP.new
 1783     assert_equal('/foo/bar',
 1784                  ftp.send(:parse257, '257 "/foo/bar" directory created'))
 1785     assert_equal('/foo/bar"baz',
 1786                  ftp.send(:parse257, '257 "/foo/bar""baz" directory created'))
 1787     assert_equal('/foo/x"y"z',
 1788                  ftp.send(:parse257, '257 "/foo/x""y""z" directory created'))
 1789     assert_equal('/foo/bar',
 1790                  ftp.send(:parse257, '257 "/foo/bar" "comment"'))
 1791     assert_equal('',
 1792                  ftp.send(:parse257, '257 "" directory created'))
 1793     assert_equal('',
 1794                  ftp.send(:parse257, '257 directory created'))
 1795     assert_raise(Net::FTPReplyError) do
 1796       ftp.send(:parse257, "500 Syntax error")
 1797     end
 1798   end
 1799 
 1800   def test_putline_reject_crlf
 1801     ftp = Net::FTP.new
 1802     assert_raise(ArgumentError) do
 1803       ftp.send(:putline, "\r")
 1804     end
 1805     assert_raise(ArgumentError) do
 1806       ftp.send(:putline, "\n")
 1807     end
 1808   end
 1809 
 1810   if defined?(OpenSSL::SSL)
 1811     def test_tls_unknown_ca
 1812       assert_raise(OpenSSL::SSL::SSLError) do
 1813         tls_test do |port|
 1814           begin
 1815             Net::FTP.new(SERVER_NAME,
 1816                          :port => port,
 1817                          :ssl => true)
 1818           rescue SystemCallError
 1819             skip $!
 1820           end
 1821         end
 1822       end
 1823     end
 1824 
 1825     def test_tls_with_ca_file
 1826       assert_nothing_raised do
 1827         tls_test do |port|
 1828           begin
 1829             Net::FTP.new(SERVER_NAME,
 1830                          :port => port,
 1831                          :ssl => { :ca_file => CA_FILE })
 1832           rescue SystemCallError
 1833             skip $!
 1834           end
 1835         end
 1836       end
 1837     end
 1838 
 1839     def test_tls_verify_none
 1840       assert_nothing_raised do
 1841         tls_test do |port|
 1842           Net::FTP.new(SERVER_ADDR,
 1843                        :port => port,
 1844                        :ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
 1845         end
 1846       end
 1847     end
 1848 
 1849     def test_tls_post_connection_check
 1850       assert_raise(OpenSSL::SSL::SSLError) do
 1851         tls_test do |port|
 1852           # SERVER_ADDR is different from the hostname in the certificate,
 1853           # so the following code should raise a SSLError.
 1854           Net::FTP.new(SERVER_ADDR,
 1855                        :port => port,
 1856                        :ssl => { :ca_file => CA_FILE })
 1857         end
 1858       end
 1859     end
 1860 
 1861     def test_active_private_data_connection
 1862       server = TCPServer.new(SERVER_ADDR, 0)
 1863       port = server.addr[1]
 1864       commands = []
 1865       session_reused_for_data_connection = nil
 1866       binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 1867       @thread = Thread.start do
 1868         sock = server.accept
 1869         begin
 1870           sock.print("220 (test_ftp).\r\n")
 1871           commands.push(sock.gets)
 1872           sock.print("234 AUTH success.\r\n")
 1873           ctx = OpenSSL::SSL::SSLContext.new
 1874           ctx.ca_file = CA_FILE
 1875           ctx.key = File.open(SERVER_KEY) { |f|
 1876             OpenSSL::PKey::RSA.new(f)
 1877           }
 1878           ctx.cert = File.open(SERVER_CERT) { |f|
 1879             OpenSSL::X509::Certificate.new(f)
 1880           }
 1881           sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 1882           sock.sync_close = true
 1883           begin
 1884             sock.accept
 1885             commands.push(sock.gets)
 1886             sock.print("200 PSBZ success.\r\n")
 1887             commands.push(sock.gets)
 1888             sock.print("200 PROT success.\r\n")
 1889             commands.push(sock.gets)
 1890             sock.print("331 Please specify the password.\r\n")
 1891             commands.push(sock.gets)
 1892             sock.print("230 Login successful.\r\n")
 1893             commands.push(sock.gets)
 1894             sock.print("200 Switching to Binary mode.\r\n")
 1895             line = sock.gets
 1896             commands.push(line)
 1897             host, port = process_port_or_eprt(sock, line)
 1898             commands.push(sock.gets)
 1899             sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 1900             conn = TCPSocket.new(host, port)
 1901             conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
 1902             conn.sync_close = true
 1903             conn.accept
 1904             session_reused_for_data_connection = conn.session_reused?
 1905             binary_data.scan(/.{1,1024}/nm) do |s|
 1906               conn.print(s)
 1907             end
 1908             conn.close
 1909             sock.print("226 Transfer complete.\r\n")
 1910           rescue OpenSSL::SSL::SSLError
 1911           end
 1912         ensure
 1913           sock.close
 1914           server.close
 1915         end
 1916       end
 1917       ftp = Net::FTP.new(SERVER_NAME,
 1918                          port: port,
 1919                          ssl: { ca_file: CA_FILE },
 1920                          passive: false)
 1921       begin
 1922         assert_equal("AUTH TLS\r\n", commands.shift)
 1923         assert_equal("PBSZ 0\r\n", commands.shift)
 1924         assert_equal("PROT P\r\n", commands.shift)
 1925         ftp.login
 1926         assert_match(/\AUSER /, commands.shift)
 1927         assert_match(/\APASS /, commands.shift)
 1928         assert_equal("TYPE I\r\n", commands.shift)
 1929         buf = ftp.getbinaryfile("foo", nil)
 1930         assert_equal(binary_data, buf)
 1931         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 1932         assert_match(/\A(PORT|EPRT) /, commands.shift)
 1933         assert_equal("RETR foo\r\n", commands.shift)
 1934         assert_equal(nil, commands.shift)
 1935         # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
 1936         # See https://github.com/openssl/openssl/pull/5967 for details.
 1937         if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
 1938           assert_equal(true, session_reused_for_data_connection)
 1939         end
 1940       ensure
 1941         ftp.close
 1942       end
 1943     end
 1944 
 1945     def test_passive_private_data_connection
 1946       server = TCPServer.new(SERVER_ADDR, 0)
 1947       port = server.addr[1]
 1948       commands = []
 1949       session_reused_for_data_connection = nil
 1950       binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 1951       @thread = Thread.start do
 1952         sock = server.accept
 1953         begin
 1954           sock.print("220 (test_ftp).\r\n")
 1955           commands.push(sock.gets)
 1956           sock.print("234 AUTH success.\r\n")
 1957           ctx = OpenSSL::SSL::SSLContext.new
 1958           ctx.ca_file = CA_FILE
 1959           ctx.key = File.open(SERVER_KEY) { |f|
 1960             OpenSSL::PKey::RSA.new(f)
 1961           }
 1962           ctx.cert = File.open(SERVER_CERT) { |f|
 1963             OpenSSL::X509::Certificate.new(f)
 1964           }
 1965           sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 1966           sock.sync_close = true
 1967           begin
 1968             sock.accept
 1969             commands.push(sock.gets)
 1970             sock.print("200 PSBZ success.\r\n")
 1971             commands.push(sock.gets)
 1972             sock.print("200 PROT success.\r\n")
 1973             commands.push(sock.gets)
 1974             sock.print("331 Please specify the password.\r\n")
 1975             commands.push(sock.gets)
 1976             sock.print("230 Login successful.\r\n")
 1977             commands.push(sock.gets)
 1978             sock.print("200 Switching to Binary mode.\r\n")
 1979             commands.push(sock.gets)
 1980             data_server = create_data_connection_server(sock)
 1981             commands.push(sock.gets)
 1982             sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 1983             conn = data_server.accept
 1984             conn = OpenSSL::SSL::SSLSocket.new(conn, ctx)
 1985             conn.sync_close = true
 1986             conn.accept
 1987             session_reused_for_data_connection = conn.session_reused?
 1988             binary_data.scan(/.{1,1024}/nm) do |s|
 1989               conn.print(s)
 1990             end
 1991             conn.close
 1992             data_server.close
 1993             sock.print("226 Transfer complete.\r\n")
 1994           rescue OpenSSL::SSL::SSLError
 1995           end
 1996         ensure
 1997           sock.close
 1998           server.close
 1999         end
 2000       end
 2001       ftp = Net::FTP.new(SERVER_NAME,
 2002                          port: port,
 2003                          ssl: { ca_file: CA_FILE },
 2004                          passive: true)
 2005       begin
 2006         assert_equal("AUTH TLS\r\n", commands.shift)
 2007         assert_equal("PBSZ 0\r\n", commands.shift)
 2008         assert_equal("PROT P\r\n", commands.shift)
 2009         ftp.login
 2010         assert_match(/\AUSER /, commands.shift)
 2011         assert_match(/\APASS /, commands.shift)
 2012         assert_equal("TYPE I\r\n", commands.shift)
 2013         buf = ftp.getbinaryfile("foo", nil)
 2014         assert_equal(binary_data, buf)
 2015         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 2016         assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
 2017         assert_equal("RETR foo\r\n", commands.shift)
 2018         assert_equal(nil, commands.shift)
 2019         # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h.
 2020         if OpenSSL::OPENSSL_LIBRARY_VERSION !~ /OpenSSL 1.1.0h/
 2021           assert_equal(true, session_reused_for_data_connection)
 2022         end
 2023       ensure
 2024         ftp.close
 2025       end
 2026     end
 2027 
 2028     def test_active_clear_data_connection
 2029       server = TCPServer.new(SERVER_ADDR, 0)
 2030       port = server.addr[1]
 2031       commands = []
 2032       binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2033       @thread = Thread.start do
 2034         sock = server.accept
 2035         begin
 2036           sock.print("220 (test_ftp).\r\n")
 2037           commands.push(sock.gets)
 2038           sock.print("234 AUTH success.\r\n")
 2039           ctx = OpenSSL::SSL::SSLContext.new
 2040           ctx.ca_file = CA_FILE
 2041           ctx.key = File.open(SERVER_KEY) { |f|
 2042             OpenSSL::PKey::RSA.new(f)
 2043           }
 2044           ctx.cert = File.open(SERVER_CERT) { |f|
 2045             OpenSSL::X509::Certificate.new(f)
 2046           }
 2047           sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 2048           sock.sync_close = true
 2049           begin
 2050             sock.accept
 2051             commands.push(sock.gets)
 2052             sock.print("331 Please specify the password.\r\n")
 2053             commands.push(sock.gets)
 2054             sock.print("230 Login successful.\r\n")
 2055             commands.push(sock.gets)
 2056             sock.print("200 Switching to Binary mode.\r\n")
 2057             line = sock.gets
 2058             commands.push(line)
 2059             host, port = process_port_or_eprt(sock, line)
 2060             commands.push(sock.gets)
 2061             sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 2062             conn = TCPSocket.new(host, port)
 2063             binary_data.scan(/.{1,1024}/nm) do |s|
 2064               conn.print(s)
 2065             end
 2066             conn.close
 2067             sock.print("226 Transfer complete.\r\n")
 2068           rescue OpenSSL::SSL::SSLError
 2069           end
 2070         ensure
 2071           sock.close
 2072           server.close
 2073         end
 2074       end
 2075       ftp = Net::FTP.new(SERVER_NAME,
 2076                          port: port,
 2077                          ssl: { ca_file: CA_FILE },
 2078                          private_data_connection: false,
 2079                          passive: false)
 2080       begin
 2081         assert_equal("AUTH TLS\r\n", commands.shift)
 2082         ftp.login
 2083         assert_match(/\AUSER /, commands.shift)
 2084         assert_match(/\APASS /, commands.shift)
 2085         assert_equal("TYPE I\r\n", commands.shift)
 2086         buf = ftp.getbinaryfile("foo", nil)
 2087         assert_equal(binary_data, buf)
 2088         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 2089         assert_match(/\A(PORT|EPRT) /, commands.shift)
 2090         assert_equal("RETR foo\r\n", commands.shift)
 2091         assert_equal(nil, commands.shift)
 2092       ensure
 2093         ftp.close
 2094       end
 2095     end
 2096 
 2097     def test_passive_clear_data_connection
 2098       server = TCPServer.new(SERVER_ADDR, 0)
 2099       port = server.addr[1]
 2100       commands = []
 2101       binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2102       @thread = Thread.start do
 2103         sock = server.accept
 2104         begin
 2105           sock.print("220 (test_ftp).\r\n")
 2106           commands.push(sock.gets)
 2107           sock.print("234 AUTH success.\r\n")
 2108           ctx = OpenSSL::SSL::SSLContext.new
 2109           ctx.ca_file = CA_FILE
 2110           ctx.key = File.open(SERVER_KEY) { |f|
 2111             OpenSSL::PKey::RSA.new(f)
 2112           }
 2113           ctx.cert = File.open(SERVER_CERT) { |f|
 2114             OpenSSL::X509::Certificate.new(f)
 2115           }
 2116           sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 2117           sock.sync_close = true
 2118           begin
 2119             sock.accept
 2120             commands.push(sock.gets)
 2121             sock.print("331 Please specify the password.\r\n")
 2122             commands.push(sock.gets)
 2123             sock.print("230 Login successful.\r\n")
 2124             commands.push(sock.gets)
 2125             sock.print("200 Switching to Binary mode.\r\n")
 2126             commands.push(sock.gets)
 2127             data_server = create_data_connection_server(sock)
 2128             commands.push(sock.gets)
 2129             sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 2130             conn = data_server.accept
 2131             binary_data.scan(/.{1,1024}/nm) do |s|
 2132               conn.print(s)
 2133             end
 2134             conn.close
 2135             data_server.close
 2136             sock.print("226 Transfer complete.\r\n")
 2137           rescue OpenSSL::SSL::SSLError
 2138           end
 2139         ensure
 2140           sock.close
 2141           server.close
 2142         end
 2143       end
 2144       ftp = Net::FTP.new(SERVER_NAME,
 2145                          port: port,
 2146                          ssl: { ca_file: CA_FILE },
 2147                          private_data_connection: false,
 2148                          passive: true)
 2149       begin
 2150         assert_equal("AUTH TLS\r\n", commands.shift)
 2151         ftp.login
 2152         assert_match(/\AUSER /, commands.shift)
 2153         assert_match(/\APASS /, commands.shift)
 2154         assert_equal("TYPE I\r\n", commands.shift)
 2155         buf = ftp.getbinaryfile("foo", nil)
 2156         assert_equal(binary_data, buf)
 2157         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 2158         assert_match(/\A(PASV|EPSV)\r\n/, commands.shift)
 2159         assert_equal("RETR foo\r\n", commands.shift)
 2160         assert_equal(nil, commands.shift)
 2161       ensure
 2162         ftp.close
 2163       end
 2164     end
 2165 
 2166     def test_tls_connect_timeout
 2167       server = TCPServer.new(SERVER_ADDR, 0)
 2168       port = server.addr[1]
 2169       commands = []
 2170       sock = nil
 2171       @thread = Thread.start do
 2172         sock = server.accept
 2173         sock.print("220 (test_ftp).\r\n")
 2174         commands.push(sock.gets)
 2175         sock.print("234 AUTH success.\r\n")
 2176       end
 2177       begin
 2178         assert_raise(Net::OpenTimeout) do
 2179           Net::FTP.new(SERVER_NAME,
 2180                        port: port,
 2181                        ssl: { ca_file: CA_FILE },
 2182                        ssl_handshake_timeout: 0.1)
 2183         end
 2184         @thread.join
 2185       ensure
 2186         sock.close if sock
 2187         server.close
 2188       end
 2189     end
 2190   end
 2191 
 2192   def test_abort_tls
 2193     return unless defined?(OpenSSL)
 2194 
 2195     commands = []
 2196     server = create_ftp_server { |sock|
 2197       sock.print("220 (test_ftp).\r\n")
 2198       commands.push(sock.gets)
 2199       sock.print("234 AUTH success.\r\n")
 2200       ctx = OpenSSL::SSL::SSLContext.new
 2201       ctx.ca_file = CA_FILE
 2202       ctx.key = File.open(SERVER_KEY) { |f|
 2203         OpenSSL::PKey::RSA.new(f)
 2204       }
 2205       ctx.cert = File.open(SERVER_CERT) { |f|
 2206         OpenSSL::X509::Certificate.new(f)
 2207       }
 2208       sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 2209       sock.sync_close = true
 2210       sock.accept
 2211       commands.push(sock.gets)
 2212       sock.print("200 PSBZ success.\r\n")
 2213       commands.push(sock.gets)
 2214       sock.print("200 PROT success.\r\n")
 2215       commands.push(sock.gets)
 2216       sock.print("331 Please specify the password.\r\n")
 2217       commands.push(sock.gets)
 2218       sock.print("230 Login successful.\r\n")
 2219       commands.push(sock.gets)
 2220       sock.print("200 Switching to Binary mode.\r\n")
 2221       commands.push(sock.gets)
 2222       sock.print("225 No transfer to ABOR.\r\n")
 2223     }
 2224     begin
 2225       begin
 2226         ftp = Net::FTP.new(SERVER_NAME,
 2227                            port: server.port,
 2228                            ssl: { ca_file: CA_FILE })
 2229         assert_equal("AUTH TLS\r\n", commands.shift)
 2230         assert_equal("PBSZ 0\r\n", commands.shift)
 2231         assert_equal("PROT P\r\n", commands.shift)
 2232         ftp.login
 2233         assert_match(/\AUSER /, commands.shift)
 2234         assert_match(/\APASS /, commands.shift)
 2235         assert_equal("TYPE I\r\n", commands.shift)
 2236         ftp.abort
 2237         assert_equal("ABOR\r\n", commands.shift)
 2238         assert_equal(nil, commands.shift)
 2239       rescue RuntimeError, LoadError
 2240         # skip (require openssl)
 2241       ensure
 2242         ftp.close if ftp
 2243       end
 2244     ensure
 2245       server.close
 2246     end
 2247   end
 2248 
 2249   def test_getbinaryfile_command_injection
 2250     skip "| is not allowed in filename on Windows" if windows?
 2251     [false, true].each do |resume|
 2252       commands = []
 2253       binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2254       server = create_ftp_server { |sock|
 2255         sock.print("220 (test_ftp).\r\n")
 2256         commands.push(sock.gets)
 2257         sock.print("331 Please specify the password.\r\n")
 2258         commands.push(sock.gets)
 2259         sock.print("230 Login successful.\r\n")
 2260         commands.push(sock.gets)
 2261         sock.print("200 Switching to Binary mode.\r\n")
 2262         line = sock.gets
 2263         commands.push(line)
 2264         host, port = process_port_or_eprt(sock, line)
 2265         commands.push(sock.gets)
 2266         sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
 2267         conn = TCPSocket.new(host, port)
 2268         binary_data.scan(/.{1,1024}/nm) do |s|
 2269           conn.print(s)
 2270         end
 2271         conn.shutdown(Socket::SHUT_WR)
 2272         conn.read
 2273         conn.close
 2274         sock.print("226 Transfer complete.\r\n")
 2275       }
 2276       begin
 2277         chdir_to_tmpdir do
 2278           begin
 2279             ftp = Net::FTP.new
 2280             ftp.resume = resume
 2281             ftp.read_timeout = RubyVM::MJIT.enabled? ? 5 : 0.2 # use large timeout for --jit-wait
 2282             ftp.connect(SERVER_ADDR, server.port)
 2283             ftp.login
 2284             assert_match(/\AUSER /, commands.shift)
 2285             assert_match(/\APASS /, commands.shift)
 2286             assert_equal("TYPE I\r\n", commands.shift)
 2287             ftp.getbinaryfile("|echo hello")
 2288             assert_equal(binary_data, File.binread("./|echo hello"))
 2289             assert_match(/\A(PORT|EPRT) /, commands.shift)
 2290             assert_equal("RETR |echo hello\r\n", commands.shift)
 2291             assert_equal(nil, commands.shift)
 2292           ensure
 2293             ftp.close if ftp
 2294           end
 2295         end
 2296       ensure
 2297         server.close
 2298       end
 2299     end
 2300   end
 2301 
 2302   def test_gettextfile_command_injection
 2303     skip "| is not allowed in filename on Windows" if windows?
 2304     commands = []
 2305     text_data = <<EOF.gsub(/\n/, "\r\n")
 2306 foo
 2307 bar
 2308 baz
 2309 EOF
 2310     server = create_ftp_server { |sock|
 2311       sock.print("220 (test_ftp).\r\n")
 2312       commands.push(sock.gets)
 2313       sock.print("331 Please specify the password.\r\n")
 2314       commands.push(sock.gets)
 2315       sock.print("230 Login successful.\r\n")
 2316       commands.push(sock.gets)
 2317       sock.print("200 Switching to Binary mode.\r\n")
 2318       commands.push(sock.gets)
 2319       sock.print("200 Switching to ASCII mode.\r\n")
 2320       line = sock.gets
 2321       commands.push(line)
 2322       host, port = process_port_or_eprt(sock, line)
 2323       commands.push(sock.gets)
 2324       sock.print("150 Opening TEXT mode data connection for |echo hello (#{text_data.size} bytes)\r\n")
 2325       conn = TCPSocket.new(host, port)
 2326       text_data.each_line do |l|
 2327         conn.print(l)
 2328       end
 2329       conn.shutdown(Socket::SHUT_WR)
 2330       conn.read
 2331       conn.close
 2332       sock.print("226 Transfer complete.\r\n")
 2333       commands.push(sock.gets)
 2334       sock.print("200 Switching to Binary mode.\r\n")
 2335     }
 2336     begin
 2337       chdir_to_tmpdir do
 2338         begin
 2339           ftp = Net::FTP.new
 2340           ftp.connect(SERVER_ADDR, server.port)
 2341           ftp.login
 2342           assert_match(/\AUSER /, commands.shift)
 2343           assert_match(/\APASS /, commands.shift)
 2344           assert_equal("TYPE I\r\n", commands.shift)
 2345           ftp.gettextfile("|echo hello")
 2346           assert_equal(text_data.gsub(/\r\n/, "\n"),
 2347                        File.binread("./|echo hello"))
 2348           assert_equal("TYPE A\r\n", commands.shift)
 2349           assert_match(/\A(PORT|EPRT) /, commands.shift)
 2350           assert_equal("RETR |echo hello\r\n", commands.shift)
 2351           assert_equal("TYPE I\r\n", commands.shift)
 2352           assert_equal(nil, commands.shift)
 2353         ensure
 2354           ftp.close if ftp
 2355         end
 2356       end
 2357     ensure
 2358       server.close
 2359     end
 2360   end
 2361 
 2362   def test_putbinaryfile_command_injection
 2363     skip "| is not allowed in filename on Windows" if windows?
 2364     commands = []
 2365     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2366     received_data = nil
 2367     server = create_ftp_server { |sock|
 2368       sock.print("220 (test_ftp).\r\n")
 2369       commands.push(sock.gets)
 2370       sock.print("331 Please specify the password.\r\n")
 2371       commands.push(sock.gets)
 2372       sock.print("230 Login successful.\r\n")
 2373       commands.push(sock.gets)
 2374       sock.print("200 Switching to Binary mode.\r\n")
 2375       line = sock.gets
 2376       commands.push(line)
 2377       host, port = process_port_or_eprt(sock, line)
 2378       commands.push(sock.gets)
 2379       sock.print("150 Opening BINARY mode data connection for |echo hello (#{binary_data.size} bytes)\r\n")
 2380       conn = TCPSocket.new(host, port)
 2381       received_data = conn.read
 2382       conn.close
 2383       sock.print("226 Transfer complete.\r\n")
 2384     }
 2385     begin
 2386       chdir_to_tmpdir do
 2387         File.binwrite("./|echo hello", binary_data)
 2388         begin
 2389           ftp = Net::FTP.new
 2390           ftp.read_timeout = 0.2
 2391           ftp.connect(SERVER_ADDR, server.port)
 2392           ftp.login
 2393           assert_match(/\AUSER /, commands.shift)
 2394           assert_match(/\APASS /, commands.shift)
 2395           assert_equal("TYPE I\r\n", commands.shift)
 2396           ftp.putbinaryfile("|echo hello")
 2397           assert_equal(binary_data, received_data)
 2398           assert_match(/\A(PORT|EPRT) /, commands.shift)
 2399           assert_equal("STOR |echo hello\r\n", commands.shift)
 2400           assert_equal(nil, commands.shift)
 2401         ensure
 2402           ftp.close if ftp
 2403         end
 2404       end
 2405     ensure
 2406       server.close
 2407     end
 2408   end
 2409 
 2410   def test_puttextfile_command_injection
 2411     skip "| is not allowed in filename on Windows" if windows?
 2412     commands = []
 2413     received_data = nil
 2414     server = create_ftp_server { |sock|
 2415       sock.print("220 (test_ftp).\r\n")
 2416       commands.push(sock.gets)
 2417       sock.print("331 Please specify the password.\r\n")
 2418       commands.push(sock.gets)
 2419       sock.print("230 Login successful.\r\n")
 2420       commands.push(sock.gets)
 2421       sock.print("200 Switching to Binary mode.\r\n")
 2422       commands.push(sock.gets)
 2423       sock.print("200 Switching to ASCII mode.\r\n")
 2424       line = sock.gets
 2425       commands.push(line)
 2426       host, port = process_port_or_eprt(sock, line)
 2427       commands.push(sock.gets)
 2428       sock.print("150 Opening TEXT mode data connection for |echo hello\r\n")
 2429       conn = TCPSocket.new(host, port)
 2430       received_data = conn.read
 2431       conn.close
 2432       sock.print("226 Transfer complete.\r\n")
 2433       commands.push(sock.gets)
 2434       sock.print("200 Switching to Binary mode.\r\n")
 2435     }
 2436     begin
 2437       chdir_to_tmpdir do
 2438         File.open("|echo hello", "w") do |f|
 2439           f.puts("foo")
 2440           f.puts("bar")
 2441           f.puts("baz")
 2442         end
 2443         begin
 2444           ftp = Net::FTP.new
 2445           ftp.connect(SERVER_ADDR, server.port)
 2446           ftp.login
 2447           assert_match(/\AUSER /, commands.shift)
 2448           assert_match(/\APASS /, commands.shift)
 2449           assert_equal("TYPE I\r\n", commands.shift)
 2450           ftp.puttextfile("|echo hello")
 2451           assert_equal(<<EOF.gsub(/\n/, "\r\n"), received_data)
 2452 foo
 2453 bar
 2454 baz
 2455 EOF
 2456           assert_equal("TYPE A\r\n", commands.shift)
 2457           assert_match(/\A(PORT|EPRT) /, commands.shift)
 2458           assert_equal("STOR |echo hello\r\n", commands.shift)
 2459           assert_equal("TYPE I\r\n", commands.shift)
 2460           assert_equal(nil, commands.shift)
 2461         ensure
 2462           ftp.close if ftp
 2463         end
 2464       end
 2465     ensure
 2466       server.close
 2467     end
 2468   end
 2469 
 2470   def test_ignore_pasv_ip
 2471     commands = []
 2472     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2473     server = create_ftp_server(nil, "127.0.0.1") { |sock|
 2474       sock.print("220 (test_ftp).\r\n")
 2475       commands.push(sock.gets)
 2476       sock.print("331 Please specify the password.\r\n")
 2477       commands.push(sock.gets)
 2478       sock.print("230 Login successful.\r\n")
 2479       commands.push(sock.gets)
 2480       sock.print("200 Switching to Binary mode.\r\n")
 2481       line = sock.gets
 2482       commands.push(line)
 2483       data_server = TCPServer.new("127.0.0.1", 0)
 2484       port = data_server.local_address.ip_port
 2485       sock.printf("227 Entering Passive Mode (999,0,0,1,%s).\r\n",
 2486                   port.divmod(256).join(","))
 2487       commands.push(sock.gets)
 2488       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 2489       conn = data_server.accept
 2490       binary_data.scan(/.{1,1024}/nm) do |s|
 2491         conn.print(s)
 2492       end
 2493       conn.shutdown(Socket::SHUT_WR)
 2494       conn.read
 2495       conn.close
 2496       data_server.close
 2497       sock.print("226 Transfer complete.\r\n")
 2498     }
 2499     begin
 2500       begin
 2501         ftp = Net::FTP.new
 2502         ftp.passive = true
 2503         ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
 2504         ftp.connect("127.0.0.1", server.port)
 2505         ftp.login
 2506         assert_match(/\AUSER /, commands.shift)
 2507         assert_match(/\APASS /, commands.shift)
 2508         assert_equal("TYPE I\r\n", commands.shift)
 2509         buf = ftp.getbinaryfile("foo", nil)
 2510         assert_equal(binary_data, buf)
 2511         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 2512         assert_equal("PASV\r\n", commands.shift)
 2513         assert_equal("RETR foo\r\n", commands.shift)
 2514         assert_equal(nil, commands.shift)
 2515       ensure
 2516         ftp.close if ftp
 2517       end
 2518     ensure
 2519       server.close
 2520     end
 2521   end
 2522 
 2523   def test_use_pasv_ip
 2524     commands = []
 2525     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2526     server = create_ftp_server(nil, "127.0.0.1") { |sock|
 2527       sock.print("220 (test_ftp).\r\n")
 2528       commands.push(sock.gets)
 2529       sock.print("331 Please specify the password.\r\n")
 2530       commands.push(sock.gets)
 2531       sock.print("230 Login successful.\r\n")
 2532       commands.push(sock.gets)
 2533       sock.print("200 Switching to Binary mode.\r\n")
 2534       line = sock.gets
 2535       commands.push(line)
 2536       data_server = TCPServer.new("127.0.0.1", 0)
 2537       port = data_server.local_address.ip_port
 2538       sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
 2539                   port.divmod(256).join(","))
 2540       commands.push(sock.gets)
 2541       sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
 2542       conn = data_server.accept
 2543       binary_data.scan(/.{1,1024}/nm) do |s|
 2544         conn.print(s)
 2545       end
 2546       conn.shutdown(Socket::SHUT_WR)
 2547       conn.read
 2548       conn.close
 2549       data_server.close
 2550       sock.print("226 Transfer complete.\r\n")
 2551     }
 2552     begin
 2553       begin
 2554         ftp = Net::FTP.new
 2555         ftp.passive = true
 2556         ftp.use_pasv_ip = true
 2557         ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
 2558         ftp.connect("127.0.0.1", server.port)
 2559         ftp.login
 2560         assert_match(/\AUSER /, commands.shift)
 2561         assert_match(/\APASS /, commands.shift)
 2562         assert_equal("TYPE I\r\n", commands.shift)
 2563         buf = ftp.getbinaryfile("foo", nil)
 2564         assert_equal(binary_data, buf)
 2565         assert_equal(Encoding::ASCII_8BIT, buf.encoding)
 2566         assert_equal("PASV\r\n", commands.shift)
 2567         assert_equal("RETR foo\r\n", commands.shift)
 2568         assert_equal(nil, commands.shift)
 2569       ensure
 2570         ftp.close if ftp
 2571       end
 2572     ensure
 2573       server.close
 2574     end
 2575   end
 2576 
 2577   def test_use_pasv_invalid_ip
 2578     commands = []
 2579     binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
 2580     server = create_ftp_server(nil, "127.0.0.1") { |sock|
 2581       sock.print("220 (test_ftp).\r\n")
 2582       commands.push(sock.gets)
 2583       sock.print("331 Please specify the password.\r\n")
 2584       commands.push(sock.gets)
 2585       sock.print("230 Login successful.\r\n")
 2586       commands.push(sock.gets)
 2587       sock.print("200 Switching to Binary mode.\r\n")
 2588       line = sock.gets
 2589       commands.push(line)
 2590       sock.print("227 Entering Passive Mode (999,0,0,1,48,57).\r\n")
 2591       commands.push(sock.gets)
 2592     }
 2593     begin
 2594       begin
 2595         ftp = Net::FTP.new
 2596         ftp.passive = true
 2597         ftp.use_pasv_ip = true
 2598         ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
 2599         ftp.connect("127.0.0.1", server.port)
 2600         ftp.login
 2601         assert_match(/\AUSER /, commands.shift)
 2602         assert_match(/\APASS /, commands.shift)
 2603         assert_equal("TYPE I\r\n", commands.shift)
 2604         assert_raise(SocketError) do
 2605           ftp.getbinaryfile("foo", nil)
 2606         end
 2607       ensure
 2608         ftp.close if ftp
 2609       end
 2610     ensure
 2611       server.close
 2612     end
 2613   end
 2614 
 2615   private
 2616 
 2617   def create_ftp_server(sleep_time = nil, addr = SERVER_ADDR)
 2618     server = TCPServer.new(addr, 0)
 2619     @thread = Thread.start do
 2620       if sleep_time
 2621         sleep(sleep_time)
 2622       end
 2623       sock = server.accept
 2624       begin
 2625         sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1)
 2626         yield(sock)
 2627         sock.shutdown(Socket::SHUT_WR)
 2628         sock.read unless sock.eof?
 2629       ensure
 2630         sock.close
 2631       end
 2632     end
 2633     def server.port
 2634       addr[1]
 2635     end
 2636     return server
 2637   end
 2638 
 2639   def tls_test
 2640     server = TCPServer.new(SERVER_ADDR, 0)
 2641     port = server.addr[1]
 2642     commands = []
 2643     @thread = Thread.start do
 2644       sock = server.accept
 2645       begin
 2646         sock.print("220 (test_ftp).\r\n")
 2647         commands.push(sock.gets)
 2648         sock.print("234 AUTH success.\r\n")
 2649         ctx = OpenSSL::SSL::SSLContext.new
 2650         ctx.ca_file = CA_FILE
 2651         ctx.key = File.open(SERVER_KEY) { |f|
 2652           OpenSSL::PKey::RSA.new(f)
 2653         }
 2654         ctx.cert = File.open(SERVER_CERT) { |f|
 2655           OpenSSL::X509::Certificate.new(f)
 2656         }
 2657         sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
 2658         sock.sync_close = true
 2659         begin
 2660           sock.accept
 2661           commands.push(sock.gets)
 2662           sock.print("200 PSBZ success.\r\n")
 2663           commands.push(sock.gets)
 2664           sock.print("200 PROT success.\r\n")
 2665         rescue OpenSSL::SSL::SSLError, SystemCallError
 2666         end
 2667       ensure
 2668         sock.close
 2669         server.close
 2670       end
 2671     end
 2672     ftp = yield(port)
 2673     ftp.close
 2674 
 2675     assert_equal("AUTH TLS\r\n", commands.shift)
 2676     assert_equal("PBSZ 0\r\n", commands.shift)
 2677     assert_equal("PROT P\r\n", commands.shift)
 2678   end
 2679 
 2680   def process_port_or_eprt(sock, line)
 2681     case line
 2682     when /\APORT (.*)/
 2683       port_args = $1.split(/,/)
 2684       host = port_args[0, 4].join(".")
 2685       port = port_args[4, 2].map(&:to_i).inject {|x, y| (x << 8) + y}
 2686       sock.print("200 PORT command successful.\r\n")
 2687       return host, port
 2688     when /\AEPRT \|2\|(.*?)\|(.*?)\|/
 2689       host = $1
 2690       port = $2.to_i
 2691       sock.print("200 EPRT command successful.\r\n")
 2692       return host, port
 2693     else
 2694       flunk "PORT or EPRT expected"
 2695     end
 2696   end
 2697 
 2698   def create_data_connection_server(sock)
 2699     data_server = TCPServer.new(SERVER_ADDR, 0)
 2700     port = data_server.local_address.ip_port
 2701     if data_server.local_address.ipv4?
 2702       sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
 2703                   port.divmod(256).join(","))
 2704     elsif data_server.local_address.ipv6?
 2705       sock.printf("229 Entering Extended Passive Mode (|||%d|)\r\n", port)
 2706     else
 2707       flunk "Invalid local address"
 2708     end
 2709     return data_server
 2710   end
 2711 
 2712   def chdir_to_tmpdir
 2713     Dir.mktmpdir do |dir|
 2714       pwd = Dir.pwd
 2715       Dir.chdir(dir)
 2716       begin
 2717         yield
 2718       ensure
 2719         Dir.chdir(pwd)
 2720       end
 2721     end
 2722   end
 2723 end