1package ProFTPD::Tests::Commands::STAT;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use Cwd;
8use File::Path qw(mkpath);
9use File::Spec;
10use IO::Handle;
11
12use ProFTPD::TestSuite::FTP;
13use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite);
14
15$| = 1;
16
17my $order = 0;
18
19my $TESTS = {
20  stat_file => {
21    order => ++$order,
22    test_class => [qw(forking)],
23  },
24
25  stat_dir => {
26    order => ++$order,
27    test_class => [qw(forking)],
28  },
29
30  stat_dir_with_files_bug3990 => {
31    order => ++$order,
32    test_class => [qw(bug forking)],
33  },
34
35  stat_symlink_showsymlinks_off => {
36    order => ++$order,
37    test_class => [qw(forking)],
38  },
39
40  stat_symlink_showsymlinks_on => {
41    order => ++$order,
42    test_class => [qw(forking)],
43  },
44
45  stat_symlink_showsymlinks_off_chrooted_bug4219 => {
46    order => ++$order,
47    test_class => [qw(forking rootprivs)],
48  },
49
50  stat_symlink_showsymlinks_on_chrooted_bug4219 => {
51    order => ++$order,
52    test_class => [qw(forking rootprivs)],
53  },
54
55  stat_system => {
56    order => ++$order,
57    test_class => [qw(forking)],
58  },
59
60  stat_enoent => {
61    order => ++$order,
62    test_class => [qw(forking)],
63  },
64
65  stat_listoptions_bug3295 => {
66    order => ++$order,
67    test_class => [qw(bug forking)],
68  },
69
70};
71
72sub new {
73  return shift()->SUPER::new(@_);
74}
75
76sub list_tests {
77  return testsuite_get_runnable_tests($TESTS);
78}
79
80sub stat_file {
81  my $self = shift;
82  my $tmpdir = $self->{tmpdir};
83  my $setup = test_setup($tmpdir, 'cmds');
84
85  my $test_file = File::Spec->rel2abs($setup->{config_file});
86
87  my $config = {
88    PidFile => $setup->{pid_file},
89    ScoreboardFile => $setup->{scoreboard_file},
90    SystemLog => $setup->{log_file},
91
92    AuthUserFile => $setup->{auth_user_file},
93    AuthGroupFile => $setup->{auth_group_file},
94
95    IfModules => {
96      'mod_delay.c' => {
97        DelayEngine => 'off',
98      },
99    },
100  };
101
102  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
103    $config);
104
105  # Open pipes, for use between the parent and child processes.  Specifically,
106  # the child will indicate when it's done with its test by writing a message
107  # to the parent.
108  my ($rfh, $wfh);
109  unless (pipe($rfh, $wfh)) {
110    die("Can't open pipe: $!");
111  }
112
113  my $ex;
114
115  # Fork child
116  $self->handle_sigchld();
117  defined(my $pid = fork()) or die("Can't fork: $!");
118  if ($pid) {
119    eval {
120      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
121      $client->login($setup->{user}, $setup->{passwd});
122      $client->stat('cmds.conf');
123
124      my $resp_code = $client->response_code();
125      my $resp_msgs = $client->response_msgs();
126      my $resp_msg = $resp_msgs->[1];
127      $client->quit();
128
129      my $expected = 213;
130      $self->assert($expected == $resp_code,
131        test_msg("Expected response code $expected, got $resp_code"));
132
133      $expected = '^(\s+)?\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
134      $self->assert(qr/$expected/, $resp_msg,
135        test_msg("Expected response message '$expected', got '$resp_msg'"));
136    };
137    if ($@) {
138      $ex = $@;
139    }
140
141    $wfh->print("done\n");
142    $wfh->flush();
143
144  } else {
145    eval { server_wait($setup->{config_file}, $rfh) };
146    if ($@) {
147      warn($@);
148      exit 1;
149    }
150
151    exit 0;
152  }
153
154  # Stop server
155  server_stop($setup->{pid_file});
156  $self->assert_child_ok($pid);
157
158  test_cleanup($setup->{log_file}, $ex);
159}
160
161sub stat_dir {
162  my $self = shift;
163  my $tmpdir = $self->{tmpdir};
164  my $setup = test_setup($tmpdir, 'cmds');
165
166  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
167  mkpath($test_dir);
168
169  if ($< == 0) {
170    unless (chmod(0755, $test_dir)) {
171      die("Can't set perms on $test_dir to 0755: $!");
172    }
173
174    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
175      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
176    }
177  }
178
179  my $config = {
180    PidFile => $setup->{pid_file},
181    ScoreboardFile => $setup->{scoreboard_file},
182    SystemLog => $setup->{log_file},
183
184    AuthUserFile => $setup->{auth_user_file},
185    AuthGroupFile => $setup->{auth_group_file},
186
187    IfModules => {
188      'mod_delay.c' => {
189        DelayEngine => 'off',
190      },
191    },
192  };
193
194  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
195    $config);
196
197  # Open pipes, for use between the parent and child processes.  Specifically,
198  # the child will indicate when it's done with its test by writing a message
199  # to the parent.
200  my ($rfh, $wfh);
201  unless (pipe($rfh, $wfh)) {
202    die("Can't open pipe: $!");
203  }
204
205  my $ex;
206
207  # Fork child
208  $self->handle_sigchld();
209  defined(my $pid = fork()) or die("Can't fork: $!");
210  if ($pid) {
211    eval {
212      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
213      $client->login($setup->{user}, $setup->{passwd});
214      $client->stat('test.d');
215
216      my $resp_code = $client->response_code();
217      my $resp_msgs = $client->response_msgs();
218      my $resp_msg = $resp_msgs->[1];
219      $client->quit();
220
221      my $expected = 212;
222      $self->assert($expected == $resp_code,
223        test_msg("Expected response code $expected, got $resp_code"));
224
225      $expected = '^(\s+)?\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
226      $self->assert(qr/$expected/, $resp_msg,
227        test_msg("Expected response message '$expected', got '$resp_msg'"));
228    };
229    if ($@) {
230      $ex = $@;
231    }
232
233    $wfh->print("done\n");
234    $wfh->flush();
235
236  } else {
237    eval { server_wait($setup->{config_file}, $rfh) };
238    if ($@) {
239      warn($@);
240      exit 1;
241    }
242
243    exit 0;
244  }
245
246  # Stop server
247  server_stop($setup->{pid_file});
248  $self->assert_child_ok($pid);
249
250  test_cleanup($setup->{log_file}, $ex);
251}
252
253sub stat_dir_with_files_bug3990 {
254  my $self = shift;
255  my $tmpdir = $self->{tmpdir};
256  my $setup = test_setup($tmpdir, 'cmds');
257
258  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
259  mkpath($test_dir);
260
261  for (my $i = 0; $i < 5; $i++) {
262    my $test_file = File::Spec->rel2abs("$test_dir/file$i.dat");
263    if (open(my $fh, "> $test_file")) {
264      close($fh);
265
266    } else {
267      die("Can't open $test_file: $!");
268    }
269  }
270
271  for (my $i = 0; $i < 5; $i++) {
272    my $test_file = File::Spec->rel2abs("$test_dir/.hidden$i.dat");
273    if (open(my $fh, "> $test_file")) {
274      close($fh);
275
276    } else {
277      die("Can't open $test_file: $!");
278    }
279  }
280
281  # Make sure that, if we're running as root, that the home directory has
282  # permissions/privs set for the account we create
283  if ($< == 0) {
284    unless (chmod(0755, $test_dir)) {
285      die("Can't set perms on $test_dir to 0755: $!");
286    }
287
288    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
289      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
290    }
291  }
292
293  my $config = {
294    PidFile => $setup->{pid_file},
295    ScoreboardFile => $setup->{scoreboard_file},
296    SystemLog => $setup->{log_file},
297
298    AuthUserFile => $setup->{auth_user_file},
299    AuthGroupFile => $setup->{auth_group_file},
300
301    IfModules => {
302      'mod_delay.c' => {
303        DelayEngine => 'off',
304      },
305    },
306  };
307
308  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
309    $config);
310
311  # Open pipes, for use between the parent and child processes.  Specifically,
312  # the child will indicate when it's done with its test by writing a message
313  # to the parent.
314  my ($rfh, $wfh);
315  unless (pipe($rfh, $wfh)) {
316    die("Can't open pipe: $!");
317  }
318
319  my $ex;
320
321  # Fork child
322  $self->handle_sigchld();
323  defined(my $pid = fork()) or die("Can't fork: $!");
324  if ($pid) {
325    eval {
326      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
327      $client->login($setup->{user}, $setup->{passwd});
328      $client->stat('test.d');
329
330      my $resp_code = $client->response_code();
331      my $resp_msgs = $client->response_msgs();
332      my $resp_nmsgs = scalar(@$resp_msgs);
333      my $resp_msg = $resp_msgs->[1];
334      $client->quit();
335
336      my $expected = 212;
337      $self->assert($expected == $resp_code,
338        test_msg("Expected response code $expected, got $resp_code"));
339
340      $expected = '^(\s+)?\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
341      $self->assert(qr/$expected/, $resp_msg,
342        test_msg("Expected response message '$expected', got '$resp_msg'"));
343
344      $expected = 14;
345      $self->assert($expected == $resp_nmsgs,
346        test_msg("Expected $expected response lines, got $resp_nmsgs"));
347    };
348    if ($@) {
349      $ex = $@;
350    }
351
352    $wfh->print("done\n");
353    $wfh->flush();
354
355  } else {
356    eval { server_wait($setup->{config_file}, $rfh) };
357    if ($@) {
358      warn($@);
359      exit 1;
360    }
361
362    exit 0;
363  }
364
365  # Stop server
366  server_stop($setup->{pid_file});
367  $self->assert_child_ok($pid);
368
369  test_cleanup($setup->{log_file}, $ex);
370}
371
372sub stat_symlink_showsymlinks_off {
373  my $self = shift;
374  my $tmpdir = $self->{tmpdir};
375  my $setup = test_setup($tmpdir, 'cmds');
376
377  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
378  mkpath($test_dir);
379
380  my $test_file = File::Spec->rel2abs("$test_dir/test.txt");
381  if (open(my $fh, "> $test_file")) {
382    unless (close($fh)) {
383      die("Can't write $test_file: $!");
384    }
385
386  } else {
387    die("Can't open $test_file: $!");
388  }
389
390  # Change to the test directory in order to create a relative path in the
391  # symlink we need
392
393  my $cwd = getcwd();
394  unless (chdir($test_dir)) {
395    die("Can't chdir to $test_dir: $!");
396  }
397
398  unless (symlink('./test.txt', './test.lnk')) {
399    die("Can't symlink 'test.lnk' to './test.txt': $!");
400  }
401
402  unless (chdir($cwd)) {
403    die("Can't chdir to $cwd: $!");
404  }
405
406  if ($< == 0) {
407    unless (chmod(0755, $test_dir)) {
408      die("Can't set perms on $test_dir to 0755: $!");
409    }
410
411    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
412      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
413    }
414  }
415
416  my $config = {
417    PidFile => $setup->{pid_file},
418    ScoreboardFile => $setup->{scoreboard_file},
419    SystemLog => $setup->{log_file},
420
421    AuthUserFile => $setup->{auth_user_file},
422    AuthGroupFile => $setup->{auth_group_file},
423
424    ShowSymlinks => 'off',
425
426    IfModules => {
427      'mod_delay.c' => {
428        DelayEngine => 'off',
429      },
430    },
431  };
432
433  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
434    $config);
435
436  # Open pipes, for use between the parent and child processes.  Specifically,
437  # the child will indicate when it's done with its test by writing a message
438  # to the parent.
439  my ($rfh, $wfh);
440  unless (pipe($rfh, $wfh)) {
441    die("Can't open pipe: $!");
442  }
443
444  my $ex;
445
446  # Fork child
447  $self->handle_sigchld();
448  defined(my $pid = fork()) or die("Can't fork: $!");
449  if ($pid) {
450    eval {
451      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
452      $client->login($setup->{user}, $setup->{passwd});
453      $client->stat('test.d/test.lnk');
454
455      my $resp_code = $client->response_code();
456      my $resp_msgs = $client->response_msgs();
457      my $resp_msg = $resp_msgs->[1];
458      $client->quit();
459
460      if ($ENV{TEST_VERBOSE}) {
461        use Data::Dumper;
462        print STDERR "# Response:\n", Dumper($resp_msgs), "\n";
463      }
464
465      my $expected = 213;
466      $self->assert($expected == $resp_code,
467        test_msg("Expected response code $expected, got $resp_code"));
468
469      $expected = '^(\s+)?\-\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
470      $self->assert(qr/$expected/, $resp_msg,
471        test_msg("Expected response message '$expected', got '$resp_msg'"));
472    };
473    if ($@) {
474      $ex = $@;
475    }
476
477    $wfh->print("done\n");
478    $wfh->flush();
479
480  } else {
481    eval { server_wait($setup->{config_file}, $rfh) };
482    if ($@) {
483      warn($@);
484      exit 1;
485    }
486
487    exit 0;
488  }
489
490  # Stop server
491  server_stop($setup->{pid_file});
492  $self->assert_child_ok($pid);
493
494  test_cleanup($setup->{log_file}, $ex);
495}
496
497sub stat_symlink_showsymlinks_on {
498  my $self = shift;
499  my $tmpdir = $self->{tmpdir};
500  my $setup = test_setup($tmpdir, 'cmds');
501
502  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
503  mkpath($test_dir);
504
505  my $test_file = File::Spec->rel2abs("$test_dir/test.txt");
506  if (open(my $fh, "> $test_file")) {
507    unless (close($fh)) {
508      die("Can't write $test_file: $!");
509    }
510
511  } else {
512    die("Can't open $test_file: $!");
513  }
514
515  # Change to the test directory in order to create a relative path in the
516  # symlink we need
517
518  my $cwd = getcwd();
519  unless (chdir($test_dir)) {
520    die("Can't chdir to $test_dir: $!");
521  }
522
523  unless (symlink('./test.txt', './test.lnk')) {
524    die("Can't symlink 'test.lnk' to './test.txt': $!");
525  }
526
527  unless (chdir($cwd)) {
528    die("Can't chdir to $cwd: $!");
529  }
530
531  if ($< == 0) {
532    unless (chmod(0755, $test_dir)) {
533      die("Can't set perms on $test_dir to 0755: $!");
534    }
535
536    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
537      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
538    }
539  }
540
541  my $config = {
542    PidFile => $setup->{pid_file},
543    ScoreboardFile => $setup->{scoreboard_file},
544    SystemLog => $setup->{log_file},
545
546    AuthUserFile => $setup->{auth_user_file},
547    AuthGroupFile => $setup->{auth_group_file},
548
549    ShowSymlinks => 'on',
550
551    IfModules => {
552      'mod_delay.c' => {
553        DelayEngine => 'off',
554      },
555    },
556  };
557
558  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
559    $config);
560
561  # Open pipes, for use between the parent and child processes.  Specifically,
562  # the child will indicate when it's done with its test by writing a message
563  # to the parent.
564  my ($rfh, $wfh);
565  unless (pipe($rfh, $wfh)) {
566    die("Can't open pipe: $!");
567  }
568
569  my $ex;
570
571  # Fork child
572  $self->handle_sigchld();
573  defined(my $pid = fork()) or die("Can't fork: $!");
574  if ($pid) {
575    eval {
576      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
577      $client->login($setup->{user}, $setup->{passwd});
578      $client->stat('test.d/test.lnk');
579
580      my $resp_code = $client->response_code();
581      my $resp_msgs = $client->response_msgs();
582      my $resp_msg = $resp_msgs->[1];
583      $client->quit();
584
585      if ($ENV{TEST_VERBOSE}) {
586        use Data::Dumper;
587        print STDERR "# Response:\n", Dumper($resp_msgs), "\n";
588      }
589
590      my $expected = 213;
591      $self->assert($expected == $resp_code,
592        test_msg("Expected response code $expected, got $resp_code"));
593
594      $expected = '^(\s+)?l\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
595      $self->assert(qr/$expected/, $resp_msg,
596        test_msg("Expected response message '$expected', got '$resp_msg'"));
597
598      $expected = '.*?test\.d\/test\.lnk \-> test\.d\/test\.txt$';
599      $self->assert(qr/$expected/, $resp_msg,
600        test_msg("Expected response message '$expected', got '$resp_msg'"));
601    };
602    if ($@) {
603      $ex = $@;
604    }
605
606    $wfh->print("done\n");
607    $wfh->flush();
608
609  } else {
610    eval { server_wait($setup->{config_file}, $rfh) };
611    if ($@) {
612      warn($@);
613      exit 1;
614    }
615
616    exit 0;
617  }
618
619  # Stop server
620  server_stop($setup->{pid_file});
621  $self->assert_child_ok($pid);
622
623  test_cleanup($setup->{log_file}, $ex);
624}
625
626sub stat_symlink_showsymlinks_off_chrooted_bug4219 {
627  my $self = shift;
628  my $tmpdir = $self->{tmpdir};
629  my $setup = test_setup($tmpdir, 'cmds');
630
631  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
632  mkpath($test_dir);
633
634  my $test_file = File::Spec->rel2abs("$test_dir/test.txt");
635  if (open(my $fh, "> $test_file")) {
636    unless (close($fh)) {
637      die("Can't write $test_file: $!");
638    }
639
640  } else {
641    die("Can't open $test_file: $!");
642  }
643
644  # Change to the test directory in order to create a relative path in the
645  # symlink we need
646
647  my $cwd = getcwd();
648  unless (chdir($test_dir)) {
649    die("Can't chdir to $test_dir: $!");
650  }
651
652  unless (symlink('./test.txt', './test.lnk')) {
653    die("Can't symlink 'test.lnk' to './test.txt': $!");
654  }
655
656  unless (chdir($cwd)) {
657    die("Can't chdir to $cwd: $!");
658  }
659
660  if ($< == 0) {
661    unless (chmod(0755, $test_dir)) {
662      die("Can't set perms on $test_dir to 0755: $!");
663    }
664
665    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
666      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
667    }
668  }
669
670  my $config = {
671    PidFile => $setup->{pid_file},
672    ScoreboardFile => $setup->{scoreboard_file},
673    SystemLog => $setup->{log_file},
674
675    AuthUserFile => $setup->{auth_user_file},
676    AuthGroupFile => $setup->{auth_group_file},
677
678    DefaultRoot => '~',
679    ShowSymlinks => 'off',
680
681    IfModules => {
682      'mod_delay.c' => {
683        DelayEngine => 'off',
684      },
685    },
686  };
687
688  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
689    $config);
690
691  # Open pipes, for use between the parent and child processes.  Specifically,
692  # the child will indicate when it's done with its test by writing a message
693  # to the parent.
694  my ($rfh, $wfh);
695  unless (pipe($rfh, $wfh)) {
696    die("Can't open pipe: $!");
697  }
698
699  my $ex;
700
701  # Fork child
702  $self->handle_sigchld();
703  defined(my $pid = fork()) or die("Can't fork: $!");
704  if ($pid) {
705    eval {
706      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
707      $client->login($setup->{user}, $setup->{passwd});
708      $client->stat('test.d/test.lnk');
709
710      my $resp_code = $client->response_code();
711      my $resp_msgs = $client->response_msgs();
712      my $resp_msg = $resp_msgs->[1];
713      $client->quit();
714
715      if ($ENV{TEST_VERBOSE}) {
716        use Data::Dumper;
717        print STDERR "# Response:\n", Dumper($resp_msgs), "\n";
718      }
719
720      my $expected = 213;
721      $self->assert($expected == $resp_code,
722        test_msg("Expected response code $expected, got $resp_code"));
723
724      $expected = '^(\s+)?\-\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
725      $self->assert(qr/$expected/, $resp_msg,
726        test_msg("Expected response message '$expected', got '$resp_msg'"));
727    };
728    if ($@) {
729      $ex = $@;
730    }
731
732    $wfh->print("done\n");
733    $wfh->flush();
734
735  } else {
736    eval { server_wait($setup->{config_file}, $rfh) };
737    if ($@) {
738      warn($@);
739      exit 1;
740    }
741
742    exit 0;
743  }
744
745  # Stop server
746  server_stop($setup->{pid_file});
747  $self->assert_child_ok($pid);
748
749  test_cleanup($setup->{log_file}, $ex);
750}
751
752sub stat_symlink_showsymlinks_on_chrooted_bug4219 {
753  my $self = shift;
754  my $tmpdir = $self->{tmpdir};
755  my $setup = test_setup($tmpdir, 'cmds');
756
757  my $test_dir = File::Spec->rel2abs("$tmpdir/test.d");
758  mkpath($test_dir);
759
760  my $test_file = File::Spec->rel2abs("$test_dir/test.txt");
761  if (open(my $fh, "> $test_file")) {
762    unless (close($fh)) {
763      die("Can't write $test_file: $!");
764    }
765
766  } else {
767    die("Can't open $test_file: $!");
768  }
769
770  # Change to the test directory in order to create a relative path in the
771  # symlink we need
772
773  my $cwd = getcwd();
774  unless (chdir($test_dir)) {
775    die("Can't chdir to $test_dir: $!");
776  }
777
778  unless (symlink('./test.txt', './test.lnk')) {
779    die("Can't symlink 'test.lnk' to './test.txt': $!");
780  }
781
782  unless (chdir($cwd)) {
783    die("Can't chdir to $cwd: $!");
784  }
785
786  if ($< == 0) {
787    unless (chmod(0755, $test_dir)) {
788      die("Can't set perms on $test_dir to 0755: $!");
789    }
790
791    unless (chown($setup->{uid}, $setup->{gid}, $test_dir)) {
792      die("Can't set owner of $test_dir to $setup->{uid}/$setup->{gid}: $!");
793    }
794  }
795
796  my $config = {
797    PidFile => $setup->{pid_file},
798    ScoreboardFile => $setup->{scoreboard_file},
799    SystemLog => $setup->{log_file},
800
801    AuthUserFile => $setup->{auth_user_file},
802    AuthGroupFile => $setup->{auth_group_file},
803
804    DefaultRoot => '~',
805    ShowSymlinks => 'on',
806
807    IfModules => {
808      'mod_delay.c' => {
809        DelayEngine => 'off',
810      },
811    },
812  };
813
814  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
815    $config);
816
817  # Open pipes, for use between the parent and child processes.  Specifically,
818  # the child will indicate when it's done with its test by writing a message
819  # to the parent.
820  my ($rfh, $wfh);
821  unless (pipe($rfh, $wfh)) {
822    die("Can't open pipe: $!");
823  }
824
825  my $ex;
826
827  # Fork child
828  $self->handle_sigchld();
829  defined(my $pid = fork()) or die("Can't fork: $!");
830  if ($pid) {
831    eval {
832      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
833      $client->login($setup->{user}, $setup->{passwd});
834      $client->stat('test.d/test.lnk');
835
836      my $resp_code = $client->response_code();
837      my $resp_msgs = $client->response_msgs();
838      my $resp_msg = $resp_msgs->[1];
839      $client->quit();
840
841      if ($ENV{TEST_VERBOSE}) {
842        use Data::Dumper;
843        print STDERR "# Response:\n", Dumper($resp_msgs), "\n";
844      }
845
846      my $expected = 213;
847      $self->assert($expected == $resp_code,
848        test_msg("Expected response code $expected, got $resp_code"));
849
850      $expected = '^(\s+)?l\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
851      $self->assert(qr/$expected/, $resp_msg,
852        test_msg("Expected response message '$expected', got '$resp_msg'"));
853
854      $expected = '.*?test\.d\/test\.lnk \-> \/test\.d\/test\.txt$';
855      $self->assert(qr/$expected/, $resp_msg,
856        test_msg("Expected response message '$expected', got '$resp_msg'"));
857    };
858    if ($@) {
859      $ex = $@;
860    }
861
862    $wfh->print("done\n");
863    $wfh->flush();
864
865  } else {
866    eval { server_wait($setup->{config_file}, $rfh) };
867    if ($@) {
868      warn($@);
869      exit 1;
870    }
871
872    exit 0;
873  }
874
875  # Stop server
876  server_stop($setup->{pid_file});
877  $self->assert_child_ok($pid);
878
879  test_cleanup($setup->{log_file}, $ex);
880}
881
882sub stat_system {
883  my $self = shift;
884  my $tmpdir = $self->{tmpdir};
885  my $setup = test_setup($tmpdir, 'cmds');
886
887  my $config = {
888    PidFile => $setup->{pid_file},
889    ScoreboardFile => $setup->{scoreboard_file},
890    SystemLog => $setup->{log_file},
891
892    AuthUserFile => $setup->{auth_user_file},
893    AuthGroupFile => $setup->{auth_group_file},
894
895    IfModules => {
896      'mod_delay.c' => {
897        DelayEngine => 'off',
898      },
899    },
900  };
901
902  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
903    $config);
904
905  # Open pipes, for use between the parent and child processes.  Specifically,
906  # the child will indicate when it's done with its test by writing a message
907  # to the parent.
908  my ($rfh, $wfh);
909  unless (pipe($rfh, $wfh)) {
910    die("Can't open pipe: $!");
911  }
912
913  my $ex;
914
915  # Fork child
916  $self->handle_sigchld();
917  defined(my $pid = fork()) or die("Can't fork: $!");
918  if ($pid) {
919    eval {
920      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
921      $client->login($setup->{user}, $setup->{passwd});
922      $client->stat();
923
924      my $resp_code = $client->response_code();
925      my $resp_msgs = $client->response_msgs();
926      $client->quit();
927
928      my $expected = 211;
929      $self->assert($expected == $resp_code,
930        test_msg("Expected response code $expected, got $resp_code"));
931    };
932    if ($@) {
933      $ex = $@;
934    }
935
936    $wfh->print("done\n");
937    $wfh->flush();
938
939  } else {
940    eval { server_wait($setup->{config_file}, $rfh) };
941    if ($@) {
942      warn($@);
943      exit 1;
944    }
945
946    exit 0;
947  }
948
949  # Stop server
950  server_stop($setup->{pid_file});
951  $self->assert_child_ok($pid);
952
953  test_cleanup($setup->{log_file}, $ex);
954}
955
956sub stat_enoent {
957  my $self = shift;
958  my $tmpdir = $self->{tmpdir};
959  my $setup = test_setup($tmpdir, 'cmds');
960
961  my $config = {
962    PidFile => $setup->{pid_file},
963    ScoreboardFile => $setup->{scoreboard_file},
964    SystemLog => $setup->{log_file},
965
966    AuthUserFile => $setup->{auth_user_file},
967    AuthGroupFile => $setup->{auth_group_file},
968
969    IfModules => {
970      'mod_delay.c' => {
971        DelayEngine => 'off',
972      },
973    },
974  };
975
976  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
977    $config);
978
979  # Open pipes, for use between the parent and child processes.  Specifically,
980  # the child will indicate when it's done with its test by writing a message
981  # to the parent.
982  my ($rfh, $wfh);
983  unless (pipe($rfh, $wfh)) {
984    die("Can't open pipe: $!");
985  }
986
987  my $ex;
988
989  # Fork child
990  $self->handle_sigchld();
991  defined(my $pid = fork()) or die("Can't fork: $!");
992  if ($pid) {
993    eval {
994      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
995      $client->login($setup->{user}, $setup->{passwd});
996
997      my $enoent_file = 'foo.bar.baz.d';
998
999      eval { $client->stat($enoent_file) };
1000      unless ($@) {
1001        die("STAT succeeded unexpectedly");
1002      }
1003
1004      my $resp_code = $client->response_code();
1005      my $resp_msg = $client->response_msg();
1006      $client->quit();
1007
1008      my $expected = 450;
1009      $self->assert($expected == $resp_code,
1010        test_msg("Expected response code $expected, got $resp_code"));
1011
1012      $expected = "$enoent_file: No such file or directory";
1013      $self->assert($expected eq $resp_msg,
1014        test_msg("Expected response message '$expected', got '$resp_msg'"));
1015    };
1016    if ($@) {
1017      $ex = $@;
1018    }
1019
1020    $wfh->print("done\n");
1021    $wfh->flush();
1022
1023  } else {
1024    eval { server_wait($setup->{config_file}, $rfh) };
1025    if ($@) {
1026      warn($@);
1027      exit 1;
1028    }
1029
1030    exit 0;
1031  }
1032
1033  # Stop server
1034  server_stop($setup->{pid_file});
1035  $self->assert_child_ok($pid);
1036
1037  test_cleanup($setup->{log_file}, $ex);
1038}
1039
1040sub stat_listoptions_bug3295 {
1041  my $self = shift;
1042  my $tmpdir = $self->{tmpdir};
1043  my $setup = test_setup($tmpdir, 'cmds');
1044
1045  my $config = {
1046    PidFile => $setup->{pid_file},
1047    ScoreboardFile => $setup->{scoreboard_file},
1048    SystemLog => $setup->{log_file},
1049
1050    AuthUserFile => $setup->{auth_user_file},
1051    AuthGroupFile => $setup->{auth_group_file},
1052
1053    ListOptions => '-1',
1054
1055    IfModules => {
1056      'mod_delay.c' => {
1057        DelayEngine => 'off',
1058      },
1059    },
1060  };
1061
1062  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1063    $config);
1064
1065  # Open pipes, for use between the parent and child processes.  Specifically,
1066  # the child will indicate when it's done with its test by writing a message
1067  # to the parent.
1068  my ($rfh, $wfh);
1069  unless (pipe($rfh, $wfh)) {
1070    die("Can't open pipe: $!");
1071  }
1072
1073  my $ex;
1074
1075  # Fork child
1076  $self->handle_sigchld();
1077  defined(my $pid = fork()) or die("Can't fork: $!");
1078  if ($pid) {
1079    eval {
1080      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
1081      $client->login($setup->{user}, $setup->{passwd});
1082
1083      my ($resp_code, $resp_msg) = $client->stat('cmds.conf');
1084      $client->quit();
1085
1086      my $expected = 213;
1087      $self->assert($expected == $resp_code,
1088        test_msg("Expected response code $expected, got $resp_code"));
1089
1090      $expected = '^(\s+)?\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$';
1091      $self->assert(qr/$expected/, $resp_msg,
1092        test_msg("Expected response message '$expected', got '$resp_msg'"));
1093    };
1094    if ($@) {
1095      $ex = $@;
1096    }
1097
1098    $wfh->print("done\n");
1099    $wfh->flush();
1100
1101  } else {
1102    eval { server_wait($setup->{config_file}, $rfh) };
1103    if ($@) {
1104      warn($@);
1105      exit 1;
1106    }
1107
1108    exit 0;
1109  }
1110
1111  # Stop server
1112  server_stop($setup->{pid_file});
1113  $self->assert_child_ok($pid);
1114
1115  test_cleanup($setup->{log_file}, $ex);
1116}
1117
11181;
1119