home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume13 / rftp / part01 next >
Text File  |  1990-06-05  |  15KB  |  691 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v13i026: recursive ftp'er in Perl
  3. From: mikef@hpsadle.hp.com (Mike Ferrara)
  4. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5.  
  6. Posting-number: Volume 13, Issue 26
  7. Submitted-by: mikef@hpsadle.hp.com (Mike Ferrara)
  8. Archive-name: rftp/part01
  9.  
  10. Here's a recursive ftp'er for grabbing, putting, and listing whole
  11. file trees via ftp. It uses perl to drive ftp, so in order to use it
  12. you need perl. There are two versions, rftp and rftp.nosockets. rftp.nosockets
  13. is much smaller, slower and has many fewer features. It was my proof of
  14. concept code, but it works without perl sockets. That's why I put it
  15. in the shar. For more info, see the code, or the man page. You may also
  16. need to fool with the #! line at the beginning, and make sure you've 
  17. done a "makelib" (from perl distribution) on /usr/include/sys/sockets.h.
  18. This has been tested on perl 3 patchlevel 18.
  19.  
  20.   Mike Ferrara M/S 2LRR
  21.   HP Signal Analysis Div R&D
  22.   1212 Valley House Drive
  23.   Rohnert Park, CA 94928
  24.   (707) 794-4479
  25.   mikef%hpsadle@hp-sde.sde.hp.com
  26.   mikef@hpsadle.hp.com
  27. --------------------------cut here-----------------------------
  28.  
  29. # This is a shell archive.  Remove anything before this line,
  30. # then unpack it by saving it in a file and typing "sh file".
  31. #
  32. # Wrapped by Mike Ferrara <mikef@hpsadle> on Tue Jun  5 10:41:15 1990
  33. #
  34. # This archive contains:
  35. #    rftp    
  36. #
  37.  
  38. LANG=""; export LANG
  39. PATH=/bin:/usr/bin:$PATH; export PATH
  40.  
  41. echo mkdir - rftp
  42. mkdir rftp
  43.  
  44. echo x - rftp/rftp
  45. cat >rftp/rftp <<'@EOF'
  46. #!/usr/local/bin/perl
  47.  
  48. #Recursively decend via anon FTP and either get a listing
  49. #or retrieve the tree.
  50. # Usage:
  51. #      rftp [options] host [list-file]
  52. # Options --- 
  53. #   [-s<source_dir>] Specify the root for transfer on remote host (default "/")
  54. #   [-d<dest_dir>] Specify the root for transfer on local host (default ".")
  55. #   [-l] Just a listing, thank you.
  56. #   [-a] ASCII mode transfers (default: BIN)
  57. #   [-g] get files
  58. #   [-p] put files
  59. #   [-u<user>:<passwd>] Specify a userid and passwd
  60. #   [-b] debug mode
  61. #   return value is 0 if ok.
  62. #                   1 if an error occurred during transfer.
  63. #                   2 if login failed.
  64. #                   
  65. #
  66. # Mail bugs or comments to:
  67. #
  68. #  Mike Ferrara M/S 2LRR
  69. #  HP Signal Analysis Div R&D
  70. #  1212 Valley House Drive
  71. #  Rohnert Park, CA 94928
  72. #  (707) 794-4479
  73. #  mikef%hpsadle@hp-sde.sde.hp.com
  74. #  mikef@hpsadle.hp.com
  75.  
  76. #main
  77. $cd="";
  78. $dirs[1]='/';
  79. $source='.';
  80. $dest='.';
  81. $ftpin="/tmp/ftpin$$";
  82. $ftpout="/tmp/ftpout$$";
  83. $listing=1;
  84. $bin=1;
  85. $reader=0;
  86. $writer=0;
  87. $user='anonymous';
  88. $passwd=`hostname`;
  89. $debug=0;
  90.  
  91. #Setup signal handler
  92. $SIG{'INT'}='cleanup';
  93. $SIG{'HUP'}='cleanup';
  94. $SIG{'QUIT'}='cleanup';
  95. $SIG{'TERM'}='cleanup';
  96.  
  97. &parseopts;
  98. if (!($putting)&&!($listing)) {
  99.    if (-d $dest) {
  100.      }
  101.    else {
  102.      system("mkdir -p $dest");
  103.      }
  104.    chdir $dest;
  105. }
  106. &setupcomm;
  107. if ($writer) {
  108.    open(LSOUT,">$lsout");
  109.    open(FTPIN,"| ftp -i -v -n 1>$ftpout 2>&1");
  110.    select(FTPIN);$|=1;select(stdout);
  111.    &sendftp ("open $host");
  112.    &sendftp ("user $user $passwd");
  113.    while (1) { #Read from the socket to see if login worked.
  114.         $_=<NS>;
  115.         last if (/^230\s/);
  116.         exit(2) if (/^530\s/);
  117.         } 
  118.    if ($bin==1) {
  119.       &sendftp("bin");
  120.       }
  121.    undef($lastdirectory);
  122.    if (!$putting) {
  123.       &recurse;
  124.       }
  125.    if ($putting) {
  126.       &putfiles;
  127.       }
  128.    &sendftp("quit");
  129.    close(FTPIN);
  130.    &cleanup;
  131. }
  132. if ($reader) {
  133.    &readloop;
  134.    }
  135.  
  136. sub putfiles {
  137.    &sendftp("bin") if ($bin);
  138.    open(FIND,"find $source -print |");
  139.    while ($_=<FIND>) {
  140.        chop;
  141.        $destfile="$dest/$_";
  142.        $destfile=~s,/\./,/,g;
  143.        $destfile=~s,//,/,g;
  144.        $destfile=~s,\.$,,;
  145.        $destfile=~s,/$,,;
  146.        $srcfile="$source/$_";
  147.        $srcfile=~s,/\./,/,g;
  148.        $srcfile=~s,//,/,g;
  149.        if (-f $_) {
  150.           &sendftp("put $srcfile $destfile");
  151.           &readsock;
  152.           next;
  153.           }
  154.        if (-d $_) {
  155.           &sendftp("mkdir $destfile");
  156.           }
  157.        }
  158.     close(FIND); 
  159. }          
  160.  
  161.  
  162. sub parseopts {
  163.    &Getopts('abs:d:lu:pg');
  164.    $host=shift(@ARGV);
  165.    if (! defined($host)) {
  166.       die "I need you to tell me the hostname!";
  167.       }
  168.    if (defined($opt_s)) {
  169.       $source=$opt_s;
  170.       }
  171.    if (defined($opt_d)) {
  172.       $dest=$opt_d;
  173.       }
  174.    if ($opt_a==1) {
  175.       $bin=0;
  176.       }
  177.    if ($opt_l) {
  178.       $listing=1;
  179.       $bin=0;
  180.       $lsout=shift(@ARGV);
  181.       }
  182.    if (defined($lsout)) {
  183.       }
  184.    else {
  185.       $lsout='-';
  186.       }
  187.    if (defined($opt_u)) {
  188.       ($user,$passwd)=split(":",$opt_u);
  189.       }
  190.    if (defined($opt_g)) {
  191.       $listing=0;
  192.       $putting=0;
  193.       die "What do you want to do? put OR get?" if (defined($opt_p));
  194.       }
  195.    if (defined($opt_p)) {
  196.       $listing=0;
  197.       $putting=1;
  198.       die "What do you want to do? put OR get?" if (defined($opt_g));
  199.       }
  200.    $debug=1 if (defined($opt_b));
  201. }
  202.  
  203.  
  204. # getopts.pl - a better getopt.pl
  205. # Usage:
  206. #      do Getopts('a:bc');  # -a takes arg. -b & -c not. Sets opt_* as a
  207. #                           #  side effect.
  208. sub Getopts {
  209.     local($argumentative) = @_;
  210.     local(@args,$_,$first,$rest,$errs);
  211.     local($[) = 0;
  212.  
  213.     @args = split( / */, $argumentative );
  214.     while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
  215.     ($first,$rest) = ($1,$2);
  216.     $pos = index($argumentative,$first);
  217.     if($pos >= $[) {
  218.         if($args[$pos+1] eq ':') {
  219.         shift(@ARGV);
  220.         if($rest eq '') {
  221.             $rest = shift(@ARGV);
  222.         }
  223.         eval "\$opt_$first = \$rest;";
  224.         }
  225.         else {
  226.         eval "\$opt_$first = 1";
  227.         if($rest eq '') {
  228.             shift(@ARGV);
  229.         }
  230.         else {
  231.             $ARGV[0] = "-$rest";
  232.         }
  233.         }
  234.     }
  235.     else {
  236.         print STDERR "Unknown option: $first\n";
  237.         ++$errs;
  238.         if($rest ne '') {
  239.         $ARGV[0] = "-$rest";
  240.         }
  241.         else {
  242.         shift(@ARGV);
  243.         }
  244.     }
  245.     }
  246.     $errs == 0;
  247. }
  248.  
  249. #
  250. # readloop -- keep reading stuff from the $ftpout file and
  251. # stuffing it over the socket.
  252. #
  253. sub readloop {
  254.      while (1) {
  255.      if (-f $ftpout) {
  256.         open (FTPOUT,$ftpout);      
  257.         while (1) {
  258.            $_=<FTPOUT>;
  259.            if (/^221\sGoodbye/) {
  260.               last;
  261.               }
  262.            print(S $_);
  263.            }
  264.         exit(0);
  265.         }
  266.      }
  267. }
  268.  
  269. #
  270. # Workhorse subroutine, gets a whole directory or listing
  271. # of a whole directory. It also forms the list of directories
  272. # below the current one.
  273. #
  274. sub readsock {
  275.           $i=1;
  276.           $n=0;
  277.           while (1) {
  278.              $_=<NS>;
  279.              if ($listing==1) {
  280.                 print (LSOUT $_) if (!/^\s?$/ && !/^[0-9]*\s/);
  281.                 }
  282.              if (/^d/) {
  283.                 chop;
  284.                 split;
  285.                 $dirs[$i]=pop(@_);
  286.                 $i=$i+1;
  287.                 }
  288.              if (/^-/) {
  289.                 chop;
  290.                 split;
  291.                 $fname[$n]=pop(@_);
  292.                 $n=$n+1;
  293.                 }           
  294.              if (/^226\s/) {
  295.                 last;
  296.                 }
  297.              if ((/^5[0-9][0-9]\s/)&&(!/^5[0-9][0-9]\sbytes/i)) {
  298.                 print (stderr "A fatal error occurred during transfer:");
  299.                 print (stderr $_);
  300.                 exit(1);
  301.                 }
  302.              }
  303. }
  304.  
  305. #
  306. # Do the recursion, using getdir as the workhorse.
  307. #
  308. sub recurse {
  309.     local(@dirlist)=@dirs;
  310.     local($currentparent)=shift(@dirlist);
  311.        while (defined($child=shift(@dirlist))) {
  312.           $cd="$source/$currentparent/$child";
  313.           undef @dirs;
  314.           $cd=~s,//,/,g;
  315.           $cd=~s,//,/,g;
  316.           $cd=~s,/$,,;
  317.           if (($cd EQ $lastdirectory) && ($lastdirectory NE "")) {
  318.              die "OOOPS! I'm looping!!";
  319.              }
  320.           &sendftp("dir $cd");
  321. #         print ("dir $cd\n");
  322.           if ($listing==1) {
  323.              print(LSOUT "\n$cd:\n");
  324.              }
  325.           &readsock;
  326.           if ($listing == 0) {
  327.              $ddir="$dest/$currentparent/$child";
  328.              $ddir=~s,//,/,g;
  329.              $ddir=~s,//,/,g;
  330.              system("mkdir -p $ddir");
  331.              while (defined($file=shift(@fname))){
  332.                 &sendftp("get $cd/$file $ddir/$file");
  333.                 &readsock;
  334.                 }
  335.              }
  336.           $lastdirectory=$cd;
  337.           $dirs[0]="$currentparent/$child";
  338.           &recurse;
  339.        }
  340. }
  341.  
  342.  
  343. #
  344. # Delete the temporary files, close the output, and leave
  345. #
  346. sub cleanup {
  347.    unlink($ftpout);
  348.    kill 15,$childpid;
  349.    close(LSOUT);
  350.    exit(0);
  351.    }
  352.  
  353.  
  354. sub sendftp {
  355.    $line=@_[0];
  356.    $line="$line\n" if (!($line=~m/\n$/));
  357.    print (STDERR "$line") if ($debug);
  358.    print (FTPIN $line);
  359.    }
  360.  
  361. #
  362. # Setup socket based communication between the child and parent
  363. # and fork 
  364. #
  365. sub setupcomm {
  366.    do 'sys/socket.h' || die "Can't do sys/socket.h";
  367.    $port=$$;
  368.    $sockaddr='S n a4 x8';
  369.    chop($hostname=`hostname`);
  370.    ($name,$aliases,$proto)=getprotobyname('tcp');
  371.    ($name,$aliases,$type,$len,$thisaddr)=gethostbyname($hostname);
  372.    if ($childpid == fork) {
  373.       $reader=1; #The child reads FTP output, client
  374.       sleep 3;
  375.       $client=pack($sockaddr,&AF_INET,0,$thisaddr);
  376.       $server=pack($sockaddr,&AF_INET,$port,$thisaddr);
  377.   
  378.       socket(S,&PF_INET,&SOCK_STREAM,$proto) || die "socket: $!";
  379.       bind(S,$client) || die "bind: $!";
  380.       connect(S,$server) || die "connect: $!";
  381.  
  382.       select(S);$|=1;select(stdout);
  383.       }
  384.    else {
  385.       $writer=1; #The parent writes FTP input and effects transfers., server.
  386.       $server=pack($sockaddr,&AF_INET,$port,"\0\0\0\0");
  387.       select(NS);$|=1;select(stdout);
  388.   
  389.       socket(S,&PF_INET,&SOCK_STREAM,$proto) || die "socket: $!";
  390.       bind(S,$server) || die "bind: $!";
  391.       listen(S,5) || die "connect: $!";
  392.  
  393.       select(S);$|=1;select(stdout);
  394.       loop: ($addr=accept(NS,S)) || goto loop;
  395.       ($af,$port,$inetaddr)=unpack($sockaddr,$addr);
  396.       }   
  397. }
  398. @EOF
  399.  
  400. chmod 755 rftp/rftp
  401.  
  402. echo x - rftp/rftp.1
  403. cat >rftp/rftp.1 <<'@EOF'
  404. .TH RFTP 1
  405. .ds )H
  406. .ds ]W March 1990
  407. .SH NAME
  408. rftp \- recursively ftp on a remote machine
  409. .SH SYNOPSIS
  410. .B rftp 
  411. [
  412. .I options
  413. ]
  414. .I host
  415. [
  416. .I listing-file
  417. ]
  418. .SH DESCRIPTION
  419. .I Rftp\^
  420. Drives ftp to recursively descend file trees, either for listings,
  421. or to get or put a bunch of files keeping the tree structure intact.
  422. Rftp is written in perl and requires that berkeley sockets be wired
  423. into it. This generally requires that a "makelib" be run on
  424. "/usr/include/sys/sockets.h" before running rftp. Rftp works as follows:
  425. 1) fork a copy of itself, and setup socket based IPC between parent
  426. and child.
  427. 2) start ftp, with output redirected to /tmp/ftpout$$.
  428. 3) poke stdin of ftp while child reads /tmp/ftpout$$, and stuffs
  429. the output back to the parent over the socket.
  430. .SS Options
  431. .PP
  432. There are several options:
  433. .TP
  434. .B [\-s source-dir]
  435. specifies the source directory for the transfer. Default is "."
  436. .TP
  437. .B  [\-d dest-dir]
  438. specifies the destination directory for the transfer. Default is "."
  439. .TP
  440. .B  [\-a]
  441. force transfer mode to ascii. Default is binary (image).
  442. .TP
  443. .B  [\-l]
  444. for directory listing only. This is the default action. Listings 
  445. will go to stdout or to "listing-file" if specified.
  446. .TP
  447. .B  [\-u username:passwd]
  448. specify a user name and password for logging in. Default is user
  449. "anonymous" password `hostname`.
  450. .TP
  451. .B  [-g] 
  452. get files from remote host.
  453. .TP
  454. .B  [\-p]
  455. send (put) files to remote host.
  456. .TP
  457. .SH DIAGNOSTICS
  458. The program tries to clue you in to common failures.  Source is available.
  459. Return codes are as follows: 0 for normal exit, 1 for a failure during a
  460. transfer, and 2 for a login failure
  461. .SH EXAMPLES
  462. This command gets a directory listing from machine "jupiter" and writes 
  463. it in file "xyzzy".
  464. .IP
  465. rftp jupiter xyzzy
  466. .TP
  467. This command retrieves the tree headed at "pub/pub" into "/users/foo/xx"
  468. on machine "jupiter"
  469. .IP
  470. rftp -spub/pub -d/users/foo/xx -g jupiter
  471. .SH AUTHOR
  472. .I rftp
  473. was developed by Mike Ferrara of Hewlett-Packard (Signal Analysis Division)
  474. mikef%hpsad@hp-sde.sde.hp.com or mikef@hpsadle.hp.com.
  475. .PP
  476. .SH "STANDARDS CONFORMANCE"
  477. .IR None.
  478.  
  479. @EOF
  480.  
  481. chmod 666 rftp/rftp.1
  482.  
  483. echo x - rftp/rftp.nosockets
  484. cat >rftp/rftp.nosockets <<'@EOF'
  485. #!/usr/local/bin/perl
  486.  
  487. #Recursively decend via anon FTP and either get a listing
  488. #or retrieve the tree.
  489. # Usage:
  490. #      rftp [options] host [list-file]
  491. # Options --- 
  492. #   [-s<source_dir>] Specify the root for transfer on remote host (default "/")
  493. #   [-d<dest_dir>] Specify the root for transfer on local host (default ".")
  494. #   [-l] Just a listing, thank you.
  495. #   [-a] ASCII mode transfers (default: BIN)
  496.  
  497.  
  498. ;# getopts.pl - a better getopt.pl
  499. ;# Usage:
  500. ;#      do Getopts('a:bc');  # -a takes arg. -b & -c not. Sets opt_* as a
  501. ;#                           #  side effect.
  502. sub Getopts {
  503.     local($argumentative) = @_;
  504.     local(@args,$_,$first,$rest,$errs);
  505.     local($[) = 0;
  506.  
  507.     @args = split( / */, $argumentative );
  508.     while(($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
  509.     ($first,$rest) = ($1,$2);
  510.     $pos = index($argumentative,$first);
  511.     if($pos >= $[) {
  512.         if($args[$pos+1] eq ':') {
  513.         shift(@ARGV);
  514.         if($rest eq '') {
  515.             $rest = shift(@ARGV);
  516.         }
  517.         eval "\$opt_$first = \$rest;";
  518.         }
  519.         else {
  520.         eval "\$opt_$first = 1";
  521.         if($rest eq '') {
  522.             shift(@ARGV);
  523.         }
  524.         else {
  525.             $ARGV[0] = "-$rest";
  526.         }
  527.         }
  528.     }
  529.     else {
  530.         print STDERR "Unknown option: $first\n";
  531.         ++$errs;
  532.         if($rest ne '') {
  533.         $ARGV[0] = "-$rest";
  534.         }
  535.         else {
  536.         shift(@ARGV);
  537.         }
  538.     }
  539.     }
  540.     $errs == 0;
  541. }
  542.  
  543. 1;
  544.  
  545.  
  546. sub getdir {
  547.    $i=1;
  548.    undef @dirs;
  549.    $cd=~s,//,/,g;
  550.    $mcd=$cd;
  551.    $mcd=~s,^/,,;
  552.    if ($cd EQ $lastdirectory) {
  553.       die "OOOPS! I'm looping!!";
  554.       }
  555.    open(FTPIN,">$ftpin");
  556.    print(FTPIN "open $host \n");
  557.    print(FTPIN "user anonymous xx\n");
  558.    print(FTPIN "cd /\n");
  559.    if ($bin==1) {
  560.       print(FTPIN "bin \n");
  561.       }
  562.    print(FTPIN "dir $cd\n");
  563.    if ($listing == 0) {
  564.       system("mkdir -p $dest/$cd");
  565.       print (FTPIN "mget $mcd/* \n");
  566.       }
  567.    print(FTPIN "quit \n");
  568.    close(FTPIN);
  569. #   system("ftp -i -n <$ftpin 1>$ftpout 2>&1");
  570.    system("ftp -i -n <$ftpin 1>$ftpout ");
  571.    open(FTPOUT,"$ftpout");
  572.    if ($listing==1) {
  573.       print(LSOUT "\n$cd:\n");
  574.    }
  575.    $dirs[0]=$cd;
  576.    while($_=<FTPOUT>) {
  577.       if ($listing==1) {
  578.          print (LSOUT $_) if (!/^\s?$/);
  579.          }
  580.       if (/^\s?d/) {
  581.          chop;
  582.          split;
  583.          $dirs[$i]=pop(@_);
  584.          $i=$i+1;
  585.          }
  586.       }
  587.    close(FTPOUT);
  588.    $lastdirectory=$cd;
  589. }
  590.  
  591. sub recurse {
  592.     local(@dirlist)=@dirs;
  593.     local($currentparent)=shift(@dirlist);
  594.        while (defined($child=shift(@dirlist))) {
  595.           $cd="$currentparent/$child";
  596.           &getdir;
  597.           &recurse;
  598.           }
  599.    }
  600.  
  601. sub cleanup {
  602.    unlink($ftpin,$ftpout);
  603.    }
  604.  
  605. #main
  606. $cd="";
  607. $dirs[1]='/';
  608. $dest='.';
  609. $ftpin="/tmp/ftpin$$";
  610. $ftpout="/tmp/ftpout$$";
  611. $listing=0;
  612. $bin=1;
  613.  
  614. $SIG{'INT'}='cleanup';
  615. $SIG{'HUP'}='cleanup';
  616. $SIG{'QUIT'}='cleanup';
  617. $SIG{'TERM'}='cleanup';
  618.  
  619. &Getopts('as:d:l');
  620.  
  621. $host=shift(@ARGV);
  622. if (! defined($host)) {
  623.    die "I need you to tell me the hostname!";
  624.    }
  625.  
  626.  
  627. if (defined($opt_s)) {
  628.    $dirs[1]=$opt_s;
  629.    }
  630. if (defined($opt_d)) {
  631.    $dest=$opt_d;
  632.    }
  633. if ($opt_a==1) {
  634.    $bin=0;
  635.    }
  636. if (-d $dest) {
  637.   }
  638. else {
  639.   mkdir($dest,0755) || die "$dest already exists and is not a directory!";
  640.   }
  641. if ($opt_l) {
  642.    $listing=1;
  643.    $bin=0;
  644.    $lsout=shift(@ARGV);
  645.    }
  646.  
  647. if (defined($lsout)) {
  648.    }
  649. else {
  650.    $lsout='-';
  651.    }
  652. open(LSOUT,">$lsout");
  653.  
  654.  
  655. chdir $dest;
  656. &recurse;
  657. &cleanup;
  658. close(LSOUT);
  659. exit(0);
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.  
  668.  
  669.  
  670.  
  671.  
  672.  
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.  
  680. @EOF
  681.  
  682. chmod 755 rftp/rftp.nosockets
  683.  
  684. chmod 755 rftp
  685.  
  686. exit 0
  687.  
  688.  
  689.