mod_case/0000755000175000017500000000000011560344252010446 5ustar tjtjmod_case/mod_case.html0000644000175000017500000001202311553331744013110 0ustar tjtj 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).

mod_case 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 scan the directory again, this time looking for 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:

  http://www.castaglia.org/proftpd/

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.

mod_case handles the following FTP commands: APPE, CWD, DELE, LIST, MDTM, MKD, MLSD, MLST, NLST, RETR, RMD, RNFR, RNTO, SIZE, STOR, XCWD, XMKD, and XRMD.

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-writeable 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.2 source code. Then follow the usual steps for using third-party modules in proftpd:
  ./configure --with-modules=mod_case
  make
  make install



Author: $Author: tj $
Last Updated: $Date: 2011/04/19 16:09:58 $


© Copyright 2004-2011 TJ Saunders
All Rights Reserved


mod_case/mod_case.c0000644000175000017500000004347411560345132012376 0ustar tjtj/* * ProFTPD: mod_case -- provides case-insensivity * * Copyright (c) 2004-2011 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * This is mod_case, contrib software for proftpd 1.2 and above. * For more information contact TJ Saunders . * * $Id: mod_case.c,v 1.9 2011/05/04 21:50:41 tj Exp tj $ */ #include "conf.h" #include "privs.h" #define MOD_CASE_VERSION "mod_case/0.7" /* 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; /* 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_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) { /* 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) { 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 { cmd->argv[1] = pstrcat(cmd->pool, dir, file, NULL); /* 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 = pstrcat(cmd->pool, dir, file, NULL); } } 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, char **matched_file) { DIR *dirh; struct dirent *dent; char *file_match; /* Open the directory. */ dirh = pr_fsio_opendir(dir); if (dirh == NULL) { (void) pr_log_writefile(case_logfd, MOD_CASE_VERSION, "error opening directory '%s': %s", dir, strerror(errno)); 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) { 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 */ MODRET case_pre_cmd(cmd_rec *cmd) { config_rec *c; char *path = NULL, *dir = NULL, *file = NULL, *file_match = NULL, *tmp; const char *proto = 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] && 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 { path = pstrdup(cmd->tmp_pool, cmd->argv[1]); } } /* 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); 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. */ 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. */ 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, *file_match = NULL, *src_ptr, *dst_ptr, *ptr; const char *proto = 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] && 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; } } file_len = strlen(src_file); 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 { /* 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; res = case_have_file(cmd->tmp_pool, dst_dir, dst_file, file_len, &file_match); if (res < 0) { return PR_DECLINED(cmd); } if (res == TRUE && file_match != NULL) { /* Replace the destination path */ dst_path = pdircat(cmd->tmp_pool, dst_dir, file_match, NULL); modified_arg = TRUE; } else { /* No match (or exact match) found; restore the original src_path. */ if (dst_ptr != NULL) { *dst_ptr = '/'; } } /* Overwrite the client-given paths. */ if (modified_arg) { 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) { int bool, argc; char **argv; config_rec *c; CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR); CHECK_ARGS(cmd, 1); bool = 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 (bool != -1) { *((unsigned int *) c->argv[0]) = bool; return PR_HANDLED(cmd); } /* Parse the parameter as a command list. */ argc = cmd->argc-1; argv = 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_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 is for mod_copy's SITE CPFR and SITE CPTO commands. */ /* XXX Need to handle SITE CHMOD, SITE CHGRP as well */ /* 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 }; mod_case/t/0000755000175000017500000000000011547155521010715 5ustar tjtjmod_case/t/lib/0000755000175000017500000000000011547155514011465 5ustar tjtjmod_case/t/lib/ProFTPD/0000755000175000017500000000000011547155514012703 5ustar tjtjmod_case/t/lib/ProFTPD/Tests/0000755000175000017500000000000011547155514014005 5ustar tjtjmod_case/t/lib/ProFTPD/Tests/Modules/0000755000175000017500000000000011555364201015407 5ustar tjtjmod_case/t/lib/ProFTPD/Tests/Modules/mod_case/0000755000175000017500000000000011560344722017163 5ustar tjtjmod_case/t/lib/ProFTPD/Tests/Modules/mod_case/sftp.pm0000644000175000017500000016231511560343630020502 0ustar tjtjpackage 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)], }, }; 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; $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'")); $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); } 1; mod_case/t/lib/ProFTPD/Tests/Modules/mod_case.pm0000644000175000017500000020424411555364201017525 0ustar tjtjpackage 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_list => { 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)], }, }; 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 = 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 $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) { 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 = 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; 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")); $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) { 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 = 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; 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")); $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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) { 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 = 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 $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->stat("TeSt.TxT"); my $expected; $expected = 211; $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) { 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 = 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 $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->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'")); }; 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_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 = 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; 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) { 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 = 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; 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) { 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 = 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; 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) { 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); } 1; mod_case/t/modules/0000755000175000017500000000000011555364206012366 5ustar tjtjmod_case/t/modules/mod_case/0000755000175000017500000000000011560341276014136 5ustar tjtjmod_case/t/modules/mod_case/sftp.t0000644000175000017500000000027211547155647015312 0ustar tjtj#!/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"); mod_case/t/modules/mod_case.t0000644000175000017500000000026411555364206014327 0ustar tjtj#!/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");