1use strict;
2use Test::More tests => 82;
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# Enter passive mode and get a port number.
40print OUTFD0 "PASV\r\n";
41$_ = <INFD1>;
42ok (/^227 Entering Passive Mode \(127,0,0,1,(.*),(.*)\)/);
43
44my $port = $1 * 256 + $2;
45
46# Upload some files.
47ok (upload ("test1", "Oh I can see them now"));
48ok (upload ("test2", "Clutching a hankerchief"));
49ok (upload ("test3", "And blowing me a kiss"));
50ok (upload ("test4", "Discreetly asking how"));
51ok (upload ("test5", "How came he died so young"));
52ok (upload ("test6", "Or was he very old"));
53
54# Create some subdirectories.
55print OUTFD0 "MKD sub1\r\n";
56$_ = <INFD1>;
57ok (/^250/);
58
59print OUTFD0 "MKD sub2\r\n";
60$_ = <INFD1>;
61ok (/^250/);
62
63print OUTFD0 "MKD sub3\r\n";
64$_ = <INFD1>;
65ok (/^250/);
66
67# LIST the files.
68print OUTFD0 "LIST\r\n";
69$_ = <INFD1>;
70ok (/^150/);
71
72my $sock = new IO::Socket::INET
73  (PeerAddr => "127.0.0.1:$port",
74   Proto => "tcp")
75  or die "socket: $!";
76
77# Read the synthetic lines first.
78$_ = $sock->getline;
79ok ($_ && /^total/);
80$_ = $sock->getline;
81ok ($_ && / \.\r\n$/);
82$_ = $sock->getline;
83ok ($_ && / \.\.\r\n$/);
84
85# Read the files -- they aren't necessarily in alphabetical order (but
86# ought they to be?) [XXX]
87my @filenames = ();
88for (my $i = 0; $i < 9; ++$i)
89  {
90    $_ = $sock->getline;
91    ok ($_ && / ((test|sub)[1-9])\r\n$/);
92    push @filenames, $1;
93  }
94
95$_ = $sock->getline;
96ok (!$_);
97
98@filenames = sort @filenames;
99ok (join (" ", @filenames) eq
100    "sub1 sub2 sub3 test1 test2 test3 test4 test5 test6");
101
102# Check return code.
103$_ = <INFD1>;
104ok (/^226/);
105
106# LIST * the files.
107print OUTFD0 "LIST *\r\n";
108$_ = <INFD1>;
109ok (/^150/);
110
111$sock = new IO::Socket::INET
112  (PeerAddr => "127.0.0.1:$port",
113   Proto => "tcp")
114  or die "socket: $!";
115
116# Read the files -- they aren't necessarily in alphabetical order (but
117# ought they to be?) [XXX]
118@filenames = ();
119for (my $i = 0; $i < 9; ++$i)
120  {
121    $_ = $sock->getline;
122    ok ($_ && / ((test|sub)[1-9])\r\n$/);
123    push @filenames, $1;
124  }
125
126$_ = $sock->getline;
127ok (!$_);
128
129@filenames = sort @filenames;
130ok (join (" ", @filenames) eq
131    "sub1 sub2 sub3 test1 test2 test3 test4 test5 test6");
132
133# Check return code.
134$_ = <INFD1>;
135ok (/^226/);
136
137# LIST s* the files.
138print OUTFD0 "LIST s*\r\n";
139$_ = <INFD1>;
140ok (/^150/);
141
142$sock = new IO::Socket::INET
143  (PeerAddr => "127.0.0.1:$port",
144   Proto => "tcp")
145  or die "socket: $!";
146
147# Read the files -- they aren't necessarily in alphabetical order (but
148# ought they to be?) [XXX]
149@filenames = ();
150for (my $i = 0; $i < 3; ++$i)
151  {
152    $_ = $sock->getline;
153    ok ($_ && / (sub[1-9])\r\n$/);
154    push @filenames, $1;
155  }
156
157$_ = $sock->getline;
158ok (!$_);
159
160@filenames = sort @filenames;
161ok (join (" ", @filenames) eq
162    "sub1 sub2 sub3");
163
164# Check return code.
165$_ = <INFD1>;
166ok (/^226/);
167
168# NLST the files.
169print OUTFD0 "NLST\r\n";
170$_ = <INFD1>;
171ok (/^150/);
172
173$sock = new IO::Socket::INET
174  (PeerAddr => "127.0.0.1:$port",
175   Proto => "tcp")
176  or die "socket: $!";
177
178# Read the files -- they aren't necessarily in alphabetical order (but
179# ought they to be?) [XXX]
180@filenames = ();
181for (my $i = 0; $i < 9; ++$i)
182  {
183    $_ = $sock->getline;
184    ok ($_ && /^((test|sub)[1-9])\r\n$/);
185    push @filenames, $1;
186  }
187
188$_ = $sock->getline;
189ok (!$_);
190
191@filenames = sort @filenames;
192ok (join (" ", @filenames) eq
193    "sub1 sub2 sub3 test1 test2 test3 test4 test5 test6");
194
195# Check return code.
196$_ = <INFD1>;
197ok (/^226/);
198
199# NLST * the files.
200print OUTFD0 "NLST *\r\n";
201$_ = <INFD1>;
202ok (/^150/);
203
204$sock = new IO::Socket::INET
205  (PeerAddr => "127.0.0.1:$port",
206   Proto => "tcp")
207  or die "socket: $!";
208
209# Read the files -- they aren't necessarily in alphabetical order (but
210# ought they to be?) [XXX]
211@filenames = ();
212for (my $i = 0; $i < 9; ++$i)
213  {
214    $_ = $sock->getline;
215    ok ($_ && /^((test|sub)[1-9])\r\n$/);
216    push @filenames, $1;
217  }
218
219@filenames = sort @filenames;
220ok (join (" ", @filenames) eq
221    "sub1 sub2 sub3 test1 test2 test3 test4 test5 test6");
222
223# Check return code.
224$_ = <INFD1>;
225ok (/^226/);
226
227# NLST t* the files.
228print OUTFD0 "NLST t*\r\n";
229$_ = <INFD1>;
230ok (/^150/);
231
232$sock = new IO::Socket::INET
233  (PeerAddr => "127.0.0.1:$port",
234   Proto => "tcp")
235  or die "socket: $!";
236
237# Read the files -- they aren't necessarily in alphabetical order (but
238# ought they to be?) [XXX]
239@filenames = ();
240for (my $i = 0; $i < 6; ++$i)
241  {
242    $_ = $sock->getline;
243    ok ($_ && /^(test[1-9])\r\n$/);
244    push @filenames, $1;
245  }
246
247@filenames = sort @filenames;
248ok (join (" ", @filenames) eq
249    "test1 test2 test3 test4 test5 test6");
250
251# Check return code.
252$_ = <INFD1>;
253ok (/^226/);
254
255print OUTFD0 "QUIT\r\n";
256$_ = <INFD1>;
257
258sub upload
259  {
260    my $filename = shift;
261    my $content = join "", @_;
262
263    print OUTFD0 "STOR $filename\r\n";
264    $_ = <INFD1>;
265    return 0 unless /^150/;
266
267    # Connect to passive mode port.
268    my $sock = new IO::Socket::INET
269      (PeerAddr => "127.0.0.1:$port",
270       Proto => "tcp")
271	or die "socket: $!";
272
273    # Store some data.
274    $sock->print ($content);
275    $sock->close;
276
277    # Check return code.
278    $_ = <INFD1>;
279    return /^226/;
280  }
281
282__END__
283