#/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 = 'user@feenix.metronet.com';

{
	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();
}