diff options
author | Perl 5 Porters <perl5-porters@africa.nicoh.com> | 1996-12-19 16:44:00 +1200 |
---|---|---|
committer | Chip Salzenberg <chip@atlantic.net> | 1996-12-19 16:44:00 +1200 |
commit | 5f05dabc4054964aa3b10f44f8468547f051cdf8 (patch) | |
tree | 7bcc2c7b6d5cf44e7f0111bac2240ca979d9c804 /lib/Net | |
parent | 6a3992aa749356d657a4c0e14be8c2f4c2f4f999 (diff) | |
download | perl-5f05dabc4054964aa3b10f44f8468547f051cdf8.tar.gz |
[inseparable changes from patch from perl5.003_11 to perl5.003_12]
CORE LANGUAGE CHANGES
Subject: Support C<delete @hash{@keys}>
From: Chip Salzenberg <chip@atlantic.net>
Files: op.c op.h opcode.pl pod/perldiag.pod pod/perlfunc.pod pp.c t/op/delete.t
Subject: Autovivify scalars
From: Chip Salzenberg <chip@atlantic.net>
Files: dump.c op.c op.h pp.c pp_hot.c
DOCUMENTATION
Subject: Update pods: perldelta -> perlnews, perli18n -> perllocale
From: Tom Christiansen <tchrist@perl.com>
Files: MANIFEST pod/perl.pod pod/perldelta.pod pod/perli18n.pod pod/perlnews.pod
Subject: perltoot.pod
Date: Mon, 09 Dec 1996 07:44:10 -0700
From: Tom Christiansen <tchrist@mox.perl.com>
Files: MANIFEST pod/perltoot.pod
Msg-ID: <199612091444.HAA09947@toy.perl.com>
(applied based on p5p patch as commit 32e22efaa9ec59b73a208b6c532a0b435e2c6462)
Subject: Perlguts, version 25
Date: Fri, 6 Dec 96 11:40:27 PST
From: Jeff Okamoto <okamoto@hpcc123.corp.hp.com>
Files: pod/perlguts.pod
private-msgid: <199612061940.AA055461228@hpcc123.corp.hp.com>
Subject: pod patches for English errors
Date: Mon, 09 Dec 1996 13:33:11 -0800
From: Steve Kelem <steve.kelem@xilinx.com>
Files: pod/*.pod
Msg-ID: <24616.850167191@castor>
(applied based on p5p patch as commit 0135f10892ed8a21c4dbd1fca21fbcc365df99dd)
Subject: Misc doc updates
Date: Sat, 14 Dec 1996 18:56:33 -0700
From: Tom Christiansen <tchrist@mox.perl.com>
Files: pod/*
Subject: Re: perldelta.pod
Here are some diffs to the _11 pods. I forgot to add perldelta to
perl.pod though.
And *PLEASE* fix the Artistic License so it no longer has the bogus
"whomever" misdeclined in the nominative case:
under the copyright of this Package, but belong to whomever generated
them, and may be sold commercially, and may be aggregated with this
It should obviously be "whoever".
p5p-msgid: <199612150156.SAA12506@mox.perl.com>
OTHER CORE CHANGES
Subject: Allow assignment to empty array values during foreach()
From: Chip Salzenberg <chip@atlantic.net>
Files: cop.h global.sym mg.c op.c perl.h pp_hot.c proto.h sv.c
Subject: Fix nested closures
From: Chip Salzenberg <chip@atlantic.net>
Files: op.c opcode.pl pp.c pp_ctl.c pp_hot.c
Subject: Fix core dump on auto-vivification
From: Chip Salzenberg <chip@atlantic.net>
Files: pp_hot.c
Subject: Fix core dump on C<open $undef_var, "X">
From: Chip Salzenberg <chip@atlantic.net>
Files: pp_sys.c
Subject: Fix -T/-B on globs and globrefs
From: Chip Salzenberg <chip@atlantic.net>
Files: pp_sys.c
Subject: Fix memory management of $`, $&, and $'
From: Chip Salzenberg <chip@atlantic.net>
Files: pp_hot.c regexec.c
Subject: Fix paren matching during backtracking
From: Chip Salzenberg <chip@atlantic.net>
Files: regexec.c
Subject: Fix memory leak and std{in,out,err} death in perl_{con,de}str
From: Chip Salzenberg <chip@atlantic.net>
Files: miniperlmain.c perl.c perl.h sv.c
Subject: Discard garbage bytes at end of prototype()
From: Chip Salzenberg <chip@atlantic.net>
Files: pp.c
Subject: Fix local($pack::{foo})
From: Chip Salzenberg <chip@atlantic.net>
Files: global.sym pp.c pp_hot.c proto.h scope.c
Subject: Disable warn, die, and parse hooks _before_ global destruction
From: Chip Salzenberg <chip@atlantic.net>
Files: perl.c
Subject: Re: Bug in formline
Date: Sun, 08 Dec 1996 14:58:32 -0500
From: Gurusamy Sarathy <gsar@engin.umich.edu>
Files: pp_ctl.c
Msg-ID: <199612081958.OAA26025@aatma.engin.umich.edu>
(applied based on p5p patch as commit b386bda18108ba86d0b76ebe2d8745eafa80f39e)
Subject: Fix C<@a = ($a,$b,$c,$d) = (1,2)>
From: Chip Salzenberg <chip@atlantic.net>
Files: pp_hot.c
Subject: Properly support and document newRV{,_inc,_noinc}
From: Chip Salzenberg <chip@atlantic.net>
Files: global.sym pod/perlguts.pod sv.c sv.h
Subject: Allow lvalue pos inside recursive function
From: Chip Salzenberg <chip@atlantic.net>
Files: op.c pp.c pp_ctl.c pp_hot.c
PORTABILITY
Subject: Make $privlib contents compatible with 5.003
From: Chip Salzenberg <chip@atlantic.net>
Files: INSTALL ext/Opcode/Safe.pm installperl lib/FileHandle.pm lib/Test/Harness.pm
Subject: Support $bincompat3 config variable; update metaconfig units
From: Chip Salzenberg <chip@atlantic.net>
Files: Configure MANIFEST compat3.sym config_h.SH embed.pl global.sym old_embed.pl old_global.sym old_perl_exp.SH perl_exp.SH
Subject: Look for gettimeofday() in Configure
Date: Wed, 11 Dec 1996 15:49:57 +0100
From: John Hughes <john@AtlanTech.COM>
Files: Configure config_H config_h.SH pp.c
Subject: perl5.003_11, Should base use of gettimeofday on HAS_GETTIMEOFDAY, not I_SYS_TIME
I've been installing perl5.003_11 on a SCO system that has the TCP/IP runtime
installed but not the TCP/IP development system.
Unfortunately the <sys/time.h> include file is included in the TCP/IP runtime
while libsocket.a is in the development system.
This means that pp.c decides to use "gettimeofday" because <sys/time.h> is
present but I can't link the perl that gets compiled.
So, here's a patch to base the use of "gettimeofday" on "HAS_GETTIMEOFDAY"
instead of "I_SYS_TIME". I also took the liberty of removing the special
case for plan9 (I assume plan9 has <sys/time.h> but no gettimeofday. Am I
right?).
p5p-msgid: <01BBE77A.F6F37F80@malvinas.AtlanTech.COM>
Subject: Make $startperl a relative path if people want portable scrip
From: Chip Salzenberg <chip@atlantic.net>
Files: Configure
Subject: Homogenize use of "eval exec" hack
From: Chip Salzenberg <chip@atlantic.net>
Files: Porting/Glossary eg/README eg/nih eg/sysvipc/ipcmsg eg/sysvipc/ipcsem eg/sysvipc/ipcshm lib/diagnostics.pm makeaperl.SH pod/checkpods.PL pod/perlrun.pod pod/pod2html.PL pod/pod2latex.PL pod/pod2man.PL pod/pod2text.PL utils/c2ph.PL utils/h2ph.PL utils/h2xs.PL utils/perlbug.PL utils/perldoc.PL utils/pl2pm.PL x2p/a2py.c x2p/find2perl.PL x2p/s2p.PL
Subject: LynxOS support
Date: Thu, 12 Dec 1996 09:25:00 PST
From: Greg Seibert <seibert@Lynx.COM>
Files: Configure MANIFEST hints/lynxos.sh t/op/stat.t
Msg-ID: <m0vYEsY-0000IZC@kzinti.lynx.com>
(applied based on p5p patch as commit 6693373533b15e559fd8f0f1877e5e6ec15483cc)
Subject: Re: db-recno.t failures with _11 on Freebsd 2.1-stable
Date: 11 Dec 1996 18:58:56 -0500
From: Roderick Schertler <roderick@gate.net>
Files: INSTALL hints/freebsd.sh
Msg-ID: <pzohg0r5tr.fsf@eeyore.ibcinc.com>
(applied based on p5p patch as commit 10e40321ee752c58e3407b204c74c8049894cb51)
Subject: VMS patches to 5.003_11
Date: Mon, 09 Dec 1996 23:16:10 -0500 (EST)
From: Charles Bailey <bailey@HMIVAX.HUMGEN.UPENN.EDU>
Files: MANIFEST regexec.c t/lib/filehand.t util.c vms/*
private-msgid: <01ICTR32LCZG001A1D@hmivax.humgen.upenn.edu>
TESTING
Subject: recurse recurse recurse ...
Date: Mon, 9 Dec 1996 23:44:27 +0200 (EET)
From: Jarkko Hietaniemi <jhi@cc.hut.fi>
Files: MANIFEST t/op/recurse.t
private-msgid: <199612092144.XAA29025@alpha.hut.fi>
UTILITIES, LIBRARY, AND EXTENSIONS
Subject: Add CPAN and Net::FTP
From: Chip Salzenberg <chip@atlantic.net>
Files: MANIFEST lib/CPAN.pm lib/CPAN/FirstTime.pm lib/CPAN/Nox.pm lib/Net/FTP.pm lib/Net/Netrc.pm lib/Net/Socket.pm pod/perlmod.pod
Subject: Add File::Compare
Date: Mon, 16 Dec 1996 18:44:59 GMT
From: Nick Ing-Simmons <nik@tiuk.ti.com>
Files: MANIFEST lib/File/Compare.pm pod/perlmod.pod
Msg-ID: <199612161844.SAA02152@pluto>
(applied based on p5p patch as commit ec971c5c328aca84fb827f69f2cc1dc3be81f830)
Subject: Add Tie::RefHash
Date: Sun, 15 Dec 1996 18:58:08 -0500
From: Gurusamy Sarathy <gsar@engin.umich.edu>
Files: MANIFEST lib/Tie/RefHash.pm pod/perlmod.pod
Msg-ID: <199612152358.SAA28665@aatma.engin.umich.edu>
(applied based on p5p patch as commit 9a079709134ebbf4c935cc8752fdb564e5c82b94)
Subject: Put "splain" in utils.
From: Chip Salzenberg <chip@atlantic.net>
Files: Makefile.SH installperl utils/Makefile utils/splain.PL
Subject: Some h2ph fixes
Date: Fri, 13 Dec 1996 11:34:12 -0800
From: Jeff Okamoto <okamoto@hpcc123.corp.hp.com>
Files: utils/h2ph.PL
Here is a message regarding changes to h2ph that should probably be folded
into the 5.004 release.
p5p-msgid: <199612131934.AA289845652@hpcc123.corp.hp.com>
Diffstat (limited to 'lib/Net')
-rw-r--r-- | lib/Net/FTP.pm | 943 | ||||
-rw-r--r-- | lib/Net/Netrc.pm | 123 | ||||
-rw-r--r-- | lib/Net/Socket.pm | 332 |
3 files changed, 1398 insertions, 0 deletions
diff --git a/lib/Net/FTP.pm b/lib/Net/FTP.pm new file mode 100644 index 0000000000..64b21fe751 --- /dev/null +++ b/lib/Net/FTP.pm @@ -0,0 +1,943 @@ +;# Net::FTP.pm +;# +;# Copyright (c) 1995 Graham Barr <Graham.Barr@tiuk.ti.com>. All rights +;# reserved. This program is free software; you can redistribute it and/or +;# modify it under the same terms as Perl itself. + +;#Notes +;# should I have a dataconn::close sub which calls response ?? +;# FTP should hold state reguarding cmds sent +;# A::read needs some more thought +;# A::write What is previous pkt ended in \r or not ?? +;# need to do some heavy tidy-ing up !!!! +;# need some documentation + +package Net::FTP; + +=head1 NAME + +Net::FTP - FTP Client class + +=head1 SYNOPSIS + + require Net::FTP; + + $ftp = Net::FTP->new("some.host.name"); + $ftp->login("anonymous","me@here.there"); + $ftp->cwd("/pub"); + $ftp->get("that.file"); + $ftp->quit; + +=head1 DESCRIPTION + +C<Net::FTP> is a class implementing a simple FTP client in Perl as described +in RFC959 + +=head2 TO BE CONTINUED ... + +=cut + +require 5.001; +use Socket 1.3; +use Carp; +use Net::Socket; + +@ISA = qw(Net::Socket); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.18 $ =~ /(\d+)\.(\d+)/); +sub Version { $VERSION } + +use strict; + +=head1 METHODS + +All methods return 0 or undef upon failure + +=head2 * new($host [, option => value [,...]] ) + +Constructor for the FTP client. It will create the connection to the +remote host. Possible options are: + + Port => port to use for FTP connection + Timeout => set timeout value (defaults to 120) + Debug => debug level + +=cut + +sub FTP_READY { 0 } # Ready +sub FTP_RESPONSE { 1 } # Waiting for a response +sub FTP_XFER { 2 } # Doing data xfer + +sub new { + my $pkg = shift; + my $host = shift; + my %arg = @_; + my $me = bless Net::Socket->new(Peer => $host, + Service => 'ftp', + Port => $arg{Port} || 'ftp' + ), $pkg; + + ${*$me} = ""; # partial response text + @{*$me} = (); # Last response text + + %{*$me} = (%{*$me}, # Copy current values + Code => 0, # Last response code + Type => 'A', # Ascii/Binary/etc mode + Timeout => $arg{Timeout} || 120, # Timeout value + Debug => $arg{Debug} || 0, # Output debug information + FtpHost => $host, # Remote hostname + State => FTP_RESPONSE, # Current state + + ############################################################## + # Other elements used during the lifetime of the object are + # + # LISTEN Listen socket + # DATA Data socket + ); + + $me->autoflush(1); + + $me->debug($arg{Debug}) + if(exists $arg{Debug}); + + unless(2 == $me->response()) + { + $me->close(); + undef $me; + } + + $me; +} + +## +## User interface methods +## + +=head2 * debug( $value ) + +Set the level of debug information for this object. If no argument is given +then the current state is returned. Otherwise the state is changed to +C<$value>and the previous state returned. + +=cut + +sub debug { + my $me = shift; + my $debug = ${*$me}{Debug}; + + if(@_) + { + ${*$me}{Debug} = 0 + shift; + + printf STDERR "\n$me VERSION %s\n", $Net::FTP::VERSION + if(${*$me}{Debug}); + } + + $debug; +} + +=head2 quit + +Send the QUIT command to the remote FTP server and close the socket connection. + +=cut + +sub quit { + my $me = shift; + + return undef + unless $me->QUIT; + + close($me); + + return 1; +} + +=head2 ascii/ebcdic/binary/byte + +Put the remote FTP server ant the FTP package into the given mode +of data transfer. + +=cut + +sub ascii { shift->type('A',@_); } +sub ebcdic { shift->type('E',@_); } +sub binary { shift->type('I',@_); } +sub byte { shift->type('L',@_); } + +# Allow the user to send a command directly, BE CAREFUL !! + +sub quot { + my $me = shift; + my $cmd = shift; + + $me->send_cmd( uc $cmd, @_); + + $me->response(); +} + +=head2 login([$login [, $password [, $account]]]) + +Log into the remote FTP server with the given login information. If +no arguments are given then the users $HOME/.netrc file is searched +for the remote server's hostname. If no information is found then +a login of I<anonymous> is used. If no password is given and the login +is anonymous then the users Email address will be used for a password + +=cut + +sub login { + my $me = shift; + my $user = shift; + my $pass = shift if(defined $user); + my $acct = shift if(defined $pass); + my $ok; + + unless(defined $user) + { + require Net::Netrc; + my $rc = Net::Netrc->lookup(${*$me}{FtpHost}); + + ($user,$pass,$acct) = $rc->lpa() + if $rc; + } + + $user = "anonymous" + unless defined $user; + + $pass = "-" . (getpwuid($>))[0] . "@" + if !defined $pass && $user eq "anonymous"; + + $ok = $me->USER($user); + + $ok = $me->PASS($pass) + if $ok == 3; + + $ok = $me->ACCT($acct || "") + if $ok == 3; + + $ok == 2; +} + +=head2 authorise($auth, $resp) + +This is a protocol used by some firewall ftp proxies. It is used +to authorise the user to send data out. + +=cut + +sub authorise { + my($me,$auth,$resp) = @_; + my $ok; + + carp "Net::FTP::authorise <auth> <resp>\n" + unless defined $auth && defined $resp; + + $ok = $me->AUTH($auth); + + $ok = $me->RESP($resp) + if $ok == 3; + + $ok == 2; +} + +=head2 rename( $oldname, $newname) + +Rename a file on the remote FTP server from C<$oldname> to C<$newname> + +=cut + +sub rename { + my($me,$from,$to) = @_; + + croak "Net::FTP:rename <from> <to>\n" + unless defined $from && defined $to; + + $me->RNFR($from) and $me->RNTO($to); +} + +sub type { + my $me = shift; + my $type = shift; + my $ok = 0; + + return ${*$me}{Type} + unless defined $type; + + return undef + unless($me->TYPE($type,@_)); + + ${*$me}{Type} = join(" ",$type,@_); +} + +sub abort { + my $me = shift; + + ${*$me}{DATA}->abort() + if defined ${*$me}{DATA}; +} + +sub get { + my $me = shift; + my $remote = shift; + my $local = shift; + my $where = shift || 0; + my($loc,$len,$buf,$resp,$localfd,$data); + local *FD; + + $localfd = ref($local) ? fileno($local) + : 0; + + ($local = $remote) =~ s#^.*/## unless(defined $local); + + if($localfd) + { + $loc = $local; + } + else + { + $loc = \*FD; + + unless(($where) ? open($loc,">>$local") : open($loc,">$local")) + { + carp "Cannot open Local file $local: $!\n"; + return undef; + } + } + + if ($where) { + $data = $me->rest_cmd($where,$remote) or + return undef; + } + else { + $data = $me->retr($remote) or + return undef; + } + + $buf = ''; + + do + { + $len = $data->read($buf,1024); + } + while($len > 0 && syswrite($loc,$buf,$len) == $len); + + close($loc) + unless $localfd; + + $data->close() == 2; # implied $me->response +} + +sub cwd { + my $me = shift; + my $dir = shift || "/"; + + return $dir eq ".." ? $me->CDUP() + : $me->CWD($dir); +} + +sub pwd { + my $me = shift; + + $me->PWD() ? ($me->message =~ /\"([^\"]+)/)[0] + : undef; +} + +sub put { shift->send("stor",@_) } +sub put_unique { shift->send("stou",@_) } +sub append { shift->send("appe",@_) } + +sub nlst { shift->data_cmd("NLST",@_) } +sub list { shift->data_cmd("LIST",@_) } +sub retr { shift->data_cmd("RETR",@_) } +sub stor { shift->data_cmd("STOR",@_) } +sub stou { shift->data_cmd("STOU",@_) } +sub appe { shift->data_cmd("APPE",@_) } + +sub send { + my $me = shift; + my $cmd = shift; + my $local = shift; + my $remote = shift; + my($loc,$sock,$len,$buf,$localfd); + local *FD; + + $localfd = ref($local) ? fileno($local) + : 0; + + unless(defined $remote) + { + croak "Must specify remote filename with stream input\n" + if $localfd; + + ($remote = $local) =~ s%.*/%%; + } + + if($localfd) + { + $loc = $local; + } + else + { + $loc = \*FD; + + unless(open($loc,"<$local")) + { + carp "Cannot open Local file $local: $!\n"; + return undef; + } + } + + $cmd = lc $cmd; + + $sock = $me->$cmd($remote) or + return undef; + + do + { + $len = sysread($loc,$buf,1024); + } + while($len && $sock->write($buf,$len) == $len); + + close($loc) + unless $localfd; + + $sock->close(); + + ($remote) = $me->message =~ /unique file name:\s*(\S*)\s*\)/ + if $cmd eq 'stou' ; + + return $remote; +} + +sub port { + my $me = shift; + my $port = shift; + my $ok; + + unless(defined $port) + { + my $listen; + + if(defined ${*$me}{LISTEN}) + { + ${*$me}{LISTEN}->close(); + delete ${*$me}{LISTEN}; + } + + # create a Listen socket at same address as the command socket + + $listen = Net::Socket->new(Listen => 5, + Service => 'ftp', + Addr => $me->sockhost, + ); + + ${*$me}{LISTEN} = $listen; + + my($myport, @myaddr) = ($listen->sockport, split(/\./,$listen->sockhost)); + + $port = join(',', @myaddr, $myport >> 8, $myport & 0xff); + } + + $ok = $me->PORT($port); + + ${*$me}{Port} = $port; + + $ok; +} + +sub ls { shift->list_cmd("NLST",@_); } +sub lsl { shift->list_cmd("LIST",@_); } + +sub pasv { + my $me = shift; + my $hostport; + + return undef + unless $me->PASV(); + + ($hostport) = $me->message =~ /(\d+(,\d+)+)/; + + ${*$me}{Pasv} = $hostport; +} + +## +## Communication methods +## + +sub timeout { + my $me = shift; + my $timeout = ${*$me}{Timeout}; + + ${*$me}{Timeout} = 0 + shift if(@_); + + $timeout; +} + +sub accept { + my $me = shift; + + return undef unless defined ${*$me}{LISTEN}; + + my $data = ${*$me}{LISTEN}->accept; + + ${*$me}{LISTEN}->close(); + delete ${*$me}{LISTEN}; + + ${*$data}{Timeout} = ${*$me}{Timeout}; + ${*$data}{Cmd} = $me; + ${*$data} = ""; + + ${*$me}{State} = FTP_XFER; + ${*$me}{DATA} = bless $data, "Net::FTP::" . ${*$me}{Type}; +} + +sub message { + my $me = shift; + join("\n", @{*$me}); +} + +sub ok { + my $me = shift; + my $code = ${*$me}{Code} || 0; + + 0 < $code && $code < 400; +} + +sub code { + my $me = shift; + + ${*$me}{Code}; +} + +sub list_cmd { + my $me = shift; + my $cmd = lc shift; + my $data = $me->$cmd(@_); + + return undef + unless(defined $data); + + bless $data, "Net::FTP::A"; # Force ASCII mode + + my $databuf = ''; + my $buf = ''; + + while($data->read($databuf,1024)) { + $buf .= $databuf; + } + + my $list = [ split(/\n/,$buf) ]; + + $data->close(); + + wantarray ? @{$list} : $list; +} + +sub data_cmd { + my $me = shift; + my $cmd = uc shift; + my $ok = 1; + my $pasv = defined ${*$me}{Pasv} ? 1 : 0; + + $ok = $me->port + unless $pasv || defined ${*$me}{Port}; + + $ok = $me->$cmd(@_) + if $ok; + + return $pasv ? $ok + : $ok ? $me->accept() + : undef; +} + +sub rest_cmd { + my $me = shift; + my $ok = 1; + my $pasv = defined ${*$me}{Pasv} ? 1 : 0; + my $where = shift; + my $file = shift; + + $ok = $me->port + unless $pasv || defined ${*$me}{Port}; + + $ok = $me->REST($where) + if $ok; + + $ok = $me->RETR($file) + if $ok; + + return $pasv ? $ok + : $ok ? $me->accept() + : undef; +} + +sub cmd { + my $me = shift; + + $me->send_cmd(@_); + $me->response(); +} + +sub send_cmd { + my $me = shift; + + if(scalar(@_)) { + my $cmd = join(" ", @_) . "\r\n"; + + delete ${*$me}{Pasv}; + delete ${*$me}{Port}; + + syswrite($me,$cmd,length $cmd); + + ${*$me}{State} = FTP_RESPONSE; + + printf STDERR "\n$me>> %s", $cmd=~/^(pass|resp)/i ? "$1 ....\n" : $cmd + if $me->debug; + } + + $me; +} + +sub pasv_wait { + my $me = shift; + my $non_pasv = shift; + my $file; + + my($rin,$rout); + vec($rin,fileno($me),1) = 1; + select($rout=$rin, undef, undef, undef); + + $me->response(); + $non_pasv->response(); + + return undef + unless $me->ok() && $non_pasv->ok(); + + return $1 + if $me->message =~ /unique file name:\s*(\S*)\s*\)/; + + return $1 + if $non_pasv->message =~ /unique file name:\s*(\S*)\s*\)/; + + return 1; +} + +sub response { + my $me = shift; + my $timeout = ${*$me}{Timeout}; + my($code,$more,$rin,$rout,$partial,$buf) = (undef,0,'','','',''); + + @{*$me} = (); # the responce + $buf = ${*$me}; + my @buf = (); + + vec($rin,fileno($me),1) = 1; + + do + { + if(length($buf) || ($timeout==0) || select($rout=$rin, undef, undef, $timeout)) + { + unless(length($buf) || sysread($me, $buf, 1024)) + { + carp "Unexpected EOF on command channel"; + return undef; + } + + substr($buf,0,0) = $partial; ## prepend from last sysread + + @buf = split(/\r?\n/, $buf); ## break into lines + + $partial = (substr($buf, -1, 1) eq "\n") ? '' + : pop(@buf); + + $buf = ""; + + while (@buf) + { + my $cmd = shift @buf; + print STDERR "$me<< $cmd\n" + if $me->debug; + + ($code,$more) = ($1,$2) + if $cmd =~ /^(\d\d\d)(.)/; + + push(@{*$me},$'); + + last unless(defined $more && $more eq "-"); + } + } + else + { + carp "$me: Timeout" if($me->debug); + return undef; + } + } + while((scalar(@{*$me}) == 0) || (defined $more && $more eq "-")); + + ${*$me} = @buf ? join("\n",@buf,"") : ""; + ${*$me} .= $partial; + + ${*$me}{Code} = $code; + ${*$me}{State} = FTP_READY; + + substr($code,0,1); +} + +;######################################## +;# +;# RFC959 commands +;# + +sub no_imp { croak "Not implemented\n"; } + +sub ABOR { shift->send_cmd("ABOR")->response() == 2} +sub CDUP { shift->send_cmd("CDUP")->response() == 2} +sub NOOP { shift->send_cmd("NOOP")->response() == 2} +sub PASV { shift->send_cmd("PASV")->response() == 2} +sub QUIT { shift->send_cmd("QUIT")->response() == 2} +sub DELE { shift->send_cmd("DELE",@_)->response() == 2} +sub CWD { shift->send_cmd("CWD", @_)->response() == 2} +sub PORT { shift->send_cmd("PORT",@_)->response() == 2} +sub RMD { shift->send_cmd("RMD", @_)->response() == 2} +sub MKD { shift->send_cmd("MKD", @_)->response() == 2} +sub PWD { shift->send_cmd("PWD", @_)->response() == 2} +sub TYPE { shift->send_cmd("TYPE",@_)->response() == 2} +sub APPE { shift->send_cmd("APPE",@_)->response() == 1} +sub LIST { shift->send_cmd("LIST",@_)->response() == 1} +sub NLST { shift->send_cmd("NLST",@_)->response() == 1} +sub RETR { shift->send_cmd("RETR",@_)->response() == 1} +sub STOR { shift->send_cmd("STOR",@_)->response() == 1} +sub STOU { shift->send_cmd("STOU",@_)->response() == 1} +sub RNFR { shift->send_cmd("RNFR",@_)->response() == 3} +sub RNTO { shift->send_cmd("RNTO",@_)->response() == 2} +sub ACCT { shift->send_cmd("ACCT",@_)->response() == 2} +sub RESP { shift->send_cmd("RESP",@_)->response() == 2} +sub REST { shift->send_cmd("REST",@_)->response() == 3} +sub USER { my $ok = shift->send_cmd("USER",@_)->response();($ok == 2 || $ok == 3) ? $ok : 0;} +sub PASS { my $ok = shift->send_cmd("PASS",@_)->response();($ok == 2 || $ok == 3) ? $ok : 0;} +sub AUTH { my $ok = shift->send_cmd("AUTH",@_)->response();($ok == 2 || $ok == 3) ? $ok : 0;} + +sub ALLO { no_imp; } +sub SMNT { no_imp; } +sub HELP { no_imp; } +sub MODE { no_imp; } +sub SITE { no_imp; } +sub SYST { no_imp; } +sub STAT { no_imp; } +sub STRU { no_imp; } +sub REIN { no_imp; } + +package Net::FTP::dataconn; +use Carp; +no strict 'vars'; + +sub abort { + my $fd = shift; + my $ftp = ${*$fd}{Cmd}; + + $ftp->send_cmd("ABOR"); + $fd->close(); +} + +sub close { + my $fd = shift; + my $ftp = ${*$fd}{Cmd}; + + $fd->Net::Socket::close(); + delete ${*$ftp}{DATA}; + + $ftp->response(); +} + +sub timeout { + my $me = shift; + my $timeout = ${*$me}{Timeout}; + + ${*$me}{Timeout} = 0 + shift if(@_); + + $timeout; +} + +sub _select { + my $fd = shift; + local *timeout = \$_[0]; shift; + my $rw = shift; + my($rin,$win); + + return 1 unless $timeout; + + $rin = ''; + vec($rin,fileno($fd),1) = 1; + + $win = $rw ? undef : $rin; + $rin = undef unless $rw; + + my $nfound = select($rin, $win, undef, $timeout); + + croak "select: $!" + if $nfound < 0; + + return $nfound; +} + +sub can_read { + my $fd = shift; + local *timeout = \$_[0]; + + $fd->_select($timeout,1); +} + +sub can_write { + my $fd = shift; + local *timeout = \$_[0]; + + $fd->_select($timeout,0); +} + +sub cmd { + my $me = shift; + + ${*$me}{Cmd}; +} + + +@Net::FTP::L::ISA = qw(Net::FTP::I); +@Net::FTP::E::ISA = qw(Net::FTP::I); + +package Net::FTP::A; +@Net::FTP::A::ISA = qw(Net::FTP::dataconn); +use Carp; + +no strict 'vars'; + +sub read { + my $fd = shift; + local *buf = \$_[0]; shift; + my $size = shift || croak 'read($buf,$size,[$offset])'; + my $offset = shift || 0; + my $timeout = ${*$fd}{Timeout}; + my $l; + + croak "Bad offset" + if($offset < 0); + + $offset = length $buf + if($offset > length $buf); + + $l = 0; + READ: + { + $fd->can_read($timeout) or + croak "Timeout"; + + my $n = sysread($fd, ${*$fd}, $size, length ${*$fd}); + + return $n + unless($n >= 0); + +# my $lf = substr(${*$fd},-1,1) eq "\r" ? chop(${*$fd}) +# : ""; + + my $lf = (length ${*$fd} > 0 && substr(${*$fd},-1,1) eq "\r") ? chop(${*$fd}) + : ""; + + ${*$fd} =~ s/\r\n/\n/go; + + substr($buf,$offset) = ${*$fd}; + + $l += length(${*$fd}); + $offset += length(${*$fd}); + + ${*$fd} = $lf; + + redo READ + if($l == 0 && $n > 0); + + if($n == 0 && $l == 0) + { + substr($buf,$offset) = ${*$fd}; + ${*$fd} = ""; + } + } + + return $l; +} + +sub write { + my $fd = shift; + local *buf = \$_[0]; shift; + my $size = shift || croak 'write($buf,$size,[$timeout])'; + my $timeout = @_ ? shift : ${*$fd}{Timeout}; + + $fd->can_write($timeout) or + croak "Timeout"; + + # What is previous pkt ended in \r or not ?? + + my $tmp; + ($tmp = $buf) =~ s/(?!\r)\n/\r\n/g; + + my $len = $size + length($tmp) - length($buf); + my $wrote = syswrite($fd, $tmp, $len); + + if($wrote >= 0) + { + $wrote = $wrote == $len ? $size + : $len - $wrote + } + + return $wrote; +} + +package Net::FTP::I; +@Net::FTP::I::ISA = qw(Net::FTP::dataconn); +use Carp; + +no strict 'vars'; + +sub read { + my $fd = shift; + local *buf = \$_[0]; shift; + my $size = shift || croak 'read($buf,$size,[$timeout])'; + my $timeout = @_ ? shift : ${*$fd}{Timeout}; + + $fd->can_read($timeout) or + croak "Timeout"; + + my $n = sysread($fd, $buf, $size); + + $n; +} + +sub write { + my $fd = shift; + local *buf = \$_[0]; shift; + my $size = shift || croak 'write($buf,$size,[$timeout])'; + my $timeout = @_ ? shift : ${*$fd}{Timeout}; + + $fd->can_write($timeout) or + croak "Timeout"; + + syswrite($fd, $buf, $size); +} + +=head2 AUTHOR + +Graham Barr <Graham.Barr@tiuk.ti.com> + +=head2 REVISION + +$Revision: 1.17 $ + +=head2 COPYRIGHT + +Copyright (c) 1995 Graham Barr. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. + +=cut + + +1; + diff --git a/lib/Net/Netrc.pm b/lib/Net/Netrc.pm new file mode 100644 index 0000000000..58f066363d --- /dev/null +++ b/lib/Net/Netrc.pm @@ -0,0 +1,123 @@ +package Net::Netrc; + +use Carp; +use strict; + +my %netrc = (); + +sub _readrc { + my $host = shift; + my $file = (getpwuid($>))[7] . "/.netrc"; + my($login,$pass,$acct) = (undef,undef,undef); + local *NETRC; + local $_; + + $netrc{default} = undef; + + my @stat = stat($file); + + if(@stat) + { + if($stat[2] & 077) + { + carp "Bad permissions: $file"; + return (); + } + if($stat[4] != $<) + { + carp "Not owner: $file"; + return (); + } + } + + if(open(NETRC,$file)) + { + my($mach,$macdef,$tok,@tok) = (0,0); + + while(<NETRC>) + { + undef $macdef if /\A\n\Z/; + + if($macdef) + { + push(@$macdef,$_); + next; + } + + push(@tok, split(/[\s\n]+/, $_)); + +TOKEN: + while(@tok) + { + if($tok[0] eq "default") + { + shift(@tok); + $mach = $netrc{default} = {}; + + next TOKEN; + } + + last TOKEN unless @tok > 1; + $tok = shift(@tok); + + if($tok eq "machine") + { + my $host = shift @tok; + $mach = $netrc{$host} = {}; + } + elsif($tok =~ /^(login|password|account)$/) + { + next TOKEN unless $mach; + my $value = shift @tok; + $mach->{$1} = $value; + } + elsif($tok eq "macdef") + { + next TOKEN unless $mach; + my $value = shift @tok; + $mach->{macdef} = {} unless exists $mach->{macdef}; + $macdef = $mach->{machdef}{$value} = []; + } + } + } + close(NETRC); + } +} + +sub lookup { + my $pkg = shift; + my $mach = shift; + + _readrc() unless exists $netrc{default}; + + return bless \$mach if exists $netrc{$mach}; + + return bless \("default") if defined $netrc{default}; + + return undef; +} + +sub login { + my $me = shift; + $me = $netrc{$$me}; + exists $me->{login} ? $me->{login} : undef; +} + +sub account { + my $me = shift; + $me = $netrc{$$me}; + exists $me->{account} ? $me->{account} : undef; +} + +sub password { + my $me = shift; + $me = $netrc{$$me}; + exists $me->{password} ? $me->{password} : undef; +} + +sub lpa { + my $me = shift; + ($me->login, $me->password, $me->account); +} + +1; diff --git a/lib/Net/Socket.pm b/lib/Net/Socket.pm new file mode 100644 index 0000000000..d24e625233 --- /dev/null +++ b/lib/Net/Socket.pm @@ -0,0 +1,332 @@ +package Net::Socket; + +=head1 NAME + +Net::Socket - TEMPORARY Socket filedescriptor class, so Net::FTP still +works while IO::Socket is having a re-fit <GBARR> + +=head1 DESCRIPTION + +NO TEXT --- THIS MODULE IS TEMPORARY + +=cut + +require 5.001; +use Socket 1.3; +use Carp; +require Exporter; + +@ISA = qw(Exporter); +@EXPORT_OK = @Socket::EXPORT; + +$VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/); +sub Version { $VERSION } + +## +## Really WANT FileHandle::new to return this !!! +## +my $seq = 0; +sub _gensym { + my $pkg = @_ ? ref($_[0]) || $_[0] : ""; + local *{$pkg . "::GLOB" . ++$seq}; + \delete ${$pkg . "::"}{'GLOB' . $seq}; +} + +my %socket_type = ( + tcp => SOCK_STREAM, + udp => SOCK_DGRAM, + rpc => SOCK_DGRAM, +); + +# Peer => remote host name for a 'connect' socket +# Proto => specifiy protocol by it self (but override by Service) +# Service => require service eg 'ftp' or 'ftp/tcp', overrides Proto +# Port => port num for connect eg 'ftp' or 21, defaults to Service +# Bind => port to bind to, defaults to INADDR_ANY +# Listen => queue size for listen +# +# if Listen is defined then a listen socket is created, else if the socket +# type, which is derived from the protocol, is SOCK_STREAM then a connect +# is called + +=head2 new( %args ) + +The new constructor takes its arguments in the form of a hash. Accepted +arguments are + + Peer => remote host name for a 'connect' socket + Proto => specifiy protocol by it self (but override by Service) + Service => require service eg 'ftp' or 'ftp/tcp', overrides Proto + Port => port num for connect eg 'ftp' or 21, defaults to Service + Bind => port to bind to, defaults to INADDR_ANY + Listen => queue size for listen + +=cut + +sub new { + my $pkg = shift; + my %arg = @_; + + my $proto = $arg{Proto} || ""; + my $bindport = $arg{Bind} || 0; + my $servport = $arg{Port} || 0; + + my $service = $arg{Service} || $servport || $bindport; + + ($service,$proto) = split(m,/,, $service) + if $service =~ m,/,; + + my @serv = $service =~ /\D/ ? getservbyname($service,$proto) + : getservbyport($service,$proto); + + $proto = $proto || $serv[3]; + + croak "cannot determine protocol" + unless $proto; + + my @proto = $proto =~ /\D/ ? getprotobyname($proto) + : getprotobynumber($proto); + + croak "unknown protocol" + unless @proto; + + my $type = $arg{Type} || $socket_type{$proto[0]} or + croak "Unknown socket type"; + + my $bindaddr = exists $arg{Addr} ? inet_aton($arg{Addr}) + : INADDR_ANY; + + croak "bad bind address $arg{Addr}" + unless $bindaddr; + + my $sock = bless _gensym(), ref($pkg) || $pkg; + + socket($sock, AF_INET, $type, $proto[2]) or + croak "socket: $!"; + + $bindport = (getservbyname($bindport,$proto))[2] + if $bindport =~ /\D/; + + bind($sock, sockaddr_in($bindport, $bindaddr)) or + croak "bind: $!"; + + if(defined $arg{Listen}) + { + my $queue = $arg{Listen} || 1; + + listen($sock, $queue) or + croak "listen: $!"; + } + else + { + $servport = $serv[2] || 0 + unless $servport =~ /^\d+$/ && $servport > 0; + + croak "cannot determine port" + unless($servport); + + my $destaddr = defined $arg{Peer} ? inet_aton($arg{Peer}) + : undef; + + my $peername = defined $destaddr ? sockaddr_in($servport,$destaddr) + : undef; + + + if($type == SOCK_STREAM || $destaddr) + { + croak "bad peer address" + unless defined $destaddr; + + connect($sock, $peername) or + croak "connect: $!"; + + ${*$sock}{Peername} = getpeername($sock); + } + else + { + ${*$sock}{Peername} = $peername; + } + } + + ${*$sock}{Sockname} = getsockname($sock); + + $sock; +} + +=head2 autoflush( [$val] ) + +Set the file descriptor to autoflush, depending on C<$val> + +=cut + +sub autoflush { + my $sock = shift; + my $val = @_ ? shift : 0; + + select((select($sock), $| = $val)[$[]); +} + +=head2 accept + +perform the system call C<accept> on the socket and return a new Net::Socket +object. This object can be used to communicate with the client that was trying +to connect. + +=cut + +sub accept { + my $sock = shift; + + my $new = bless _gensym(); + + accept($new,$sock) or + croak "accept: $!"; + + ${*$new}{Peername} = getpeername($new) or + croak "getpeername: $!"; + + ${*$new}{Sockname} = getsockname($new) or + croak "getsockname: $!"; + + $new; +} + +=head2 close + +Close the file descriptor + +=cut + +sub close { + my $sock = shift; + + delete ${*$sock}{Sockname}; + delete ${*$sock}{Peername}; + + close($sock); +} + +=head2 dup + +Create a duplicate of the socket object + +=cut + +sub dup { + my $sock = shift; + my $dup = bless _gensym(), ref($sock); + + if(open($dup,">&" . fileno($sock))) { + # Copy all the internals + ${*$dup} = ${*$sock}; + @{*$dup} = @{*$sock}; + %{*$dup} = %{*$sock}; + } + else { + undef $dup; + } + + $dup; +} + +# Some info about the local socket + +=head2 sockname + +Return a packed sockaddr structure for the socket + +=head2 sockaddr + +Return the address part of the sockaddr structure for the socket + +=head2 sockport + +Return the port number that the socket is using on the local host + +=head2 sockhost + +Return the address part of the sockaddr structure for the socket in a +text form xx.xx.xx.xx + +=cut + +sub sockname { my $sock = shift; ${*$sock}{Sockname} } +sub sockaddr { (sockaddr_in(shift->sockname))[1]} +sub sockport { (sockaddr_in(shift->sockname))[0]} +sub sockhost { inet_ntoa( shift->sockaddr);} + +# Some info about the remote socket, for connect-d sockets + +=head2 peername, peeraddr, peerport, peerhost + +Same as for the sock* functions, but returns the data about the peer +host instead of the local host. + +=cut + +sub peername { my $sock = shift; ${*$sock}{Peername} or croak "no peer" } +sub peeraddr { (sockaddr_in(shift->peername))[1]} +sub peerport { (sockaddr_in(shift->peername))[0]} +sub peerhost { inet_ntoa( shift->peeraddr);} + +=head2 send( $buf [, $flags [, $to]] ) + +For a udp socket, send the contents of C<$buf> to the remote host C<$to> using +flags C<$flags>. + +If C<$to> is not specified then the data is sent to the host which the socket +last communicated with, ie sent to or recieved from. + +If C<$flags> is ommited then 0 is used + +=cut + +sub send { + my $sock = shift; + local *buf = \$_[0]; shift; + my $flags = shift || 0; + my $to = shift || $sock->peername; + + # remember who we send to + ${*$sock}{Peername} = $to; + + send($sock, $buf, $flags, $to); +} + +=head2 recv( $buf, $len [, $flags] ) + +Receive C<$len> bytes of data from the socket and place into C<$buf> + +If C<$flags> is ommited then 0 is used + +=cut + +sub recv { + my $sock = shift; + local *buf = \$_[0]; shift; + my $len = shift; + my $flags = shift || 0; + + # remember who we recv'd from + ${*$sock}{Peername} = recv($sock, $buf='', $len, $flags); +} + +=head1 AUTHOR + +Graham Barr <Graham.Barr@tiuk.ti.com> + +=head1 REVISION + +$Revision: 1.2 $ + +=head1 COPYRIGHT + +Copyright (c) 1995 Graham Barr. All rights reserved. This program is free +software; you can redistribute it and/or modify it under the same terms +as Perl itself. + +=cut + +1; # Keep require happy + + |