80b5f20
--- contrib/mod_copy.c
80b5f20
+++ contrib/mod_copy.c
80b5f20
@@ -31,7 +31,7 @@
80b5f20
 
80b5f20
 #include "conf.h"
80b5f20
 
80b5f20
-#define MOD_COPY_VERSION	"mod_copy/0.4"
80b5f20
+#define MOD_COPY_VERSION	"mod_copy/0.5"
80b5f20
 
80b5f20
 /* Make sure the version of proftpd is as necessary. */
80b5f20
 #if PROFTPD_VERSION_NUMBER < 0x0001030401
80b5f20
@@ -40,6 +40,8 @@
80b5f20
 
80b5f20
 extern pr_response_t *resp_list, *resp_err_list;
80b5f20
 
80b5f20
+static int copy_engine = TRUE;
80b5f20
+
80b5f20
 static const char *trace_channel = "copy";
80b5f20
 
80b5f20
 /* These are copied largely from src/mkhome.c */
80b5f20
@@ -471,10 +473,37 @@ static int copy_paths(pool *p, const cha
80b5f20
   return 0;
80b5f20
 }
80b5f20
 
80b5f20
+/* Configuration handlers
80b5f20
+ */
80b5f20
+
80b5f20
+/* usage: CopyEngine on|off */
80b5f20
+MODRET set_copyengine(cmd_rec *cmd) {
80b5f20
+  int engine = -1;
80b5f20
+  config_rec *c;
80b5f20
+
80b5f20
+  CHECK_ARGS(cmd, 1);
80b5f20
+  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
80b5f20
+
80b5f20
+  engine = get_boolean(cmd, 1);
80b5f20
+  if (engine == -1) {
80b5f20
+    CONF_ERROR(cmd, "expected Boolean parameter");
80b5f20
+  }
80b5f20
+
80b5f20
+  c = add_config_param(cmd->argv[0], 1, NULL);
80b5f20
+  c->argv[0] = palloc(c->pool, sizeof(int));
80b5f20
+  *((int *) c->argv[0]) = engine;
80b5f20
+
80b5f20
+  return PR_HANDLED(cmd);
80b5f20
+}
80b5f20
+
80b5f20
 /* Command handlers
80b5f20
  */
80b5f20
 
80b5f20
 MODRET copy_copy(cmd_rec *cmd) {
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return PR_DECLINED(cmd);
80b5f20
+  }
80b5f20
+
80b5f20
   if (cmd->argc < 2) {
80b5f20
     return PR_DECLINED(cmd);
80b5f20
   }
80b5f20
@@ -539,12 +568,26 @@ MODRET copy_cpfr(cmd_rec *cmd) {
80b5f20
   register unsigned int i;
80b5f20
   int res;
80b5f20
   char *path = "";
80b5f20
+  unsigned char *authenticated = NULL;
80b5f20
+
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return PR_DECLINED(cmd);
80b5f20
+  }
80b5f20
 
80b5f20
   if (cmd->argc < 3 ||
80b5f20
       strncasecmp(cmd->argv[1], "CPFR", 5) != 0) {
80b5f20
     return PR_DECLINED(cmd);
80b5f20
   }
80b5f20
 
80b5f20
+  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
80b5f20
+  if (authenticated == NULL ||
80b5f20
+      *authenticated == FALSE) {
80b5f20
+    pr_response_add_err(R_530, _("Please login with USER and PASS"));
80b5f20
+  
80b5f20
+    errno = EPERM;
80b5f20
+    return PR_ERROR(cmd);
80b5f20
+  }
80b5f20
+
80b5f20
   CHECK_CMD_MIN_ARGS(cmd, 3);
80b5f20
 
80b5f20
   /* Construct the target file name by concatenating all the parameters after
80b5f20
@@ -594,12 +637,26 @@ MODRET copy_cpfr(cmd_rec *cmd) {
80b5f20
 MODRET copy_cpto(cmd_rec *cmd) {
80b5f20
   register unsigned int i;
80b5f20
   char *from, *to = "";
80b5f20
+  unsigned char *authenticated = NULL;
80b5f20
+
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return PR_DECLINED(cmd);
80b5f20
+  }
80b5f20
 
80b5f20
   if (cmd->argc < 3 ||
80b5f20
       strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
80b5f20
     return PR_DECLINED(cmd);
80b5f20
   }
80b5f20
 
80b5f20
+  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
80b5f20
+  if (authenticated == NULL ||
80b5f20
+      *authenticated == FALSE) {
80b5f20
+    pr_response_add_err(R_530, _("Please login with USER and PASS"));
80b5f20
+
80b5f20
+    errno = EPERM;
80b5f20
+    return PR_ERROR(cmd);
80b5f20
+  }
80b5f20
+
80b5f20
   CHECK_CMD_MIN_ARGS(cmd, 3);
80b5f20
 
80b5f20
   from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL);
80b5f20
@@ -632,6 +689,10 @@ MODRET copy_cpto(cmd_rec *cmd) {
80b5f20
 }
80b5f20
 
80b5f20
 MODRET copy_log_site(cmd_rec *cmd) {
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return PR_DECLINED(cmd);
80b5f20
+  }
80b5f20
+
80b5f20
   if (cmd->argc < 3 ||
80b5f20
       strncasecmp(cmd->argv[1], "CPTO", 5) != 0) {
80b5f20
     return PR_DECLINED(cmd);
80b5f20
@@ -643,23 +704,58 @@ MODRET copy_log_site(cmd_rec *cmd) {
80b5f20
   return PR_DECLINED(cmd);
80b5f20
 }
80b5f20
 
80b5f20
+MODRET copy_post_pass(cmd_rec *cmd) {
80b5f20
+  config_rec *c;
80b5f20
+
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return PR_DECLINED(cmd);
80b5f20
+  }
80b5f20
+
80b5f20
+  /* The CopyEngine directive may have been changed for this user by
80b5f20
+   * e.g. mod_ifsession, thus we check again.
80b5f20
+   */
80b5f20
+  c = find_config(main_server->conf, CONF_PARAM, "CopyEngine", FALSE);
80b5f20
+  if (c != NULL) {
80b5f20
+    copy_engine = *((int *) c->argv[0]);
80b5f20
+  }
80b5f20
+
80b5f20
+  return PR_DECLINED(cmd);
80b5f20
+}
80b5f20
+
80b5f20
 /* Initialization functions
80b5f20
  */
80b5f20
 
80b5f20
 static int copy_sess_init(void) {
80b5f20
+  config_rec *c;
80b5f20
+
80b5f20
+  c = find_config(main_server->conf, CONF_PARAM, "CopyEngine", FALSE);
80b5f20
+  if (c != NULL) {
80b5f20
+    copy_engine = *((int *) c->argv[0]);
80b5f20
+  }
80b5f20
+
80b5f20
+  if (copy_engine == FALSE) {
80b5f20
+    return 0;
80b5f20
+  }
80b5f20
+
80b5f20
   /* Advertise support for the SITE command */
80b5f20
   pr_feat_add("SITE COPY");
80b5f20
-
80b5f20
   return 0;
80b5f20
 }
80b5f20
 
80b5f20
 /* Module API tables
80b5f20
  */
80b5f20
 
80b5f20
+static conftable copy_conftab[] = {
80b5f20
+  { "CopyEngine",	set_copyengine,		NULL },
80b5f20
+
80b5f20
+  { NULL }
80b5f20
+};
80b5f20
+
80b5f20
 static cmdtable copy_cmdtab[] = {
80b5f20
   { CMD, 	C_SITE, G_WRITE,	copy_copy,	FALSE,	FALSE, CL_MISC },
80b5f20
   { CMD, 	C_SITE, G_DIRS,		copy_cpfr,	FALSE,	FALSE, CL_MISC },
80b5f20
   { CMD, 	C_SITE, G_WRITE,	copy_cpto,	FALSE,	FALSE, CL_MISC },
80b5f20
+  { POST_CMD,	C_PASS,	G_NONE,		copy_post_pass, FALSE,	FALSE },
80b5f20
   { LOG_CMD, 	C_SITE, G_NONE,		copy_log_site,	FALSE,	FALSE },
80b5f20
   { LOG_CMD_ERR, C_SITE, G_NONE,	copy_log_site,	FALSE,	FALSE },
80b5f20
 
80b5f20
@@ -676,7 +772,7 @@ module copy_module = {
80b5f20
   "copy",
80b5f20
 
80b5f20
   /* Module configuration handler table */
80b5f20
-  NULL,
80b5f20
+  copy_conftab,
80b5f20
 
80b5f20
   /* Module command handler table */
80b5f20
   copy_cmdtab,
80b5f20
--- doc/contrib/mod_copy.html
80b5f20
+++ doc/contrib/mod_copy.html
80b5f20
@@ -27,22 +27,40 @@ ProFTPD 1.3.x, and is not compile
80b5f20
 instructions are discussed here.
80b5f20
 
80b5f20
 

80b5f20
-The most current version of mod_copy can be found at:
80b5f20
-
80b5f20
-  http://www.castaglia.org/proftpd/
80b5f20
-
80b5f20
+The most current version of mod_copy is distributed with the
80b5f20
+ProFTPD source code.
80b5f20
 
80b5f20
 

Author

80b5f20
 

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

Directives

80b5f20
+
    80b5f20
    +  
  • CopyEngine
  • 80b5f20
    +
    80b5f20
    +
    80b5f20
     

    SITE Commands

    80b5f20
     
      80b5f20
         
    • SITE CPFR
    • 80b5f20
         
    • SITE CPTO
    • 80b5f20
       
      80b5f20
       
      80b5f20
      +

      80b5f20
      +
      80b5f20
      +

      CopyEngine

      80b5f20
      +Syntax: CopyEngine on|off
      80b5f20
      +Default: CopyEngine on
      80b5f20
      +Context: server config, <VirtualHost>, <Global>
      80b5f20
      +Module: mod_radius
      80b5f20
      +Compatibility: 1.3.6rc1 and later
      80b5f20
      +
      80b5f20
      +

      80b5f20
      +The CopyEngine directive enables or disables the module's
      80b5f20
      +handling of SITE COPY et al commands.  If it is set to
      80b5f20
      +off this module ignores these commands.
      80b5f20
      +
      80b5f20
      +

      80b5f20
       
      80b5f20
       

      SITE CPFR

      80b5f20
       This SITE command specifies the source file/directory to use
      80b5f20
      @@ -118,13 +136,8 @@ your existing server:
      80b5f20
       

      80b5f20
       

      80b5f20
       
      80b5f20
      -Author: $Author: castaglia $
      80b5f20
      -Last Updated: $Date: 2010/03/10 19:20:43 $
      80b5f20
      -
      80b5f20
      -

      80b5f20
      -
      80b5f20
       <font size=2>
      80b5f20
      -© Copyright 2009-2010 TJ Saunders
      80b5f20
      +© Copyright 2009-2015 TJ Saunders
      80b5f20
        All Rights Reserved
      80b5f20
       </font>
      80b5f20
       
      80b5f20
      --- RELEASE_NOTES
      80b5f20
      +++ RELEASE_NOTES
      80b5f20
      @@ -6,6 +6,14 @@ This file contains a description of the
      80b5f20
       releases.  More information on these changes can be found in the NEWS and
      80b5f20
       ChangeLog files.
      80b5f20
       
      80b5f20
      +Upcoming 1.3.5a
      80b5f20
      +---------------
      80b5f20
      +
      80b5f20
      +  + New Configuration Directives
      80b5f20
      +
      80b5f20
      +    CopyEngine (Bug#4169)
      80b5f20
      +
      80b5f20
      +
      80b5f20
       1.3.5
      80b5f20
       ---------
      80b5f20
       
      80b5f20
      --- tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm
      80b5f20
      +++ tests/t/lib/ProFTPD/Tests/Modules/mod_copy.pm
      80b5f20
      @@ -21,6 +21,11 @@ my $TESTS = {
      80b5f20
           test_class => [qw(forking)],
      80b5f20
         },
      80b5f20
       
      80b5f20
      +  copy_file_no_login => {
      80b5f20
      +    order => ++$order,
      80b5f20
      +    test_class => [qw(bug forking)],
      80b5f20
      +  },
      80b5f20
      +
      80b5f20
         copy_dir => {
      80b5f20
           order => ++$order,
      80b5f20
           test_class => [qw(forking)],
      80b5f20
      @@ -86,6 +91,11 @@ my $TESTS = {
      80b5f20
           test_class => [qw(forking)],
      80b5f20
         },
      80b5f20
       
      80b5f20
      +  copy_cpfr_cpto_no_login => {
      80b5f20
      +    order => ++$order,
      80b5f20
      +    test_class => [qw(bug forking)],
      80b5f20
      +  },
      80b5f20
      +
      80b5f20
         copy_cpto_no_cpfr => {
      80b5f20
           order => ++$order,
      80b5f20
           test_class => [qw(forking)],
      80b5f20
      @@ -263,6 +273,137 @@ sub copy_file {
      80b5f20
         unlink($log_file);
      80b5f20
       }
      80b5f20
       
      80b5f20
      +sub copy_file_no_login {
      80b5f20
      +  my $self = shift;
      80b5f20
      +  my $tmpdir = $self->{tmpdir};
      80b5f20
      +
      80b5f20
      +  my $config_file = "$tmpdir/copy.conf";
      80b5f20
      +  my $pid_file = File::Spec->rel2abs("$tmpdir/copy.pid");
      80b5f20
      +  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/copy.scoreboard");
      80b5f20
      +
      80b5f20
      +  my $log_file = File::Spec->rel2abs('tests.log');
      80b5f20
      +
      80b5f20
      +  my $auth_user_file = File::Spec->rel2abs("$tmpdir/copy.passwd");
      80b5f20
      +  my $auth_group_file = File::Spec->rel2abs("$tmpdir/copy.group");
      80b5f20
      +
      80b5f20
      +  my $user = 'proftpd';
      80b5f20
      +  my $passwd = 'test';
      80b5f20
      +  my $group = 'ftpd';
      80b5f20
      +  my $home_dir = File::Spec->rel2abs($tmpdir);
      80b5f20
      +  my $uid = 500;
      80b5f20
      +  my $gid = 500;
      80b5f20
      +
      80b5f20
      +  # Make sure that, if we're running as root, that the home directory has
      80b5f20
      +  # permissions/privs set for the account we create
      80b5f20
      +  if ($< == 0) {
      80b5f20
      +    unless (chmod(0755, $home_dir)) {
      80b5f20
      +      die("Can't set perms on $home_dir to 0755: $!");
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    unless (chown($uid, $gid, $home_dir)) {
      80b5f20
      +      die("Can't set owner of $home_dir to $uid/$gid: $!");
      80b5f20
      +    }
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
      80b5f20
      +    '/bin/bash');
      80b5f20
      +  auth_group_write($auth_group_file, $group, $gid, $user);
      80b5f20
      +
      80b5f20
      +  my $src_file = File::Spec->rel2abs("$home_dir/foo.txt");
      80b5f20
      +  if (open(my $fh, "> $src_file")) {
      80b5f20
      +    print $fh "Hello, World!\n";
      80b5f20
      +
      80b5f20
      +    unless (close($fh)) {
      80b5f20
      +      die("Can't write $src_file: $!");
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +  } else {
      80b5f20
      +    die("Can't open $src_file: $!");
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  my $dst_file = File::Spec->rel2abs("$home_dir/bar.txt");
      80b5f20
      +
      80b5f20
      +  my $config = {
      80b5f20
      +    PidFile => $pid_file,
      80b5f20
      +    ScoreboardFile => $scoreboard_file,
      80b5f20
      +    SystemLog => $log_file,
      80b5f20
      +
      80b5f20
      +    AuthUserFile => $auth_user_file,
      80b5f20
      +    AuthGroupFile => $auth_group_file,
      80b5f20
      +
      80b5f20
      +    IfModules => {
      80b5f20
      +      'mod_delay.c' => {
      80b5f20
      +        DelayEngine => 'off',
      80b5f20
      +      },
      80b5f20
      +    },
      80b5f20
      +  };
      80b5f20
      +
      80b5f20
      +  my ($port, $config_user, $config_group) = config_write($config_file, $config);
      80b5f20
      +
      80b5f20
      +  # Open pipes, for use between the parent and child processes.  Specifically,
      80b5f20
      +  # the child will indicate when it's done with its test by writing a message
      80b5f20
      +  # to the parent.
      80b5f20
      +  my ($rfh, $wfh);
      80b5f20
      +  unless (pipe($rfh, $wfh)) {
      80b5f20
      +    die("Can't open pipe: $!");
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  my $ex;
      80b5f20
      +
      80b5f20
      +  # Fork child
      80b5f20
      +  $self->handle_sigchld();
      80b5f20
      +  defined(my $pid = fork()) or die("Can't fork: $!");
      80b5f20
      +  if ($pid) {
      80b5f20
      +    eval {
      80b5f20
      +      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
      80b5f20
      +
      80b5f20
      +      eval { $client->site('COPY', 'foo.txt', 'bar.txt') };
      80b5f20
      +      unless ($@) { 
      80b5f20
      +        die("SITE COPY succeeded unexpectedly");
      80b5f20
      +      }
      80b5f20
      +
      80b5f20
      +      my $resp_code = $client->response_code();
      80b5f20
      +      my $resp_msg = $client->response_msg();
      80b5f20
      +
      80b5f20
      +      my $expected;
      80b5f20
      +      $expected = 530;
      80b5f20
      +      $self->assert($expected == $resp_code,
      80b5f20
      +        test_msg("Expected response code $expected, got $resp_code"));
      80b5f20
      +
      80b5f20
      +      $expected = "Please login with USER and PASS";
      80b5f20
      +      $self->assert($expected eq $resp_msg,
      80b5f20
      +        test_msg("Expected response message '$expected', got '$resp_msg'"));
      80b5f20
      +    };
      80b5f20
      +
      80b5f20
      +    if ($@) {
      80b5f20
      +      $ex = $@;
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    $wfh->print("done\n");
      80b5f20
      +    $wfh->flush();
      80b5f20
      +
      80b5f20
      +  } else {
      80b5f20
      +    eval { server_wait($config_file, $rfh) };
      80b5f20
      +    if ($@) {
      80b5f20
      +      warn($@);
      80b5f20
      +      exit 1;
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    exit 0;
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  # Stop server
      80b5f20
      +  server_stop($pid_file);
      80b5f20
      +
      80b5f20
      +  $self->assert_child_ok($pid);
      80b5f20
      +
      80b5f20
      +  if ($ex) {
      80b5f20
      +    die($ex);
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  unlink($log_file);
      80b5f20
      +}
      80b5f20
      +
      80b5f20
       sub copy_dir {
      80b5f20
         my $self = shift;
      80b5f20
         my $tmpdir = $self->{tmpdir};
      80b5f20
      @@ -2578,6 +2719,153 @@ sub copy_cpfr_cpto {
      80b5f20
           };
      80b5f20
       
      80b5f20
           if ($@) {
      80b5f20
      +      $ex = $@;
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    $wfh->print("done\n");
      80b5f20
      +    $wfh->flush();
      80b5f20
      +
      80b5f20
      +  } else {
      80b5f20
      +    eval { server_wait($config_file, $rfh) };
      80b5f20
      +    if ($@) {
      80b5f20
      +      warn($@);
      80b5f20
      +      exit 1;
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    exit 0;
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  # Stop server
      80b5f20
      +  server_stop($pid_file);
      80b5f20
      +
      80b5f20
      +  $self->assert_child_ok($pid);
      80b5f20
      +
      80b5f20
      +  if ($ex) {
      80b5f20
      +    die($ex);
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  unlink($log_file);
      80b5f20
      +}
      80b5f20
      +
      80b5f20
      +sub copy_cpfr_cpto_no_login {
      80b5f20
      +  my $self = shift;
      80b5f20
      +  my $tmpdir = $self->{tmpdir};
      80b5f20
      +
      80b5f20
      +  my $config_file = "$tmpdir/copy.conf";
      80b5f20
      +  my $pid_file = File::Spec->rel2abs("$tmpdir/copy.pid");
      80b5f20
      +  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/copy.scoreboard");
      80b5f20
      +
      80b5f20
      +  my $log_file = File::Spec->rel2abs('tests.log');
      80b5f20
      +
      80b5f20
      +  my $auth_user_file = File::Spec->rel2abs("$tmpdir/copy.passwd");
      80b5f20
      +  my $auth_group_file = File::Spec->rel2abs("$tmpdir/copy.group");
      80b5f20
      +
      80b5f20
      +  my $user = 'proftpd';
      80b5f20
      +  my $passwd = 'test';
      80b5f20
      +  my $group = 'ftpd';
      80b5f20
      +  my $home_dir = File::Spec->rel2abs($tmpdir);
      80b5f20
      +  my $uid = 500;
      80b5f20
      +  my $gid = 500;
      80b5f20
      +
      80b5f20
      +  # Make sure that, if we're running as root, that the home directory has
      80b5f20
      +  # permissions/privs set for the account we create
      80b5f20
      +  if ($< == 0) {
      80b5f20
      +    unless (chmod(0755, $home_dir)) {
      80b5f20
      +      die("Can't set perms on $home_dir to 0755: $!");
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +    unless (chown($uid, $gid, $home_dir)) {
      80b5f20
      +      die("Can't set owner of $home_dir to $uid/$gid: $!");
      80b5f20
      +    }
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
      80b5f20
      +    '/bin/bash');
      80b5f20
      +  auth_group_write($auth_group_file, $group, $gid, $user);
      80b5f20
      +
      80b5f20
      +  my $src_file = File::Spec->rel2abs("$home_dir/foo.txt");
      80b5f20
      +  if (open(my $fh, "> $src_file")) {
      80b5f20
      +    print $fh "Hello, World!\n";
      80b5f20
      +
      80b5f20
      +    unless (close($fh)) {
      80b5f20
      +      die("Can't write $src_file: $!");
      80b5f20
      +    }
      80b5f20
      +
      80b5f20
      +  } else {
      80b5f20
      +    die("Can't open $src_file: $!");
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  my $dst_file = File::Spec->rel2abs("$home_dir/bar.txt");
      80b5f20
      +
      80b5f20
      +  my $config = {
      80b5f20
      +    PidFile => $pid_file,
      80b5f20
      +    ScoreboardFile => $scoreboard_file,
      80b5f20
      +    SystemLog => $log_file,
      80b5f20
      +
      80b5f20
      +    AuthUserFile => $auth_user_file,
      80b5f20
      +    AuthGroupFile => $auth_group_file,
      80b5f20
      +
      80b5f20
      +    IfModules => {
      80b5f20
      +      'mod_delay.c' => {
      80b5f20
      +        DelayEngine => 'off',
      80b5f20
      +      },
      80b5f20
      +    },
      80b5f20
      +  };
      80b5f20
      +
      80b5f20
      +  my ($port, $config_user, $config_group) = config_write($config_file, $config);
      80b5f20
      +
      80b5f20
      +  # Open pipes, for use between the parent and child processes.  Specifically,
      80b5f20
      +  # the child will indicate when it's done with its test by writing a message
      80b5f20
      +  # to the parent.
      80b5f20
      +  my ($rfh, $wfh);
      80b5f20
      +  unless (pipe($rfh, $wfh)) {
      80b5f20
      +    die("Can't open pipe: $!");
      80b5f20
      +  }
      80b5f20
      +
      80b5f20
      +  my $ex;
      80b5f20
      +
      80b5f20
      +  # Fork child
      80b5f20
      +  $self->handle_sigchld();
      80b5f20
      +  defined(my $pid = fork()) or die("Can't fork: $!");
      80b5f20
      +  if ($pid) {
      80b5f20
      +    eval {
      80b5f20
      +      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
      80b5f20
      + 
      80b5f20
      +      eval { $client->site('CPFR', 'foo.txt') };
      80b5f20
      +      unless ($@) {
      80b5f20
      +        die("SITE CPFR succeeded unexpectedly");
      80b5f20
      +      }
      80b5f20
      +
      80b5f20
      +      my $resp_code = $client->response_code();
      80b5f20
      +      my $resp_msg = $client->response_msg();
      80b5f20
      +
      80b5f20
      +      my $expected;
      80b5f20
      +      $expected = 530;
      80b5f20
      +      $self->assert($expected == $resp_code,
      80b5f20
      +        test_msg("Expected response code $expected, got $resp_code"));
      80b5f20
      +
      80b5f20
      +      $expected = "Please login with USER and PASS";
      80b5f20
      +      $self->assert($expected eq $resp_msg,
      80b5f20
      +        test_msg("Expected response message '$expected', got '$resp_msg'"));
      80b5f20
      +
      80b5f20
      +      eval { $client->site('CPTO', 'bar.txt') };
      80b5f20
      +      unless ($@) {
      80b5f20
      +        die("SITE CPTO succeeded unexpectedly");
      80b5f20
      +      }
      80b5f20
      +
      80b5f20
      +      $resp_code = $client->response_code();
      80b5f20
      +      $resp_msg = $client->response_msg();
      80b5f20
      +
      80b5f20
      +      $expected = 530;
      80b5f20
      +      $self->assert($expected == $resp_code,
      80b5f20
      +        test_msg("Expected response code $expected, got $resp_code"));
      80b5f20
      +
      80b5f20
      +      $expected = "Please login with USER and PASS";
      80b5f20
      +      $self->assert($expected eq $resp_msg,
      80b5f20
      +        test_msg("Expected response message '$expected', got '$resp_msg'"));
      80b5f20
      +    };
      80b5f20
      +
      80b5f20
      +    if ($@) {
      80b5f20
             $ex = $@;
      80b5f20
           }
      80b5f20