1package ProFTPD::Tests::Config::PathDenyFilter;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use File::Path qw(mkpath);
8use File::Spec;
9use IO::Handle;
10
11use ProFTPD::TestSuite::FTP;
12use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite);
13
14$| = 1;
15
16my $order = 0;
17
18my $TESTS = {
19  pathdenyfilter_dele_allowed => {
20    order => ++$order,
21    test_class => [qw(forking)],
22  },
23
24  pathdenyfilter_dele_denied => {
25    order => ++$order,
26    test_class => [qw(forking)],
27  },
28
29  pathdenyfilter_mkd_allowed => {
30    order => ++$order,
31    test_class => [qw(forking)],
32  },
33
34  pathdenyfilter_mkd_denied => {
35    order => ++$order,
36    test_class => [qw(forking)],
37  },
38
39  pathdenyfilter_rmd_allowed => {
40    order => ++$order,
41    test_class => [qw(forking)],
42  },
43
44  pathdenyfilter_rmd_denied => {
45    order => ++$order,
46    test_class => [qw(forking)],
47  },
48
49  pathdenyfilter_rnfr_allowed => {
50    order => ++$order,
51    test_class => [qw(forking)],
52  },
53
54  pathdenyfilter_rnfr_denied => {
55    order => ++$order,
56    test_class => [qw(forking)],
57  },
58
59  pathdenyfilter_rnto_allowed => {
60    order => ++$order,
61    test_class => [qw(forking)],
62  },
63
64  pathdenyfilter_rnto_denied => {
65    order => ++$order,
66    test_class => [qw(forking)],
67  },
68
69  pathdenyfilter_stor_allowed => {
70    order => ++$order,
71    test_class => [qw(forking)],
72  },
73
74  pathdenyfilter_stor_denied => {
75    order => ++$order,
76    test_class => [qw(forking)],
77  },
78
79  pathdenyfilter_stor_denied_bug3032 => {
80    order => ++$order,
81    test_class => [qw(bug forking)],
82  },
83
84  pathdenyfilter_stor_trailing_space => {
85    order => ++$order,
86    test_class => [qw(forking)],
87  },
88
89  pathdenyfilter_stor_leading_space => {
90    order => ++$order,
91    test_class => [qw(forking)],
92  },
93
94  pathdenyfilter_stor_leading_trailing_space => {
95    order => ++$order,
96    test_class => [qw(forking)],
97  },
98
99  pathdenyfilter_stor_denied_nocase_bug3592 => {
100    order => ++$order,
101    test_class => [qw(bug forking)],
102  },
103
104  pathdenyfilter_stor_dotfiles_denied => {
105    order => ++$order,
106    test_class => [qw(forking)],
107  },
108
109};
110
111sub new {
112  return shift()->SUPER::new(@_);
113}
114
115sub list_tests {
116  return testsuite_get_runnable_tests($TESTS);
117}
118
119sub pathdenyfilter_dele_allowed {
120  my $self = shift;
121  my $tmpdir = $self->{tmpdir};
122  my $setup = test_setup($tmpdir, 'config');
123
124  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
125  if (open(my $fh, "> $test_file")) {
126    close($fh);
127
128  } else {
129    die("Can't open $test_file: $!");
130  }
131
132  my $config = {
133    PidFile => $setup->{pid_file},
134    ScoreboardFile => $setup->{scoreboard_file},
135    SystemLog => $setup->{log_file},
136
137    AuthUserFile => $setup->{auth_user_file},
138    AuthGroupFile => $setup->{auth_group_file},
139    DefaultChdir => '~',
140
141    AllowOverwrite => 'on',
142    PathDenyFilter => '\.tmp$',
143
144    IfModules => {
145      'mod_delay.c' => {
146        DelayEngine => 'off',
147      },
148    },
149  };
150
151  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
152    $config);
153
154  # Open pipes, for use between the parent and child processes.  Specifically,
155  # the child will indicate when it's done with its test by writing a message
156  # to the parent.
157  my ($rfh, $wfh);
158  unless (pipe($rfh, $wfh)) {
159    die("Can't open pipe: $!");
160  }
161
162  my $ex;
163
164  # Fork child
165  $self->handle_sigchld();
166  defined(my $pid = fork()) or die("Can't fork: $!");
167  if ($pid) {
168    eval {
169      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
170      $client->login($setup->{user}, $setup->{passwd});
171
172      my ($resp_code, $resp_msg) = $client->dele('test.txt');
173
174      my $expected = 250;
175      $self->assert($expected == $resp_code,
176        test_msg("Expected response code $expected, got $resp_code"));
177
178      $expected = 'DELE command successful';
179      $self->assert($expected eq $resp_msg,
180        test_msg("Expected response message '$expected', got '$resp_msg'"));
181
182      $client->quit();
183    };
184    if ($@) {
185      $ex = $@;
186    }
187
188    $wfh->print("done\n");
189    $wfh->flush();
190
191  } else {
192    eval { server_wait($setup->{config_file}, $rfh) };
193    if ($@) {
194      warn($@);
195      exit 1;
196    }
197
198    exit 0;
199  }
200
201  # Stop server
202  server_stop($setup->{pid_file});
203  $self->assert_child_ok($pid);
204
205  test_cleanup($setup->{log_file}, $ex);
206}
207
208sub pathdenyfilter_dele_denied {
209  my $self = shift;
210  my $tmpdir = $self->{tmpdir};
211  my $setup = test_setup($tmpdir, 'config');
212
213  my $config = {
214    PidFile => $setup->{pid_file},
215    ScoreboardFile => $setup->{scoreboard_file},
216    SystemLog => $setup->{log_file},
217
218    AuthUserFile => $setup->{auth_user_file},
219    AuthGroupFile => $setup->{auth_group_file},
220    DefaultChdir => '~',
221
222    PathDenyFilter => '\.txt$',
223
224    IfModules => {
225      'mod_delay.c' => {
226        DelayEngine => 'off',
227      },
228    },
229  };
230
231  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
232    $config);
233
234  # Open pipes, for use between the parent and child processes.  Specifically,
235  # the child will indicate when it's done with its test by writing a message
236  # to the parent.
237  my ($rfh, $wfh);
238  unless (pipe($rfh, $wfh)) {
239    die("Can't open pipe: $!");
240  }
241
242  my $ex;
243
244  # Fork child
245  $self->handle_sigchld();
246  defined(my $pid = fork()) or die("Can't fork: $!");
247  if ($pid) {
248    eval {
249      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
250      $client->login($setup->{user}, $setup->{passwd});
251
252      my $filename = 'test.txt';
253
254      eval { $client->dele($filename) };
255      unless ($@) {
256        die("DELE $filename succeeded unexpectedly");
257      }
258
259      my $resp_code = $client->response_code();
260      my $resp_msg = $client->response_msg();
261
262      my $expected = 550;
263      $self->assert($expected == $resp_code,
264        test_msg("Expected response code $expected, got $resp_code"));
265
266      $expected = "$filename: Forbidden filename";
267      $self->assert($expected eq $resp_msg,
268        test_msg("Expected response message '$expected', got '$resp_msg'"));
269
270      $client->quit();
271    };
272    if ($@) {
273      $ex = $@;
274    }
275
276    $wfh->print("done\n");
277    $wfh->flush();
278
279  } else {
280    eval { server_wait($setup->{config_file}, $rfh) };
281    if ($@) {
282      warn($@);
283      exit 1;
284    }
285
286    exit 0;
287  }
288
289  # Stop server
290  server_stop($setup->{pid_file});
291  $self->assert_child_ok($pid);
292
293  test_cleanup($setup->{log_file}, $ex);
294}
295
296sub pathdenyfilter_mkd_allowed {
297  my $self = shift;
298  my $tmpdir = $self->{tmpdir};
299  my $setup = test_setup($tmpdir, 'config');
300
301  my $path = File::Spec->rel2abs("$tmpdir/subdir.d");
302
303  my $config = {
304    PidFile => $setup->{pid_file},
305    ScoreboardFile => $setup->{scoreboard_file},
306    SystemLog => $setup->{log_file},
307
308    AuthUserFile => $setup->{auth_user_file},
309    AuthGroupFile => $setup->{auth_group_file},
310    DefaultChdir => '~',
311
312    AllowOverwrite => 'on',
313    PathDenyFilter => '\.dir$',
314
315    IfModules => {
316      'mod_delay.c' => {
317        DelayEngine => 'off',
318      },
319    },
320  };
321
322  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
323    $config);
324
325  # Open pipes, for use between the parent and child processes.  Specifically,
326  # the child will indicate when it's done with its test by writing a message
327  # to the parent.
328  my ($rfh, $wfh);
329  unless (pipe($rfh, $wfh)) {
330    die("Can't open pipe: $!");
331  }
332
333  my $ex;
334
335  # Fork child
336  $self->handle_sigchld();
337  defined(my $pid = fork()) or die("Can't fork: $!");
338  if ($pid) {
339    eval {
340      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
341      $client->login($setup->{user}, $setup->{passwd});
342
343      my ($resp_code, $resp_msg) = $client->mkd('subdir.d');
344
345      my $expected = 257;
346      $self->assert($expected == $resp_code,
347        test_msg("Expected response code $expected, got $resp_code"));
348
349      # Mac OSX hack
350      if ($^O eq 'darwin') {
351        $path = '/private' . $path;
352      }
353
354      $expected = "\"$path\" - Directory successfully created";
355      $self->assert($expected eq $resp_msg,
356        test_msg("Expected response message '$expected', got '$resp_msg'"));
357
358      $client->quit();
359    };
360    if ($@) {
361      $ex = $@;
362    }
363
364    $wfh->print("done\n");
365    $wfh->flush();
366
367  } else {
368    eval { server_wait($setup->{config_file}, $rfh) };
369    if ($@) {
370      warn($@);
371      exit 1;
372    }
373
374    exit 0;
375  }
376
377  # Stop server
378  server_stop($setup->{pid_file});
379  $self->assert_child_ok($pid);
380
381  test_cleanup($setup->{log_file}, $ex);
382}
383
384sub pathdenyfilter_mkd_denied {
385  my $self = shift;
386  my $tmpdir = $self->{tmpdir};
387  my $setup = test_setup($tmpdir, 'config');
388
389  my $config = {
390    PidFile => $setup->{pid_file},
391    ScoreboardFile => $setup->{scoreboard_file},
392    SystemLog => $setup->{log_file},
393
394    AuthUserFile => $setup->{auth_user_file},
395    AuthGroupFile => $setup->{auth_group_file},
396    DefaultChdir => '~',
397
398    PathDenyFilter => '\.dir$',
399
400    IfModules => {
401      'mod_delay.c' => {
402        DelayEngine => 'off',
403      },
404    },
405  };
406
407  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
408    $config);
409
410  # Open pipes, for use between the parent and child processes.  Specifically,
411  # the child will indicate when it's done with its test by writing a message
412  # to the parent.
413  my ($rfh, $wfh);
414  unless (pipe($rfh, $wfh)) {
415    die("Can't open pipe: $!");
416  }
417
418  my $ex;
419
420  # Fork child
421  $self->handle_sigchld();
422  defined(my $pid = fork()) or die("Can't fork: $!");
423  if ($pid) {
424    eval {
425      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
426      $client->login($setup->{user}, $setup->{passwd});
427
428      my $dirname = 'subdir.dir';
429
430      eval { $client->mkd($dirname) };
431      unless ($@) {
432        die("MKD $dirname succeeded unexpectedly");
433      }
434
435      my $resp_code = $client->response_code();
436      my $resp_msg = $client->response_msg();
437
438      my $expected = 550;
439      $self->assert($expected == $resp_code,
440        test_msg("Expected response code $expected, got $resp_code"));
441
442      $expected = "$dirname: Forbidden filename";
443      $self->assert($expected eq $resp_msg,
444        test_msg("Expected response message '$expected', got '$resp_msg'"));
445
446      $client->quit();
447    };
448    if ($@) {
449      $ex = $@;
450    }
451
452    $wfh->print("done\n");
453    $wfh->flush();
454
455  } else {
456    eval { server_wait($setup->{config_file}, $rfh) };
457    if ($@) {
458      warn($@);
459      exit 1;
460    }
461
462    exit 0;
463  }
464
465  # Stop server
466  server_stop($setup->{pid_file});
467  $self->assert_child_ok($pid);
468
469  test_cleanup($setup->{log_file}, $ex);
470}
471
472sub pathdenyfilter_rmd_allowed {
473  my $self = shift;
474  my $tmpdir = $self->{tmpdir};
475  my $setup = test_setup($tmpdir, 'config');
476
477  mkpath("$tmpdir/subdir.d");
478
479  my $config = {
480    PidFile => $setup->{pid_file},
481    ScoreboardFile => $setup->{scoreboard_file},
482    SystemLog => $setup->{log_file},
483
484    AuthUserFile => $setup->{auth_user_file},
485    AuthGroupFile => $setup->{auth_group_file},
486    DefaultChdir => '~',
487
488    AllowOverwrite => 'on',
489    PathDenyFilter => '\.dir$',
490
491    IfModules => {
492      'mod_delay.c' => {
493        DelayEngine => 'off',
494      },
495    },
496  };
497
498  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
499    $config);
500
501  # Open pipes, for use between the parent and child processes.  Specifically,
502  # the child will indicate when it's done with its test by writing a message
503  # to the parent.
504  my ($rfh, $wfh);
505  unless (pipe($rfh, $wfh)) {
506    die("Can't open pipe: $!");
507  }
508
509  my $ex;
510
511  # Fork child
512  $self->handle_sigchld();
513  defined(my $pid = fork()) or die("Can't fork: $!");
514  if ($pid) {
515    eval {
516      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
517      $client->login($setup->{user}, $setup->{passwd});
518
519      my ($resp_code, $resp_msg) = $client->rmd('subdir.d');
520
521      my $expected = 250;
522      $self->assert($expected == $resp_code,
523        test_msg("Expected response code $expected, got $resp_code"));
524
525      $expected = 'RMD command successful';
526      $self->assert($expected eq $resp_msg,
527        test_msg("Expected response message '$expected', got '$resp_msg'"));
528
529      $client->quit();
530    };
531    if ($@) {
532      $ex = $@;
533    }
534
535    $wfh->print("done\n");
536    $wfh->flush();
537
538  } else {
539    eval { server_wait($setup->{config_file}, $rfh) };
540    if ($@) {
541      warn($@);
542      exit 1;
543    }
544
545    exit 0;
546  }
547
548  # Stop server
549  server_stop($setup->{pid_file});
550  $self->assert_child_ok($pid);
551
552  test_cleanup($setup->{log_file}, $ex);
553}
554
555sub pathdenyfilter_rmd_denied {
556  my $self = shift;
557  my $tmpdir = $self->{tmpdir};
558  my $setup = test_setup($tmpdir, 'config');
559
560  my $config = {
561    PidFile => $setup->{pid_file},
562    ScoreboardFile => $setup->{scoreboard_file},
563    SystemLog => $setup->{log_file},
564
565    AuthUserFile => $setup->{auth_user_file},
566    AuthGroupFile => $setup->{auth_group_file},
567    DefaultChdir => '~',
568
569    PathDenyFilter => '\.dir$',
570
571    IfModules => {
572      'mod_delay.c' => {
573        DelayEngine => 'off',
574      },
575    },
576  };
577
578  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
579    $config);
580
581  # Open pipes, for use between the parent and child processes.  Specifically,
582  # the child will indicate when it's done with its test by writing a message
583  # to the parent.
584  my ($rfh, $wfh);
585  unless (pipe($rfh, $wfh)) {
586    die("Can't open pipe: $!");
587  }
588
589  my $ex;
590
591  # Fork child
592  $self->handle_sigchld();
593  defined(my $pid = fork()) or die("Can't fork: $!");
594  if ($pid) {
595    eval {
596      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
597      $client->login($setup->{user}, $setup->{passwd});
598
599      my $dirname = 'subdir.dir';
600
601      eval { $client->rmd($dirname) };
602      unless ($@) {
603        die("RMD $dirname succeeded unexpectedly");
604      }
605
606      my $resp_code = $client->response_code();
607      my $resp_msg = $client->response_msg();
608
609      my $expected = 550;
610      $self->assert($expected == $resp_code,
611        test_msg("Expected response code $expected, got $resp_code"));
612
613      $expected = "$dirname: Forbidden filename";
614      $self->assert($expected eq $resp_msg,
615        test_msg("Expected response message '$expected', got '$resp_msg'"));
616
617      $client->quit();
618    };
619    if ($@) {
620      $ex = $@;
621    }
622
623    $wfh->print("done\n");
624    $wfh->flush();
625
626  } else {
627    eval { server_wait($setup->{config_file}, $rfh) };
628    if ($@) {
629      warn($@);
630      exit 1;
631    }
632
633    exit 0;
634  }
635
636  # Stop server
637  server_stop($setup->{pid_file});
638  $self->assert_child_ok($pid);
639
640  test_cleanup($setup->{log_file}, $ex);
641}
642
643sub pathdenyfilter_rnfr_allowed {
644  my $self = shift;
645  my $tmpdir = $self->{tmpdir};
646  my $setup = test_setup($tmpdir, 'config');
647
648  mkpath("$tmpdir/subdir.d");
649
650  my $config = {
651    PidFile => $setup->{pid_file},
652    ScoreboardFile => $setup->{scoreboard_file},
653    SystemLog => $setup->{log_file},
654
655    AuthUserFile => $setup->{auth_user_file},
656    AuthGroupFile => $setup->{auth_group_file},
657    DefaultChdir => '~',
658
659    AllowOverwrite => 'on',
660    PathDenyFilter => '\.dir$',
661
662    IfModules => {
663      'mod_delay.c' => {
664        DelayEngine => 'off',
665      },
666    },
667  };
668
669  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
670    $config);
671
672  # Open pipes, for use between the parent and child processes.  Specifically,
673  # the child will indicate when it's done with its test by writing a message
674  # to the parent.
675  my ($rfh, $wfh);
676  unless (pipe($rfh, $wfh)) {
677    die("Can't open pipe: $!");
678  }
679
680  my $ex;
681
682  # Fork child
683  $self->handle_sigchld();
684  defined(my $pid = fork()) or die("Can't fork: $!");
685  if ($pid) {
686    eval {
687      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
688      $client->login($setup->{user}, $setup->{passwd});
689
690      my ($resp_code, $resp_msg) = $client->rnfr('subdir.d');
691
692      my $expected = 350;
693      $self->assert($expected == $resp_code,
694        test_msg("Expected response code $expected, got $resp_code"));
695
696      $expected = 'File or directory exists, ready for destination name';
697      $self->assert($expected eq $resp_msg,
698        test_msg("Expected response message '$expected', got '$resp_msg'"));
699
700      $client->quit();
701    };
702    if ($@) {
703      $ex = $@;
704    }
705
706    $wfh->print("done\n");
707    $wfh->flush();
708
709  } else {
710    eval { server_wait($setup->{config_file}, $rfh) };
711    if ($@) {
712      warn($@);
713      exit 1;
714    }
715
716    exit 0;
717  }
718
719  # Stop server
720  server_stop($setup->{pid_file});
721  $self->assert_child_ok($pid);
722
723  test_cleanup($setup->{log_file}, $ex);
724}
725
726sub pathdenyfilter_rnfr_denied {
727  my $self = shift;
728  my $tmpdir = $self->{tmpdir};
729  my $setup = test_setup($tmpdir, 'config');
730
731  my $config = {
732    PidFile => $setup->{pid_file},
733    ScoreboardFile => $setup->{scoreboard_file},
734    SystemLog => $setup->{log_file},
735
736    AuthUserFile => $setup->{auth_user_file},
737    AuthGroupFile => $setup->{auth_group_file},
738    DefaultChdir => '~',
739
740    PathDenyFilter => '\.d$',
741
742    IfModules => {
743      'mod_delay.c' => {
744        DelayEngine => 'off',
745      },
746    },
747  };
748
749  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
750    $config);
751
752  # Open pipes, for use between the parent and child processes.  Specifically,
753  # the child will indicate when it's done with its test by writing a message
754  # to the parent.
755  my ($rfh, $wfh);
756  unless (pipe($rfh, $wfh)) {
757    die("Can't open pipe: $!");
758  }
759
760  my $ex;
761
762  # Fork child
763  $self->handle_sigchld();
764  defined(my $pid = fork()) or die("Can't fork: $!");
765  if ($pid) {
766    eval {
767      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
768      $client->login($setup->{user}, $setup->{passwd});
769
770      my $dirname = 'subdir.d';
771
772      eval { $client->rnfr($dirname) };
773      unless ($@) {
774        die("RNFR $dirname succeeded unexpectedly");
775      }
776
777      my $resp_code = $client->response_code();
778      my $resp_msg = $client->response_msg();
779
780      my $expected = 550;
781      $self->assert($expected == $resp_code,
782        test_msg("Expected response code $expected, got $resp_code"));
783
784      $expected = "$dirname: Forbidden filename";
785      $self->assert($expected eq $resp_msg,
786        test_msg("Expected response message '$expected', got '$resp_msg'"));
787
788      $client->quit();
789    };
790    if ($@) {
791      $ex = $@;
792    }
793
794    $wfh->print("done\n");
795    $wfh->flush();
796
797  } else {
798    eval { server_wait($setup->{config_file}, $rfh) };
799    if ($@) {
800      warn($@);
801      exit 1;
802    }
803
804    exit 0;
805  }
806
807  # Stop server
808  server_stop($setup->{pid_file});
809  $self->assert_child_ok($pid);
810
811  test_cleanup($setup->{log_file}, $ex);
812}
813
814sub pathdenyfilter_rnto_allowed {
815  my $self = shift;
816  my $tmpdir = $self->{tmpdir};
817  my $setup = test_setup($tmpdir, 'config');
818
819  my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir.d");
820  mkpath($sub_dir);
821
822  if ($< == 0) {
823    unless (chown($setup->{uid}, $setup->{gid}, $sub_dir)) {
824      die("Can't set owner of $sub_dir to $setup->{uid}/$setup->{gid}: $!");
825    }
826  }
827
828  my $config = {
829    PidFile => $setup->{pid_file},
830    ScoreboardFile => $setup->{scoreboard_file},
831    SystemLog => $setup->{log_file},
832
833    AuthUserFile => $setup->{auth_user_file},
834    AuthGroupFile => $setup->{auth_group_file},
835    DefaultChdir => '~',
836
837    AllowOverwrite => 'on',
838    PathDenyFilter => '\.dir$',
839
840    IfModules => {
841      'mod_delay.c' => {
842        DelayEngine => 'off',
843      },
844    },
845  };
846
847  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
848    $config);
849
850  # Open pipes, for use between the parent and child processes.  Specifically,
851  # the child will indicate when it's done with its test by writing a message
852  # to the parent.
853  my ($rfh, $wfh);
854  unless (pipe($rfh, $wfh)) {
855    die("Can't open pipe: $!");
856  }
857
858  my $ex;
859
860  # Fork child
861  $self->handle_sigchld();
862  defined(my $pid = fork()) or die("Can't fork: $!");
863  if ($pid) {
864    eval {
865      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
866      $client->login($setup->{user}, $setup->{passwd});
867
868      $client->rnfr('subdir.d');
869      my ($resp_code, $resp_msg) = $client->rnto('foo.d');
870
871      my $expected = 250;
872      $self->assert($expected == $resp_code,
873        test_msg("Expected response code $expected, got $resp_code"));
874
875      $expected = 'Rename successful';
876      $self->assert($expected eq $resp_msg,
877        test_msg("Expected response message '$expected', got '$resp_msg'"));
878
879      $client->quit();
880    };
881    if ($@) {
882      $ex = $@;
883    }
884
885    $wfh->print("done\n");
886    $wfh->flush();
887
888  } else {
889    eval { server_wait($setup->{config_file}, $rfh) };
890    if ($@) {
891      warn($@);
892      exit 1;
893    }
894
895    exit 0;
896  }
897
898  # Stop server
899  server_stop($setup->{pid_file});
900  $self->assert_child_ok($pid);
901
902  test_cleanup($setup->{log_file}, $ex);
903}
904
905sub pathdenyfilter_rnto_denied {
906  my $self = shift;
907  my $tmpdir = $self->{tmpdir};
908  my $setup = test_setup($tmpdir, 'config');
909
910  mkpath("$tmpdir/subdir.d");
911
912  my $config = {
913    PidFile => $setup->{pid_file},
914    ScoreboardFile => $setup->{scoreboard_file},
915    SystemLog => $setup->{log_file},
916
917    AuthUserFile => $setup->{auth_user_file},
918    AuthGroupFile => $setup->{auth_group_file},
919    DefaultChdir => '~',
920
921    PathDenyFilter => '\.dir$',
922
923    IfModules => {
924      'mod_delay.c' => {
925        DelayEngine => 'off',
926      },
927    },
928  };
929
930  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
931    $config);
932
933  # Open pipes, for use between the parent and child processes.  Specifically,
934  # the child will indicate when it's done with its test by writing a message
935  # to the parent.
936  my ($rfh, $wfh);
937  unless (pipe($rfh, $wfh)) {
938    die("Can't open pipe: $!");
939  }
940
941  my $ex;
942
943  # Fork child
944  $self->handle_sigchld();
945  defined(my $pid = fork()) or die("Can't fork: $!");
946  if ($pid) {
947    eval {
948      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
949      $client->login($setup->{user}, $setup->{passwd});
950
951      $client->rnfr('subdir.d');
952      my $dirname = 'foo.dir';
953
954      eval { $client->rnto($dirname) };
955      unless ($@) {
956        die("RNTO $dirname succeeded unexpectedly");
957      }
958
959      my $resp_code = $client->response_code();
960      my $resp_msg = $client->response_msg();
961
962      my $expected = 550;
963      $self->assert($expected == $resp_code,
964        test_msg("Expected response code $expected, got $resp_code"));
965
966      $expected = "$dirname: Forbidden filename";
967      $self->assert($expected eq $resp_msg,
968        test_msg("Expected response message '$expected', got '$resp_msg'"));
969
970      $client->quit();
971    };
972    if ($@) {
973      $ex = $@;
974    }
975
976    $wfh->print("done\n");
977    $wfh->flush();
978
979  } else {
980    eval { server_wait($setup->{config_file}, $rfh) };
981    if ($@) {
982      warn($@);
983      exit 1;
984    }
985
986    exit 0;
987  }
988
989  # Stop server
990  server_stop($setup->{pid_file});
991  $self->assert_child_ok($pid);
992
993  test_cleanup($setup->{log_file}, $ex);
994}
995
996sub pathdenyfilter_stor_allowed {
997  my $self = shift;
998  my $tmpdir = $self->{tmpdir};
999  my $setup = test_setup($tmpdir, 'config');
1000
1001  my $config = {
1002    PidFile => $setup->{pid_file},
1003    ScoreboardFile => $setup->{scoreboard_file},
1004    SystemLog => $setup->{log_file},
1005
1006    AuthUserFile => $setup->{auth_user_file},
1007    AuthGroupFile => $setup->{auth_group_file},
1008    DefaultChdir => '~',
1009
1010    AllowOverwrite => 'on',
1011    PathDenyFilter => '\.jpg$',
1012
1013    IfModules => {
1014      'mod_delay.c' => {
1015        DelayEngine => 'off',
1016      },
1017    },
1018  };
1019
1020  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1021    $config);
1022
1023  # Open pipes, for use between the parent and child processes.  Specifically,
1024  # the child will indicate when it's done with its test by writing a message
1025  # to the parent.
1026  my ($rfh, $wfh);
1027  unless (pipe($rfh, $wfh)) {
1028    die("Can't open pipe: $!");
1029  }
1030
1031  my $ex;
1032
1033  # Fork child
1034  $self->handle_sigchld();
1035  defined(my $pid = fork()) or die("Can't fork: $!");
1036  if ($pid) {
1037    eval {
1038      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1039      $client->login($setup->{user}, $setup->{passwd});
1040
1041      my $conn = $client->stor_raw('test.txt');
1042      unless ($conn) {
1043        die("Failed to STOR test.txt: " . $client->response_code() . " " .
1044          $client->response_msg());
1045      }
1046
1047      my $buf = "Hello, World!\n";
1048      $conn->write($buf, length($buf));
1049      $conn->close();
1050
1051      my $resp_code = $client->response_code();
1052      my $resp_msg = $client->response_msg();
1053
1054      my $expected = 226;
1055      $self->assert($expected == $resp_code,
1056        test_msg("Expected response code $expected, got $resp_code"));
1057
1058      $expected = 'Transfer complete';
1059      $self->assert($expected eq $resp_msg,
1060        test_msg("Expected response message '$expected', got '$resp_msg'"));
1061
1062      $client->quit();
1063    };
1064    if ($@) {
1065      $ex = $@;
1066    }
1067
1068    $wfh->print("done\n");
1069    $wfh->flush();
1070
1071  } else {
1072    eval { server_wait($setup->{config_file}, $rfh) };
1073    if ($@) {
1074      warn($@);
1075      exit 1;
1076    }
1077
1078    exit 0;
1079  }
1080
1081  # Stop server
1082  server_stop($setup->{pid_file});
1083  $self->assert_child_ok($pid);
1084
1085  test_cleanup($setup->{log_file}, $ex);
1086}
1087
1088sub pathdenyfilter_stor_denied {
1089  my $self = shift;
1090  my $tmpdir = $self->{tmpdir};
1091  my $setup = test_setup($tmpdir, 'config');
1092
1093  my $config = {
1094    PidFile => $setup->{pid_file},
1095    ScoreboardFile => $setup->{scoreboard_file},
1096    SystemLog => $setup->{log_file},
1097
1098    AuthUserFile => $setup->{auth_user_file},
1099    AuthGroupFile => $setup->{auth_group_file},
1100    DefaultChdir => '~',
1101
1102    AllowOverwrite => 'on',
1103    PathDenyFilter => '\.jpg$',
1104
1105    IfModules => {
1106      'mod_delay.c' => {
1107        DelayEngine => 'off',
1108      },
1109    },
1110  };
1111
1112  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1113    $config);
1114
1115  # Open pipes, for use between the parent and child processes.  Specifically,
1116  # the child will indicate when it's done with its test by writing a message
1117  # to the parent.
1118  my ($rfh, $wfh);
1119  unless (pipe($rfh, $wfh)) {
1120    die("Can't open pipe: $!");
1121  }
1122
1123  my $ex;
1124
1125  # Fork child
1126  $self->handle_sigchld();
1127  defined(my $pid = fork()) or die("Can't fork: $!");
1128  if ($pid) {
1129    eval {
1130      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1131      $client->login($setup->{user}, $setup->{passwd});
1132
1133      my $filename = 'test.jpg';
1134
1135      my $conn = $client->stor_raw($filename);
1136      if ($conn) {
1137        die("STOR test.txt succeeded unexpectedly");
1138      }
1139
1140      my $resp_code = $client->response_code();
1141      my $resp_msg = $client->response_msg();
1142
1143      my $expected = 550;
1144      $self->assert($expected == $resp_code,
1145        test_msg("Expected response code $expected, got $resp_code"));
1146
1147      $expected = "$filename: Forbidden filename";
1148      $self->assert($expected eq $resp_msg,
1149        test_msg("Expected response message '$expected', got '$resp_msg'"));
1150
1151      $client->quit();
1152    };
1153    if ($@) {
1154      $ex = $@;
1155    }
1156
1157    $wfh->print("done\n");
1158    $wfh->flush();
1159
1160  } else {
1161    eval { server_wait($setup->{config_file}, $rfh) };
1162    if ($@) {
1163      warn($@);
1164      exit 1;
1165    }
1166
1167    exit 0;
1168  }
1169
1170  # Stop server
1171  server_stop($setup->{pid_file});
1172  $self->assert_child_ok($pid);
1173
1174  test_cleanup($setup->{log_file}, $ex);
1175}
1176
1177sub pathdenyfilter_stor_denied_bug3032 {
1178  my $self = shift;
1179  my $tmpdir = $self->{tmpdir};
1180  my $setup = test_setup($tmpdir, 'config');
1181
1182  my $config = {
1183    PidFile => $setup->{pid_file},
1184    ScoreboardFile => $setup->{scoreboard_file},
1185    SystemLog => $setup->{log_file},
1186
1187    AuthUserFile => $setup->{auth_user_file},
1188    AuthGroupFile => $setup->{auth_group_file},
1189    DefaultChdir => '~',
1190
1191    AllowOverwrite => 'on',
1192    PathDenyFilter => '[^[:print:]]',
1193
1194    IfModules => {
1195      'mod_delay.c' => {
1196        DelayEngine => 'off',
1197      },
1198    },
1199  };
1200
1201  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1202    $config);
1203
1204  # Open pipes, for use between the parent and child processes.  Specifically,
1205  # the child will indicate when it's done with its test by writing a message
1206  # to the parent.
1207  my ($rfh, $wfh);
1208  unless (pipe($rfh, $wfh)) {
1209    die("Can't open pipe: $!");
1210  }
1211
1212  my $ex;
1213
1214  # Fork child
1215  $self->handle_sigchld();
1216  defined(my $pid = fork()) or die("Can't fork: $!");
1217  if ($pid) {
1218    eval {
1219      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1220      $client->login($setup->{user}, $setup->{passwd});
1221
1222      my $filename = "test\b";
1223
1224      my $conn = $client->stor_raw($filename);
1225      if ($conn) {
1226        die("STOR test.txt succeeded unexpectedly");
1227      }
1228
1229      my $resp_code = $client->response_code();
1230      my $resp_msg = $client->response_msg();
1231
1232      my $expected = 550;
1233      $self->assert($expected == $resp_code,
1234        test_msg("Expected response code $expected, got $resp_code"));
1235
1236      $expected = "$filename: Forbidden filename";
1237      $self->assert($expected eq $resp_msg,
1238        test_msg("Expected response message '$expected', got '$resp_msg'"));
1239
1240      $client->quit();
1241    };
1242    if ($@) {
1243      $ex = $@;
1244    }
1245
1246    $wfh->print("done\n");
1247    $wfh->flush();
1248
1249  } else {
1250    eval { server_wait($setup->{config_file}, $rfh) };
1251    if ($@) {
1252      warn($@);
1253      exit 1;
1254    }
1255
1256    exit 0;
1257  }
1258
1259  # Stop server
1260  server_stop($setup->{pid_file});
1261  $self->assert_child_ok($pid);
1262
1263  test_cleanup($setup->{log_file}, $ex);
1264}
1265
1266sub pathdenyfilter_stor_trailing_space {
1267  my $self = shift;
1268  my $tmpdir = $self->{tmpdir};
1269  my $setup = test_setup($tmpdir, 'config');
1270
1271  my $config = {
1272    PidFile => $setup->{pid_file},
1273    ScoreboardFile => $setup->{scoreboard_file},
1274    SystemLog => $setup->{log_file},
1275
1276    AuthUserFile => $setup->{auth_user_file},
1277    AuthGroupFile => $setup->{auth_group_file},
1278    DefaultChdir => '~',
1279
1280    AllowOverwrite => 'on',
1281    PathDenyFilter => '"\.* $"',
1282
1283    IfModules => {
1284      'mod_delay.c' => {
1285        DelayEngine => 'off',
1286      },
1287    },
1288  };
1289
1290  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1291    $config);
1292
1293  # Open pipes, for use between the parent and child processes.  Specifically,
1294  # the child will indicate when it's done with its test by writing a message
1295  # to the parent.
1296  my ($rfh, $wfh);
1297  unless (pipe($rfh, $wfh)) {
1298    die("Can't open pipe: $!");
1299  }
1300
1301  my $ex;
1302
1303  # Fork child
1304  $self->handle_sigchld();
1305  defined(my $pid = fork()) or die("Can't fork: $!");
1306  if ($pid) {
1307    eval {
1308      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1309      $client->login($setup->{user}, $setup->{passwd});
1310
1311      my $filename = 'test ';
1312
1313      my $conn = $client->stor_raw($filename);
1314      if ($conn) {
1315        die("STOR $filename succeeded unexpectedly");
1316      }
1317
1318      my $resp_code = $client->response_code();
1319      my $resp_msg = $client->response_msg();
1320
1321      my $expected = 550;
1322      $self->assert($expected == $resp_code,
1323        test_msg("Expected response code $expected, got $resp_code"));
1324
1325      $expected = "$filename: Forbidden filename";
1326      $self->assert($expected eq $resp_msg,
1327        test_msg("Expected response message '$expected', got '$resp_msg'"));
1328
1329      $client->quit();
1330    };
1331    if ($@) {
1332      $ex = $@;
1333    }
1334
1335    $wfh->print("done\n");
1336    $wfh->flush();
1337
1338  } else {
1339    eval { server_wait($setup->{config_file}, $rfh) };
1340    if ($@) {
1341      warn($@);
1342      exit 1;
1343    }
1344
1345    exit 0;
1346  }
1347
1348  # Stop server
1349  server_stop($setup->{pid_file});
1350  $self->assert_child_ok($pid);
1351
1352  test_cleanup($setup->{log_file}, $ex);
1353}
1354
1355sub pathdenyfilter_stor_leading_space {
1356  my $self = shift;
1357  my $tmpdir = $self->{tmpdir};
1358  my $setup = test_setup($tmpdir, 'config');
1359
1360  my $config = {
1361    PidFile => $setup->{pid_file},
1362    ScoreboardFile => $setup->{scoreboard_file},
1363    SystemLog => $setup->{log_file},
1364
1365    AuthUserFile => $setup->{auth_user_file},
1366    AuthGroupFile => $setup->{auth_group_file},
1367    DefaultChdir => '~',
1368
1369    AllowOverwrite => 'on',
1370    PathDenyFilter => '"(\.*/)? [^/]*$"',
1371
1372    IfModules => {
1373      'mod_delay.c' => {
1374        DelayEngine => 'off',
1375      },
1376    },
1377  };
1378
1379  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1380    $config);
1381
1382  # Open pipes, for use between the parent and child processes.  Specifically,
1383  # the child will indicate when it's done with its test by writing a message
1384  # to the parent.
1385  my ($rfh, $wfh);
1386  unless (pipe($rfh, $wfh)) {
1387    die("Can't open pipe: $!");
1388  }
1389
1390  my $ex;
1391
1392  # Fork child
1393  $self->handle_sigchld();
1394  defined(my $pid = fork()) or die("Can't fork: $!");
1395  if ($pid) {
1396    eval {
1397      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1398      $client->login($setup->{user}, $setup->{passwd});
1399
1400      my $filename = ' test';
1401
1402      my $conn = $client->stor_raw($filename);
1403      if ($conn) {
1404        die("STOR $filename succeeded unexpectedly");
1405      }
1406
1407      my $resp_code = $client->response_code();
1408      my $resp_msg = $client->response_msg();
1409
1410      my $expected = 550;
1411      $self->assert($expected == $resp_code,
1412        test_msg("Expected response code $expected, got $resp_code"));
1413
1414      $expected = "$filename: Forbidden filename";
1415      $self->assert($expected eq $resp_msg,
1416        test_msg("Expected response message '$expected', got '$resp_msg'"));
1417
1418      $client->quit();
1419    };
1420    if ($@) {
1421      $ex = $@;
1422    }
1423
1424    $wfh->print("done\n");
1425    $wfh->flush();
1426
1427  } else {
1428    eval { server_wait($setup->{config_file}, $rfh) };
1429    if ($@) {
1430      warn($@);
1431      exit 1;
1432    }
1433
1434    exit 0;
1435  }
1436
1437  # Stop server
1438  server_stop($setup->{pid_file});
1439  $self->assert_child_ok($pid);
1440
1441  test_cleanup($setup->{log_file}, $ex);
1442}
1443
1444sub pathdenyfilter_stor_leading_trailing_space {
1445  my $self = shift;
1446  my $tmpdir = $self->{tmpdir};
1447  my $setup = test_setup($tmpdir, 'config');
1448
1449  my $config = {
1450    PidFile => $setup->{pid_file},
1451    ScoreboardFile => $setup->{scoreboard_file},
1452    SystemLog => $setup->{log_file},
1453
1454    AuthUserFile => $setup->{auth_user_file},
1455    AuthGroupFile => $setup->{auth_group_file},
1456    DefaultChdir => '~',
1457
1458    AllowOverwrite => 'on',
1459    PathDenyFilter => '"(\.*/)?( [^/]*$|\.* $)"',
1460
1461    IfModules => {
1462      'mod_delay.c' => {
1463        DelayEngine => 'off',
1464      },
1465    },
1466  };
1467
1468  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1469    $config);
1470
1471  # Open pipes, for use between the parent and child processes.  Specifically,
1472  # the child will indicate when it's done with its test by writing a message
1473  # to the parent.
1474  my ($rfh, $wfh);
1475  unless (pipe($rfh, $wfh)) {
1476    die("Can't open pipe: $!");
1477  }
1478
1479  my $ex;
1480
1481  # Fork child
1482  $self->handle_sigchld();
1483  defined(my $pid = fork()) or die("Can't fork: $!");
1484  if ($pid) {
1485    eval {
1486      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1487      $client->login($setup->{user}, $setup->{passwd});
1488
1489      my $filename = ' test ';
1490
1491      my $conn = $client->stor_raw($filename);
1492      if ($conn) {
1493        die("STOR $filename succeeded unexpectedly");
1494      }
1495
1496      my $resp_code = $client->response_code();
1497      my $resp_msg = $client->response_msg();
1498
1499      my $expected = 550;
1500      $self->assert($expected == $resp_code,
1501        test_msg("Expected response code $expected, got $resp_code"));
1502
1503      $expected = "$filename: Forbidden filename";
1504      $self->assert($expected eq $resp_msg,
1505        test_msg("Expected response message '$expected', got '$resp_msg'"));
1506
1507      $client->quit();
1508    };
1509    if ($@) {
1510      $ex = $@;
1511    }
1512
1513    $wfh->print("done\n");
1514    $wfh->flush();
1515
1516  } else {
1517    eval { server_wait($setup->{config_file}, $rfh) };
1518    if ($@) {
1519      warn($@);
1520      exit 1;
1521    }
1522
1523    exit 0;
1524  }
1525
1526  # Stop server
1527  server_stop($setup->{pid_file});
1528  $self->assert_child_ok($pid);
1529
1530  test_cleanup($setup->{log_file}, $ex);
1531}
1532
1533sub pathdenyfilter_stor_denied_nocase_bug3592 {
1534  my $self = shift;
1535  my $tmpdir = $self->{tmpdir};
1536  my $setup = test_setup($tmpdir, 'config');
1537
1538  my $config = {
1539    PidFile => $setup->{pid_file},
1540    ScoreboardFile => $setup->{scoreboard_file},
1541    SystemLog => $setup->{log_file},
1542
1543    AuthUserFile => $setup->{auth_user_file},
1544    AuthGroupFile => $setup->{auth_group_file},
1545    DefaultChdir => '~',
1546
1547    AllowOverwrite => 'on',
1548    PathDenyFilter => '\.jpg$ [nocase]',
1549
1550    IfModules => {
1551      'mod_delay.c' => {
1552        DelayEngine => 'off',
1553      },
1554    },
1555  };
1556
1557  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1558    $config);
1559
1560  # Open pipes, for use between the parent and child processes.  Specifically,
1561  # the child will indicate when it's done with its test by writing a message
1562  # to the parent.
1563  my ($rfh, $wfh);
1564  unless (pipe($rfh, $wfh)) {
1565    die("Can't open pipe: $!");
1566  }
1567
1568  my $ex;
1569
1570  # Fork child
1571  $self->handle_sigchld();
1572  defined(my $pid = fork()) or die("Can't fork: $!");
1573  if ($pid) {
1574    eval {
1575      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1576      $client->login($setup->{user}, $setup->{passwd});
1577
1578      my $filename = 'test.JpG';
1579
1580      my $conn = $client->stor_raw($filename);
1581      if ($conn) {
1582        die("STOR test.txt succeeded unexpectedly");
1583      }
1584
1585      my $resp_code = $client->response_code();
1586      my $resp_msg = $client->response_msg();
1587
1588      my $expected = 550;
1589      $self->assert($expected == $resp_code,
1590        test_msg("Expected response code $expected, got $resp_code"));
1591
1592      $expected = "$filename: Forbidden filename";
1593      $self->assert($expected eq $resp_msg,
1594        test_msg("Expected response message '$expected', got '$resp_msg'"));
1595
1596      $client->quit();
1597    };
1598    if ($@) {
1599      $ex = $@;
1600    }
1601
1602    $wfh->print("done\n");
1603    $wfh->flush();
1604
1605  } else {
1606    eval { server_wait($setup->{config_file}, $rfh) };
1607    if ($@) {
1608      warn($@);
1609      exit 1;
1610    }
1611
1612    exit 0;
1613  }
1614
1615  # Stop server
1616  server_stop($setup->{pid_file});
1617  $self->assert_child_ok($pid);
1618
1619  test_cleanup($setup->{log_file}, $ex);
1620}
1621
1622sub pathdenyfilter_stor_dotfiles_denied {
1623  my $self = shift;
1624  my $tmpdir = $self->{tmpdir};
1625  my $setup = test_setup($tmpdir, 'config');
1626
1627  my $config = {
1628    PidFile => $setup->{pid_file},
1629    ScoreboardFile => $setup->{scoreboard_file},
1630    SystemLog => $setup->{log_file},
1631    TraceLog => $setup->{log_file},
1632    Trace => 'filter:10',
1633
1634    AuthUserFile => $setup->{auth_user_file},
1635    AuthGroupFile => $setup->{auth_group_file},
1636    DefaultChdir => '~',
1637
1638    AllowOverwrite => 'on',
1639    PathDenyFilter => '\.[^/]*$',
1640
1641    IfModules => {
1642      'mod_delay.c' => {
1643        DelayEngine => 'off',
1644      },
1645    },
1646  };
1647
1648  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1649    $config);
1650
1651  # Open pipes, for use between the parent and child processes.  Specifically,
1652  # the child will indicate when it's done with its test by writing a message
1653  # to the parent.
1654  my ($rfh, $wfh);
1655  unless (pipe($rfh, $wfh)) {
1656    die("Can't open pipe: $!");
1657  }
1658
1659  my $ex;
1660
1661  # Fork child
1662  $self->handle_sigchld();
1663  defined(my $pid = fork()) or die("Can't fork: $!");
1664  if ($pid) {
1665    eval {
1666      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1667      $client->login($setup->{user}, $setup->{passwd});
1668
1669      my $filename = '.ftpaccess';
1670
1671      my $conn = $client->stor_raw($filename);
1672      if ($conn) {
1673        die("STOR $filename succeeded unexpectedly");
1674      }
1675
1676      my $resp_code = $client->response_code();
1677      my $resp_msg = $client->response_msg();
1678
1679      my $expected = 550;
1680      $self->assert($expected == $resp_code,
1681        test_msg("Expected response code $expected, got $resp_code"));
1682
1683      $expected = "$filename: Forbidden filename";
1684      $self->assert($expected eq $resp_msg,
1685        test_msg("Expected response message '$expected', got '$resp_msg'"));
1686
1687      $client->quit();
1688    };
1689    if ($@) {
1690      $ex = $@;
1691    }
1692
1693    $wfh->print("done\n");
1694    $wfh->flush();
1695
1696  } else {
1697    eval { server_wait($setup->{config_file}, $rfh) };
1698    if ($@) {
1699      warn($@);
1700      exit 1;
1701    }
1702
1703    exit 0;
1704  }
1705
1706  # Stop server
1707  server_stop($setup->{pid_file});
1708  $self->assert_child_ok($pid);
1709
1710  test_cleanup($setup->{log_file}, $ex);
1711}
1712
17131;
1714