proftpd-mod_case/0000755000175000001440000000000013063310625013317 5ustar hilleusersproftpd-mod_case/t/0000755000175000001440000000000013063310625013562 5ustar hilleusersproftpd-mod_case/t/lib/0000755000175000001440000000000013063310625014330 5ustar hilleusersproftpd-mod_case/t/lib/ProFTPD/0000755000175000001440000000000013063310625015546 5ustar hilleusersproftpd-mod_case/t/lib/ProFTPD/Tests/0000755000175000001440000000000013063310625016650 5ustar hilleusersproftpd-mod_case/t/lib/ProFTPD/Tests/Modules/0000755000175000001440000000000013063310625020260 5ustar hilleusersproftpd-mod_case/t/lib/ProFTPD/Tests/Modules/mod_case.pm0000644000175000001440000030133713063310625022377 0ustar hilleuserspackage ProFTPD::Tests::Modules::mod_case; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { caseignore_appe => { order => ++$order, test_class => [qw(forking)], }, caseignore_cwd => { order => ++$order, test_class => [qw(forking)], }, caseignore_xcwd => { order => ++$order, test_class => [qw(forking)], }, caseignore_dele => { order => ++$order, test_class => [qw(forking)], }, caseignore_mdtm => { order => ++$order, test_class => [qw(forking)], }, caseignore_mkd => { order => ++$order, test_class => [qw(forking)], }, caseignore_xmkd => { order => ++$order, test_class => [qw(forking)], }, caseignore_mlsd => { order => ++$order, test_class => [qw(forking)], }, caseignore_mlst => { order => ++$order, test_class => [qw(forking)], }, caseignore_retr => { order => ++$order, test_class => [qw(forking)], }, caseignore_rmd => { order => ++$order, test_class => [qw(forking)], }, caseignore_xrmd => { order => ++$order, test_class => [qw(forking)], }, caseignore_rnfr => { order => ++$order, test_class => [qw(forking)], }, caseignore_rnto => { order => ++$order, test_class => [qw(forking)], }, caseignore_size => { order => ++$order, test_class => [qw(forking)], }, caseignore_stat => { order => ++$order, test_class => [qw(forking)], }, caseignore_stor => { order => ++$order, test_class => [qw(forking)], }, caseignore_stor_filename_with_spaces => { order => ++$order, test_class => [qw(forking)], }, caseignore_list => { order => ++$order, test_class => [qw(forking)], }, caseignore_list_filename_with_spaces => { order => ++$order, test_class => [qw(forking)], }, caseignore_nlst => { order => ++$order, test_class => [qw(forking)], }, caseignore_list_extlog_var_r => { order => ++$order, test_class => [qw(forking rootprivs)], }, caseignore_site_chmod => { order => ++$order, test_class => [qw(forking)], }, caseignore_site_chmod_filename_with_spaces => { order => ++$order, test_class => [qw(forking)], }, caseignore_site_chgrp => { order => ++$order, test_class => [qw(forking)], }, caseignore_site_chgrp_filename_with_spaces => { order => ++$order, test_class => [qw(forking)], }, caseignore_cmds_cwd => { order => ++$order, test_class => [qw(forking bug)], }, caseignore_multi_cwds_to_dst => { order => ++$order, test_class => [qw(forking bug)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub caseignore_appe { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->appe_raw("TeSt.TxT"); unless ($conn) { die("APPE TeSt.TxT failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf = "Hello again!\n"; $conn->write($buf, length($buf), 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_cwd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->cwd('SuBdIr'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'CWD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->pwd(); $expected = 257; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); if ($^O eq 'darwin') { # MacOSX-specific hack to deal with their tmp filesystem $sub_dir = ('/private' . $sub_dir); } $expected = "\"$sub_dir\" is the current directory"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_xcwd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->xcwd('SuBdIr'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'XCWD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->pwd(); $expected = 257; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); if ($^O eq 'darwin') { # MacOSX-specific hack to deal with their tmp filesystem $sub_dir = ('/private' . $sub_dir); } $expected = "\"$sub_dir\" is the current directory"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_dele { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->dele('TeSt.TxT'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'DELE command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_mdtm { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->mdtm('TeSt.TxT'); my $expected; $expected = 213; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = '\d+'; $self->assert(qr/$expected/, $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_mkd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); eval { $client->mkd('TeSt.D') }; unless ($@) { die("MKD TeSt.D succeeded unexpectedly"); } my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 550; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'test.d: File exists'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_xmkd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); eval { $client->xmkd('TeSt.D') }; unless ($@) { die("XMKD TeSt.D succeeded unexpectedly"); } my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 550; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'test.d: File exists'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_mlsd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->mlsd('TeSt.D'); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_mlst { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->mlst("TeSt.TxT"); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_retr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->retr_raw("TeSt.TxT"); unless ($conn) { die("RETR TeSt.TxT failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_rmd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->rmd('TeSt.D'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'RMD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_xrmd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->xrmd('TeSt.D'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'XRMD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_rnfr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->rnfr('TeSt.TxT'); my $expected; $expected = 350; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'File or directory exists, ready for destination name'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_rnto { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $src_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $src_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$home_dir/dst.txt"); if (open(my $fh, "> $dst_file")) { print $fh "I'm here.\n"; unless (close($fh)) { die("Can't write $dst_file: $!"); } } else { die("Can't open $dst_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'off', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->rnfr('TeSt.TxT'); my $expected; $expected = 350; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'File or directory exists, ready for destination name'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); eval { $client->rnto('DsT.tXt') }; unless ($@) { die("RNTO DsT.tXt succeeded unexpectedly"); } $resp_code = $client->response_code(); $resp_msg = $client->response_msg(); $expected = 550; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'dst.txt: Rename permission denied'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_size { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->type('binary'); my ($resp_code, $resp_msg) = $client->size("TeSt.TxT"); my $expected; $expected = 213; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = '14'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_stat { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->stat("TeSt.TxT"); my $expected; $expected = 213; $self->assert($expected == $resp_code, "Expected response code $expected, got $resp_code"); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_stor { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $new_file = File::Spec->rel2abs("$home_dir/TeSt.TxT"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->stor_raw("TeSt.TxT"); unless ($conn) { die("STOR TeSt.TxT failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf = "Hello again!\n"; $conn->write($buf, length($buf), 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); # Make sure that the original file exists... $self->assert(-f $test_file, test_msg("File '$test_file' does not exist as expected")); # ...and that the new file does not. Unfortunately, we cannot do this # check on MacOSX; its default filesystem is case-insensitive but # case-preserving. Yuck. if ($^O ne 'darwin') { $self->assert(!-f $new_file, test_msg("File '$new_file' exists unexpectedly")); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_stor_filename_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test file.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $new_file = File::Spec->rel2abs("$home_dir/TeSt FiLe.TxT"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->stor_raw("TeSt FiLe.TxT"); unless ($conn) { die("STOR TeSt FiLe.TxT failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf = "Hello again!\n"; $conn->write($buf, length($buf), 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); # Make sure that the original file exists... $self->assert(-f $test_file, test_msg("File '$test_file' does not exist as expected")); # ...and that the new file does not. Unfortunately, we cannot do this # check on MacOSX; its default filesystem is case-insensitive but # case-preserving. Yuck. if ($^O ne 'darwin') { $self->assert(!-f $new_file, test_msg("File '$new_file' exists unexpectedly")); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_list { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->list_raw('-a -l SuBdIr'); unless ($conn) { die("Failed to LIST: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 8192, 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); # We have to be careful of the fact that readdir returns directory # entries in an unordered fashion. my $res = {}; my $lines = [split(/\n/, $buf)]; foreach my $line (@$lines) { if ($line =~ /^\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$/) { $res->{$1} = 1; } } my $expected = { '.' => 1, '..' => 1, }; my $ok = 1; my $mismatch; foreach my $name (keys(%$res)) { unless (defined($expected->{$name})) { $mismatch = $name; $ok = 0; last; } } $self->assert($ok, test_msg("Unexpected name '$mismatch' appeared in LIST data")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_list_filename_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/sub dir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->list_raw('-a -l SuB dIr'); unless ($conn) { die("Failed to LIST: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 8192, 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); # We have to be careful of the fact that readdir returns directory # entries in an unordered fashion. my $res = {}; my $lines = [split(/\n/, $buf)]; foreach my $line (@$lines) { if ($line =~ /^\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$/) { $res->{$1} = 1; } } my $expected = { '.' => 1, '..' => 1, }; my $ok = 1; my $mismatch; foreach my $name (keys(%$res)) { unless (defined($expected->{$name})) { $mismatch = $name; $ok = 0; last; } } $self->assert($ok, test_msg("Unexpected name '$mismatch' appeared in LIST data")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_nlst { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->nlst_raw('-a -l SuBdIr'); unless ($conn) { die("Failed to NLST: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 8192, 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); # We have to be careful of the fact that readdir returns directory # entries in an unordered fashion. my $res = {}; my $lines = [split(/\n/, $buf)]; foreach my $line (@$lines) { $res->{$line} = 1; } my $expected = { 'subdir/.' => 1, 'subdir/..' => 1, }; my $ok = 1; my $mismatch; foreach my $name (keys(%$res)) { unless (defined($expected->{$name})) { $mismatch = $name; $ok = 0; last; } } $self->assert($ok, test_msg("Unexpected name '$mismatch' appeared in LIST data")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_list_extlog_var_r { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $ext_log = File::Spec->rel2abs("$tmpdir/custom.log"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, DefaultRoot => '~', LogFormat => 'custom "%r"', ExtendedLog => "$ext_log DIRS custom", IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my $conn = $client->list_raw('/SuBdIr'); unless ($conn) { die("Failed to LIST: " . $client->response_code() . " " . $client->response_msg()); } my $buf; $conn->read($buf, 8192, 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'Transfer complete'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); # We have to be careful of the fact that readdir returns directory # entries in an unordered fashion. my $res = {}; my $lines = [split(/\n/, $buf)]; foreach my $line (@$lines) { if ($line =~ /^\S+\s+\d+\s+\S+\s+\S+\s+.*?\s+(\S+)$/) { $res->{$1} = 1; } } my $expected = { '.' => 1, '..' => 1, }; my $ok = 1; my $mismatch; foreach my $name (keys(%$res)) { unless (defined($expected->{$name})) { $mismatch = $name; $ok = 0; last; } } $self->assert($ok, test_msg("Unexpected name '$mismatch' appeared in LIST data")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } # Now, read in the ExtendedLog, and see whether the %r variable was # properly written out. if (open(my $fh, "< $ext_log")) { my $line = <$fh>; chomp($line); close($fh); my $expected = 'LIST /subdir'; $self->assert($expected eq $line, test_msg("Expected '$expected', got '$line'")); } else { die("Can't read $ext_log: $!"); } unlink($log_file); } sub caseignore_site_chmod { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->site('CHMOD', '444', 'TeSt.TxT'); my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 200; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'SITE CHMOD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); my $file_mode = sprintf("%lo", (stat($test_file))[2] & 07777); $expected = '444'; $self->assert($expected eq $file_mode, test_msg("Expected '$expected', got '$file_mode'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_chmod_filename_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test file.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->site('CHMOD', '444', 'TeSt FiLe.TxT'); my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 200; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'SITE CHMOD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); my $file_mode = sprintf("%lo", (stat($test_file))[2] & 07777); $expected = '444'; $self->assert($expected eq $file_mode, test_msg("Expected '$expected', got '$file_mode'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_chgrp { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $file_group = (config_get_identity())[1]; my $file_gid = (split(' ', $())[0]; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } $file_group = $group; $file_gid = $gid; } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->site('CHGRP', $file_gid, 'TeSt.TxT'); my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 200; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'SITE CHGRP command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); my $test_gid = (stat($test_file))[5]; $expected = $file_gid; $self->assert($expected == $test_gid, test_msg("Expected GID $expected, got $test_gid")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_chgrp_filename_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $file_group = (config_get_identity())[1]; my $file_gid = (split(' ', $())[0]; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } $file_group = $group; $file_gid = $gid; } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_file = File::Spec->rel2abs("$home_dir/test file.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->site('CHGRP', $file_gid, 'TeSt FiLe.TxT'); my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); my $expected; $expected = 200; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'SITE CHGRP command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); $client->quit(); my $test_gid = (stat($test_file))[5]; $expected = $file_gid; $self->assert($expected == $test_gid, test_msg("Expected GID $expected, got $test_gid")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_cmds_cwd { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $sub_dir = File::Spec->rel2abs("$tmpdir/subdir"); mkpath($sub_dir); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $sub_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $sub_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'APPE,CWD,DELE,LIST,MDTM,MKD,MLSD,MLST,NLST,RETR,RMD,RNFR,RNTO,SIZE,STOR,XCWD,XMKD,XRMD,CD,', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg) = $client->cwd('SuBdIr'); my $expected; $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = 'CWD command successful'; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->pwd(); $expected = 257; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); if ($^O eq 'darwin') { # MacOSX-specific hack to deal with their tmp filesystem $sub_dir = ('/private' . $sub_dir); } $expected = "\"$sub_dir\" is the current directory"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_multi_cwds_to_dst { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'case'); my $dst_dir = File::Spec->rel2abs("$tmpdir/foo/bar/baz/quxx/quzz.d"); mkpath($dst_dir); my $test_file = File::Spec->rel2abs("$dst_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $setup->{log_file}, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0); $client->login($setup->{user}, $setup->{passwd}); $client->cwd("FOO"); $client->cwd("bAr"); $client->cwd("BaZ"); $client->cwd("quXX"); $client->cwd("QuZz.D"); my $conn = $client->appe_raw("TeSt.TxT"); unless ($conn) { die("APPE TeSt.TxT failed: " . $client->response_code() . " " . $client->response_msg()); } my $buf = "Hello again!\n"; $conn->write($buf, length($buf), 25); eval { $conn->close() }; my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); $self->assert_transfer_ok($resp_code, $resp_msg); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); test_cleanup($setup->{log_file}, $ex); } 1; proftpd-mod_case/t/lib/ProFTPD/Tests/Modules/mod_case/0000755000175000001440000000000013063310625022032 5ustar hilleusersproftpd-mod_case/t/lib/ProFTPD/Tests/Modules/mod_case/copy.pm0000644000175000001440000003751113063310625023351 0ustar hilleuserspackage ProFTPD::Tests::Modules::mod_case::copy; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use File::Copy; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use POSIX qw(:fcntl_h); use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { caseignore_site_copy => { order => ++$order, test_class => [qw(forking mod_copy)], }, caseignore_site_cpfr_cpto => { order => ++$order, test_class => [qw(forking mod_copy)], }, caseignore_site_cpfr_cpto_overwrite => { order => ++$order, test_class => [qw(forking mod_copy)], }, caseignore_site_cpfr_cpto_filenames_with_spaces => { order => ++$order, test_class => [qw(forking mod_copy)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub caseignore_site_copy { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $src_file = File::Spec->rel2abs("$home_dir/src.txt"); if (open(my $fh, "> $src_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$home_dir/src.txt"); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $src_file)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $src_file)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg); ($resp_code, $resp_msg) = $client->site('COPY', 'SrC.txt', 'dst.txt'); my $expected; $expected = 200; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "SITE COPY command successful"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); unless (-f $dst_file) { die("File $dst_file does not exist as expected"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_cpfr_cpto { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $src_file = File::Spec->rel2abs("$home_dir/src.txt"); if (open(my $fh, "> $src_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$home_dir/src.txt"); # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $src_file)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $src_file)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg); ($resp_code, $resp_msg) = $client->site('CPFR', 'SrC.txt'); my $expected; $expected = 350; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "File or directory exists, ready for destination name"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->site('CPTO', 'dst.txt'); $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "Copy successful"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); unless (-f $dst_file) { die("File $dst_file does not exist as expected"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_cpfr_cpto_overwrite { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $src_file = File::Spec->rel2abs("$home_dir/src.txt"); if (open(my $fh, "> $src_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$home_dir/dst.txt"); if (open(my $fh, "> $dst_file")) { unless (close($fh)) { die("Can't write $dst_file: $!"); } } else { die("Can't open $dst_file: $!"); } # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $src_file)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $src_file)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg); ($resp_code, $resp_msg) = $client->site('CPFR', 'SrC.tXt'); my $expected; $expected = 350; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "File or directory exists, ready for destination name"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->site('CPTO', 'DsT.tXt'); $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "Copy successful"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); unless (-f $dst_file) { die("File $dst_file does not exist as expected"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } sub caseignore_site_cpfr_cpto_filenames_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; my $src_file = File::Spec->rel2abs("$home_dir/src file.txt"); if (open(my $fh, "> $src_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$home_dir/dst file.txt"); if (open(my $fh, "> $dst_file")) { unless (close($fh)) { die("Can't write $dst_file: $!"); } } else { die("Can't open $dst_file: $!"); } # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir, $src_file)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir, $src_file)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); my ($resp_code, $resp_msg); ($resp_code, $resp_msg) = $client->site('CPFR', 'SrC fIlE.tXt'); my $expected; $expected = 350; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "File or directory exists, ready for destination name"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); ($resp_code, $resp_msg) = $client->site('CPTO', 'DsT fIlE.tXt'); $expected = 250; $self->assert($expected == $resp_code, test_msg("Expected $expected, got $resp_code")); $expected = "Copy successful"; $self->assert($expected eq $resp_msg, test_msg("Expected '$expected', got '$resp_msg'")); unless (-f $dst_file) { die("File $dst_file does not exist as expected"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } 1; proftpd-mod_case/t/lib/ProFTPD/Tests/Modules/mod_case/tls.pm0000644000175000001440000001054713063310625023201 0ustar hilleuserspackage ProFTPD::Tests::Modules::mod_case::tls; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use File::Copy; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use POSIX qw(:fcntl_h); use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { caseignore_tls_retr => { order => ++$order, test_class => [qw(forking mod_tls)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub caseignore_tls_retr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = test_get_logfile(); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $cert_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_tls/server-cert.pem"); my $ca_file = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_tls/ca-cert.pem"); my $dst_file = File::Spec->rel2abs("$tmpdir/dst.txt"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10 case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_tls.c' => { TLSEngine => 'on', TLSLog => $log_file, TLSProtocol => 'SSLv3 TLSv1', TLSRequired => 'on', TLSRSACertificateFile => $cert_file, TLSCACertificateFile => $ca_file, TLSOptions => 'NoSessionReuseRequired', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::FTPSSL; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { # Give the server a chance to start up sleep(1); my $client = Net::FTPSSL->new('127.0.0.1', Encryption => 'E', Port => $port, ); unless ($client) { die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr()); } unless ($client->login($user, $passwd)) { die("Can't login: " . $client->last_message()); } unless ($client->binary()) { die("Can't set transfer mode to binary: " . $client->last_message()); } unless ($client->get('CaSe.CoNf', $dst_file)) { die("Can't download 'CaSe.CoNf' to '$dst_file': " . $client->last_message()); } $client->quit(); $self->assert(-f $dst_file, test_msg("File $dst_file does not exist as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { test_append_logfile($log_file, $ex); unlink($log_file); die($ex); } unlink($log_file); } 1; proftpd-mod_case/t/lib/ProFTPD/Tests/Modules/mod_case/sftp.pm0000644000175000001440000021615013063310625023351 0ustar hilleuserspackage ProFTPD::Tests::Modules::mod_case::sftp; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use File::Copy; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use POSIX qw(:fcntl_h); use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { caseignore_sftp_realpath => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_lstat => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_setstat => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_opendir => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_stat => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_readlink => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_symlink_src => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_symlink_dst => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_download => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_upload => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_mkdir => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_rmdir => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_remove => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_rename => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_rename_overwrite => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, caseignore_sftp_rename_filenames_with_spaces => { order => ++$order, test_class => [qw(forking mod_sftp sftp)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub set_up { my $self = shift; $self->SUPER::set_up(@_); # Make sure that mod_sftp does not complain about permissions on the hostkey # files. my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); unless (chmod(0400, $rsa_host_key, $dsa_host_key)) { die("Can't set perms on $rsa_host_key, $dsa_host_key: $!"); } } sub caseignore_sftp_realpath { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $sub_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($sub_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $cwd = $sftp->realpath('TeSt.D'); unless ($cwd) { my ($err_code, $err_name) = $sftp->error(); die("Can't get real path for 'TeSt.D': [$err_name] ($err_code)"); } my $expected; if ($^O eq 'darwin') { # MacOSX-specific hack dealing with their tmp filesystem handling $sub_dir = ('/private' . $sub_dir); } $expected = $sub_dir; $self->assert($expected eq $cwd, test_msg("Expected '$expected', got '$cwd'")); $sftp = undef; $ssh2->disconnect(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_lstat { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_size = (stat($test_file))[7]; my $test_symlink = File::Spec->rel2abs("$tmpdir/test.lnk"); unless (symlink($test_file, $test_symlink)) { die("Can't symlink $test_symlink to $test_file: $!"); } my $test_symlink_size = (lstat($test_symlink))[7]; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $attrs = $sftp->stat('TeSt.LnK', 0); unless ($attrs) { my ($err_code, $err_name) = $sftp->error(); die("FXP_LSTAT TeSt.LnK failed: [$err_name] ($err_code)"); } my $expected; $expected = $test_symlink_size; my $file_size = $attrs->{size}; $self->assert($expected == $file_size, test_msg("Expected '$expected', got '$file_size'")); $expected = $<; my $file_uid = $attrs->{uid}; $self->assert($expected == $file_uid, test_msg("Expected '$expected', got '$file_uid'")); $expected = $(; my $file_gid = $attrs->{gid}; $self->assert($expected == $file_gid, test_msg("Expected '$expected', got '$file_gid'")); $sftp = undef; $ssh2->disconnect(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_setstat { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->setstat('TeSt.TxT', atime => 0, mtime => 0, ); unless ($res) { my ($err_code, $err_name) = $sftp->error(); die("Can't setstat TeSt.TxT: [$err_name] ($err_code)"); } my $attrs = $sftp->stat('test.txt'); unless ($attrs) { my ($err_code, $err_name) = $sftp->error(); die("Can't stat test.txt: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); my $expected; $expected = 0; my $file_atime = $attrs->{atime}; $self->assert($expected == $file_atime, test_msg("Expected '$expected', got '$file_atime'")); my $file_mtime = $attrs->{mtime}; $self->assert($expected == $file_mtime, test_msg("Expected '$expected', got '$file_mtime'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_opendir { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $sub_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($sub_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $dir = $sftp->opendir('TeSt.D'); unless ($dir) { my ($err_code, $err_name) = $sftp->error(); die("Can't open directory 'TeSt.D': [$err_name] ($err_code)"); } my $res = {}; my $file = $dir->read(); while ($file) { $res->{$file->{name}} = $file; $file = $dir->read(); } my $expected = { '.' => 1, '..' => 1, }; # To issue the FXP_CLOSE, we have to explicitly destroy the dirhandle $dir = undef; $sftp = undef; $ssh2->disconnect(); my $ok = 1; my $mismatch; my $seen = []; foreach my $name (keys(%$res)) { push(@$seen, $name); unless (defined($expected->{$name})) { $mismatch = $name; $ok = 0; last; } } unless ($ok) { die("Unexpected name '$mismatch' appeared in READDIR data") } # Now remove from $expected all of the paths we saw; if there are # any entries remaining in $expected, something went wrong. foreach my $name (@$seen) { delete($expected->{$name}); } my $remaining = scalar(keys(%$expected)); $self->assert(0 == $remaining, test_msg("Expected 0, got $remaining")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_stat { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_size = (stat($test_file))[7]; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $attrs = $sftp->stat('TeSt.TxT', 1); unless ($attrs) { my ($err_code, $err_name) = $sftp->error(); die("Can't stat TeSt.TxT: [$err_name] ($err_code)"); } my $expected; $expected = $test_size; my $file_size = $attrs->{size}; $self->assert($expected == $file_size, test_msg("Expected '$expected', got '$file_size'")); $expected = $<; my $file_uid = $attrs->{uid}; $self->assert($expected == $file_uid, test_msg("Expected '$expected', got '$file_uid'")); $expected = $(; my $file_gid = $attrs->{gid}; $self->assert($expected == $file_gid, test_msg("Expected '$expected', got '$file_gid'")); $sftp = undef; $ssh2->disconnect(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_readlink { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_symlink = File::Spec->rel2abs("$tmpdir/test.lnk"); unless (symlink($test_file, $test_symlink)) { die("Can't symlink $test_symlink to $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $path = $sftp->readlink('TeSt.LnK'); unless ($path) { my ($err_code, $err_name) = $sftp->error(); die("Can't readlink TeSt.LnK: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); $self->assert($test_file eq $path, test_msg("Expected '$test_file', got '$path'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_symlink_src { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_symlink = File::Spec->rel2abs("$tmpdir/test.lnk"); # Not sure why, but Net::SSH2::SFTP seems to need a little more time # for this test case. my $timeout_idle = 15; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', TimeoutIdle => 5, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->symlink('TeSt.TxT', 'test.lnk'); unless (defined($res)) { my ($err_code, $err_name) = $sftp->error(); die("Can't symlink test.lnk to TeSt.TxT: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); $self->assert(-l $test_symlink, test_msg("Symlink $test_symlink does not exist as expected")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh, $timeout_idle + 1) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_symlink_dst { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_symlink = File::Spec->rel2abs("$tmpdir/test.lnk"); if (open(my $fh, "> $test_symlink")) { close($fh); } else { die("Can't open $test_symlink: $!"); } # Not sure why, but Net::SSH2::SFTP seems to need a little more time # for this test case. my $timeout_idle = 15; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', TimeoutIdle => 5, IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->symlink('test.txt', 'TeSt.LnK'); if ($res) { die("Symlink of TeSt.LnK to test.txt succeeded unexpectedly"); } $sftp = undef; $ssh2->disconnect(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh, $timeout_idle + 1) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_download { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $test_sz = -s $test_file; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $fh = $sftp->open('TeSt.TxT', O_RDONLY); unless ($fh) { my ($err_code, $err_name) = $sftp->error(); die("Can't open TeSt.TxT: [$err_name] ($err_code)"); } my $buf; my $size = 0; my $res = $fh->read($buf, 8192); while ($res) { $size += $res; $res = $fh->read($buf, 8192); } # To issue the FXP_CLOSE, we have to explicitly destroy the filehandle $fh = undef; $sftp = undef; $ssh2->disconnect(); $self->assert($test_sz == $size, test_msg("Expected $test_sz, got $size")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_upload { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { close($fh); } else { die("Can't open $test_file: $!"); } my $test_sz = 32; my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $fh = $sftp->open('TeSt.TxT', O_WRONLY|O_CREAT, 0644); unless ($fh) { my ($err_code, $err_name) = $sftp->error(); die("Can't open TeSt.TxT: [$err_name] ($err_code)"); } print $fh "ABCD" x 8; # To issue the FXP_CLOSE, we have to explicitly destroy the filehandle $fh = undef; $sftp = undef; $ssh2->disconnect(); my $size = -s $test_file; $self->assert($size == $test_sz, test_msg("Expected $test_sz, got $size")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_mkdir { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $bad_dir = File::Spec->rel2abs("$home_dir/TeSt.D"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->mkdir('TeSt.D'); if ($res) { die("MKDIR TeSt.D succeeded unexpectedly"); } my ($err_code, $err_name) = $sftp->error(); $sftp = undef; $ssh2->disconnect(); my $expected = 'SSH_FX_FAILURE'; $self->assert($expected eq $err_name, test_msg("Expected '$expected', got '$err_name'")); if ($^O ne 'darwin') { # Avoid this check on Mac OSX, due to its default case-insensitve # (but case-preserving) filesystem. Yuck. $self->assert(!-d $bad_dir, test_msg("Directory $bad_dir exists unexpectedly")); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_rmdir { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_dir = File::Spec->rel2abs("$home_dir/test.d"); mkpath($test_dir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->rmdir('TeSt.D'); unless ($res) { my ($err_code, $err_name) = $sftp->error(); die("Can't rmdir TeSt.D: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); $self->assert(!-d $test_dir, test_msg("Directory $test_dir exists unexpectedly")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_remove { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $test_file = File::Spec->rel2abs("$home_dir/test.txt"); if (open(my $fh, "> $test_file")) { print $fh "Hello, World!\n"; unless (close($fh)) { die("Can't write $test_file: $!"); } } else { die("Can't open $test_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->unlink('TeSt.TxT'); unless ($res) { my ($err_code, $err_name) = $sftp->error(); die("Can't remove TeSt.TxT: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); $self->assert(!-f $test_file, test_msg("File $test_file exists unexpectedly")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_rename { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $src_file = File::Spec->rel2abs("$tmpdir/src.txt"); if (open(my $fh, "> $src_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$tmpdir/dst.txt"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $flags = (0x01|0x02|0x04); my $res = $sftp->rename('SrC.tXt', 'dst.txt', $flags); unless (defined($res)) { my ($err_code, $err_name) = $sftp->error(); die("Can't rename SrC.tXt to dst.txt: [$err_name] ($err_code)"); } $sftp = undef; $ssh2->disconnect(); $self->assert(-f $dst_file, test_msg("File '$dst_file' does not exist as expected")); $self->assert(!-f $src_file, test_msg("File '$src_file' exists unexpectedly")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_rename_overwrite { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $src_file = File::Spec->rel2abs("$tmpdir/src.txt"); if (open(my $fh, "> $src_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$tmpdir/dst.txt"); if (open(my $fh, "> $dst_file")) { unless (close($fh)) { die("Can't write $dst_file: $!"); } } else { die("Can't open $dst_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->rename('SrC.tXt', 'dSt.TxT'); if ($res) { # We expect this to fail because libssh2 uses SFTP version 3, and that # version does not specify RENAME flags for overwriting an existing # file. die("RENAME of 'SrC.tXt' to 'dSt.TxT' succeeded unexpectedly"); } my ($err_code, $err_name) = $sftp->error(); my $expected = 'SSH_FX_FILE_ALREADY_EXISTS'; $self->assert($expected eq $err_name, test_msg("Expected error name '$expected', got '$err_name'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub caseignore_sftp_rename_filenames_with_spaces { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/case.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/case.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/case.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/case.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/case.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs($tmpdir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $rsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_rsa_key"); my $dsa_host_key = File::Spec->rel2abs("$ENV{PROFTPD_TEST_DIR}/t/etc/modules/mod_sftp/ssh_host_dsa_key"); my $src_file = File::Spec->rel2abs("$tmpdir/src file.txt"); if (open(my $fh, "> $src_file")) { print $fh "ABCD" x 1024; unless (close($fh)) { die("Can't write $src_file: $!"); } } else { die("Can't open $src_file: $!"); } my $dst_file = File::Spec->rel2abs("$tmpdir/dst file.txt"); if (open(my $fh, "> $dst_file")) { unless (close($fh)) { die("Can't write $dst_file: $!"); } } else { die("Can't open $dst_file: $!"); } my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'case:20', AuthUserFile => $auth_user_file, AuthGroupFile => $auth_group_file, AllowOverwrite => 'on', AllowStoreRestart => 'on', IfModules => { 'mod_case.c' => { CaseEngine => 'on', CaseIgnore => 'on', CaseLog => $log_file, }, 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_sftp.c' => [ "SFTPEngine on", "SFTPLog $log_file", "SFTPHostKey $rsa_host_key", "SFTPHostKey $dsa_host_key", ], }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } require Net::SSH2; my $ex; # Ignore SIGPIPE local $SIG{PIPE} = sub { }; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $ssh2 = Net::SSH2->new(); sleep(1); unless ($ssh2->connect('127.0.0.1', $port)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't connect to SSH2 server: [$err_name] ($err_code) $err_str"); } unless ($ssh2->auth_password($user, $passwd)) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't login to SSH2 server: [$err_name] ($err_code) $err_str"); } my $sftp = $ssh2->sftp(); unless ($sftp) { my ($err_code, $err_name, $err_str) = $ssh2->error(); die("Can't use SFTP on SSH2 server: [$err_name] ($err_code) $err_str"); } my $res = $sftp->rename('SrC fIlE.tXt', 'dSt FiLe.TxT'); if ($res) { # We expect this to fail because libssh2 uses SFTP version 3, and that # version does not specify RENAME flags for overwriting an existing # file. die("RENAME of 'SrC fIlE.tXt' to 'dSt FiLe.TxT' succeeded unexpectedly"); } my ($err_code, $err_name) = $sftp->error(); my $expected = 'SSH_FX_FILE_ALREADY_EXISTS'; $self->assert($expected eq $err_name, test_msg("Expected error name '$expected', got '$err_name'")); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } 1; proftpd-mod_case/t/modules/0000755000175000001440000000000013063310625015232 5ustar hilleusersproftpd-mod_case/t/modules/mod_case/0000755000175000001440000000000013063310625017004 5ustar hilleusersproftpd-mod_case/t/modules/mod_case/copy.t0000644000175000001440000000027213063310625020144 0ustar hilleusers#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_case::copy"); proftpd-mod_case/t/modules/mod_case/tls.t0000644000175000001440000000027113063310625017773 0ustar hilleusers#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_case::tls"); proftpd-mod_case/t/modules/mod_case/sftp.t0000644000175000001440000000027213063310625020146 0ustar hilleusers#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_case::sftp"); proftpd-mod_case/t/modules/mod_case.t0000644000175000001440000000026413063310625017173 0ustar hilleusers#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_case"); proftpd-mod_case/.gitignore0000644000175000001440000000002413063310625015303 0ustar hilleusers*.lo *~ *.swo *.swp proftpd-mod_case/.gitattributes0000644000175000001440000000006213063310625016210 0ustar hilleusers*.pl linguist-language=C *.pm linguist-language=C proftpd-mod_case/README.md0000644000175000001440000000046613063310625014604 0ustar hilleusersThe `mod_case` module for ProFTPD provides case-insensitivity support to FTP commands; useful for dealing with Windows FTP clients. For further module documentation, see the [mod_case.html](https://htmlpreview.github.io/?https://github.com/Castaglia/proftpd-mod_case/blob/master/mod_case.html) documentation. proftpd-mod_case/mod_case.c0000644000175000001440000006613313063310625015246 0ustar hilleusers/* * ProFTPD: mod_case -- provides case-insensivity * Copyright (c) 2004-2017 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * This is mod_case, contrib software for proftpd 1.3.x and above. * For more information contact TJ Saunders . */ #include "conf.h" #include "privs.h" #define MOD_CASE_VERSION "mod_case/0.8" /* Make sure the version of proftpd is as necessary. */ #if PROFTPD_VERSION_NUMBER < 0x0001030402 # error "ProFTPD 1.3.4rc2 or later required" #endif static int case_engine = FALSE; static int case_logfd = -1; static const char *trace_channel = "case"; /* Support routines */ static int case_expr_eval_cmds(cmd_rec *cmd, array_header *list) { int cmd_id, found; register unsigned int i; for (i = 0; i < list->nelts; i++) { char *c = ((char **) list->elts)[i]; found = 0; if (*c == '!') { found = !found; c++; } cmd_id = pr_cmd_get_id(c); if (cmd_id > 0) { if (pr_cmd_cmp(cmd, cmd_id) == 0) { found = !found; } } else { /* Fallback to doing a full strcmp(3). */ if (strcmp(cmd->argv[0], c) == 0) { found = !found; } } if (found) { return 1; } } return 0; } static char *case_get_opts_path(cmd_rec *cmd, int *path_index) { char *ptr; char *path; size_t pathlen; if (cmd->arg == NULL) { return NULL; } ptr = path = cmd->arg; pathlen = strlen(path); if (pathlen == 0) { return NULL; } while (isspace((int) *ptr)) { pr_signals_handle(); ptr++; } if (*ptr == '-') { /* Options are found; skip past the leading whitespace. */ path = ptr; } while (path && *path == '-') { /* Advance to the next whitespace */ while (*path != '\0' && !isspace((int) *path)) { path++; } ptr = path; while (*ptr && isspace((int) *ptr)) { pr_signals_handle(); ptr++; } if (*ptr == '-') { /* Options are found; skip past the leading whitespace. */ path = ptr; } else if (*(path + 1) == ' ') { /* If the next character is a blank space, advance just one * character. */ path++; break; } else { path = ptr; break; } } pathlen = strlen(path); if (pathlen == 0) { return NULL; } *path_index = (ptr - cmd->arg); return path; } static void case_replace_copy_paths(cmd_rec *cmd, const char *proto, const char *src_path, const char *dst_path) { /* Minor nit: if src_path/dst_path is "//", then reduce it to just "/". */ if (strcmp(src_path, "//") == 0) { src_path = pstrdup(cmd->tmp_pool, "/"); } if (strcmp(dst_path, "//") == 0) { dst_path = pstrdup(cmd->tmp_pool, "/"); } if (strncmp(proto, "ftp", 4) == 0 || strncmp(proto, "ftps", 5) == 0) { array_header *argv; /* We should only be handling SITE COPY (over FTP/FTPS) requests here */ argv = make_array(cmd->pool, 4, sizeof(char *)); *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[0]); *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[1]); *((char **) push_array(argv)) = pstrdup(cmd->pool, src_path); *((char **) push_array(argv)) = pstrdup(cmd->pool, dst_path); cmd->argc = argv->nelts; *((char **) push_array(argv)) = NULL; cmd->argv = argv->elts; cmd->arg = pstrcat(cmd->pool, cmd->argv[1], " ", src_path, " ", dst_path, NULL); } return; } static void case_replace_link_paths(cmd_rec *cmd, const char *proto, const char *src_path, const char *dst_path) { /* Minor nit: if src_path/dst_path is "//", then reduce it to just "/". */ if (strcmp(src_path, "//") == 0) { src_path = pstrdup(cmd->tmp_pool, "/"); } if (strcmp(dst_path, "//") == 0) { dst_path = pstrdup(cmd->tmp_pool, "/"); } if (strncmp(proto, "sftp", 5) == 0) { /* We should only be handling SFTP SYMLINK and LINK requests here. */ cmd->arg = pstrcat(cmd->pool, src_path, "\t", dst_path, NULL); if (cmd->argv[1] != cmd->arg) { cmd->argv[1] = cmd->arg; } } return; } static void case_replace_path(cmd_rec *cmd, const char *proto, const char *dir, const char *file, int path_index) { /* Minor nit: if dir is "//", then reduce it to just "/". */ if (strcmp(dir, "//") == 0) { dir = pstrdup(cmd->tmp_pool, "/"); } if (strncmp(proto, "ftp", 4) == 0 || strncmp(proto, "ftps", 5) == 0) { /* Special handling of LIST/NLST/STAT commands, which can take options */ if (pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STAT_ID) == 0) { /* XXX Be sure to overwrite the entire cmd->argv array, not just * cmd->arg. */ if (path_index > 0) { char *arg; arg = pstrdup(cmd->tmp_pool, cmd->arg); arg[path_index] = '\0'; arg = pstrcat(cmd->pool, arg, dir, file, NULL); cmd->arg = arg; } else { cmd->arg = pstrcat(cmd->pool, dir, file, NULL); } } else { char *arg, *dup_path, *path; array_header *argv; int flags = PR_STR_FL_PRESERVE_COMMENTS; path = pstrcat(cmd->pool, dir, file, NULL); /* Be sure to overwrite the entire cmd->argv array, not just cmd->arg. */ argv = make_array(cmd->pool, 2, sizeof(char *)); *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[0]); if (pr_cmd_cmp(cmd, PR_CMD_SITE_ID) == 0) { if (strncmp(cmd->argv[1], "CHGRP", 6) == 0 || strncmp(cmd->argv[1], "CHMOD", 6) == 0) { *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[1]); *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[2]); } else if (strncmp(cmd->argv[1], "CPFR", 5) == 0 || strncmp(cmd->argv[1], "CPTO", 5) == 0) { *((char **) push_array(argv)) = pstrdup(cmd->pool, cmd->argv[1]); } } /* Handle spaces in the new path properly by breaking them up and adding * them into the argv. */ dup_path = pstrdup(cmd->tmp_pool, path); arg = pr_str_get_word(&dup_path, flags); while (arg != NULL) { pr_signals_handle(); *((char **) push_array(argv)) = pstrdup(cmd->pool, arg); arg = pr_str_get_word(&dup_path, flags); } cmd->argc = argv->nelts; *((char **) push_array(argv)) = NULL; cmd->argv = argv->elts; pr_cmd_clear_cache(cmd); /* In the case of many commands, we also need to overwrite cmd->arg. */ if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_CWD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_DELE_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MKD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MDTM_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MLSD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MLST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RMD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNFR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_SIZE_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_XCWD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_XMKD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_XRMD_ID) == 0) { cmd->arg = path; } } if (pr_trace_get_level(trace_channel) >= 19) { register unsigned int i; pr_trace_msg(trace_channel, 19, "replacing path: cmd->argc = %d", cmd->argc); for (i = 0; i < cmd->argc; i++) { pr_trace_msg(trace_channel, 19, "replacing path: cmd->argv[%u] = '%s'", i, cmd->argv[i]); } } return; } if (strncmp(proto, "sftp", 5) == 0) { /* Main SFTP commands */ if (pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MKD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RMD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_DELE_ID) == 0 || pr_cmd_strcmp(cmd, "LSTAT") == 0 || pr_cmd_strcmp(cmd, "OPENDIR") == 0 || pr_cmd_strcmp(cmd, "READLINK") == 0 || pr_cmd_strcmp(cmd, "REALPATH") == 0 || pr_cmd_strcmp(cmd, "SETSTAT") == 0 || pr_cmd_strcmp(cmd, "STAT") == 0) { cmd->arg = pstrcat(cmd->pool, dir, file, NULL); } return; } } static int case_have_file(pool *p, const char *dir, const char *file, size_t file_len, const char **matched_file) { DIR *dirh; struct dirent *dent; const char *file_match; /* Open the directory. */ dirh = pr_fsio_opendir(dir); if (dirh == NULL) { int xerrno = errno; (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "error opening directory '%s': %s", dir, strerror(xerrno)); errno = xerrno; return -1; } /* Escape any existing fnmatch(3) characters in the file name. */ file_match = pstrdup(p, file); if (strchr(file_match, '?') != NULL) { file_match = sreplace(p, file_match, "?", "\\?", NULL); } if (strchr(file_match, '*') != NULL) { file_match = sreplace(p, file_match, "*", "\\*", NULL); } if (strchr(file_match, '[') != NULL) { file_match = sreplace(p, file_match, "[", "\\[", NULL); } /* For each file in the directory, check it against the given name, both * as an exact match and as a possible match. */ dent = pr_fsio_readdir(dirh); while (dent != NULL) { pr_signals_handle(); if (strncmp(dent->d_name, file, file_len + 1) == 0) { (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "found exact match"); pr_fsio_closedir(dirh); *matched_file = NULL; return TRUE; } if (pr_fnmatch(file_match, dent->d_name, PR_FNM_CASEFOLD) == 0) { (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "found case-insensitive match '%s' for '%s'", dent->d_name, file_match); pr_fsio_closedir(dirh); *matched_file = pstrdup(p, dent->d_name); return TRUE; } dent = pr_fsio_readdir(dirh); } /* Close the directory. */ pr_fsio_closedir(dirh); return FALSE; } /* Command handlers */ /* The SITE COPY requests are different enough to warrant their own command * handler. */ MODRET case_pre_copy(cmd_rec *cmd) { config_rec *c; const char *proto, *file_match = NULL; char *src_path, *src_dir = NULL, *src_file = NULL, *dst_path, *dst_dir = NULL, *dst_file = NULL, *src_ptr, *dst_ptr; size_t file_len; int modified_arg = FALSE, res; if (!case_engine) { return PR_DECLINED(cmd); } c = find_config(CURRENT_CONF, CONF_PARAM, "CaseIgnore", FALSE); if (c == NULL) { return PR_DECLINED(cmd); } if (*((unsigned int *) c->argv[0]) != TRUE) { return PR_DECLINED(cmd); } if (c->argv[1] != NULL && case_expr_eval_cmds(cmd, *((array_header **) c->argv[1])) == 0) { return PR_DECLINED(cmd); } proto = pr_session_get_protocol(0); if (strncasecmp(cmd->argv[2], "HELP", 5) == 0) { /* Ignore SITE COPY HELP requests */ return PR_DECLINED(cmd); } /* We know the protocol here will always be "ftp" or "ftps", right? And that * we are only handling SITE COPY requests here. */ if (cmd->argc != 4) { /* Malformed SITE COPY cmd_rec */ (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "malformed SITE COPY request, ignoring"); return PR_DECLINED(cmd); } src_path = cmd->argv[2]; dst_path = cmd->argv[3]; /* Separate the path into directory and file components. */ src_ptr = strrchr(src_path, '/'); if (src_ptr == NULL) { src_dir = "."; src_file = src_path; } else { if (src_ptr != src_path) { *src_ptr = '\0'; src_dir = src_path; src_file = src_ptr + 1; } else { /* Handle the case where the path is "/path". */ src_dir = "/"; src_file = src_ptr + 1; } } dst_ptr = strrchr(dst_path, '/'); if (dst_ptr == NULL) { dst_dir = "."; dst_file = dst_path; } else { if (dst_ptr != dst_path) { *dst_ptr = '\0'; dst_dir = dst_path; dst_file = dst_ptr + 1; } else { /* Handle the case where the path is "/path". */ dst_dir = "/"; dst_file = dst_ptr + 1; } } pr_trace_msg(trace_channel, 9, "checking client-sent source path '%s', destination path '%s'", src_path, dst_path); file_len = strlen(src_file); pr_trace_msg(trace_channel, 9, "checking for file '%s' in directory '%s'", src_file, src_dir); res = case_have_file(cmd->tmp_pool, src_dir, src_file, file_len, &file_match); if (res < 0) { return PR_DECLINED(cmd); } if (res == TRUE && file_match != NULL) { /* Replace the source path */ src_path = pdircat(cmd->tmp_pool, src_dir, file_match, NULL); modified_arg = TRUE; } else { pr_trace_msg(trace_channel, 9, "no case-insensitive matches found for file '%s' in directory '%s'", src_file, src_dir); /* No match (or exact match) found; restore the original src_path. */ if (src_ptr != NULL) { *src_ptr = '/'; } } file_len = strlen(dst_file); file_match = NULL; pr_trace_msg(trace_channel, 9, "checking for file '%s' in directory '%s'", dst_file, dst_dir); res = case_have_file(cmd->tmp_pool, dst_dir, dst_file, file_len, &file_match); if (res == TRUE) { if (file_match != NULL) { /* Replace the destination path */ dst_path = pdircat(cmd->tmp_pool, dst_dir, file_match, NULL); modified_arg = TRUE; } } else { pr_trace_msg(trace_channel, 9, "no case-insensitive matches found for file '%s' in directory '%s'", dst_file, dst_dir); /* No match (or exact match) found; restore the original dst_path. */ if (dst_ptr != NULL) { *dst_ptr = '/'; } } /* Overwrite the client-given paths. */ if (modified_arg) { case_replace_copy_paths(cmd, proto, src_path, dst_path); } return PR_DECLINED(cmd); } MODRET case_pre_cmd(cmd_rec *cmd) { config_rec *c; char *path = NULL, *dir = NULL, *file = NULL, *replace_path = NULL, *tmp; const char *proto = NULL, *file_match = NULL; size_t file_len; int path_index = -1, res; if (!case_engine) { return PR_DECLINED(cmd); } c = find_config(CURRENT_CONF, CONF_PARAM, "CaseIgnore", FALSE); if (c == NULL) { return PR_DECLINED(cmd); } if (*((unsigned int *) c->argv[0]) != TRUE) { return PR_DECLINED(cmd); } if (c->argv[1] != NULL && case_expr_eval_cmds(cmd, *((array_header **) c->argv[1])) == 0) { return PR_DECLINED(cmd); } proto = pr_session_get_protocol(0); if (strncmp(proto, "sftp", 5) == 0) { path = pstrdup(cmd->tmp_pool, cmd->arg); } else { /* Special handling of LIST/NLST/STAT, given that they may have options * in the command. */ if (pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STAT_ID) == 0) { path = case_get_opts_path(cmd, &path_index); /* LIST, NLST, and STAT can send no path arguments. If that's the * case, we're done. */ if (path == NULL) { return PR_DECLINED(cmd); } } else if (pr_cmd_cmp(cmd, PR_CMD_SITE_ID) == 0) { register unsigned int i; if (strncmp(cmd->argv[1], "COPY", 5) == 0) { return case_pre_copy(cmd); } if (strncmp(cmd->argv[1], "CHGRP", 6) == 0 || strncmp(cmd->argv[1], "CHMOD", 6) == 0) { if (cmd->argc < 4) { pr_trace_msg(trace_channel, 3, "ignoring SITE %s: not enough parameters (%d)", cmd->argv[1], cmd->argc - 2); return PR_DECLINED(cmd); } path = ""; /* Skip over "SITE, "CHMOD" (or "CHGRP"), and the mode (or group). */ for (i = 3; i < cmd->argc; i++) { path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); } } else if (strncmp(cmd->argv[1], "CPFR", 5) == 0 || strncmp(cmd->argv[1], "CPTO", 5) == 0) { if (cmd->argc < 3) { pr_trace_msg(trace_channel, 3, "ignoring SITE %s: not enough parameters (%d)", cmd->argv[1], cmd->argc - 2); return PR_DECLINED(cmd); } path = ""; /* Skip over "SITE, and "CPFR" (or "CPTO"). */ for (i = 2; i < cmd->argc; i++) { path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", pr_fs_decode_path(cmd->tmp_pool, cmd->argv[i]), NULL); } } else { (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "unsupported SITE %s command, ignoring", cmd->argv[1]); return PR_DECLINED(cmd); } } else { path = pstrdup(cmd->tmp_pool, cmd->arg); } } pr_trace_msg(trace_channel, 9, "checking client-sent path '%s'", path); /* Separate the path into directory and file components. */ tmp = strrchr(path, '/'); if (tmp == NULL) { dir = "."; file = path; } else { if (tmp != path) { *tmp++ = '\0'; dir = path; file = tmp; } else { /* Handle the case where the path is "/path". */ dir = "/"; file = tmp + 1; } } file_len = strlen(file); pr_trace_msg(trace_channel, 9, "checking for file '%s' in directory '%s'", file, dir); res = case_have_file(cmd->tmp_pool, dir, file, file_len, &file_match); if (res < 0) { return PR_DECLINED(cmd); } if (res == FALSE) { /* No match found. */ pr_trace_msg(trace_channel, 9, "no case-insensitive matches found for file '%s' in directory '%s'", file, dir); return PR_DECLINED(cmd); } /* We found a match for the given file. */ if (file_match == NULL) { /* Exact match found; nothing more to do. */ return PR_DECLINED(cmd); } /* Overwrite the client-given path. */ replace_path = tmp ? pstrcat(cmd->tmp_pool, dir, "/", NULL) : ""; replace_path = pdircat(cmd->tmp_pool, replace_path, file_match, NULL); pr_trace_msg(trace_channel, 9, "replacing path '%s' with '%s'", path, replace_path); case_replace_path(cmd, proto, tmp ? pstrcat(cmd->pool, dir, "/", NULL) : "", file_match, path_index); return PR_DECLINED(cmd); } /* The SYMLINK/LINK SFTP requests are different enough to warrant their own * command handler. */ MODRET case_pre_link(cmd_rec *cmd) { config_rec *c; char *arg = NULL, *src_path, *src_dir = NULL, *src_file = NULL, *dst_path, *dst_dir = NULL, *dst_file = NULL, *src_ptr, *dst_ptr, *ptr; const char *proto = NULL, *file_match = NULL; size_t file_len; int modified_arg = FALSE, res; if (!case_engine) { return PR_DECLINED(cmd); } c = find_config(CURRENT_CONF, CONF_PARAM, "CaseIgnore", FALSE); if (c == NULL) { return PR_DECLINED(cmd); } if (*((unsigned int *) c->argv[0]) != TRUE) { return PR_DECLINED(cmd); } if (c->argv[1] != NULL && case_expr_eval_cmds(cmd, *((array_header **) c->argv[1])) == 0) { return PR_DECLINED(cmd); } proto = pr_session_get_protocol(0); /* We know the protocol here will always be "sftp", right? And that we * are only handling SFTP SYMLINK and LINK requests here. */ arg = pstrdup(cmd->tmp_pool, cmd->arg); ptr = strchr(arg, '\t'); if (ptr == NULL) { /* Malformed SFTP SYMLINK/LINK cmd_rec. */ (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "malformed SFTP %s request, ignoring", cmd->argv[0]); return PR_DECLINED(cmd); } *ptr = '\0'; src_path = arg; dst_path = ptr + 1; /* Separate the path into directory and file components. */ src_ptr = strrchr(src_path, '/'); if (src_ptr == NULL) { src_dir = "."; src_file = src_path; } else { if (src_ptr != src_path) { *src_ptr = '\0'; src_dir = src_path; src_file = src_ptr + 1; } else { /* Handle the case where the path is "/path". */ src_dir = "/"; src_file = src_ptr + 1; } } dst_ptr = strrchr(dst_path, '/'); if (dst_ptr == NULL) { dst_dir = "."; dst_file = dst_path; } else { if (dst_ptr != dst_path) { *dst_ptr = '\0'; dst_dir = dst_path; dst_file = dst_ptr + 1; } else { /* Handle the case where the path is "/path". */ dst_dir = "/"; dst_file = dst_ptr + 1; } } pr_trace_msg(trace_channel, 9, "checking client-sent source path '%s', destination path '%s'", src_path, dst_path); file_len = strlen(src_file); pr_trace_msg(trace_channel, 9, "checking for file '%s' in directory '%s'", src_file, src_dir); res = case_have_file(cmd->tmp_pool, src_dir, src_file, file_len, &file_match); if (res == TRUE) { if (file_match != NULL) { /* Replace the source path */ src_path = pdircat(cmd->tmp_pool, src_dir, file_match, NULL); modified_arg = TRUE; } } else { pr_trace_msg(trace_channel, 9, "no case-insensitive matches found for file '%s' in directory '%s'", src_file, src_dir); /* No match (or exact match) found; restore the original src_path. */ if (src_ptr != NULL) { *src_ptr = '/'; } } file_len = strlen(dst_file); file_match = NULL; pr_trace_msg(trace_channel, 9, "checking for file '%s' in directory '%s'", dst_file, dst_dir); res = case_have_file(cmd->tmp_pool, dst_dir, dst_file, file_len, &file_match); if (res == TRUE) { if (file_match != NULL) { /* Replace the destination path */ dst_path = pdircat(cmd->tmp_pool, dst_dir, file_match, NULL); modified_arg = TRUE; } } else { pr_trace_msg(trace_channel, 9, "no case-insensitive matches found for file '%s' in directory '%s'", dst_file, dst_dir); /* No match (or exact match) found; restore the original dst_path. */ if (dst_ptr != NULL) { *dst_ptr = '/'; } } /* Overwrite the client-given paths. */ if (modified_arg) { pr_trace_msg(trace_channel, 9, "replacing %s paths with '%s' and '%s'", cmd->argv[0], src_path, dst_path); case_replace_link_paths(cmd, proto, src_path, dst_path); } return PR_DECLINED(cmd); } /* Configuration handlers */ /* usage: CaseEngine on|off */ MODRET set_caseengine(cmd_rec *cmd) { int bool; config_rec *c; CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); CHECK_ARGS(cmd, 1); bool = get_boolean(cmd, 1); if (bool == -1) CONF_ERROR(cmd, "expected Boolean parameter"); c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = pcalloc(c->pool, sizeof(unsigned int)); *((unsigned int *) c->argv[0]) = bool; return PR_HANDLED(cmd); } /* usage: CaseIgnore on|off|cmd-list */ MODRET set_caseignore(cmd_rec *cmd) { unsigned int argc; int ignore = FALSE; char **argv; config_rec *c; CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR); CHECK_ARGS(cmd, 1); ignore = get_boolean(cmd, 1); c = add_config_param(cmd->argv[0], 2, NULL, NULL); c->flags |= CF_MERGEDOWN_MULTI; c->argv[0] = pcalloc(c->pool, sizeof(unsigned int)); *((unsigned int *) c->argv[0]) = 1; if (ignore != -1) { *((unsigned int *) c->argv[0]) = ignore; return PR_HANDLED(cmd); } /* Parse the parameter as a command list. */ argc = cmd->argc-1; argv = (char **) cmd->argv; c->argv[1] = pcalloc(c->pool, sizeof(array_header *)); *((array_header **) c->argv[1]) = pr_expr_create(c->pool, &argc, argv); return PR_HANDLED(cmd); } /* usage: CaseLog path|"none" */ MODRET set_caselog(cmd_rec *cmd) { CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL); CHECK_ARGS(cmd, 1); if (pr_fs_valid_path(cmd->argv[1]) < 0) CONF_ERROR(cmd, "must be an absolute path"); add_config_param_str(cmd->argv[0], 1, cmd->argv[1]); return PR_HANDLED(cmd); } /* Initialization functions */ static int case_sess_init(void) { config_rec *c; c = find_config(main_server->conf, CONF_PARAM, "CaseEngine", FALSE); if (c != NULL && *((unsigned int *) c->argv[0]) == TRUE) { case_engine = TRUE; } if (!case_engine) { return 0; } c = find_config(main_server->conf, CONF_PARAM, "CaseLog", FALSE); if (c == NULL) return 0; if (strncasecmp((char *) c->argv[0], "none", 5) != 0) { int res; pr_signals_block(); PRIVS_ROOT res = pr_log_openfile((char *) c->argv[0], &case_logfd, 0660); PRIVS_RELINQUISH pr_signals_unblock(); if (res < 0) { pr_log_pri(PR_LOG_NOTICE, MOD_CASE_VERSION ": error opening CaseLog '%s': %s", (char *) c->argv[0], strerror(errno)); } } return 0; } /* Module API tables */ static conftable case_conftab[] = { { "CaseEngine", set_caseengine, NULL }, { "CaseIgnore", set_caseignore, NULL }, { "CaseLog", set_caselog, NULL }, { NULL } }; static cmdtable case_cmdtab[] = { { PRE_CMD, C_APPE, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_CWD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_DELE, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_LIST, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_MDTM, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_MKD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_MLSD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_MLST, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_NLST, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_RETR, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_RMD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_RNFR, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_RNTO, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_SITE, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_SIZE, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_STAT, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_STOR, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_XCWD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_XMKD, G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, C_XRMD, G_NONE, case_pre_cmd, TRUE, FALSE }, /* The following are SFTP requests */ { PRE_CMD, "LINK", G_NONE, case_pre_link, TRUE, FALSE }, { PRE_CMD, "LSTAT", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "OPENDIR", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "READLINK", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "REALPATH", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "SETSTAT", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "STAT", G_NONE, case_pre_cmd, TRUE, FALSE }, { PRE_CMD, "SYMLINK", G_NONE, case_pre_link, TRUE, FALSE }, { 0, NULL } }; module case_module = { NULL, NULL, /* Module API version 2.0 */ 0x20, /* Module name */ "case", /* Module configuration handler table */ case_conftab, /* Module command handler table */ case_cmdtab, /* Module authentication handler table */ NULL, /* Module initialization function */ NULL, /* Session initialization function */ case_sess_init, /* Module version */ MOD_CASE_VERSION }; proftpd-mod_case/mod_case.html0000644000175000001440000001256713063310625015772 0ustar hilleusers ProFTPD module mod_case

ProFTPD module mod_case



The mod_case module is designed to help ProFTPD be case-insensitive, for those sites that may need it (e.g. those that are migrating from a Windows environment or have mounted Windows filesystems).

The mod_case module works by performing two checks on the filename used in FTP commands. First, mod_case will scan the directory to see if there is already a file whose name exactly matches the given filename. If not, mod_case will then looks for any case-insensitive matches.

This module is contained in the mod_case.c file for ProFTPD 1.3.x, and is not compiled by default. Installation instructions are discussed here.

The most current version of mod_case can be found at:

  https://github.com/Castaglia/proftpd-mod_case

Author

Please contact TJ Saunders <tj at castaglia.org> with any questions, concerns, or suggestions regarding this module.

Directives


CaseEngine

Syntax: CaseEngine on|off
Default: off
Context: server config, <VirtualHost>, <Global>
Module: mod_case
Compatibility: 1.2.9 and later

The CaseEngine directive enables or disables the module's runtime case-matching engine. If it is set to off this module does no case-insensitive checking. Use this directive to disable the module instead of commenting out all mod_case directives.


CaseIgnore

Syntax: CaseIgnore on|off|cmd-list
Default: off
Context: server config, <VirtualHost>, <Global>, <Anonymous>, <Directory>
Module: mod_case
Compatibility: 1.2.9 and later

The CaseIgnore directive is used to enable case-insensitive matching, possibly on a per-FTP command basis. If it is set to off, no case-insensitive matching is performed. If set to on, then case-insensitive matcing is performed for all FTP commands that mod_case handles (see below). Otherwise, one can configure a cmd-list, which is a comma-separated list of FTP commands for which mod_case is to do case-insensitive matching.

The mod_case module handles the following FTP commands:

Examples:

  # Enable case-insensitivity for all FTP commands handled by mod_case
  CaseIgnore on

  # Enable case-insensitivity only for downloads
  CaseIgnore RETR

  # Enable case-insensitivity for uploads and downloads
  CaseIgnore APPE,RETR,STOR


CaseLog

Syntax: CaseLog path|"none"
Default: None
Context: server config, <VirtualHost>, <Global>
Module: mod_case
Compatibility: 1.2.9 and later

The CaseLog directive is used to a specify a log file for mod_case reporting and debugging, and can be done a per-server basis. The path parameter must be the full path to the file to use for logging. Note that this path must not be to a world-writable directory and, unless AllowLogSymlinks is explicitly set to on (generally a bad idea), the path must not be a symbolic link.

If path is "none", no logging will be done at all; this setting can be used to override a CaseLog setting inherited from a <Global> context.


Installation

To install mod_case, copy the mod_case.c file into
  proftpd-dir/contrib/
after unpacking the latest proftpd-1.3.x source code. Then follow the usual steps for using third-party modules in proftpd:
  ./configure --with-modules=mod_case
  make
  make install



© Copyright 2004-2015 TJ Saunders
All Rights Reserved