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