#/usr/local/bin/perl

## ftpr, last update 91/08/16
## usage: ftpr [-a] [-d] [-t timeout] [-n] hostname topdir yes-regex except-regex
## topdir may be whitespace-separated list of topdirs
## yes-regex defaults to . (meaning everything)
## except-regex defaults to ' ' (meaning no exceptions)

# slight hack here to make ftp talk to the kermit pipe and download direct
# - wjm


require 'chat2.pl';

$| = 1; # not much output, but we like to see it as it happens
$timeout = 60;
$dasha = "";
$nflag = 0;
$host = "localhost";
$topdir = ".";
$yesregex = ".";
$noregex = " ";
$user = "anonymous";
$pass = '[email protected]';

{
       last unless $ARGV[0] =~ /^-/;
       $_ = shift;
       $trace++, redo if /^-d/; # debug mode
       $timeout = $1, redo if /^-t(\d+)/;
       $timeout = shift, redo if /^-t/;
       $dasha = "-a", redo if /^-a/;
       $nflag++, redo if /^-n/;
       die "bad flag: $_";
}

$host = shift if @ARGV;
$topdir = shift if @ARGV;
$yesregex = shift if @ARGV;
$noregex = shift if @ARGV;

die "extra args: @ARGV" if @ARGV;

($Control = &chat'open_port($host,21)) || die "open control: $!";
die "expected 2dd for initial banner, got $_"
       unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
&ctalk("user $user\n");
$_ = &clisten($timeout);
unless (/^2\d\d/) { # might be logged in already:
       die "expected 3dd for password query, got $_"
               unless /^3\d\d/;
       &ctalk("pass $pass\n");
       die "expected 2dd for logged in, got $_"
               unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
}
## all set up for a conversation

@list = split(/\s+/,$topdir);
while ($dir = shift list) {
       next if $seen{$dir}++;
       print "listing $dir\n";
       for (&list($dir)) {
               (warn "can't parse $_ in $dir"), next
                       unless ($tag,$file) = /^(.).*\s(\S+)\s*$/;
               push(@list, "$dir/$file") if
                       ($tag eq 'd') && ($file !~ /^\.\.?$/);
               if (    ($tag eq '-') &&
                       ("$dir/$file" =~ /$yesregex/o) &&
                       ("$dir/$file" !~ /$noregex/o) &&
                       (! -e "$dir/$file")
               ) {
                       print "fetching $dir/$file...\n";
                       &get("$dir/$file","$dir/$file") unless $nflag;
               }
       }
}

## shutdown
&ctalk("quit\n");
&clisten(5); # for trace
&chat'close($Control);
exit(0);

sub ctalk {
       local($text) = @_;
       print "{$text}" if $trace;
       &chat'print($Control,$text);
}

sub clisten {
       local($secs) = @_;
       local($return,$tmp);
       while (1) {
               $tmp = &chat'expect($Control, $secs, '(.*)\r?\n', '"$1\n"');
               print $tmp if $trace;
               $return .= $tmp;
               return $return if !length($tmp) || $tmp =~ /^\d\d\d /;
       }
}

sub dopen {
       local($_);

       local(@ret) = &chat'open_listen();
       &ctalk("port " .
               join(",", @ret[0,1,2,3], int($ret[4]/256), $ret[4]%256) .
               "\n");
       die "expected 2dd for data open, got $_"
               unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
       $Data = $ret[5];
}

<<'END_NOT_USED';
sub dtalk {
       local($text) = @_;
       print "{D:$text}" if $trace;
       &chat'print($Data,$text);
}
END_NOT_USED

sub dlisten {
       local($secs,$forcereturn) = @_;
       local($return,$tmp);
       while (1) {
               $tmp = &chat'expect($Data, $secs,
                       '(.|\n)+', '$&',
                       TIMEOUT, '""',
                       EOF, 'undef');
               if (defined $tmp) {
                       print "[D:$tmp]" if $trace > 1;
                       $return .= $tmp;
                       return $return unless (!$forcereturn) && (length $tmp);
                               # if timeout, return what you have
               } else { # eof
                       return $return;
                               # maybe undef
               }
       }
}

sub dclose {
       &chat'close($Data);
}

<<'END_NOT_USED';
sub nlst {
       local($dir) = @_;
       local(@files);
       local($_,$tmp);

       &dopen();
       &ctalk("nlst $dasha $dir/.\n");
       die "expected 1dd for nlst, got $_"
               unless ($_ = &clisten($timeout)) =~ /^1\d\d/;
       $_ = "";
       while (1) {
               $tmp = &dlisten($timeout);
               last unless defined $tmp;
               $_ .= $tmp;
       }
       @files = sort grep(!/^\.\.?$/, split(/\r?\n/))
               unless /^ls: /;
       die "expected 2dd for nlst complete, got $_"
               unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
       &dclose();
       @files;
}
END_NOT_USED

sub list {
       local($dir) = @_;
       local(@files);
       local($_,$tmp);

       &dopen();
       &ctalk("list $dasha $dir/.\n");
       die "expected 1dd for list, got $_"
               unless ($_ = &clisten($timeout)) =~ /^(.*\n)*1/;
       $_ = "";
       while (1) {
               $tmp = &dlisten($timeout);
               last unless defined $tmp;
               $_ .= $tmp;
       }
       @files = grep(/^\S[rwx\-]{8}/, split(/\r?\n/));
       die "expected 2dd for list complete, got $_"
               unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
       &dclose();
       @files;
}

sub get {
       local($from, $to) = @_;
       local($todir,*OUT);

       ($todir = "./$to") =~ s#(.*)/.*#$1#;
       #system "mkdir -p $todir" unless -d $todir;
       (warn "cannot create $to.TMP: $!"), return
               unless open(OUT, "|kermit -i -p e -a $to -s - ");
       select((select(OUT))[0]);
       &ctalk("type i\n");
       die "expected 2dd for type i ok, got $_"
               unless ($_ = &clisten($timeout)) =~ /^2\d\d/;
       &dopen();
       &ctalk("retr $from\n");
       unless (($_ = &clisten($timeout)) =~ /^1\d\d/) {
               warn "expected 1dd for retr, got $_";
               close(OUT);
               unlink("$to.TMP");
               &dclose();
               return;
       }
       {
               $_ = &dlisten($timeout,1);
               last unless defined $_;
               print OUT;
               redo;
       }
       close(OUT);
       unless (($_ = &clisten($timeout)) =~ /^2\d\d/) {
               warn "expected 2dd for retr complete, got $_";
               close(OUT);
               unlink("$to.TMP");
               &dclose();
               return;
       }
       &dclose();
}