home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / scripts-convex / ftp.shar / ftp / ftplib.pl < prev    next >
Encoding:
Perl Script  |  1992-08-17  |  8.1 KB  |  356 lines

  1. #
  2. #   This is a set of ftp library routines using chat2.pl
  3. #   Return code information taken from RFC 959
  4.  
  5. #   Written by Gene Spafford  <spaf@cs.purdue.edu>
  6. #       Last update: 10 April 92,   Version 0.9
  7. #
  8.  
  9. #
  10. #   Most of these routines communicate over an open ftp channel
  11. #   The channel is opened with the "ftp'open" call.
  12. #
  13.  
  14. package ftp;
  15. require "chat2.pl";
  16. require "syscall.ph";
  17.  
  18.  
  19. ###########################################################################
  20. #
  21. #  The following are the variables local to this package.
  22. #  I declare them all up front so I can remember what I called 'em. :-)
  23. #
  24. ###########################################################################
  25.  
  26. LOCAL_VARS: {    
  27.     $Control;
  28.     $Data_handle;
  29.     $Host;
  30.     $Myhost = "\0" x 65;
  31.     (syscall(&SYS_gethostname, $Myhost, 65) == 0) || 
  32.     die "Cannot 'gethostname' of local machine (in ftplib)\n";
  33.     $Myhost =~ s/\0*$//;
  34.     $NeedsCleanup;
  35.     $NeedsClose;
  36.     $ftp_error;
  37.     $ftp_matched;
  38.     $ftp_trans_flag;
  39.     @ftp_list;
  40.  
  41.     local(@tmp) = getservbyname("ftp", "tcp");
  42.     ($FTP = $tmp[2]) || 
  43.     die "Unable to get service number for 'ftp' (in ftplib)!\n";
  44.  
  45.     @std_actions = (
  46.         'TIMEOUT',
  47.         q($ftp_error = "Connection timed out for $Host!\n"; undef),
  48.         'EOF', 
  49.         q($ftp_error = "Connection to $Host timed out unexpectedly!\n"; undef)
  50.     );
  51.  
  52.     @sigs = ('INT', 'HUP', 'TERM', 'QUIT');  # sigs we'll catch & terminate on
  53. }
  54.  
  55.  
  56.  
  57. ###########################################################################
  58. #
  59. #  The following are intended to be the user-callable routines.
  60. #  Each of these does one of the ftp keyword functions.
  61. #
  62. ###########################################################################
  63.  
  64. sub error { ## Public
  65.     $ftp_error;
  66. }
  67.   
  68. #######################################################
  69.  
  70. #   cd up a directory level
  71.  
  72. sub cdup { ## Public
  73.     &do_ftp_cmd(200, "cdup");
  74. }
  75.  
  76. #######################################################
  77.  
  78. # close an open ftp connection
  79.  
  80. sub close { ## Public
  81.     return unless $NeedsClose;
  82.     &do_ftp_cmd(221, "quit");
  83.     &chat'close($Control);
  84.     undef $NeedsClose;
  85.     &do_ftp_signals(0);
  86. }
  87.  
  88. #######################################################
  89.  
  90. # change remote directory
  91.  
  92. sub cwd { ## Public
  93.     &do_ftp_cmd(250, "cwd", @_);
  94. }
  95.   
  96. #######################################################
  97.  
  98. #  delete a remote file
  99.  
  100. sub delete { ## Public
  101.      &do_ftp_cmd(250, "dele", @_); 
  102. }
  103.  
  104. #######################################################
  105.  
  106. #  get a directory listing of remote directory ("ls -l")
  107.  
  108. sub dir { ## Public
  109.     &do_ftp_listing("list", @_);
  110. }
  111.  
  112. #######################################################
  113.  
  114. #  get a remote file to a local file
  115. #    get(remote[, local])
  116.  
  117. sub get { ## Public
  118.     local($remote, $local) = @_;
  119.     ($local = $remote) unless $local;
  120.  
  121.     unless (open(DFILE, ">$local")) {
  122.     $ftp_error =  "Open of local file $local failed: $!";
  123.     return undef;
  124.     } else {
  125.     $NeedsCleanup = $local;
  126.     }
  127.  
  128.     return undef unless &do_open_dport;     # Open a data channel
  129.     unless (&do_ftp_cmd(150, "retr $remote")) {
  130.     $ftp_error .= "\nFile $remote not fetched from $Host\n";
  131.     close DFILE;
  132.     unlink $local;
  133.     undef $NeedsCleanup;
  134.     return;
  135.     }
  136.  
  137.     $ftp_trans_flag = 0;
  138.  
  139.     do {
  140.     &chat'expect($Data_handle, 60,
  141.              '.|\n', q{print DFILE ($chat'thisbuf) ||
  142.             ($ftp_trans_flag = 3); undef $chat'S},
  143.              'EOF',  '$ftp_trans_flag = 1',
  144.              'TIMEOUT', '$ftp_trans_flag = 2');
  145.     } until $ftp_trans_flag;
  146.  
  147.     close DFILE;
  148.     &chat'close($Data_handle);        # Close the data channel
  149.  
  150.     undef $NeedsCleanup;
  151.     if ($ftp_trans_flag > 1) {
  152.     unlink $local;
  153.     $ftp_error = "Unexpected " . ($ftp_trans_flag == 2 ? "timeout" :
  154.         ($ftp_trans_flag != 3 ? "failure" : "local write failure")) .
  155.                 " getting $remote\n";
  156.     }
  157.     
  158.     &do_ftp_cmd(226);
  159. }
  160.  
  161. #######################################################
  162.  
  163. #  Do a simple name list ("ls")
  164.  
  165. sub list { ## Public
  166.     &do_ftp_listing("nlst", @_);
  167. }
  168.  
  169. #######################################################
  170.  
  171. #   Make a remote directory
  172.  
  173. sub mkdir { ## Public
  174.     &do_ftp_cmd(257, "mkd", @_);
  175. }
  176.  
  177. #######################################################
  178.  
  179. #  Open an ftp connection to remote host
  180.  
  181. sub open {  ## Public
  182.     if ($NeedsClose) {
  183.     $ftp_error = "Connection still open to $Host!";
  184.     return undef;
  185.     }
  186.  
  187.     $Host = shift(@_);
  188.     local($User, $Password, $Acct) = @_;
  189.     $User = "anonymous" unless $User;
  190.     $Password = "-" . $main'ENV{'USER'} . "@$Myhost" unless $Password;
  191.     $ftp_error = '';
  192.  
  193.     unless($Control = &chat'open_port($Host, $FTP)) {
  194.     $ftp_error = "Unable to connect to $Host ftp port: $!";
  195.     return undef;
  196.     }
  197.  
  198.     unless(&chat'expect($Control, 60,
  199.                 "^220 .*\n",     "1",
  200.                 "^\d\d\d .*\n",  "undef")) {
  201.     $ftp_error = "Error establishing control connection to $Host";
  202.         &chat'close($Control);
  203.     return undef;
  204.     }
  205.     &do_ftp_signals($NeedsClose = 1);
  206.  
  207.     unless (&do_ftp_cmd(331, "user $User")) {
  208.     $ftp_error .= "\nUser command failed establishing connection to $Host";
  209.     return undef;
  210.     }
  211.  
  212.     unless (&do_ftp_cmd("(230|332|202)", "pass $Password")) {
  213.     $ftp_error .= "\nPassword command failed establishing connection to $Host";
  214.     return undef;
  215.     }
  216.  
  217.     return 1 unless $Acct;
  218.  
  219.     unless (&do_ftp_cmd("(230|202)", "pass $Password")) {
  220.     $ftp_error .= "\nAcct command failed establishing connection to $Host";
  221.     return undef;
  222.     }
  223.     1;
  224. }
  225.  
  226. #######################################################
  227.  
  228. #  Get name of current remote directory
  229.  
  230. sub pwd { ## Public
  231.     if (&do_ftp_cmd(257, "pwd")) {
  232.     $ftp_matched =~ m/^257 (.+)\r?\n/;
  233.     $1;
  234.     } else {
  235.     undef;
  236.     }    
  237. }
  238.  
  239. #######################################################
  240.  
  241. #  Rename a remote file
  242.  
  243. sub rename { ## Public
  244.     local($from, $to) = @_;
  245.  
  246.     &do_ftp_cmd(350, "rnfr $from") && &do_ftp_cmd(250, "rnto $to");
  247. }
  248.  
  249. #######################################################
  250.  
  251. #  Set transfer type
  252.  
  253. sub type { ## Public
  254.     &do_ftp_cmd(200, "type", @_); 
  255. }
  256.  
  257.  
  258. ###########################################################################
  259. #
  260. #  The following are intended to be utility routines used only locally.
  261. #  Users should not call these directly.
  262. #
  263. ###########################################################################
  264.  
  265. sub do_ftp_cmd {  ## Private
  266.     local($okay, @commands, $val) = @_;
  267.  
  268.     $commands[0] && 
  269.     &chat'print($Control, join(" ", @commands), "\r\n");
  270.  
  271.     &chat'expect($Control, 60, 
  272.          "^$okay .*\\n",        '$ftp_matched = $&; 1',
  273.          '^(\d)\d\d .*\\n', '($String = $&) =~ y/\r\n//d; 
  274.              $ftp_error = qq{Unexpected reply for ' .
  275.              "@commands" . ': $String}; 
  276.              $1 > 3 ? undef : 1',
  277.          @std_actions
  278.         );
  279. }
  280.  
  281. #######################################################
  282.  
  283. sub do_ftp_listing { ## Private
  284.     local(@lcmd) = @_;
  285.     @ftp_list = ();
  286.     $ftp_trans_flag = 0;
  287.  
  288.     return undef unless &do_open_dport;
  289.  
  290.     return undef unless &do_ftp_cmd(150, @lcmd);
  291.     do {            #  Following is grotty, but chat2 makes us do it
  292.         &chat'expect($Data_handle, 30,
  293.         "(.*)\r?\n",    'push(@ftp_list, $1)',
  294.         "EOF",     '$ftp_trans_flag = 1');
  295.     } until $ftp_trans_flag;
  296.  
  297.     &chat'close($Data_handle);
  298.     return undef unless &do_ftp_cmd(226);
  299.  
  300.     grep(y/\r\n//d, @ftp_list);
  301.     @ftp_list;
  302. }  
  303.  
  304. #######################################################
  305.  
  306. sub do_open_dport { ## Private
  307.     local(@foo, $port) = &chat'open_listen;
  308.     ($port, $Data_handle) = splice(@foo, 4, 2);
  309.  
  310.     unless ($Data_handle) {
  311.     $ftp_error =  "Unable to open data port: $!";
  312.     return undef;
  313.     }
  314.  
  315.     push(@foo, $port >> 8, $port & 0xff);
  316.     local($myhost) = (join(',', @foo));
  317.     
  318.     &do_ftp_cmd(200, "port $myhost");
  319. }
  320.  
  321. #######################################################
  322. #
  323. #  To cleanup after a problem
  324. #
  325.  
  326. sub do_ftp_abort {
  327.     die unless $NeedsClose;
  328.  
  329.     &chat'print($Control, "abor", "\r\n");
  330.     &chat'close($Data_handle);
  331.     &chat'expect($Control, 10, '.', undef);
  332.     &chat'close($Control);
  333.  
  334.     close DFILE;
  335.     unlink($NeedsCleanup) if $NeedsCleanup;
  336.     die;
  337. }
  338.  
  339. #######################################################
  340. #
  341. #  To set signals to do the abort properly
  342. #
  343.  
  344. sub do_ftp_signals {
  345.     local($flag, $sig) = @_;
  346.  
  347.     local ($old, $new) = ('DEFAULT', "ftp'do_ftp_abort");
  348.     $flag || (($old, $new) = ($new, $old));
  349.     foreach $sig (@sigs) {
  350.     ($SIG{$sig} == $old) && ($SIG{$sig} = $new);
  351.     }
  352. }
  353.  
  354. 1;
  355.