1use strict; 2use Test::More tests => 18; 3use POSIX qw(dup2); 4use IO::Handle; 5use FileHandle; 6 7use Net::FTPServer::InMem::Server; 8 9pipe INFD0, OUTFD0 or die "pipe: $!"; 10pipe INFD1, OUTFD1 or die "pipe: $!"; 11my $pid = fork (); 12die unless defined $pid; 13unless ($pid) { # Child process (the server). 14 POSIX::dup2 (fileno INFD0, 0); 15 POSIX::dup2 (fileno OUTFD1, 1); 16 close INFD0; 17 close OUTFD0; 18 close INFD1; 19 close OUTFD1; 20 my $ftps = Net::FTPServer::InMem::Server->run 21 (['--test', '-d', '-C', '/dev/null']); 22 exit; 23} 24 25# Parent process (the test script). 26close INFD0; 27close OUTFD1; 28OUTFD0->autoflush (1); 29 30$_ = <INFD1>; 31print OUTFD0 "USER rich\r\n"; 32$_ = <INFD1>; 33ok (/^331/); 34 35print OUTFD0 "PASS 123456\r\n"; 36$_ = <INFD1>; 37ok (/^230 Welcome rich\./); 38 39# Use binary mode. 40print OUTFD0 "TYPE I\r\n"; 41$_ = <INFD1>; 42ok (/^200/); 43 44# Enter passive mode and get a port number. 45print OUTFD0 "PASV\r\n"; 46$_ = <INFD1>; 47ok (/^227 Entering Passive Mode \(127,0,0,1,(.*),(.*)\)/); 48 49my $port = $1 * 256 + $2; 50 51# Upload a large binary file. 52print OUTFD0 "STOR test\r\n"; 53$_ = <INFD1>; 54ok (/^150/); 55 56# Connect to the passive mode port. 57my $sock = new IO::Socket::INET 58 (PeerAddr => "127.0.0.1:$port", 59 Proto => "tcp") 60 or die "socket: $!"; 61 62my $buffer = ""; 63 64for (my $i = 0; $i < 10000; ++$i) 65 { 66 my $c = chr (int (rand 256)); 67 $buffer .= $c; 68 } 69$sock->print ($buffer); 70$sock->close; 71 72# Check the return code. 73$_ = <INFD1>; 74ok (/^226/); 75 76# Grab random parts of the file and check. 77ok (download_and_check ("test", 5000, substr ($buffer, 5000))); 78ok (download_and_check ("test", 1000, substr ($buffer, 1000))); 79ok (download_and_check ("test", 500, substr ($buffer, 500))); 80ok (download_and_check ("test", 10000, substr ($buffer, 10000))); 81ok (download_and_check ("test", 0, substr ($buffer, 0))); 82 83# Upload a smallish ascii file. 84print OUTFD0 "TYPE A\r\n"; 85$_ = <INFD1>; 86ok (/^200/); 87 88print OUTFD0 "STOR asctest\r\n"; 89$_ = <INFD1>; 90ok (/^150/); 91 92$sock = new IO::Socket::INET 93 (PeerAddr => "127.0.0.1:$port", 94 Proto => "tcp") 95 or die "socket: $!"; 96 97$sock->print ("0123456789\r\n", 98 "abcdefghij\r\n", 99 "klmnopqrst\r\n", 100 "uvwxyzABCD\r\n"); 101$sock->close; 102 103$_ = <INFD1>; 104ok (/^226/); 105 106# Try a restartable download, in ASCII mode. Note how we are counting 107# those end of line characters. 108print OUTFD0 "REST 33\r\n"; 109$_ = <INFD1>; 110ok (/^350/); 111 112print OUTFD0 "RETR asctest\r\n"; 113$_ = <INFD1>; 114ok (/^150/); 115 116$sock = new IO::Socket::INET 117 (PeerAddr => "127.0.0.1:$port", 118 Proto => "tcp") 119 or die "socket: $!"; 120 121$_ = $sock->getline; 122ok ($_ && $_ eq "uvwxyzABCD\r\n"); 123 124$sock->close; 125 126$_ = <INFD1>; 127ok (/^226/); 128 129print OUTFD0 "QUIT\r\n"; 130$_ = <INFD1>; 131 132exit; 133 134sub download_and_check 135 { 136 my $filename = shift; 137 my $restart = shift; 138 my $expected_data = shift; 139 140 # Perform restartable download. 141 print OUTFD0 "REST $restart\r\n"; 142 $_ = <INFD1>; 143 return 0 unless /^350/; 144 145 # Download. 146 print OUTFD0 "RETR $filename\r\n"; 147 $_ = <INFD1>; 148 return 0 unless /^150/; 149 150 # Connect to the passive mode port. 151 my $sock = new IO::Socket::INET 152 (PeerAddr => "127.0.0.1:$port", 153 Proto => "tcp") 154 or die "socket: $!"; 155 156 # Read the data. 157 my $actual_data; 158 my $r = $sock->read ($actual_data, 30000); 159 160 $sock->close; 161 162 # Check the return code. 163 $_ = <INFD1>; 164 return 0 unless /^226/; 165 166 # Check the data. 167 return 0 unless $r == length ($expected_data); 168 169 return $expected_data eq $actual_data; 170 } 171 172__END__ 173