summaryrefslogtreecommitdiff
path: root/lib/Net
diff options
context:
space:
mode:
authorPerl 5 Porters <perl5-porters@africa.nicoh.com>1996-12-19 16:44:00 +1200
committerChip Salzenberg <chip@atlantic.net>1996-12-19 16:44:00 +1200
commit5f05dabc4054964aa3b10f44f8468547f051cdf8 (patch)
tree7bcc2c7b6d5cf44e7f0111bac2240ca979d9c804 /lib/Net
parent6a3992aa749356d657a4c0e14be8c2f4c2f4f999 (diff)
downloadperl-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.pm943
-rw-r--r--lib/Net/Netrc.pm123
-rw-r--r--lib/Net/Socket.pm332
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
+
+