#-*-perl-*- # This is a wrapper to the lchat.pl routines that make life easier # to do ftp type work. # Mostly by Lee McLoughlin # based on original version by Alan R. Martello # And by A.Macpherson@bnr.co.uk for multi-homed hosts # # Basic usage: # $ftp_port = 21; # $retry_call = 1; # $attempts = 2; # if( &ftp'open( $site, $ftp_port, $retry_call, $attempts ) != 1 ){ # die "failed to open ftp connection"; # } # if( ! &ftp'login( $user, $pass ) ){ # die "failed to login"; # } # &ftp'type( $text_mode ? 'A' : 'I' ); # if( ! &ftp'get( $remote_filename, $local_filename, 0 ) ){ # die "failed to get file; # } # &ftp'quit(); # # # $Id: ftp.pl,v 2.6 1994/06/06 18:37:37 lmjm Exp lmjm $ # $Log: ftp.pl,v $ # Revision 2.6 1994/06/06 18:37:37 lmjm # Switched to lchat - a subset of chat. # Allow for 'remote help's need to parse the help strings in the continuations # Use real_site for proxy connections. # Allow for cr stripping and corrected use of buffer (from Andrew). # # Revision 2.5 1994/04/29 20:11:04 lmjm # Converted to use rfc1123. # # Revision 2.4 1994/01/26 14:59:07 lmjm # Added DG result code. # # Revision 2.3 1994/01/18 21:58:18 lmjm # Reduce calls to sigset. # Reset to old signal after use. # # Revision 2.2 1993/12/14 11:09:06 lmjm # Use installed socket.ph. # Allow for more returns. # # Revision 2.1 1993/06/28 15:02:00 lmjm # Full 2.1 release # # require 'sys/socket.ph'; # lchat.pl is a special subset of chat2.pl that avoids some memory leaks. require 'lchat.pl'; package ftp; $retry_pause = 60; # Pause before retrying a login. if( defined( &main'PF_INET ) ){ $pf_inet = &main'PF_INET; $sock_stream = &main'SOCK_STREAM; local($name, $aliases, $proto) = getprotobyname( 'tcp' ); $tcp_proto = $proto; } else { # XXX hardwired $PF_INET, $SOCK_STREAM, 'tcp' # but who the heck would change these anyway? (:-) $pf_inet = 2; $sock_stream = 1; $tcp_proto = 6; } # If the remote ftp daemon doesn't respond within this time presume its dead # or something. $timeout = 120; # Timeout a read if I don't get data back within this many seconds $timeout_read = 3 * $timeout; # Timeout an open $timeout_open = $timeout; $ftp'version = '$Revision: 2.6 $'; # This is a "global" it contains the last response from the remote ftp server # for use in error messages $ftp'response = ""; # Also ftp'NS is the socket containing the data coming in from the remote ls # command. # The size of block to be read or written when talking to the remote # ftp server $ftp'ftpbufsize = 4096; # How often to print a hash out, when debugging $ftp'hashevery = 1024; # Output a newline after this many hashes to prevent outputing very long lines $ftp'hashnl = 70; # Is there a connection open? $ftp'service_open = 0; # If a proxy connection then who am I really talking to? $real_site = ""; # Where error/log reports are sent to $ftp'showfd = 'STDERR'; # Should a 421 be treated as a connection close and return 99 from # ftp'expect. This is against rfc1123 recommendations but I've found # it to be a wise default. $drop_on_421 = 1; # Name of a function to call on a pathname to map it into a remote # pathname. $ftp'mapunixout = ''; $ftp'manunixin = ''; # This is just a tracing aid. $ftp_show = 0; # Wether to keep the continuation messages so the user can look at them $ftp'keep_continuations = 0; # Uncomment to turn on lots of debugging. # &ftp'debug( 10 ); sub ftp'debug { $ftp_show = @_[0]; if( $ftp_show > 9 ){ $chat'debug = 1; } } sub ftp'set_timeout { local( $to ) = @_; return if $to == $timeout; $timeout = $to; $timeout_open = $timeout; $timeout_read = 3 * $timeout; if( $ftp_show ){ print $ftp'showfd "ftp timeout set to $timeout\n"; } } sub ftp'open_alarm { die "timeout: open"; } sub ftp'timed_open { local( $site, $ftp_port, $retry_call, $attempts ) = @_; local( $connect_site, $connect_port ); local( $ret ); alarm( $timeout_open ); while( $attempts-- ){ if( $ftp_show ){ print $ftp'showfd "proxy connecting via $proxy_gateway [$proxy_ftp_port]\n" if $proxy; print $ftp'showfd "Connecting to $site"; if( $ftp_port != 21 ){ print $ftp'showfd " [port $ftp_port]"; } print $ftp'showfd "\n"; } if( $proxy ) { if( ! $proxy_gateway ) { # if not otherwise set $proxy_gateway = "internet-gateway"; } if( $debug ) { print $ftp'showfd "using proxy services of $proxy_gateway, "; print $ftp'showfd "at $proxy_ftp_port\n"; } $connect_site = $proxy_gateway; $connect_port = $proxy_ftp_port; $real_site = $site; } else { $connect_site = $site; $connect_port = $ftp_port; } if( ! &chat'open_port( $connect_site, $connect_port ) ){ if( $retry_call ){ print $ftp'showfd "Failed to connect\n" if $ftp_show; next; } else { print $ftp'showfd "proxy connection failed " if $proxy; print $ftp'showfd "Cannot open ftp to $connect_site\n" if $ftp_show; return 0; } } $ret = &ftp'expect( $timeout, 2, 1 ); # ready for login to $site if( $ret != 1 ){ &chat'close(); next; } return 1; } continue { print $ftp'showfd "Pausing between retries\n"; sleep( $retry_pause ); } return 0; } sub main'ftp__sighandler { local( $sig ) = @_; local( $msg ) = "Caught a SIG$sig flagging connection down"; $ftp'service_open = 0; if( $ftp_logger ){ eval "&$ftp_logger( \$msg )"; } } sub ftp'set_signals { $ftp_logger = @_; $SIG{ 'PIPE' } = "ftp__sighandler"; } # Set the mapunixout and mapunixin functions sub ftp'set_namemap { ($ftp'mapunixout, $ftp'mapunixin) = @_; if( $debug ) { print $ftp'showfd "mapunixout = $ftp'mapunixout, $mapunixin = $ftp'mapunixin\n"; } } sub ftp'open { local( $site, $ftp_port, $retry_call, $attempts ) = @_; local( $old_sig ) = $SIG{ 'ALRM' }; $SIG{ 'ALRM' } = "ftp\'open_alarm"; local( $ret ) = eval "&timed_open( '$site', $ftp_port, $retry_call, $attempts )"; alarm( 0 ); $SIG{ 'ALRM' } = $old_sig; if( $@ =~ /^timeout/ ){ return -1; } if( $ret ){ $ftp'service_open = 1; } return $ret; } sub ftp'login { local( $remote_user, $remote_password ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $proxy ){ # Should site or real_site be used here? &ftp'send( "USER $remote_user@$real_site" ); } else { &ftp'send( "USER $remote_user" ); } $ret = &ftp'expect( $timeout, 2, 1, # $remote_user logged in 331, 2, # send password for $remote_user 332, 0 ); # account for login - not yet supported if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret == 1 ){ # Logged in no password needed return 1; } elsif( $ret == 2 ){ # A password is needed &ftp'send( "PASS $remote_password" ); $ret = &ftp'expect( $timeout, 2, 1 ); # $remote_user logged in if( $ret == 99 ){ &service_closed(); } elsif( $ret == 1 ){ # Logged in return 1; } } # If I got here I failed to login return 0; } sub service_closed { $ftp'service_open = 0; &chat'close(); } sub ftp'close { &ftp'quit(); $ftp'service_open = 0; &chat'close(); } # Change directory # return 1 if successful # 0 on a failure sub ftp'cwd { local( $dir ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $ftp'mapunixout ){ $dir = eval "&$ftp'mapunixout( \$dir, 'd' )"; } &ftp'send( "CWD $dir" ); $ret = &ftp'expect( $timeout, 2, 1 ); # working directory = $dir if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # Get a full directory listing: # &ftp'dir( remote LIST options ) # Start a list going with the given options. # Presuming that the remote deamon uses the ls command to generate the # data to send back then then you can send it some extra options (eg: -lRa) # return 1 if sucessful and 0 on a failure sub ftp'dir_open { local( $options ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( ! &ftp'open_data_socket() ){ return 0; } if( $options ){ &ftp'send( "LIST $options" ); } else { &ftp'send( "LIST" ); } $ret = &ftp'expect( $timeout, 1, 1 ); # reading directory if( $ret == 99 ){ &service_closed(); $ret = 0; } if( ! $ret ){ &ftp'close_data_socket; return 0; } accept( NS, S ) || die "accept failed $!"; # # the data should be coming at us now # return 1; } # Close down reading the result of a remote ls command # return 1 if successful and 0 on failure sub ftp'dir_close { local( $ret ); if( ! $ftp'service_open ){ return 0; } # read the close # $ret = &ftp'expect($timeout, 2, 1 ); # transfer complete, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } # shut down our end of the socket &ftp'close_data_socket; if( ! $ret ){ return 0; } return 1; } # Quit from the remote ftp server # return 1 if successful and 0 on failure sub ftp'quit { local( $ret ); $site_command_check = 0; @site_command_list = (); if( ! $ftp'service_open ){ return 0; } &ftp'send( "QUIT" ); $ret = &ftp'expect( $timeout, 2, 1 ); # transfer complete, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # Support for ftp'read sub ftp'read_alarm { die "timeout: read"; } # Support for ftp'read sub ftp'timed_read { alarm( $timeout_read ); return sysread( NS, $ftpbuf, $ftpbufsize ); } # Do not use this routing use ftp'get sub ftp'read_nosel { if( ! $ftp'service_open ){ return -1; } local( $ret ) = eval '&timed_read()'; alarm( 0 ); if( $@ =~ /^timeout/ ){ return -1; } return $ret; } sub ftp'read { if( ! $ftp'service_open ){ return -1; } $nfound = select( $read_out = $read_in, undef, undef, $to = $timeout_read ); if( $nfound <= 0 ){ return -1; } return sysread( NS, $ftpbuf, $ftpbufsize ); } sub ftp'dostrip { ($strip_cr ) = @_; } # Get a remote file back into a local file. # If no loc_fname passed then uses rem_fname. # returns 1 on success and 0 on failure sub ftp'get { local($rem_fname, $loc_fname, $restart ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $loc_fname eq "" ){ $loc_fname = $rem_fname; } if( ! &ftp'open_data_socket() ){ print $ftp'showfd "Cannot open data socket\n"; return 0; } if( $loc_fname ne '-' ){ # Find the size of the target file local( $restart_at ) = &ftp'filesize( $loc_fname ); if( $restart && $restart_at > 0 && &ftp'restart( $restart_at ) ){ $restart = 1; # Make sure the file can be updated chmod( 0644, $loc_fname ); } else { $restart = 0; unlink( $loc_fname ); } } if( $ftp'mapunixout ){ $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )"; } &ftp'send( "RETR $rem_fname" ); $ret = &ftp'expect( $timeout, 1, 1 ); # receiving $rem_fname if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret != 1 ){ print $ftp'showfd "Failure on 'RETR $rem_fname' command\n"; # shut down our end of the socket &ftp'close_data_socket; return 0; } accept( NS, S ) || die "accept failed $!"; # # the data should be coming at us now # # # open the local fname # concatenate on the end if restarting, else just overwrite if( !open( FH, ($restart ? '>>' : '>') . $loc_fname ) ){ print $ftp'showfd "Cannot create local file $loc_fname\n"; # shut down our end of the socket &ftp'close_data_socket; return 0; } local( $start_time ) = time; local( $bytes, $lasthash, $hashes ) = (0, 0, 0); # Use these three lines if you do not have the select() SYSTEM CALL in # your perl. There appears to be a memory leak in using these # and they are usually slower - so only use if you have to! # Also comment back in the $SIG... line at the end of the while() loop. # local( $old_sig ) = $SIG{ 'ALRM' }; # $SIG{ 'ALRM' } = "ftp\'read_alarm"; # while( ($len = &ftp'read_nosel()) > 0 ){ # If you have select() then use the following two lines. vec( $read_in, fileno( NS ), 1 ) = 1; while( ($len = &ftp'read()) > 0 ){ $bytes += $len; if( $strip_cr ){ $ftp'ftpbuf =~ s/\r//g; } if( $ftp_show ){ while( $bytes > ($lasthash + $ftp'hashevery) ){ print $ftp'showfd '#'; $lasthash += $ftp'hashevery; $hashes++; if( ($hashes % $ftp'hashnl) == 0 ){ print $ftp'showfd "\n"; } } } if( ! print FH $ftp'ftpbuf ){ print $ftp'showfd "\nfailed to write data"; $bytes = -1; last; } } # Add the next line back if you don't have select(). # $SIG{ 'ALRM' } = $old_sig; close( FH ); # shut down our end of the socket &ftp'close_data_socket; if( $len < 0 ){ print $ftp'showfd "\ntimed out reading data!\n"; return 0; } if( $ftp_show && $bytes > 0 ){ if( $hashes && ($hashes % $ftp'hashnl) != 0 ){ print $ftp'showfd "\n"; } local( $secs ) = (time - $start_time); if( $secs <= 0 ){ $secs = 1; # To avoid a divide by zero; } local( $rate ) = int( $bytes / $secs ); print $ftp'showfd "Got $bytes bytes ($rate bytes/sec)\n"; } # # read the close # $ret = &ftp'expect( $timeout, 2, 1 ); # transfer complete, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret && $bytes < 0 ){ $ret = 0; } return $ret; } sub ftp'delete { local( $rem_fname ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $ftp'mapunixout ){ $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )"; } &ftp'send( "DELE $rem_fname" ); $ret = &ftp'expect( $timeout, 2, 1 ); # Deleted $rem_fname if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret == 1; } sub ftp'deldir { local( $fname ) = @_; # not yet implemented # RMD } # UPDATE ME!!!!!! # Add in the hash printing and newline conversion sub ftp'put { local( $loc_fname, $rem_fname ) = @_; local( $strip_cr ); if( ! $ftp'service_open ){ return 0; } if( $loc_fname eq "" ){ $loc_fname = $rem_fname; } if( ! &ftp'open_data_socket() ){ return 0; } if( $ftp'mapunixout ){ $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )"; } &ftp'send( "STOR $rem_fname" ); # # the data should be coming at us now # local( $ret ) = &ftp'expect( $timeout, 1, 1 ); # sending $loc_fname if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret != 1 ){ # shut down our end of the socket &ftp'close_data_socket; return 0; } accept( NS, S ) || die "accept failed $!"; # # the data should be coming at us now # # # open the local fname # if( !open( FH, "<$loc_fname" ) ){ print $ftp'showfd "Cannot open local file $loc_fname\n"; # shut down our end of the socket &ftp'close_data_socket; return 0; } while( ){ if( ! $ftp'service_open ){ last; } print NS ; } close( FH ); # shut down our end of the socket to signal EOF &ftp'close_data_socket; # # read the close # $ret = &ftp'expect( $timeout, 2, 1 ); # transfer complete, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } if( ! $ret ){ print $ftp'showfd "Failure on 'STOR $loc_fname' command\n"; } return $ret; } sub ftp'restart { local( $restart_point, $ret ) = @_; if( ! $ftp'service_open ){ return 0; } &ftp'send( "REST $restart_point" ); # # see what they say $ret = &ftp'expect( $timeout, 3, 1 ); # restarting at $restart_point if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # Set the file transfer type sub ftp'type { local( $type ) = @_; if( ! $ftp'service_open ){ return 0; } &ftp'send( "TYPE $type" ); # # see what they say $ret = &ftp'expect( $timeout, 2, 1 ); # file type set to $type if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } $site_command_check = 0; @site_command_list = (); # routine to query the remote server for 'SITE' commands supported sub ftp'site_commands { local( $ret ); @site_command_list = (); $site_command_check = 0; if( ! $ftp'service_open ){ return @site_command_list; } # if we havent sent a 'HELP SITE', send it now if( !$site_command_check ){ $site_command_check = 1; &ftp'send( "HELP SITE" ); # assume the line in the HELP SITE response with the 'HELP' # command is the one for us $ftp'keep_continuations = 1; $ret = &ftp'expect( $timeout, ".*HELP.*", 1 ); $ftp'keep_continuations = 0; if( $ret == 99 ){ &service_closed(); return @site_command_list; } if( $ret != 0 ){ print $ftp'showfd "No response from HELP SITE ($ret)\n" if( $ftp_show ); } @site_command_list = split(/\s+/, $ftp'response); } return @site_command_list; } # return the pwd, or null if we can't get the pwd sub ftp'pwd { local( $ret, $cwd ); if( ! $ftp'service_open ){ return 0; } &ftp'send( "PWD" ); # # see what they say $ret = &ftp'expect( $timeout, 2, 1 ); # working dir is if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret ){ if( $ftp'response =~ /^2\d\d\s*"(.*)"\s.*$/ ){ $cwd = $1; } } return $cwd; } # return 1 for success, 0 for failure sub ftp'mkdir { local( $path ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $ftp'mapunixout ){ $path = eval "&$ftp'mapunixout( \$path, 'f' )"; } &ftp'send( "MKD $path" ); # # see what they say $ret = &ftp'expect( $timeout, 2, 1 ); # made directory $path if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # return 1 for success, 0 for failure sub ftp'chmod { local( $path, $mode ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $ftp'mapunixout ){ $path = eval "&$ftp'mapunixout( \$path, 'f' )"; } &ftp'send( sprintf( "SITE CHMOD %o $path", $mode ) ); # # see what they say $ret = &ftp'expect( $timeout, 2, 1 ); # chmod $mode $path succeeded if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # rename a file sub ftp'rename { local( $old_name, $new_name ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } if( $ftp'mapunixout ){ $old_name = eval "&$ftp'mapunixout( \$old_name, 'f' )"; } &ftp'send( "RNFR $old_name" ); # # see what they say $ret = &ftp'expect( $timeout, 3, 1 ); # OK if( $ret == 99 ){ &service_closed(); $ret = 0; } # check if the "rename from" occurred ok if( $ret ){ if( $ftp'mapunixout ){ $new_name = eval "&$ftp'mapunixout( \$new_name, 'f' )"; } &ftp'send( "RNTO $new_name" ); # # see what they say $ret = &ftp'expect( $timeout, 2, 1 ); # rename $old_name to $new_name if( $ret == 99 ){ &service_closed(); $ret = 0; } } return $ret; } sub ftp'quote { local( $cmd ) = @_; local( $ret ); if( ! $ftp'service_open ){ return 0; } &ftp'send( $cmd ); $ret = &ftp'expect( $timeout, 2, 1 ); # Remote '$cmd' OK if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret; } # ------------------------------------------------------------------------------ # These are the lower level support routines sub ftp'expectgot { ($ftp'resp, $ftp'fatalerror) = @_; if( $ftp_show ){ print $ftp'showfd "$ftp'resp\n"; } if( $ftp'keep_continuations ){ $ftp'response .= $ftp'resp; } else { $ftp'response = $ftp'resp; } } # # create the list of parameters for chat'expect # # ftp'expect( time_out, {value, return value} ); # the last response is stored in $ftp'response # sub ftp'expect { local( $ret ); local( $time_out ); local( @expect_args ); local( $code, $pre ); $ftp'response = ''; $ftp'fatalerror = 0; $time_out = shift( @_ ); if( $drop_on_421 ){ # Handle 421 specially - has to go first in case a pattern # matches on a generic 4.. response push( @expect_args, "[.|\n]*^(421 .*)\\015\\n" ); push( @expect_args, "&expectgot( \$1, 0 ); 99" ); } # Match any obvious continuations. push( @expect_args, "[.|\n]*^(\\d\\d\\d-.*|[^\\d].*)\\015\\n" ); push( @expect_args, "&expectgot( \$1, 0 ); 100" ); while( @_ ){ $code = shift( @_ ); $pre = '^'; $post = ' '; if( $code =~ /^\d\d+$/ ){ $pre = "[.|\n]*^"; } elsif( $code =~ /^\d$/ ){ $pre = "[.|\n]*^"; $post = '\d\d '; } push( @expect_args, "$pre(" . $code . $post . ".*)\\015\\n" ); push( @expect_args, "&expectgot( \$1, 0 ); " . shift( @_ ) ); } # Match any numeric response codes not explicitly looked for. push( @expect_args, "[.|\n]*^(\\d\\d\\d .*)\\015\\n" ); push( @expect_args, "&expectgot( \$1, 0 ); 0" ); # Treat all unrecognised lines as continuations push( @expect_args, "^(.*)\\015\\n" ); push( @expect_args, "&expectgot( \$1, 0 ); 100" ); # add patterns TIMEOUT and EOF push( @expect_args, 'TIMEOUT' ); push( @expect_args, "&expectgot( 'timed out', 0 ); 0" ); push( @expect_args, 'EOF' ); push( @expect_args, "&expectgot( 'remote server gone away', 1 ); 99" ); # if we see a continuation line, wait for the real info $ret = 100; while( $ret == 100 ){ if( $ftp_show > 9 ){ &printargs( $time_out, @expect_args ); } $ret = &chat'expect( $time_out, @expect_args ); } return $ret; } # # opens NS for io # sub ftp'open_data_socket { local( $sockaddr, $port ); local( $type, $myaddr, $a, $b, $c, $d ); local( $mysockaddr, $family, $hi, $lo ); $sockaddr = 'S n a4 x8'; ($a,$b,$c,$d) = unpack( 'C4', $chat'thisaddr ); $this = $chat'thisproc; socket( S, $pf_inet, $sock_stream, $tcp_proto ) || die "socket: $!"; bind( S, $this ) || die "bind: $!"; # get the port number $mysockaddr = getsockname( S ); ($family, $port, $myaddr) = unpack( $sockaddr, $mysockaddr ); $hi = ($port >> 8) & 0x00ff; $lo = $port & 0x00ff; # # we MUST do a listen before sending the port otherwise # the PORT may fail # listen( S, 5 ) || die "listen"; &ftp'send( "PORT $a,$b,$c,$d,$hi,$lo" ); return &ftp'expect( $timeout, 2, 1 ); # PORT command successful } sub ftp'close_data_socket { close( NS ); } sub ftp'send { local( $send_cmd ) = @_; if( $send_cmd =~ /\n/ ){ print $ftp'showfd "ERROR, \\n in send string for $send_cmd\n"; } if( $ftp_show ){ local( $sc ) = $send_cmd; if( $send_cmd =~ /^PASS/){ $sc = "PASS "; } print $ftp'showfd "---> $sc\n"; } &chat'print( "$send_cmd\r\n" ); } sub ftp'printargs { while( @_ ){ print $ftp'showfd shift( @_ ) . "\n"; } } sub ftp'filesize { local( $fname ) = @_; if( ! -f $fname ){ return -1; } return (stat( _ ))[ 7 ]; } # Reply codes, see RFC959: # 1yz Positive Preliminary. Expect another reply before proceeding # 2yz Positive Completion. # 3yz Positive Intermediate. More information required. # 4yz Transient Negative Completion. The user should try again. # 5yz Permanent Negative Completion. # x0z Syntax error # x1z Information # x2z Connection - control info. # x3z Authentication and accounting. # x4z Unspecified # x5z File system. # 110 Restart marker reply. # In this case, the text is exact and not left to the # particular implementation; it must read: # MARK yyyy = mmmm # Where yyyy is User-process data stream marker, and mmmm # server's equivalent marker (note the spaces between markers # and "="). # 120 Service ready in nnn minutes. # 125 Data connection already open; transfer starting. # 150 File status okay; about to open data connection. # 200 Command okay. # 202 Command not implemented, superfluous at this site. # 211 System status, or system help reply. # 212 Directory status. # 213 File status. # 214 Help message. # On how to use the server or the meaning of a particular # non-standard command. This reply is useful only to the # human user. # 215 NAME system type. # Where NAME is an official system name from the list in the # Assigned Numbers document. # 220 Service ready for new user. # 221 Service closing control connection. # Logged out if appropriate. # 225 Data connection open; no transfer in progress. # 226 Closing data connection. # Requested file action successful (for example, file # transfer or file abort). # 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). # 230 User logged in, proceed. # 250 Requested file action okay, completed. # 257 "PATHNAME" created. # 331 User name okay, need password. # 332 Need account for login. # 350 Requested file action pending further information. # 421 Service not available, closing control connection. # This may be a reply to any command if the service knows it # must shut down. # 425 Can't open data connection. # 426 Connection closed; transfer aborted. # 450 Requested file action not taken. # File unavailable (e.g., file busy). # 451 Requested action aborted: local error in processing. # 452 Requested action not taken. # Insufficient storage space in system. # 500 Syntax error, command unrecognized. # This may include errors such as command line too long. # 501 Syntax error in parameters or arguments. # 502 Command not implemented. # 503 Bad sequence of commands. # 504 Command not implemented for that parameter. # 530 Not logged in. # 532 Need account for storing files. # 550 Requested action not taken. # File unavailable (e.g., file not found, no access). # 551 Requested action aborted: page type unknown. # 552 Requested file action aborted. # Exceeded storage allocation (for current directory or # dataset). # 553 Requested action not taken. # File name not allowed. # make this package return true 1;