Newer
Older
Daniel Stenberg
committed
#!/usr/bin/perl
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://curl.haxx.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# $Id$
###########################################################################
# This is the FTP server designed for the curl test suite.
#
Daniel Stenberg
committed
# It is meant to exercise curl, it is not meant to be a fully working
# or even very standard compliant server.
#
# You may optionally specify port on the command line, otherwise it'll
# default to port 8921.
#
Daniel Stenberg
committed
# All socket/network/TCP related stuff is done by the 'sockfilt' program.
#
Daniel Stenberg
committed
use strict;
Daniel Stenberg
committed
use IPC::Open2;
Daniel Stenberg
committed
Daniel Stenberg
committed
require "ftp.pm";
Daniel Stenberg
committed
my $ftpdnum="";
# open and close each time to allow removal at any time
sub logmsg {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
Daniel Stenberg
committed
open(FTPLOG, ">>log/ftpd$ftpdnum.log");
printf FTPLOG ("%02d:%02d:%02d ", $hour, $min, $sec);
close(FTPLOG);
Daniel Stenberg
committed
sub ftpmsg {
# append to the server.input file
Daniel Stenberg
committed
open(INPUT, ">>log/server$ftpdnum.input") ||
logmsg "failed to open log/server$ftpdnum.input\n";
print INPUT @_;
close(INPUT);
# use this, open->print->close system only to make the file
# open as little as possible, to make the test suite run
# better on windows/cygwin
}
Daniel Stenberg
committed
my $verbose=0; # set to 1 for debugging
Daniel Stenberg
committed
my $retrweirdo=0;
my $retrnosize=0;
my $srcdir=".";
Daniel Stenberg
committed
my $controldelay=0; # set to 1 to delay the control connect data sending to
# test that curl deals with that nicely
Daniel Stenberg
committed
my $slavepid; # for the DATA connection sockfilt slave process
my $ipv6;
my $ext; # append to log/pid file names
my $grok_eprt;
Daniel Stenberg
committed
my $port = 8921; # just a default
Daniel Stenberg
committed
my $pidfile = ".ftpd.pid"; # a default, use --pidfile
Daniel Stenberg
committed
do {
if($ARGV[0] eq "-v") {
$verbose=1;
}
elsif($ARGV[0] eq "-s") {
$srcdir=$ARGV[1];
shift @ARGV;
}
Daniel Stenberg
committed
elsif($ARGV[0] eq "--id") {
$ftpdnum=$ARGV[1];
shift @ARGV;
}
Daniel Stenberg
committed
elsif($ARGV[0] eq "--pidfile") {
$pidfile=$ARGV[1];
shift @ARGV;
}
Daniel Stenberg
committed
elsif($ARGV[0] eq "--ipv6") {
$ipv6="--ipv6";
$ext="ipv6";
$grok_eprt = 1;
}
elsif($ARGV[0] eq "--port") {
$port = $ARGV[1];
shift @ARGV;
Daniel Stenberg
committed
}
} while(shift @ARGV);
sub catch_zap {
my $signame = shift;
print STDERR "ftpserver.pl received SIG$signame, exiting\n";
ftpkillslaves(1);
die "Somebody sent me a SIG$signame";
}
$SIG{INT} = \&catch_zap;
$SIG{KILL} = \&catch_zap;
Daniel Stenberg
committed
my $sfpid;
Daniel Stenberg
committed
Daniel Stenberg
committed
sub startsf {
my $cmd="./server/sockfilt --port $port --logfile log/sockctrl$ftpdnum$ext.log --pidfile .sockfilt$ftpdnum$ext.pid $ipv6";
$sfpid = open2(\*SFREAD, \*SFWRITE, $cmd);
Daniel Stenberg
committed
Daniel Stenberg
committed
print STDERR "$cmd\n" if($verbose);
Daniel Stenberg
committed
Daniel Stenberg
committed
print SFWRITE "PING\n";
my $pong;
sysread SFREAD, $pong, 5;
Daniel Stenberg
committed
if($pong !~ /^PONG/) {
logmsg "Failed sockfilt command: $cmd\n";
kill(9, $sfpid);
Daniel Stenberg
committed
die "Failed to start sockfilt!";
}
open(STDIN, "<&SFREAD") || die "can't dup client to stdin";
open(STDOUT, ">&SFWRITE") || die "can't dup client to stdout";
}
Daniel Stenberg
committed
# remove the file here so that if startsf() fails, it is very noticable
Daniel Stenberg
committed
unlink($pidfile);
Daniel Stenberg
committed
Daniel Stenberg
committed
startsf();
Daniel Stenberg
committed
Daniel Stenberg
committed
logmsg sprintf("FTP server listens on port IPv%d/$port\n", $ipv6?6:4);
Daniel Stenberg
committed
open(PID, ">$pidfile");
Daniel Stenberg
committed
close(PID);
logmsg("logged pid $$ in $pidfile\n");
Daniel Stenberg
committed
sub sockfilt {
my $l;
foreach $l (@_) {
printf "DATA\n%04x\n", length($l);
print $l;
}
}
Daniel Stenberg
committed
# Send data to the client on the control stream, which happens to be plain
# stdout.
sub sendcontrol {
if(!$controldelay) {
# spit it all out at once
Daniel Stenberg
committed
sockfilt @_;
Daniel Stenberg
committed
}
else {
my $a = join("", @_);
my @a = split("", $a);
for(@a) {
Daniel Stenberg
committed
sockfilt $_;
select(undef, undef, undef, 0.01);
Daniel Stenberg
committed
}
}
Daniel Stenberg
committed
my $log;
foreach $log (@_) {
my $l = $log;
$l =~ s/[\r\n]//g;
logmsg "> \"$l\"\n";
Daniel Stenberg
committed
}
Daniel Stenberg
committed
}
# Send data to the client on the data stream
sub senddata {
Daniel Stenberg
committed
my $l;
foreach $l (@_) {
printf DWRITE "DATA\n%04x\n", length($l);
print DWRITE $l;
}
Daniel Stenberg
committed
}
# USER is ok in fresh state
'USER' => 'fresh',
'PASS' => 'passwd',
'PASV' => 'loggedin|twosock',
Daniel Stenberg
committed
'EPRT' => 'loggedin|twosock',
'TYPE' => 'loggedin|twosock',
'LIST' => 'twosock',
'NLST' => 'twosock',
'RETR' => 'twosock',
'STOR' => 'twosock',
'APPE' => 'twosock',
'REST' => 'twosock',
'ACCT' => 'loggedin',
'CWD' => 'loggedin|twosock',
'SYST' => 'loggedin',
'SIZE' => 'loggedin|twosock',
'PWD' => 'loggedin|twosock',
'MKD' => 'loggedin|twosock',
'RNFR' => 'loggedin|twosock',
'RNTO' => 'loggedin|twosock',
'DELE' => 'loggedin|twosock',
'MDTM' => 'loggedin|twosock',
Daniel Stenberg
committed
# initially, we're in 'fresh' state
my %statechange = ( 'USER' => 'passwd', # USER goes to passwd state
'PASS' => 'loggedin', # PASS goes to loggedin state
Daniel Stenberg
committed
'PORT' => 'twosock', # PORT goes to twosock
Daniel Stenberg
committed
'EPRT' => 'twosock', # EPRT goes to twosock
Daniel Stenberg
committed
'PASV' => 'twosock', # PASV goes to twosock
Daniel Stenberg
committed
);
# this text is shown before the function specified below is run
my %displaytext = ('USER' => '331 We are happy you popped in!',
Daniel Stenberg
committed
'PASS' => '230 Welcome you silly person',
'PORT' => '200 You said PORT - I say FINE',
'TYPE' => '200 I modify TYPE as you wanted',
'NLST' => '150 here comes a directory',
'SYST' => '215 UNIX Type: L8', # just fake something
'QUIT' => '221 bye bye baby', # just reply something
'PWD' => '257 "/nowhere/anywhere" is current directory',
'MKD' => '257 Created your requested directory',
'REST' => '350 Yeah yeah we set it there for you',
'DELE' => '200 OK OK OK whatever you say',
'RNFR' => '350 Received your order. Please provide more',
'RNTO' => '250 Ok, thanks. File renaming completed.',
'NOOP' => '200 Yes, I\'m very good at doing nothing.',
Daniel Stenberg
committed
);
Daniel Stenberg
committed
# callback functions for certain commands
Daniel Stenberg
committed
'EPRT' => \&PORT_command,
'RETR' => \&RETR_command,
'SIZE' => \&SIZE_command,
'APPE' => \&STOR_command, # append looks like upload
Daniel Stenberg
committed
Daniel Stenberg
committed
my ($closed)=@_; # non-zero if already disconnected
if(!$closed) {
Daniel Stenberg
committed
logmsg "* disconnect data connection\n";
Daniel Stenberg
committed
print DWRITE "DISC\n";
my $i;
sysread DREAD, $i, 5;
}
else {
logmsg "data connection already disconnected\n";
}
Daniel Stenberg
committed
logmsg "=====> Closed data connection\n";
Daniel Stenberg
committed
Daniel Stenberg
committed
logmsg "* quit sockfilt for data (pid $slavepid)\n";
Daniel Stenberg
committed
print DWRITE "QUIT\n";
waitpid $slavepid, 0;
$slavepid=0;
Daniel Stenberg
committed
my $rest=0;
sub REST_command {
$rest = $_[0];
logmsg "Set REST position to $rest\n"
Daniel Stenberg
committed
}
sub LIST_command {
# print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
Daniel Stenberg
committed
my @ftpdir=("total 20\r\n",
"drwxr-xr-x 8 98 98 512 Oct 22 13:06 .\r\n",
"drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..\r\n",
"drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT\r\n",
"-r--r--r-- 1 0 1 35 Jul 16 1996 README\r\n",
"lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin\r\n",
"dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev\r\n",
"drwxrwxrwx 2 98 98 512 May 29 16:04 download.html\r\n",
"dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc\r\n",
"drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\r\n",
"dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n");
logmsg "pass LIST data on data connection\n";
Daniel Stenberg
committed
for(@ftpdir) {
Daniel Stenberg
committed
senddata $_;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
close_dataconn(0);
Daniel Stenberg
committed
sendcontrol "226 ASCII transfer complete\r\n";
Daniel Stenberg
committed
return 0;
}
sub NLST_command {
my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README");
logmsg "pass NLST data on data connection\n";
Daniel Stenberg
committed
senddata "$_\r\n";
Daniel Stenberg
committed
close_dataconn(0);
Daniel Stenberg
committed
sendcontrol "226 ASCII transfer complete\r\n";
loadtest("$srcdir/data/test$testno");
my @data = getpart("reply", "mdtm");
my $reply = $data[0];
chomp $reply;
if($reply <0) {
Daniel Stenberg
committed
sendcontrol "550 $testno: no such file.\r\n";
Daniel Stenberg
committed
sendcontrol "$reply\r\n";
Daniel Stenberg
committed
sendcontrol "500 MDTM: no such command.\r\n";
loadtest("$srcdir/data/test$testno");
Daniel Stenberg
committed
if($testno eq "verifiedserver") {
my $response = "WE ROOLZ: $$\r\n";
my $size = length($response);
sendcontrol "213 $size\r\n";
return 0;
}
if($size > -1) {
Daniel Stenberg
committed
sendcontrol "213 $size\r\n";
}
else {
Daniel Stenberg
committed
sendcontrol "550 $testno: No such file or directory.\r\n";
$size=0;
@data = getpart("reply", "data");
for(@data) {
$size += length($_);
}
if($size) {
Daniel Stenberg
committed
sendcontrol "213 $size\r\n";
Daniel Stenberg
committed
sendcontrol "550 $testno: No such file or directory.\r\n";
my ($testno) = @_;
if($testno =~ /^verifiedserver$/) {
# this is the secret command that verifies that this actually is
# the curl test server
my $response = "WE ROOLZ: $$\r\n";
my $len = length($response);
Daniel Stenberg
committed
sendcontrol "150 Binary junk ($len bytes).\r\n";
senddata "WE ROOLZ: $$\r\n";
Daniel Stenberg
committed
close_dataconn(0);
Daniel Stenberg
committed
sendcontrol "226 File transfer complete\r\n";
if($verbose) {
print STDERR "FTPD: We returned proof we are the test server\n";
}
return 0;
}
$testno =~ s/^([^0-9]*)//;
loadtest("$srcdir/data/test$testno");
my @data = getpart("reply", "data");
my $size=0;
for(@data) {
Daniel Stenberg
committed
my %hash = getpartattr("reply", "data");
if($size || $hash{'sendzero'}) {
if($rest) {
# move read pointer forward
$size -= $rest;
logmsg "REST $rest was removed from size, makes $size left\n";
Daniel Stenberg
committed
if($retrweirdo) {
Daniel Stenberg
committed
sendcontrol "150 Binary data connection for $testno () ($size bytes).\r\n",
Daniel Stenberg
committed
"226 File transfer complete\r\n";
for(@data) {
my $send = $_;
Daniel Stenberg
committed
senddata $send;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
close_dataconn(0);
Daniel Stenberg
committed
$retrweirdo=0; # switch off the weirdo again!
Daniel Stenberg
committed
else {
my $sz = "($size bytes)";
if($retrnosize) {
$sz = "size?";
}
Daniel Stenberg
committed
sendcontrol "150 Binary data connection for $testno () $sz.\r\n";
Daniel Stenberg
committed
for(@data) {
my $send = $_;
Daniel Stenberg
committed
senddata $send;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
close_dataconn(0);
Daniel Stenberg
committed
sendcontrol "226 File transfer complete\r\n";
Daniel Stenberg
committed
}
Daniel Stenberg
committed
sendcontrol "550 $testno: No such file or directory.\r\n";
logmsg "STOR test number $testno in $filename\n";
Daniel Stenberg
committed
sendcontrol "125 Gimme gimme gimme!\r\n";
open(FILE, ">$filename") ||
return 0; # failed to open output
my $line;
Daniel Stenberg
committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
my $disc=0;
while (5 == (sysread DREAD, $line, 5)) {
if($line eq "DATA\n") {
my $i;
sysread DREAD, $i, 5;
#print STDERR " GOT: $i";
my $size = hex($i);
sysread DREAD, $line, $size;
#print STDERR " GOT: $size bytes\n";
$ulsize += $size;
print FILE $line if(!$nosave);
logmsg "> Appending $size bytes to file\n";
}
elsif($line eq "DISC\n") {
# disconnect!
$disc=1;
last;
}
else {
logmsg "No support for: $line";
last;
}
}
if($nosave) {
print FILE "$ulsize bytes would've been stored here\n";
Daniel Stenberg
committed
close_dataconn($disc);
Daniel Stenberg
committed
sendcontrol "226 File transfer complete\r\n";
Daniel Stenberg
committed
sub PASV_command {
Daniel Stenberg
committed
my $pasvport;
Daniel Stenberg
committed
# We fire up a new sockfilt to do the data tranfer for us.
$slavepid = open2(\*DREAD, \*DWRITE,
"./server/sockfilt --port 0 --logfile log/sockdata$ftpdnum$ext.log --pidfile .sockdata$ftpdnum$ext.pid $ipv6");
Daniel Stenberg
committed
Daniel Stenberg
committed
print DWRITE "PING\n";
my $pong;
sysread(DREAD, $pong, 5) || die;
Daniel Stenberg
committed
Daniel Stenberg
committed
if($pong !~ /^PONG/) {
Daniel Stenberg
committed
sendcontrol "500 no free ports!\r\n";
logmsg "failed to run sockfilt for data connection\n";
return 0;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
logmsg "Run sockfilt for data on pid $slavepid\n";
Daniel Stenberg
committed
# Find out what port we listen on
my $i;
print DWRITE "PORT\n";
# READ the response code
sysread(DREAD, $i, 5) || die;
# READ the response size
sysread(DREAD, $i, 5) || die;
my $size = hex($i);
# READ the response data
sysread(DREAD, $i, $size) || die;
# The data is in the format
# IPvX/NNN
if($i =~ /IPv(\d)\/(\d+)/) {
# FIX: deal with IP protocol version
$pasvport = $2;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
sendcontrol sprintf("227 Entering Passive Mode (127,0,0,1,%d,%d)\n",
($pasvport/256), ($pasvport%256));
Daniel Stenberg
committed
sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
Daniel Stenberg
committed
Daniel Stenberg
committed
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
Daniel Stenberg
committed
# assume swift operations unless explicitly slow
alarm ($controldelay?20:5);
Daniel Stenberg
committed
# Wait for 'CNCT'
Daniel Stenberg
committed
while(sysread(DREAD, $input, 5)) {
if($input !~ /^CNCT/) {
# we wait for a connected client
logmsg "Odd, we got $input from client\n";
next;
}
logmsg "====> Client DATA connect\n";
last;
}
Daniel Stenberg
committed
alarm 0;
};
if ($@) {
# timed out
Daniel Stenberg
committed
print DWRITE "QUIT\n";
waitpid $slavepid, 0;
Daniel Stenberg
committed
logmsg "accept failed\n";
Daniel Stenberg
committed
$slavepid=0;
Daniel Stenberg
committed
return;
}
else {
Daniel Stenberg
committed
logmsg "data connection setup on port $pasvport\n";
Daniel Stenberg
committed
}
Daniel Stenberg
committed
return;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
# Support both PORT and EPRT here. Consider LPRT too.
Daniel Stenberg
committed
sub PORT_command {
Daniel Stenberg
committed
my ($arg, $cmd) = @_;
my $port;
# We always ignore the given IP and use localhost.
Daniel Stenberg
committed
Daniel Stenberg
committed
if($cmd eq "PORT") {
if($arg !~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/) {
logmsg "bad PORT-line: $arg\n";
sendcontrol "500 silly you, go away\r\n";
return 0;
}
$port = ($5<<8)+$6;
}
# EPRT |2|::1|49706|
elsif(($cmd eq "EPRT") && ($grok_eprt)) {
if($arg !~ /(\d+)\|([^\|]+)\|(\d+)/) {
sendcontrol "500 silly you, go away\r\n";
return 0;
}
sendcontrol "200 Thanks for dropping by. We contact you later\r\n";
$port = $3;
}
else {
sendcontrol "500 we don't like $cmd now\r\n";
return 0;
Daniel Stenberg
committed
}
if(!$port || $port > 65535) {
print STDERR "very illegal PORT number: $port\n";
return 1;
}
Daniel Stenberg
committed
# We fire up a new sockfilt to do the data tranfer for us.
# FIX: make it use IPv6 if need be
$slavepid = open2(\*DREAD, \*DWRITE,
"./server/sockfilt --connect $port --logfile log/sockdata$ftpdnum$ext.log --pidfile .sockdata$ftpdnum$ext.pid $ipv6");
Daniel Stenberg
committed
Daniel Stenberg
committed
print DWRITE "PING\n";
my $pong;
sysread DREAD, $pong, 5;
Daniel Stenberg
committed
Daniel Stenberg
committed
if($pong !~ /^PONG/) {
logmsg "Failed sockfilt for data connection\n";
kill(9, $slavepid);
Daniel Stenberg
committed
}
logmsg "====> Client DATA connect to port $port\n";
Daniel Stenberg
committed
Daniel Stenberg
committed
return;
}
Daniel Stenberg
committed
my %customreply;
my %customcount;
sub customize {
$nosave = 0; # default is to save as normal
$controldelay = 0; # default is no delaying the responses
open(CUSTOM, "<log/ftpserver.cmd") ||
return 1;
logmsg "FTPD: Getting commands from log/ftpserver.cmd\n";
while(<CUSTOM>) {
if($_ =~ /REPLY ([A-Z]+) (.*)/) {
$customreply{$1}=$2;
logmsg "FTPD: set custom reply for $1\n";
if($_ =~ /COUNT ([A-Z]+) (.*)/) {
# we blank the customreply for this command when having
# been used this number of times
$customcount{$1}=$2;
logmsg "FTPD: blank custom reply for $1 after $2 uses\n";
}
elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) {
$delayreply{$1}=$2;
logmsg "FTPD: delay reply for $1 with $2 seconds\n";
elsif($_ =~ /SLOWDOWN/) {
$controldelay=1;
logmsg "FTPD: send response with 0.1 sec delay between each byte\n";
}
Daniel Stenberg
committed
elsif($_ =~ /RETRWEIRDO/) {
logmsg "FTPD: instructed to use RETRWEIRDO\n";
Daniel Stenberg
committed
$retrweirdo=1;
}
elsif($_ =~ /RETRNOSIZE/) {
logmsg "FTPD: instructed to use RETRNOSIZE\n";
$retrnosize=1;
}
elsif($_ =~ /NOSAVE/) {
# don't actually store the file we upload - to be used when
# uploading insanely huge amounts
$nosave = 1;
logmsg "FTPD: NOSAVE prevents saving of uploaded data\n";
}
}
close(CUSTOM);
}
my @welcome=(
'220- _ _ ____ _ '."\r\n",
'220- ___| | | | _ \| | '."\r\n",
'220- / __| | | | |_) | | '."\r\n",
'220- | (__| |_| | _ <| |___ '."\r\n",
'220 \___|\___/|_| \_\_____|'."\r\n");
Daniel Stenberg
committed
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
while(1) {
#
# We read 'sockfilt' commands.
#
my $input;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 5; # just in case things go bad
$input = <STDIN>;
alarm 0;
};
if ($@) {
# timed out
logmsg "reading stdin timed out\n";
}
if($input !~ /^CNCT/) {
# we wait for a connected client
if(!length($input)) {
# it probably died, restart it
kill(9, $sfpid);
waitpid $sfpid, 0;
startsf();
logmsg "restarted sockfilt\n";
}
else {
logmsg "sockfilt said: $input";
}
next;
}
logmsg "====> Client connect\n";
Daniel Stenberg
committed
Daniel Stenberg
committed
kill(9, $slavepid) if($slavepid);
$slavepid=0;
&customize(); # read test control instructions
Daniel Stenberg
committed
sendcontrol @welcome;
for(@welcome) {
print STDERR "OUT: $_";
}
Daniel Stenberg
committed
Daniel Stenberg
committed
my $i;
# Now we expect to read DATA\n[hex size]\n[prot], where the [prot]
# part only is FTP lingo.
# COMMAND
sysread(STDIN, $i, 5) || die;
Daniel Stenberg
committed
if($i !~ /^DATA/) {
logmsg "sockfilt said $i";
if($i =~ /^DISC/) {
# disconnect
last;
}
next;
}
# SIZE of data
sysread(STDIN, $i, 5) || die;
my $size = hex($i);
# data
sysread STDIN, $_, $size;
ftpmsg $_;
# Remove trailing CRLF.
s/[\n\r]+$//;
Daniel Stenberg
committed
Daniel Stenberg
committed
sendcontrol "500 '$_': command not understood.\r\n";
last;
}
my $FTPCMD=$1;
my $FTPARG=$2;
my $full=$_;
Daniel Stenberg
committed
Daniel Stenberg
committed
logmsg "< \"$full\"\n";
Daniel Stenberg
committed
if($verbose) {
print STDERR "IN: $full\n";
}
my $ok = $commandok{$FTPCMD};
if($ok !~ /$state/) {
Daniel Stenberg
committed
sendcontrol "500 $FTPCMD not OK in state: $state!\r\n";
Daniel Stenberg
committed
my $newstate=$statechange{$FTPCMD};
if($newstate eq "") {
# remain in the same state
}
else {
if($state != $newstate) {
logmsg "switch to state $state\n";
}
Daniel Stenberg
committed
my $delay = $delayreply{$FTPCMD};
if($delay) {
# just go sleep this many seconds!
sleep($delay);
}
my $text;
$text = $customreply{$FTPCMD};
my $fake = $text;
if($text eq "") {
$text = $displaytext{$FTPCMD};
}
if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) {
# used enough number of times, now blank the customreply
$customreply{$FTPCMD}="";
}
Daniel Stenberg
committed
sendcontrol "$text\r\n";
Daniel Stenberg
committed
if($fake eq "") {
# only perform this if we're not faking a reply
# see if the new state is a function caller.
my $func = $commandfunc{$FTPCMD};
if($func) {
# it is!
Daniel Stenberg
committed
&$func($FTPARG, $FTPCMD);
Daniel Stenberg
committed
logmsg "====> Client disconnected\n";
Daniel Stenberg
committed
}
Daniel Stenberg
committed
print SFWRITE "QUIT\n";
waitpid $sfpid, 0;
exit;