#!/usr/bin/perl # rftp -- recursively ftp on a remote machine # http://www.cs.indiana.edu/~kinzler/home.html#other #Recursively decend via anon FTP and either get a listing #or retrieve the tree. # Usage: # rftp [options] host [list-file] # Options --- # [-s] Specify the root for transfer on remote host (default "/") # [-d] Specify the root for transfer on local host (default ".") # [-l] Just a listing, thank you. # [-a] ASCII mode transfers (default: BIN) # [-g] get files # [-p] put files # [-u:] Specify a userid and passwd # [-b] debug mode # # return value is 0 if ok. # 1 if an error occurred during transfer. # 2 if login failed. # # # Mail bugs or comments to: # # Mike Ferrara M/S 2LRR # HP Signal Analysis Div R&D # 1212 Valley House Drive # Rohnert Park, CA 94928 # (707) 794-4479 # mikef%hpsadle@hp-sde.sde.hp.com # mikef@hpsadle.hp.com # modified to not recurse into . or .. directories; kinzler, Jun 96 #main $cd=""; $dirs[1]='/'; $source='.'; $dest='.'; $ftpin="/tmp/ftpin$$"; $ftpout="/tmp/ftpout$$"; $listing=1; $bin=1; $reader=0; $writer=0; $user='anonymous'; $passwd=`hostname`; $debug=0; chop($passwd); $passwd = (gethostbyname($passwd))[0] || $passwd if $passwd ne '' && $passwd !~ /.\../; $logname = $ENV{'USER'} || $ENV{'LOGNAME'}; $passwd = "-$logname\@$passwd"; #Setup signal handler $SIG{'INT'}='cleanup'; $SIG{'HUP'}='cleanup'; $SIG{'QUIT'}='cleanup'; $SIG{'TERM'}='cleanup'; &parseopts; if (!($putting)&&!($listing)) { if (-d $dest) { } else { system("mkdir -p $dest"); } chdir $dest; } &setupcomm; if ($writer) { open(LSOUT,">$lsout"); open(FTPIN,"| ftp -i -v -n 1>$ftpout 2>&1"); select(FTPIN);$|=1;select(stdout); &sendftp ("open $host"); &sendftp ("user $user $passwd"); while (1) { #Read from the socket to see if login worked. $_=; last if (/^230\s/); print (STDERR $_), exit(2) if (/^530\s/); } if ($bin==1) { &sendftp("bin"); } undef($lastdirectory); if (!$putting) { &recurse; } if ($putting) { &putfiles; } &sendftp("quit"); close(FTPIN); &cleanup; } if ($reader) { &readloop; } sub putfiles { &sendftp("bin") if ($bin); open(FIND,"find $source -print |"); while ($_=) { chop; $destfile="$dest/$_"; $destfile=~s,/\./,/,g; $destfile=~s,//,/,g; $destfile=~s,\.$,,; $destfile=~s,/$,,; $srcfile="$source/$_"; $srcfile=~s,/\./,/,g; $srcfile=~s,//,/,g; if (-f $_) { &sendftp("put \"$srcfile\" \"$destfile\""); &readsock; next; } if (-d $_) { &sendftp("mkdir \"$destfile\""); } } close(FIND); } sub parseopts { &Getopts('abs:d:lu:pg'); $host=shift(@ARGV); if (! defined($host)) { die "I need you to tell me the hostname!"; } if (defined($opt_s)) { $source=$opt_s; } if (defined($opt_d)) { $dest=$opt_d; } if ($opt_a==1) { $bin=0; } if ($opt_l) { $listing=1; $bin=0; $lsout=shift(@ARGV); } if (defined($lsout)) { } else { $lsout='-'; } if (defined($opt_u)) { ($user,$passwd)=split(":",$opt_u); } if (defined($opt_g)) { $listing=0; $putting=0; die "What do you want to do? put OR get?" if (defined($opt_p)); } if (defined($opt_p)) { $listing=0; $putting=1; die "What do you want to do? put OR get?" if (defined($opt_g)); } $debug=1 if (defined($opt_b)); } # getopts.pl - a better getopt.pl # Usage: # do Getopts('a:bc'); # -a takes arg. -b & -c not. Sets opt_* as a # # side effect. sub Getopts { local($argumentative) = @_; local(@args,$_,$first,$rest,$errs); local($[) = 0; @args = split( / */, $argumentative ); while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) { ($first,$rest) = ($1,$2); $pos = index($argumentative,$first); if($pos >= $[) { if($args[$pos+1] eq ':') { shift(@ARGV); if($rest eq '') { $rest = shift(@ARGV); } eval "\$opt_$first = \$rest;"; } else { eval "\$opt_$first = 1"; if($rest eq '') { shift(@ARGV); } else { $ARGV[0] = "-$rest"; } } } else { print STDERR "Unknown option: $first\n"; ++$errs; if($rest ne '') { $ARGV[0] = "-$rest"; } else { shift(@ARGV); } } } $errs == 0; } # # readloop -- keep reading stuff from the $ftpout file and # stuffing it over the socket. # sub readloop { while (1) { if (-f $ftpout) { open (FTPOUT,$ftpout); while (1) { $_=; if (/^221\sGoodbye/) { last; } print(S $_); } exit(0); } } } # # Workhorse subroutine, gets a whole directory or listing # of a whole directory. It also forms the list of directories # below the current one. # sub readsock { $i=1; $n=0; while (1) { $_=; if ($listing==1) { print (LSOUT $_) if (!/^\s?$/ && !/^[0-9]*\s/); } if (/^d/) { chop; split; $dirs[$i]=pop(@_); $i=$i+1; } if (/^-/) { chop; split; $fname[$n]=pop(@_); $n=$n+1; } if (/^226\s/) { last; } if ((/^5[0-9][0-9]\s/)&&(!/^5[0-9][0-9]\sbytes/i)) { $non = (/No such file/i) ? 'non-' : ''; print (STDERR "A ${non}fatal error occurred during transfer: "); print (STDERR $_); last if $non; exit(1); } } } # # Do the recursion, using getdir as the workhorse. # sub recurse { local(@dirlist)=@dirs; local($currentparent)=shift(@dirlist); while (defined($child=shift(@dirlist))) { next if $child=~/^\.\.?$/; # kinzler, Jun 96 $cd="$source/$currentparent/$child"; undef @dirs; $cd=~s,//,/,g; $cd=~s,//,/,g; $cd=~s,/$,,; if (($cd EQ $lastdirectory) && ($lastdirectory NE "")) { die "OOOPS! I'm looping!!"; } &sendftp("dir \"$cd\""); # print ("dir \"$cd\"\n"); if ($listing==1) { print(LSOUT "\n$cd:\n"); } &readsock; if ($listing == 0) { $ddir="$dest/$currentparent/$child"; $ddir=~s,//,/,g; $ddir=~s,//,/,g; system("mkdir -p $ddir"); while (defined($file=shift(@fname))){ &sendftp("get \"$cd/$file\" \"$ddir/$file\""); &readsock; } } $lastdirectory=$cd; $dirs[0]="$currentparent/$child"; &recurse; } } # # Delete the temporary files, close the output, and leave # sub cleanup { unlink($ftpout); kill 15,$childpid; close(LSOUT); exit(0); } sub sendftp { $line=@_[0]; $line="$line\n" if (!($line=~m/\n$/)); print (STDERR "$line") if ($debug); print (FTPIN $line); } # # Setup socket based communication between the child and parent # and fork # sub setupcomm { do 'sys/socket.h' || die "Can't do sys/socket.h"; $port=$$; $sockaddr='S n a4 x8'; chop($hostname=`hostname`); ($name,$aliases,$proto)=getprotobyname('tcp'); ($name,$aliases,$type,$len,$thisaddr)=gethostbyname($hostname); if ($childpid == fork) { $reader=1; #The child reads FTP output, client sleep 3; $client=pack($sockaddr,&AF_INET,0,$thisaddr); $server=pack($sockaddr,&AF_INET,$port,$thisaddr); socket(S,&PF_INET,&SOCK_STREAM,$proto) || die "socket: $!"; bind(S,$client) || die "bind: $!"; connect(S,$server) || die "connect: $!"; select(S);$|=1;select(stdout); } else { $writer=1; #The parent writes FTP input and effects transfers., server. $server=pack($sockaddr,&AF_INET,$port,"\0\0\0\0"); select(NS);$|=1;select(stdout); socket(S,&PF_INET,&SOCK_STREAM,$proto) || die "socket: $!"; bind(S,$server) || die "bind: $!"; listen(S,5) || die "connect: $!"; select(S);$|=1;select(stdout); loop: ($addr=accept(NS,S)) || goto loop; ($af,$port,$inetaddr)=unpack($sockaddr,$addr); } }