diff --git a/NEWS b/NEWS index 7426ae915..176d25611 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,8 @@ terminate the session abruptly. - Issue 1640 - Support terminating connection after USER command when TLS is required. +- Issue 1676 - Support OPTS commands for querying configured policy for resumed + uploads, downloads. 1.3.8 - Released 04-Dec-2022 -------------------------------- diff --git a/modules/mod_xfer.c b/modules/mod_xfer.c index 1f1746f78..ec3dd20e1 100644 --- a/modules/mod_xfer.c +++ b/modules/mod_xfer.c @@ -2,7 +2,7 @@ * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu - * Copyright (c) 2001-2022 The ProFTPD Project team + * Copyright (c) 2001-2023 The ProFTPD Project team * * 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 @@ -1309,6 +1309,83 @@ static int get_hidden_store_path(cmd_rec *cmd, const char *path, return 0; } +MODRET xfer_opts_rest(cmd_rec *cmd) { + register unsigned int i; + char *method, *xfer_cmd; + unsigned char *authenticated; + + authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); + if (authenticated == NULL || + *authenticated == FALSE) { + pr_response_add_err(R_501, _("Please login with USER and PASS")); + + pr_cmd_set_errno(cmd, EPERM); + errno = EPERM; + return PR_ERROR(cmd); + } + + method = pstrdup(cmd->tmp_pool, cmd->argv[0]); + + /* Convert underscores to spaces in the method name, for prettier logging. */ + for (i = 0; method[i]; i++) { + if (method[i] == '_') { + method[i] = ' '; + } + } + + if (cmd->argc != 2) { + pr_response_add_err(R_501, _("'%s' not understood"), method); + + pr_cmd_set_errno(cmd, EINVAL); + errno = EINVAL; + return PR_ERROR(cmd); + } + + xfer_cmd = cmd->argv[1]; + if (strcasecmp(xfer_cmd, C_RETR) == 0) { + unsigned char *allow_restart = NULL; + + /* Do we allow resumed downloads? */ + allow_restart = get_param_ptr(main_server->conf, "AllowRetrieveRestart", + FALSE); + if (allow_restart == NULL || + *allow_restart == TRUE) { + pr_response_add(R_200, "%s", _("REST RETR allowed")); + return PR_HANDLED(cmd); + } + + pr_response_add_err(R_451, "%s", _("REST RETR not allowed")); + pr_cmd_set_errno(cmd, EPERM); + errno = EPERM; + return PR_ERROR(cmd); + } + + if (strcasecmp(xfer_cmd, C_STOR) == 0) { + unsigned char *allow_restart = NULL; + + /* Do we allow resumed uploads? */ + allow_restart = get_param_ptr(main_server->conf, "AllowStoreRestart", + FALSE); + if (allow_restart != NULL && + *allow_restart == TRUE) { + pr_response_add(R_200, "%s", _("REST STOR allowed")); + return PR_HANDLED(cmd); + } + + pr_response_add_err(R_451, "%s", _("REST STOR not allowed")); + pr_cmd_set_errno(cmd, EPERM); + errno = EPERM; + return PR_ERROR(cmd); + } + + /* Otherwise, it's an OPTS REST query we do not support. */ + pr_response_add_err(R_501, _("'%s' not understood"), method); + + pr_cmd_set_errno(cmd, EINVAL); + errno = EINVAL; + return PR_ERROR(cmd); +} + MODRET xfer_post_prot(cmd_rec *cmd) { CHECK_CMD_ARGS(cmd, 2); @@ -4441,6 +4518,7 @@ static cmdtable xfer_cmdtab[] = { { LOG_CMD_ERR, C_APPE,G_NONE, xfer_err_cleanup, FALSE, FALSE }, { CMD, C_ABOR, G_NONE, xfer_abor, TRUE, TRUE, CL_MISC }, { LOG_CMD, C_ABOR, G_NONE, xfer_log_abor, TRUE, TRUE, CL_MISC }, + { CMD, C_OPTS "_REST", G_NONE, xfer_opts_rest, FALSE, FALSE }, { CMD, C_REST, G_NONE, xfer_rest, TRUE, FALSE, CL_MISC }, { CMD, C_RANG, G_NONE, xfer_rang, TRUE, FALSE, CL_MISC }, { POST_CMD,C_PROT, G_NONE, xfer_post_prot, FALSE, FALSE }, diff --git a/tests/t/lib/ProFTPD/Tests/Commands/OPTS.pm b/tests/t/lib/ProFTPD/Tests/Commands/OPTS.pm index 57a33ab38..2d1c770ef 100644 --- a/tests/t/lib/ProFTPD/Tests/Commands/OPTS.pm +++ b/tests/t/lib/ProFTPD/Tests/Commands/OPTS.pm @@ -20,6 +20,41 @@ my $TESTS = { test_class => [qw(bug forking)], }, + opts_rest_without_login_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_retr_default_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_retr_allowretrieverestart_on_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_retr_allowretrieverestart_off_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_stor_default_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_stor_allowstorerestart_on_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + + opts_rest_stor_allowstorerestart_off_issue1676 => { + order => ++$order, + test_class => [qw(bug forking)], + }, + }; sub new { @@ -33,48 +68,108 @@ sub list_tests { sub opts_bug3870 { my $self = shift; my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', - my $config_file = "$tmpdir/cmds.conf"; - my $pid_file = File::Spec->rel2abs("$tmpdir/cmds.pid"); - my $scoreboard_file = File::Spec->rel2abs("$tmpdir/cmds.scoreboard"); + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + my $opts_args = ('a ' x 100); + + eval { $client->opts($opts_args) }; + unless ($@) { + die("OPTS command succeeded unexpectedly"); + } - my $log_file = test_get_logfile(); + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); - my $auth_user_file = File::Spec->rel2abs("$tmpdir/cmds.passwd"); - my $auth_group_file = File::Spec->rel2abs("$tmpdir/cmds.group"); + $client->quit(); - my $user = 'proftpd'; - my $passwd = 'test'; - my $group = 'ftpd'; - my $home_dir = File::Spec->rel2abs($tmpdir); - my $uid = 500; - my $gid = 500; + my $expected = 550; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); - # 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: $!"); + $expected = "OPTS: Invalid argument"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + if ($@) { + $ex = $@; } - unless (chown($uid, $gid, $home_dir)) { - die("Can't set owner of $home_dir to $uid/$gid: $!"); + $wfh->print("done\n"); + $wfh->flush(); + + } else { + eval { server_wait($setup->{config_file}, $rfh) }; + if ($@) { + warn($@); + exit 1; } + + exit 0; } - auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, - '/bin/bash'); - auth_group_write($auth_group_file, $group, $gid, $user); + # Stop server + server_stop($setup->{pid_file}); + $self->assert_child_ok($pid); + + test_cleanup($setup->{log_file}, $ex); +} + +sub opts_rest_without_login_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); my $config = { - PidFile => $pid_file, - ScoreboardFile => $scoreboard_file, - SystemLog => $log_file, - TraceLog => $log_file, + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, Trace => 'DEFAULT:10', - AuthUserFile => $auth_user_file, - AuthGroupFile => $auth_group_file, + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, AuthOrder => 'mod_auth_file.c', IfModules => { @@ -84,7 +179,8 @@ sub opts_bug3870 { }, }; - my ($port, $config_user, $config_group) = config_write($config_file, $config); + 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 @@ -101,12 +197,275 @@ sub opts_bug3870 { defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { + # Allow for server startup + sleep(2); + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); - $client->login($user, $passwd); - my $opts_args = ('a ' x 100); + eval { $client->opts('REST RETR') }; + unless ($@) { + die("OPTS command succeeded unexpectedly"); + } - eval { $client->opts($opts_args) }; + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 501; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = 'Please login with USER and PASS'; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + 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); +} + +sub opts_rest_retr_default_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + $client->opts('REST RETR'); + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 200; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = "REST RETR allowed"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + 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); +} + +sub opts_rest_retr_allowretrieverestart_on_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + AllowRetrieveRestart => 'on', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + $client->opts('REST RETR'); + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 200; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = "REST RETR allowed"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + 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); +} + +sub opts_rest_retr_allowretrieverestart_off_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + AllowRetrieveRestart => 'off', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + eval { $client->opts('REST RETR') }; unless ($@) { die("OPTS command succeeded unexpectedly"); } @@ -114,17 +473,105 @@ sub opts_bug3870 { my $resp_code = $client->response_code(); my $resp_msg = $client->response_msg(); - my $expected; + $client->quit(); - $expected = 550; + my $expected = 451; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); - $expected = "OPTS: Invalid argument"; + $expected = "REST RETR not allowed"; $self->assert($expected eq $resp_msg, test_msg("Expected response message '$expected', got '$resp_msg'")); }; + 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); +} + +sub opts_rest_stor_default_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + eval { $client->opts('REST STOR') }; + unless ($@) { + die("OPTS command succeeded unexpectedly"); + } + + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 451; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = "REST STOR not allowed"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; if ($@) { $ex = $@; } @@ -133,7 +580,7 @@ sub opts_bug3870 { $wfh->flush(); } else { - eval { server_wait($config_file, $rfh) }; + eval { server_wait($setup->{config_file}, $rfh) }; if ($@) { warn($@); exit 1; @@ -143,18 +590,188 @@ sub opts_bug3870 { } # Stop server - server_stop($pid_file); + server_stop($setup->{pid_file}); + $self->assert_child_ok($pid); + + test_cleanup($setup->{log_file}, $ex); +} + +sub opts_rest_stor_allowstorerestart_on_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + AllowStoreRestart => 'on', + + IfModules => { + '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 { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + $client->opts('REST STOR'); + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 200; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = "REST STOR allowed"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + 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); - if ($ex) { - test_append_logfile($log_file, $ex); - unlink($log_file); + test_cleanup($setup->{log_file}, $ex); +} - die($ex); +sub opts_rest_stor_allowstorerestart_off_issue1676 { + my $self = shift; + my $tmpdir = $self->{tmpdir}; + my $setup = test_setup($tmpdir, 'cmds'); + + my $config = { + PidFile => $setup->{pid_file}, + ScoreboardFile => $setup->{scoreboard_file}, + SystemLog => $setup->{log_file}, + TraceLog => $setup->{log_file}, + Trace => 'DEFAULT:10', + + AuthUserFile => $setup->{auth_user_file}, + AuthGroupFile => $setup->{auth_group_file}, + AuthOrder => 'mod_auth_file.c', + + AllowStoreRestart => 'off', + + IfModules => { + '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: $!"); } - unlink($log_file); + my $ex; + + # Fork child + $self->handle_sigchld(); + defined(my $pid = fork()) or die("Can't fork: $!"); + if ($pid) { + eval { + # Allow for server startup + sleep(2); + + my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); + $client->login($setup->{user}, $setup->{passwd}); + + eval { $client->opts('REST STOR') }; + unless ($@) { + die("OPTS command succeeded unexpectedly"); + } + + my $resp_code = $client->response_code(); + my $resp_msg = $client->response_msg(); + + $client->quit(); + + my $expected = 451; + $self->assert($expected == $resp_code, + test_msg("Expected response code $expected, got $resp_code")); + + $expected = "REST STOR not allowed"; + $self->assert($expected eq $resp_msg, + test_msg("Expected response message '$expected', got '$resp_msg'")); + }; + 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;