@@ -1,140 +1,317 @@
-libnet 1.27 -- Fri May 30 2014
-
- * Simplified Makefile.PL requirements.
-
-libnet 1.26 -- Fri May 30 2014
-
- * Set minimum required ExtUtils::MakeMaker version to 6.64 to ensure that all
- parameters used are supported, to save jumping through hoops to support
- earlier versions. (This should not be a problem since ExtUtils::MakeMaker
- 6.64 is easily installed into Perl 5.8.1 and above, that being the whole
- point of the new choice of minimum supported Perl version.)
- * Set minimum required Perl version to 5.8.1. This is in line with the
- minimum requirement of the "Perl Toolchain".
-
-libnet 1.25 -- Tue Feb 04 2014
-
- * Fix Net::FTP::pasv_wait() not handling errors from Net::Cmd::reponse()
- [bergner@cs.umu.se; resolves CPAN RT#50420]
- * Make inheritance from Net::Cmd clearer in the documentation [Resolves CPAN
- RT#72889]
- * Set timeout for data connection creation in Net::FTP [Oleg G; resolves CPAN
- RT#78926]
- * Stop Net::Domain::domainname() from giving out warnings in android [Brian
- Fraser]
-
-libnet 1.24 -- Mon Jan 06 2014
-
- * Fix incorrect handling of CRLF in Net::FTP [Willem Monsuwé; resolves CPAN
- RT#41642/62029]
- * POD fixes [Dominic Hargreaves; resolves CPAN RT#91761]
-
-libnet 1.23 -- Mon Aug 12 2013
-
- * Typo fixes [David Steinbrunner; resolves CPAN RT#87681]
-
-libnet 1.22_02 -- Thu Aug 08 2013
-
- * Make Net::FTP::dataconn::close() more robust [Together with changes to
- Net::FTP already made in 1.22_01, this resolves CPAN RT#37700]
- * Document scalar/list context return values from Net::Cmd::message()
- * Fix broken URL [Resolves CPAN RT#68749]
- * Fix documentation typo in Net::Netrc
- * Fix broken POD in Net::POP3
- * Improve Net::SMTP documentation of new(), auth() and message() [Resolves
- CPAN RT#36038]
- * Add proper skips to skipped tests in ftp.t
- * Import hostname.t fix from perl core commit #adeb94125a
- * Add time.t, imported from perl core commit #c85707204c
- * Add new maintainer information, with updated CPAN and GitHub links
-
-libnet 1.22_01 -- Mon May 31 09:40:25 CDT 2010
-
- * Do not create/pass a remote name if one is not given to put_unique
- * Add ->passive method to switch between PORT/PASV connections
- * Accept - in command parsed from SMTP HELO response
- * Allow group to set to a group named "0"
- * Set $@ when ->new returns undef
- * Add support for LocalAddr to be passed to ->new
- * Document that timeout is in seconds
- * Fix leading . encoding in datasend
- * Make ->supported check ->feature
- * Allow words other than FILE to prefix the unique name returned in info message from stou
- * Send ALLO command just before the store command
- * Avoid warnings when server do not prefix messages with codes
- * Use uppercase characters for xtext encoding
- * Catch timeout condition while doing an abort
- * Ensure REST is sent directly before command being restarted
- * [rt.cpan.org #49920] Fix URL [Leon Brocard]
- * Avoid long hang on Mac OS X when hostname is *.local by not calling gethostbyname [Father Chrysostomos]
- * Avoid infinite recursion in rmdir
- * Allow finding _netrc on machines that do not support .netrc [Ben Bimber]
-
-libnet 1.22 -- Sun Aug 26 07:13:18 CDT 2007
-
-Bug Fixes
- * Fix a bug in Net::Cmd that is_utf8 does not exist prior to perl 5.8.1
-
-libnet 1.21 -- Sat May 19 08:53:09 CDT 2007
-
-Bug Fixes
- * Fix bug causing utf8 encoding of 8bit strings in Net::Cmd
- * Fix precedence issue in Net::NNTP. Patch from Brendan O'Dea
- * Fixed bug causing removal of last character on the line when
- doing ASCII FTP transfers
-
-Enhancements
- * Add support for ENVID and AUTH to Net::SMTP. Patch by Mark Martinec
- * Changed default for FTP transfers to be passive
- * Added support for FTP FEAT command
-
-libnet 1.20 -- Fri Feb 2 19:42:51 CST 2007
-
-Bug Fixes
- * Fixed incorrect handling of CRLF that straddled two blocks
- * Fix bug in response() which was too liberal in what it thought was a response line
- * Silence uninitialized value warnings in Net::Cmd during testing on Win32
- * Documentations typos and updates
-
-Enhancements
- * Added support for ORCPT into Net::SMTP
- * Support for servers that expect the USER command in upper or lower case. Try USER
- first then try user if that fails
-
-libnet 1.19 -- Wed Jun 30 14:53:48 BST 2004
-
-Bug Fixes
- * Fixed datasend test to work on Win32 platform
- * Fixed Authen::SASL checking in SMTP.pm and POP3.pm
- * Fixed bug that a restarted get with Net::FTP did not append to local file
-
-libnet 1.18 -- Mon Mar 22 16:19:01 GMT 2004
-
-Bug Fixes
- * Fixed bug in CRLF translation in Net::Cmd datasend/dataend methods
- * Fixed bug in converting numbers returned by PASV command into a
- packed IP address
- * Fixed bug that caused Net::FTP->get to truncate the local file after
- the restart method had been called
- * Fixed bug in Net::FTP-.rmdir when the server returned . and .. in
- the contents of a directory
- * Fixed bug in POP3 that was sending unnecessary RSETs
-
-Enhancements
- * Added support for POP3 CAPA command
- * Added support for XVERP to Net::SMTP
- * Added Net::POP3->banner method to return the banner received from
- the server during connect
- * Added Net::POP3->auth method for performing authentication using
- SASL, requires Authen::SASL
- * Added Host option to ->new constructor of FTP, NNTP, SMTP and POP3
- which can be used instead of passing the host as the first argument
- * Added ->host method to FTP, NNTP, SMTP and POP3 to return the host
- string used for the connect. This is useful to determine which host
- was connected to when multiple hosts are specified
- * Added support for more non-standard responses to Net::FTP->size
- * Updated POD for Net::SMTP wrt. not passing a Hello parameter to the
- constructor. (Jeff Macdonald)
-
-ChangeLogs for releases prior to 1.18 may be found at
-http://svn.mutatus.co.uk/browse/libnet/tags/libnet-1.17/ChangeLog
+Revision history for Perl distribution libnet
+
+3.04 2014-11-29
+
+ - SNI is now only used for SSL connections if it is supported by
+ IO::Socket::SSL (i.e. OpenSSL version >= 1). (The previous release
+ switched to using SNI by default, which caused some CPAN Testers
+ failures.) [Steffen Ullrich, PR#10]
+
+3.03 2014-11-28
+
+ - Remodelled SSL support in Net::NNTP in the manner of Net::POP3 and
+ Net::SMTP. [Steffen Ullrich, PR#9]
+
+ - Increased minimum requred IO::Socket::SSL version from 1.999 to 2.007 to
+ fix data connection problems in Net::FTP. [Steffen Ullrich, CPAN
+ RT#100529]
+
+ - Fixed a broken port() call in pasv_xfer()/pasv_xfer_unique() in Net::FTP.
+ [Mario Preksavec, PR#8]
+
+ - Increased minimum required Socket version from 1.3 to 2.016. This may be
+ required when those modules that can support IPv6 load IO::Socket::IP (on
+ some OSes, at least). It does not appear to be necessary if they load
+ IO::Socket::INET6 or IO::Socket::INET instead, but this is not easy for
+ the end-user to control so it is simpler to always insist on Socket 2.016
+ or higher. [CPAN RT#100020]
+
+ - Fixed "Argument ... isn't numeric in subroutine entry" warnings when using
+ older versions of Perl. [CPAN RT#100020]
+
+ - Added optional Changes testing (skipped unless AUTHOR_TESTING).
+
+ - Reformatted Changes file as per CPAN::Changes::Spec.
+
+3.02 2014-10-10
+
+ - Don't run interactive prompt() in Makefile.PL when in PERL_CORE.
+
+ - Fix $smtp->auth($sasl) to try the AUTH mechanism (if present) in the
+ Authen::SASL object before falling back on other mechanisms. [CPAN
+ RT#99415]
+
+3.01 2014-10-09
+
+ - Require IO::Socket::SSL >= 1.999 to protect against a bad version (0.30)
+ of IO::Socket::IP and hopefully fix another bunch of CPAN Testers
+ failures.
+
+3.00 2014-10-09
+
+ - Skip Perl Critic, Pod and Pod Coverage tests unless AUTHOR_TESTING. [CPAN
+ RT#99399]
+
+ - Synchronize all $VERSIONs to the distribution's version number, bumping
+ that to 3.00 so that no $VERSIONs end up going backwards.
+
+1.30 2014-10-08
+
+ - Sigh. Fix PAUSE indexing problem again. Net::SMTP::SSL is already used by
+ Net-SMTP-SSL.
+
+1.29 2014-10-08
+
+ - Fix PAUSE indexing problem. Net::POP3::_SSLified and Net::SMTP::_SSLified
+ are already used by Net-SSLGlue.
+
+1.28 2014-10-08
+
+ - Improve code()/message() initialization and error handling in Net::Cmd.
+ [Tom Metro, CPAN RT#14875]
+
+ - Don't use the ALLO command on FTP servers that don't support it. [CPAN
+ RT#95717]
+
+ - Stop Makefile.PL from requiring interactive configuration when running via
+ cpan, cpanp or cpanm: just accept all defaults in these cases, as when
+ running non-interactively. [CPAN RT#48966]
+
+ - Add optional POD coverage testing.
+
+ - Add optional POD testing.
+
+ - Add optional Perl::Critic testing.
+
+ - Make code Perl::Critic clean.
+
+ - Move Net/*.pm into lib/Net/ sub-directory within distribution. This is
+ the usual layout style these days.
+
+ - Change Net::SMTP::auth() so that it now falls back to another supported
+ AUTH method if a given AUTH method fails. [Ivan Baktsheev, PR#3]
+
+ - Change Net::SMTP::auth() so that it uses the SMTP AUTH mechanism(s)
+ specified in the Authen::SASL object if one is provided instead of a
+ username. If a plain text username is specified then use the first
+ reported SMTP AUTH method supported, as usual. [Ewen McNeill, CPAN
+ RT#58002]
+
+ - Add support for IPv6 and SSL to Net::FTP, Net::NNTP, Net::POP3 and
+ Net::SMTP. These features are only available if the user has:
+
+ * a recent IO::Socket::SSL for SSL support;
+
+ * a recent IO::Socket::IP or an older IO::Socket::INET6 for IPv6 support.
+
+ If no SSL module is available it will work as before, but attempts to use
+ the SSL functionality will result in an error message. If no IPv6 modules
+ are available it will just use IPv4 as before. With IPv6 modules
+ installed one can of course still access IPv4 hosts. [Steffen Ullrich,
+ CPAN RT#93823]
+
+1.27 2014-05-30
+
+ - Simplified Makefile.PL requirements.
+
+1.26 2014-05-30
+
+ - Set minimum required ExtUtils::MakeMaker version to 6.64 to ensure that
+ all parameters used are supported, to save jumping through hoops to
+ support earlier versions. (This should not be a problem since
+ ExtUtils::MakeMaker 6.64 is easily installed into Perl 5.8.1 and above,
+ that being the whole point of the new choice of minimum supported Perl
+ version.)
+
+ - Set minimum required Perl version to 5.8.1. This is in line with the
+ minimum requirement of the "Perl Toolchain".
+
+1.25 2014-02-04
+
+ - Fix Net::FTP::pasv_wait() not handling errors from Net::Cmd::reponse().
+ [bergner@cs.umu.se, CPAN RT#50420]
+
+ - Make inheritance from Net::Cmd clearer in the documentation. [CPAN
+ RT#72889]
+
+ - Set timeout for data connection creation in Net::FTP. [Oleg G, CPAN
+ RT#78926]
+
+ - Stop Net::Domain::domainname() from giving out warnings in Android.
+ [Brian Fraser]
+
+1.24 2014-01-06
+
+ - Fix incorrect handling of CRLF in Net::FTP. [Willem Monsuwé, CPAN
+ RT#41642/62029]
+
+ - POD fixes. [Dominic Hargreaves, CPAN RT#91761]
+
+1.23 2013-08-12
+
+ - Typo fixes. [David Steinbrunner, CPAN RT#87681]
+
+1.22_02 2013-08-08
+
+ - Make Net::FTP::dataconn::close() more robust. [Together with changes to
+ Net::FTP already made in 1.22_01, this resolves CPAN RT#37700.]
+
+ - Document scalar/list context return values from Net::Cmd::message().
+
+ - Fix broken URL. [CPAN RT#68749]
+
+ - Fix documentation typo in Net::Netrc.
+
+ - Fix broken POD in Net::POP3.
+
+ - Improve Net::SMTP documentation of new(), auth() and message(). [CPAN
+ RT#36038]
+
+ - Add proper skips to skipped tests in ftp.t.
+
+ - Import hostname.t fix from Perl core commit #adeb94125a.
+
+ - Add time.t, imported from Perl core commit #c85707204c.
+
+ - Add new maintainer information, with updated CPAN and GitHub links.
+
+1.22_01 2010-05-31 09:40:25-05:00
+
+ - Do not create/pass a remote name if one is not given to put_unique.
+
+ - Add ->passive() method to switch between PORT/PASV connections.
+
+ - Accept - in command parsed from SMTP HELO response.
+
+ - Allow group to set to a group named "0".
+
+ - Set $@ when ->new() returns undef.
+
+ - Add support for LocalAddr to be passed to ->new().
+
+ - Document that timeout is in seconds.
+
+ - Fix leading . encoding in datasend().
+
+ - Make ->supported() check ->feature().
+
+ - Allow words other than FILE to prefix the unique name returned in info
+ message from stou.
+
+ - Send ALLO command just before the store command.
+
+ - Avoid warnings when server do not prefix messages with codes.
+
+ - Use uppercase characters for xtext encoding.
+
+ - Catch timeout condition while doing an abort.
+
+ - Ensure REST is sent directly before command being restarted.
+
+ - Fix URL. [Leon Brocard, CPAN RT#49920]
+
+ - Avoid long hang on Mac OS X when hostname is *.local by not calling
+ gethostbyname(). [Father Chrysostomos]
+
+ - Avoid infinite recursion in rmdir().
+
+ - Allow finding _netrc on machines that do not support .netrc. [Ben Bimber]
+
+1.22 2007-08-26 07:13:18-05:00
+
+[Bug Fixes]
+
+ - Fix a bug in Net::Cmd that is_utf8() does not exist prior to Perl 5.8.1.
+
+1.21 2007-05-19 08:53:09-05:00
+
+[Bug Fixes]
+
+ - Fix bug causing utf8 encoding of 8-bit strings in Net::Cmd.
+
+ - Fix precedence issue in Net::NNTP. [Brendan O'Dea]
+
+ - Fixed bug causing removal of last character on the line when doing ASCII
+ FTP transfers.
+
+[Enhancements]
+
+ - Add support for ENVID and AUTH to Net::SMTP. [Mark Martinec]
+
+ - Changed default for FTP transfers to be passive.
+
+ - Added support for FTP FEAT command.
+
+1.20 2007-02-02 19:42:51-06:00
+
+[Bug Fixes]
+
+ - Fixed incorrect handling of CRLF that straddled two blocks.
+
+ - Fix bug in response() which was too liberal in what it thought was a
+ response line.
+
+ - Silence uninitialized value warnings in Net::Cmd during testing on Win32.
+
+ - Documentation typos and updates.
+
+[Enhancements]
+
+ - Added support for ORCPT into Net::SMTP.
+
+ - Support for servers that expect the USER command in upper or lower case.
+ Try USER first then try user if that fails.
+
+1.19 2004-06-30 14:53:48+01:00
+
+[Bug Fixes]
+
+ - Fixed datasend() test to work on Win32 platform.
+
+ - Fixed Authen::SASL checking in Net::SMTP and Net::POP3.
+
+ - Fixed bug that a restarted get with Net::FTP did not append to local file.
+
+1.18 2004-03-22 16:19:01Z
+
+[Bug Fixes]
+
+ - Fixed bug in CRLF translation in Net::Cmd datasend()/dataend() methods.
+
+ - Fixed bug in converting numbers returned by PASV command into a packed IP
+ address.
+
+ - Fixed bug that caused Net::FTP->get() to truncate the local file after the
+ restart method had been called.
+
+ - Fixed bug in Net::FTP->rmdir() when the server returned . and .. in the
+ contents of a directory.
+
+ - Fixed bug in Net::POP3 that was sending unnecessary RSETs.
+
+[Enhancements]
+
+ - Added support for POP3 CAPA command.
+
+ - Added support for XVERP to Net::SMTP.
+
+ - Added Net::POP3->banner() method to return the banner received from the
+ server during connect.
+
+ - Added Net::POP3->auth() method for performing authentication using SASL;
+ requires Authen::SASL.
+
+ - Added Host option to ->new() constructor of FTP, NNTP, SMTP and POP3,
+ which can be used instead of passing the host as the first argument.
+
+ - Added ->host() method to FTP, NNTP, SMTP and POP3 to return the host
+ string used for the connect. This is useful to determine which host was
+ connected to when multiple hosts are specified.
+
+ - Added support for more non-standard responses to Net::FTP->size().
+
+ - Updated POD for Net::SMTP with respect to not passing a Hello parameter to
+ the constructor. [Jeff Macdonald]
+
+ChangeLogs for releases prior to 1.18 may be found at:
+https://github.com/steve-m-hay/perl-libnet/blob/v1.17/ChangeLog
@@ -1,49 +0,0 @@
-package Net::Config;
-
-require Exporter;
-use vars qw(@ISA @EXPORT %NetConfig);
-use strict;
-
-@EXPORT = qw(%NetConfig);
-@ISA = qw(Exporter);
-
-# WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-# WARNING WARNING WARNING WARNING WARNING WARNING WARNING
-#
-# Below this line is auto-generated, *ANY* changes will be lost
-
-%NetConfig = (
- # the followinf parameters are all lists of hosts for the
- # respective protocols.
- nntp_hosts => [],
- snpp_hosts => [],
- pop3_hosts => [],
- smtp_hosts => [],
- ph_hosts => [],
- daytime_hosts => [],
- time_hosts => [],
-
- # your internet domain
- inet_domain => undef,
-
- # If you have an ftp proxy firewall (not an http firewall)
- # then set this to the name of the firewall
- ftp_firewall => undef,
-
- # set if all connections done via the firewall should use
- # passive data connections
- ftp_ext_passive => 0,
-
- # set if all connections not done via the firewall should use
- # passive data connections
- ftp_int_passive => 0,
-
- # If set the make test will attempt to connect to the hosts above
- test_hosts => 0,
-
- # Used during Configure (which you are not using) to do
- # DNS lookups to ensure hosts exist
- test_exist => 0,
-
-);
-1;
@@ -1,13 +1,19 @@
-#!/usr/local/bin/perl
+#!perl
#
# $Id: Configure,v 1.8 1997/03/04 09:22:32 gbarr Exp $
+use 5.008001;
+
use strict;
-use IO::File;
-use Getopt::Std;
+use warnings;
+
use ExtUtils::MakeMaker qw(prompt);
+use Getopt::Std;
+use IO::File;
-use vars qw($opt_d $opt_o);
+## no critic (Subroutines::ProhibitSubroutinePrototypes)
+
+our($opt_d, $opt_o);
##
##
@@ -37,10 +43,9 @@ sub test_hostnames (\@)
{
my $hlist = shift;
my @h = ();
- my $host;
my $err = 0;
- foreach $host (@$hlist)
+ foreach my $host (@$hlist)
{
if(valid_host($host))
{
@@ -213,10 +218,9 @@ MASK:
sub default_hostname
{
- my $host;
my @host;
- foreach $host (@_)
+ foreach my $host (@_)
{
if(defined($host) && valid_host($host))
{
@@ -240,7 +244,10 @@ $libnet_cfg = "libnet.cfg"
my %oldcfg = ();
+{
+no warnings 'once';
$Net::Config::CONFIGURE = 1; # Suppress load of user overrides
+}
if( -f $libnet_cfg )
{
%oldcfg = ( %{ do $libnet_cfg } );
@@ -248,6 +255,7 @@ if( -f $libnet_cfg )
elsif (eval { require Net::Config })
{
$have_old = 1;
+ no warnings 'once';
%oldcfg = %Net::Config::NetConfig;
}
@@ -567,8 +575,7 @@ print "Writing $libnet_cfg\n";
print $fh "{\n";
-my $key;
-foreach $key (keys %cfg) {
+foreach my $key (keys %cfg) {
my $val = $cfg{$key};
if(!defined($val)) {
$val = "undef";
@@ -1,14 +0,0 @@
-#
-
-package Sys::Hostname;
-
-use Net::Domain qw(hostname);
-use Carp;
-
-require Exporter;
-@ISA = qw(Exporter);
-@EXPORT = qw(hostname);
-
-carp "deprecated package 'Sys::Hostname', use Net::Domain" if $^W;
-
-1;
@@ -1,42 +1,52 @@
Changes
-Config.eg
Configure
-Hostname.pm.eg Example replacement for Hostname.pm
MANIFEST
+MANIFEST.SKIP
Makefile.PL
-Net/Cmd.pm
-Net/Config.pm
-Net/Domain.pm DNS Domain name lookup
-Net/FTP.pm File Transfer Protocol Client
-Net/FTP/A.pm
-Net/FTP/E.pm
-Net/FTP/I.pm
-Net/FTP/L.pm
-Net/FTP/dataconn.pm
-Net/NNTP.pm Network News Transfer Protocol
-Net/Netrc.pm .netrc lookup routines
-Net/POP3.pm Post Office Protocol
-Net/SMTP.pm Simple Mail Transfer Protocol Client
-Net/Time.pm time & nettime protocols
-Net/libnetFAQ.pod
README
demos/ftp
-demos/inetd
demos/nntp
demos/nntp.mirror
demos/pop3
demos/smtp.self
demos/time
-install-nomake
+lib/Net/Cmd.pm
+lib/Net/Config.pm
+lib/Net/Domain.pm DNS Domain name lookup
+lib/Net/FTP.pm File Transfer Protocol Client
+lib/Net/FTP/A.pm
+lib/Net/FTP/E.pm
+lib/Net/FTP/I.pm
+lib/Net/FTP/L.pm
+lib/Net/FTP/dataconn.pm
+lib/Net/NNTP.pm Network News Transfer Protocol
+lib/Net/Netrc.pm .netrc lookup routines
+lib/Net/POP3.pm Post Office Protocol
+lib/Net/SMTP.pm Simple Mail Transfer Protocol Client
+lib/Net/Time.pm time & nettime protocols
+lib/Net/libnetFAQ.pod
+t/changes.t
t/config.t
+t/critic.t
t/datasend.t
+t/external/smtp-ssl.t
+t/external/pop3-ssl.t
+t/external/ftp-ssl.t
t/ftp.t
t/hostname.t
t/libnet_t.pl
t/netrc.t
t/nntp.t
+t/nntp_ssl.t
+t/nntp_ipv6.t
+t/pod.t
+t/pod_coverage.t
t/require.t
t/smtp.t
+t/smtp_ssl.t
+t/smtp_ipv6.t
+t/pop3_ssl.t
+t/pop3_ipv6.t
t/time.t
META.yml Module YAML meta-data (added by MakeMaker)
META.json Module JSON meta-data (added by MakeMaker)
@@ -0,0 +1,19 @@
+# Source control system files
+^\.git/
+^\.gitignore$
+
+# Files generated by Makefile.PL
+^libnet.cfg$
+^Makefile$
+^MYMETA\.json$
+^MYMETA\.yml$
+
+# Files generated by *make
+^blib/
+^pm_to_blib$
+
+# Files generated by *make clean
+^Makefile\.old$
+
+# Files generated by *make dist
+^libnet-\d\.\d\d\.tar\.gz$
@@ -40,6 +40,56 @@
}
}
}
+ },
+ "IPv6" : {
+ "description" : "IPv6 support",
+ "prereqs" : {
+ "runtime" : {
+ "requires" : {
+ "IO::Socket::IP" : "0.20"
+ }
+ }
+ }
+ },
+ "SSL" : {
+ "description" : "SSL support",
+ "prereqs" : {
+ "runtime" : {
+ "requires" : {
+ "IO::Socket::SSL" : "2.007"
+ }
+ }
+ }
+ },
+ "critictest" : {
+ "description" : "Perl::Critic testing",
+ "prereqs" : {
+ "test" : {
+ "requires" : {
+ "Test::Perl::Critic" : "0"
+ }
+ }
+ }
+ },
+ "podcoveragetest" : {
+ "description" : "POD coverage testing",
+ "prereqs" : {
+ "test" : {
+ "requires" : {
+ "Test::Pod::Coverage" : "0.08"
+ }
+ }
+ }
+ },
+ "podtest" : {
+ "description" : "POD testing",
+ "prereqs" : {
+ "test" : {
+ "requires" : {
+ "Test::Pod" : "1.00"
+ }
+ }
+ }
}
},
"prereqs" : {
@@ -69,9 +119,10 @@
"IO::Select" : "0",
"IO::Socket" : "1.05",
"POSIX" : "0",
- "Socket" : "1.3",
+ "Socket" : "2.016",
"Symbol" : "0",
"Time::Local" : "0",
+ "constant" : "0",
"perl" : "5.008001",
"strict" : "0",
"vars" : "0"
@@ -79,6 +130,7 @@
},
"test" : {
"requires" : {
+ "Config" : "0",
"Cwd" : "0"
}
}
@@ -90,5 +142,5 @@
"url" : "https://github.com/steve-m-hay/perl-libnet.git"
}
},
- "version" : "1.27"
+ "version" : "3.04"
}
@@ -3,6 +3,7 @@ abstract: 'Collection of network protocol modules'
author:
- 'Graham Barr <gbarr@pobox.com>, Steve Hay <shay@cpan.org>'
build_requires:
+ Config: '0'
Cwd: '0'
ExtUtils::MakeMaker: '0'
configure_requires:
@@ -33,6 +34,20 @@ optional_features:
requires:
Authen::SASL: '0'
MIME::Base64: '0'
+ IPv6:
+ description: 'IPv6 support'
+ requires:
+ IO::Socket::IP: '0.20'
+ SSL:
+ description: 'SSL support'
+ requires:
+ IO::Socket::SSL: '2.007'
+ critictest:
+ description: 'Perl::Critic testing'
+ podcoveragetest:
+ description: 'POD coverage testing'
+ podtest:
+ description: 'POD testing'
requires:
Carp: '0'
Errno: '0'
@@ -43,12 +58,13 @@ requires:
IO::Select: '0'
IO::Socket: '1.05'
POSIX: '0'
- Socket: '1.3'
+ Socket: '2.016'
Symbol: '0'
Time::Local: '0'
+ constant: '0'
perl: '5.008001'
strict: '0'
vars: '0'
resources:
repository: https://github.com/steve-m-hay/perl-libnet.git
-version: '1.27'
+version: '3.04'
@@ -23,6 +23,10 @@ use warnings;
use ExtUtils::MakeMaker 6.64;
use ExtUtils::MakeMaker qw(WriteMakefile);
+## no critic (Subroutines::ProhibitSubroutinePrototypes)
+
+sub running_under_cpan();
+
#===============================================================================
# INITIALIZATION
#===============================================================================
@@ -42,13 +46,25 @@ MAIN: {
my %prereq_pms = ();
$prereq_pms{'Convert::EBCDIC'} = '0.06' if $^O eq 'os390';
+ my $xt = 'n';
+ if (not running_under_cpan() and not $ENV{PERL_CORE}) {
+ $xt = prompt("Should I do external tests?\n" .
+ "These tests will fail if there is no internet" .
+ " connection or if a firewall\n" .
+ "blocks or modifies some traffic.\n" .
+ "[y/N]", 'n');
+ }
+
+ my $tests = 't/*.t';
+ $tests .= ' t/external/*.t' if $xt =~ m/^y/io;
+
WriteMakefile(
NAME => 'Net',
DISTNAME => 'libnet',
ABSTRACT => 'Collection of network protocol modules',
AUTHOR => 'Graham Barr <gbarr@pobox.com>, Steve Hay <shay@cpan.org>',
LICENSE => 'perl_5',
- VERSION => '1.27',
+ VERSION => '3.04',
META_MERGE => {
'meta-spec' => {
@@ -84,6 +100,62 @@ MAIN: {
}
}
}
+ },
+
+ SSL => {
+ description => 'SSL support',
+ prereqs => {
+ runtime => {
+ requires => {
+ 'IO::Socket::SSL' => '2.007'
+ }
+ }
+ }
+ },
+
+ IPv6 => {
+ description => 'IPv6 support',
+ prereqs => {
+ runtime => {
+ requires => {
+ 'IO::Socket::IP' => '0.20'
+ # or IO::Socket::INET6 2.62
+ }
+ }
+ }
+ },
+
+ critictest => {
+ description => 'Perl::Critic testing',
+ prereqs => {
+ test => {
+ requires => {
+ 'Test::Perl::Critic' => '0'
+ }
+ }
+ }
+ },
+
+ podtest => {
+ description => 'POD testing',
+ prereqs => {
+ test => {
+ requires => {
+ 'Test::Pod' => '1.00'
+ }
+ }
+ }
+ },
+
+ podcoveragetest => {
+ description => 'POD coverage testing',
+ prereqs => {
+ test => {
+ requires => {
+ 'Test::Pod::Coverage' => '0.08'
+ }
+ }
+ }
}
}
},
@@ -101,7 +173,8 @@ MAIN: {
},
TEST_REQUIRES => {
- 'Cwd' => '0'
+ 'Config' => '0',
+ 'Cwd' => '0'
},
PREREQ_PM => {
@@ -115,9 +188,10 @@ MAIN: {
'IO::Select' => '0',
'IO::Socket' => '1.05',
'POSIX' => '0',
- 'Socket' => '1.3',
+ 'Socket' => '2.016',
'Symbol' => '0',
'Time::Local' => '0',
+ 'constant' => '0',
'strict' => '0',
'vars' => '0'
},
@@ -128,6 +202,10 @@ MAIN: {
FILES => $CfgFile
},
+ test => {
+ TESTS => $tests
+ },
+
dist => {
PREOP => 'find $(DISTVNAME) -type d -print|xargs chmod 0755 && ' .
'find $(DISTVNAME) -type f -print|xargs chmod 0644',
@@ -142,10 +220,30 @@ MAIN: {
sub MY::post_initialize {
my $self = shift;
+
return '' if $self->{PERL_CORE};
- system(($^O eq 'VMS' ? 'mcr ': ()), $^X, 'Configure') unless -f $CfgFile;
+
+ if (not -f $CfgFile) {
+ my @args = qw(Configure);
+ push @args, '-d' if $ENV{PERL5_CPAN_IS_RUNNING} ||
+ $ENV{PERL5_CPANPLUS_IS_RUNNING} ||
+ $ENV{PERL5_CPANM_IS_RUNNING};
+ system(($^O eq 'VMS' ? 'mcr ': ()), $^X, @args)
+ }
+
$self->{PM}{$CfgFile} = $self->catfile('$(INST_LIBDIR)',$CfgPath);
+
return '';
}
#===============================================================================
+# SUBROUTINES
+#===============================================================================
+
+sub running_under_cpan() {
+ return $ENV{PERL5_CPAN_IS_RUNNING} || # cpan
+ $ENV{PERL5_CPANPLUS_IS_RUNNING} || # cpanp
+ $ENV{PERL5_CPANM_IS_RUNNING}; # cpanm
+}
+
+#===============================================================================
@@ -1,793 +0,0 @@
-# Net::Cmd.pm
-#
-# Copyright (c) 1995-2006 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::Cmd;
-
-require 5.001;
-require Exporter;
-
-use strict;
-use vars qw(@ISA @EXPORT $VERSION);
-use Carp;
-use Symbol 'gensym';
-
-BEGIN {
- if ($^O eq 'os390') {
- require Convert::EBCDIC;
-
- # Convert::EBCDIC->import;
- }
-}
-
-BEGIN {
- if (!eval { require utf8 }) {
- *is_utf8 = sub { 0 };
- }
- elsif (eval { utf8::is_utf8(undef); 1 }) {
- *is_utf8 = \&utf8::is_utf8;
- }
- elsif (eval { require Encode; Encode::is_utf8(undef); 1 }) {
- *is_utf8 = \&Encode::is_utf8;
- }
- else {
- *is_utf8 = sub { $_[0] =~ /[^\x00-\xff]/ };
- }
-}
-
-$VERSION = "2.30";
-@ISA = qw(Exporter);
-@EXPORT = qw(CMD_INFO CMD_OK CMD_MORE CMD_REJECT CMD_ERROR CMD_PENDING);
-
-
-sub CMD_INFO {1}
-sub CMD_OK {2}
-sub CMD_MORE {3}
-sub CMD_REJECT {4}
-sub CMD_ERROR {5}
-sub CMD_PENDING {0}
-
-my %debug = ();
-
-my $tr = $^O eq 'os390' ? Convert::EBCDIC->new() : undef;
-
-
-sub toebcdic {
- my $cmd = shift;
-
- unless (exists ${*$cmd}{'net_cmd_asciipeer'}) {
- my $string = $_[0];
- my $ebcdicstr = $tr->toebcdic($string);
- ${*$cmd}{'net_cmd_asciipeer'} = $string !~ /^\d+/ && $ebcdicstr =~ /^\d+/;
- }
-
- ${*$cmd}{'net_cmd_asciipeer'}
- ? $tr->toebcdic($_[0])
- : $_[0];
-}
-
-
-sub toascii {
- my $cmd = shift;
- ${*$cmd}{'net_cmd_asciipeer'}
- ? $tr->toascii($_[0])
- : $_[0];
-}
-
-
-sub _print_isa {
- no strict qw(refs);
-
- my $pkg = shift;
- my $cmd = $pkg;
-
- $debug{$pkg} ||= 0;
-
- my %done = ();
- my @do = ($pkg);
- my %spc = ($pkg, "");
-
- while ($pkg = shift @do) {
- next if defined $done{$pkg};
-
- $done{$pkg} = 1;
-
- my $v =
- defined ${"${pkg}::VERSION"}
- ? "(" . ${"${pkg}::VERSION"} . ")"
- : "";
-
- my $spc = $spc{$pkg};
- $cmd->debug_print(1, "${spc}${pkg}${v}\n");
-
- if (@{"${pkg}::ISA"}) {
- @spc{@{"${pkg}::ISA"}} = (" " . $spc{$pkg}) x @{"${pkg}::ISA"};
- unshift(@do, @{"${pkg}::ISA"});
- }
- }
-}
-
-
-sub debug {
- @_ == 1 or @_ == 2 or croak 'usage: $obj->debug([LEVEL])';
-
- my ($cmd, $level) = @_;
- my $pkg = ref($cmd) || $cmd;
- my $oldval = 0;
-
- if (ref($cmd)) {
- $oldval = ${*$cmd}{'net_cmd_debug'} || 0;
- }
- else {
- $oldval = $debug{$pkg} || 0;
- }
-
- return $oldval
- unless @_ == 2;
-
- $level = $debug{$pkg} || 0
- unless defined $level;
-
- _print_isa($pkg)
- if ($level && !exists $debug{$pkg});
-
- if (ref($cmd)) {
- ${*$cmd}{'net_cmd_debug'} = $level;
- }
- else {
- $debug{$pkg} = $level;
- }
-
- $oldval;
-}
-
-
-sub message {
- @_ == 1 or croak 'usage: $obj->message()';
-
- my $cmd = shift;
-
- wantarray
- ? @{${*$cmd}{'net_cmd_resp'}}
- : join("", @{${*$cmd}{'net_cmd_resp'}});
-}
-
-
-sub debug_text { $_[2] }
-
-
-sub debug_print {
- my ($cmd, $out, $text) = @_;
- print STDERR $cmd, ($out ? '>>> ' : '<<< '), $cmd->debug_text($out, $text);
-}
-
-
-sub code {
- @_ == 1 or croak 'usage: $obj->code()';
-
- my $cmd = shift;
-
- ${*$cmd}{'net_cmd_code'} = "000"
- unless exists ${*$cmd}{'net_cmd_code'};
-
- ${*$cmd}{'net_cmd_code'};
-}
-
-
-sub status {
- @_ == 1 or croak 'usage: $obj->status()';
-
- my $cmd = shift;
-
- substr(${*$cmd}{'net_cmd_code'}, 0, 1);
-}
-
-
-sub set_status {
- @_ == 3 or croak 'usage: $obj->set_status(CODE, MESSAGE)';
-
- my $cmd = shift;
- my ($code, $resp) = @_;
-
- $resp = [$resp]
- unless ref($resp);
-
- (${*$cmd}{'net_cmd_code'}, ${*$cmd}{'net_cmd_resp'}) = ($code, $resp);
-
- 1;
-}
-
-
-sub command {
- my $cmd = shift;
-
- unless (defined fileno($cmd)) {
- $cmd->set_status("599", "Connection closed");
- return $cmd;
- }
-
-
- $cmd->dataend()
- if (exists ${*$cmd}{'net_cmd_last_ch'});
-
- if (scalar(@_)) {
- local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
-
- my $str = join(
- " ",
- map {
- /\n/
- ? do { my $n = $_; $n =~ tr/\n/ /; $n }
- : $_;
- } @_
- );
- $str = $cmd->toascii($str) if $tr;
- $str .= "\015\012";
-
- my $len = length $str;
- my $swlen;
-
- $cmd->close
- unless (defined($swlen = syswrite($cmd, $str, $len)) && $swlen == $len);
-
- $cmd->debug_print(1, $str)
- if ($cmd->debug);
-
- ${*$cmd}{'net_cmd_resp'} = []; # the response
- ${*$cmd}{'net_cmd_code'} = "000"; # Made this one up :-)
- }
-
- $cmd;
-}
-
-
-sub ok {
- @_ == 1 or croak 'usage: $obj->ok()';
-
- my $code = $_[0]->code;
- 0 < $code && $code < 400;
-}
-
-
-sub unsupported {
- my $cmd = shift;
-
- ${*$cmd}{'net_cmd_resp'} = ['Unsupported command'];
- ${*$cmd}{'net_cmd_code'} = 580;
- 0;
-}
-
-
-sub getline {
- my $cmd = shift;
-
- ${*$cmd}{'net_cmd_lines'} ||= [];
-
- return shift @{${*$cmd}{'net_cmd_lines'}}
- if scalar(@{${*$cmd}{'net_cmd_lines'}});
-
- my $partial = defined(${*$cmd}{'net_cmd_partial'}) ? ${*$cmd}{'net_cmd_partial'} : "";
- my $fd = fileno($cmd);
-
- return undef
- unless defined $fd;
-
- my $rin = "";
- vec($rin, $fd, 1) = 1;
-
- my $buf;
-
- until (scalar(@{${*$cmd}{'net_cmd_lines'}})) {
- my $timeout = $cmd->timeout || undef;
- my $rout;
-
- my $select_ret = select($rout = $rin, undef, undef, $timeout);
- if ($select_ret > 0) {
- unless (sysread($cmd, $buf = "", 1024)) {
- carp(ref($cmd) . ": Unexpected EOF on command channel")
- if $cmd->debug;
- $cmd->close;
- return undef;
- }
-
- substr($buf, 0, 0) = $partial; ## prepend from last sysread
-
- my @buf = split(/\015?\012/, $buf, -1); ## break into lines
-
- $partial = pop @buf;
-
- push(@{${*$cmd}{'net_cmd_lines'}}, map {"$_\n"} @buf);
-
- }
- else {
- my $msg = $select_ret ? "Error or Interrupted: $!" : "Timeout";
- carp("$cmd: $msg") if ($cmd->debug);
- return undef;
- }
- }
-
- ${*$cmd}{'net_cmd_partial'} = $partial;
-
- if ($tr) {
- foreach my $ln (@{${*$cmd}{'net_cmd_lines'}}) {
- $ln = $cmd->toebcdic($ln);
- }
- }
-
- shift @{${*$cmd}{'net_cmd_lines'}};
-}
-
-
-sub ungetline {
- my ($cmd, $str) = @_;
-
- ${*$cmd}{'net_cmd_lines'} ||= [];
- unshift(@{${*$cmd}{'net_cmd_lines'}}, $str);
-}
-
-
-sub parse_response {
- return ()
- unless $_[1] =~ s/^(\d\d\d)(.?)//o;
- ($1, $2 eq "-");
-}
-
-
-sub response {
- my $cmd = shift;
- my ($code, $more) = (undef) x 2;
-
- ${*$cmd}{'net_cmd_resp'} ||= [];
-
- while (1) {
- my $str = $cmd->getline();
-
- return CMD_ERROR
- unless defined($str);
-
- $cmd->debug_print(0, $str)
- if ($cmd->debug);
-
- ($code, $more) = $cmd->parse_response($str);
- unless (defined $code) {
- $cmd->ungetline($str);
- $@ = $str; # $@ used as tunneling hack
- last;
- }
-
- ${*$cmd}{'net_cmd_code'} = $code;
-
- push(@{${*$cmd}{'net_cmd_resp'}}, $str);
-
- last unless ($more);
- }
-
- return undef unless defined $code;
- substr($code, 0, 1);
-}
-
-
-sub read_until_dot {
- my $cmd = shift;
- my $fh = shift;
- my $arr = [];
-
- while (1) {
- my $str = $cmd->getline() or return undef;
-
- $cmd->debug_print(0, $str)
- if ($cmd->debug & 4);
-
- last if ($str =~ /^\.\r?\n/o);
-
- $str =~ s/^\.\././o;
-
- if (defined $fh) {
- print $fh $str;
- }
- else {
- push(@$arr, $str);
- }
- }
-
- $arr;
-}
-
-
-sub datasend {
- my $cmd = shift;
- my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
- my $line = join("", @$arr);
-
- # encode to individual utf8 bytes if
- # $line is a string (in internal UTF-8)
- utf8::encode($line) if is_utf8($line);
-
- return 0 unless defined(fileno($cmd));
-
- my $last_ch = ${*$cmd}{'net_cmd_last_ch'};
-
- # We have not send anything yet, so last_ch = "\012" means we are at the start of a line
- $last_ch = ${*$cmd}{'net_cmd_last_ch'} = "\012" unless defined $last_ch;
-
- return 1 unless length $line;
-
- if ($cmd->debug) {
- foreach my $b (split(/\n/, $line)) {
- $cmd->debug_print(1, "$b\n");
- }
- }
-
- $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015";
-
- my $first_ch = '';
-
- if ($last_ch eq "\015") {
- # Remove \012 so it does not get prefixed with another \015 below
- # and escape the . if there is one following it because the fixup
- # below will not find it
- $first_ch = "\012" if $line =~ s/^\012(\.?)/$1$1/;
- }
- elsif ($last_ch eq "\012") {
- # Fixup below will not find the . as the first character of the buffer
- $first_ch = "." if $line =~ /^\./;
- }
-
- $line =~ s/\015?\012(\.?)/\015\012$1$1/sg;
-
- substr($line, 0, 0) = $first_ch;
-
- ${*$cmd}{'net_cmd_last_ch'} = substr($line, -1, 1);
-
- my $len = length($line);
- my $offset = 0;
- my $win = "";
- vec($win, fileno($cmd), 1) = 1;
- my $timeout = $cmd->timeout || undef;
-
- local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
-
- while ($len) {
- my $wout;
- my $s = select(undef, $wout = $win, undef, $timeout);
- if ((defined $s and $s > 0) or -f $cmd) # -f for testing on win32
- {
- my $w = syswrite($cmd, $line, $len, $offset);
- unless (defined($w)) {
- carp("$cmd: $!") if $cmd->debug;
- return undef;
- }
- $len -= $w;
- $offset += $w;
- }
- else {
- carp("$cmd: Timeout") if ($cmd->debug);
- return undef;
- }
- }
-
- 1;
-}
-
-
-sub rawdatasend {
- my $cmd = shift;
- my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
- my $line = join("", @$arr);
-
- return 0 unless defined(fileno($cmd));
-
- return 1
- unless length($line);
-
- if ($cmd->debug) {
- my $b = "$cmd>>> ";
- print STDERR $b, join("\n$b", split(/\n/, $line)), "\n";
- }
-
- my $len = length($line);
- my $offset = 0;
- my $win = "";
- vec($win, fileno($cmd), 1) = 1;
- my $timeout = $cmd->timeout || undef;
-
- local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
- while ($len) {
- my $wout;
- if (select(undef, $wout = $win, undef, $timeout) > 0) {
- my $w = syswrite($cmd, $line, $len, $offset);
- unless (defined($w)) {
- carp("$cmd: $!") if $cmd->debug;
- return undef;
- }
- $len -= $w;
- $offset += $w;
- }
- else {
- carp("$cmd: Timeout") if ($cmd->debug);
- return undef;
- }
- }
-
- 1;
-}
-
-
-sub dataend {
- my $cmd = shift;
-
- return 0 unless defined(fileno($cmd));
-
- my $ch = ${*$cmd}{'net_cmd_last_ch'};
- my $tosend;
-
- if (!defined $ch) {
- return 1;
- }
- elsif ($ch ne "\012") {
- $tosend = "\015\012";
- }
-
- $tosend .= ".\015\012";
-
- local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
-
- $cmd->debug_print(1, ".\n")
- if ($cmd->debug);
-
- syswrite($cmd, $tosend, length $tosend);
-
- delete ${*$cmd}{'net_cmd_last_ch'};
-
- $cmd->response() == CMD_OK;
-}
-
-# read and write to tied filehandle
-sub tied_fh {
- my $cmd = shift;
- ${*$cmd}{'net_cmd_readbuf'} = '';
- my $fh = gensym();
- tie *$fh, ref($cmd), $cmd;
- return $fh;
-}
-
-# tie to myself
-sub TIEHANDLE {
- my $class = shift;
- my $cmd = shift;
- return $cmd;
-}
-
-# Tied filehandle read. Reads requested data length, returning
-# end-of-file when the dot is encountered.
-sub READ {
- my $cmd = shift;
- my ($len, $offset) = @_[1, 2];
- return unless exists ${*$cmd}{'net_cmd_readbuf'};
- my $done = 0;
- while (!$done and length(${*$cmd}{'net_cmd_readbuf'}) < $len) {
- ${*$cmd}{'net_cmd_readbuf'} .= $cmd->getline() or return;
- $done++ if ${*$cmd}{'net_cmd_readbuf'} =~ s/^\.\r?\n\Z//m;
- }
-
- $_[0] = '';
- substr($_[0], $offset + 0) = substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len);
- substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len) = '';
- delete ${*$cmd}{'net_cmd_readbuf'} if $done;
-
- return length $_[0];
-}
-
-
-sub READLINE {
- my $cmd = shift;
-
- # in this context, we use the presence of readbuf to
- # indicate that we have not yet reached the eof
- return unless exists ${*$cmd}{'net_cmd_readbuf'};
- my $line = $cmd->getline;
- return if $line =~ /^\.\r?\n/;
- $line;
-}
-
-
-sub PRINT {
- my $cmd = shift;
- my ($buf, $len, $offset) = @_;
- $len ||= length($buf);
- $offset += 0;
- return unless $cmd->datasend(substr($buf, $offset, $len));
- ${*$cmd}{'net_cmd_sending'}++; # flag that we should call dataend()
- return $len;
-}
-
-
-sub CLOSE {
- my $cmd = shift;
- my $r = exists(${*$cmd}{'net_cmd_sending'}) ? $cmd->dataend : 1;
- delete ${*$cmd}{'net_cmd_readbuf'};
- delete ${*$cmd}{'net_cmd_sending'};
- $r;
-}
-
-1;
-
-__END__
-
-
-=head1 NAME
-
-Net::Cmd - Network Command class (as used by FTP, SMTP etc)
-
-=head1 SYNOPSIS
-
- use Net::Cmd;
-
- @ISA = qw(Net::Cmd);
-
-=head1 DESCRIPTION
-
-C<Net::Cmd> is a collection of methods that can be inherited by a sub class
-of C<IO::Handle>. These methods implement the functionality required for a
-command based protocol, for example FTP and SMTP.
-
-=head1 USER METHODS
-
-These methods provide a user interface to the C<Net::Cmd> object.
-
-=over 4
-
-=item debug ( VALUE )
-
-Set the level of debug information for this object. If C<VALUE> is not given
-then the current state is returned. Otherwise the state is changed to
-C<VALUE> and the previous state returned.
-
-Different packages
-may implement different levels of debug but a non-zero value results in
-copies of all commands and responses also being sent to STDERR.
-
-If C<VALUE> is C<undef> then the debug level will be set to the default
-debug level for the class.
-
-This method can also be called as a I<static> method to set/get the default
-debug level for a given class.
-
-=item message ()
-
-Returns the text message returned from the last command. In a scalar
-context it returns a single string, in a list context it will return
-each line as a separate element
-
-=item code ()
-
-Returns the 3-digit code from the last command. If a command is pending
-then the value 0 is returned
-
-=item ok ()
-
-Returns non-zero if the last code value was greater than zero and
-less than 400. This holds true for most command servers. Servers
-where this does not hold may override this method.
-
-=item status ()
-
-Returns the most significant digit of the current status code. If a command
-is pending then C<CMD_PENDING> is returned.
-
-=item datasend ( DATA )
-
-Send data to the remote server, converting LF to CRLF. Any line starting
-with a '.' will be prefixed with another '.'.
-C<DATA> may be an array or a reference to an array.
-
-=item dataend ()
-
-End the sending of data to the remote server. This is done by ensuring that
-the data already sent ends with CRLF then sending '.CRLF' to end the
-transmission. Once this data has been sent C<dataend> calls C<response> and
-returns true if C<response> returns CMD_OK.
-
-=back
-
-=head1 CLASS METHODS
-
-These methods are not intended to be called by the user, but used or
-over-ridden by a sub-class of C<Net::Cmd>
-
-=over 4
-
-=item debug_print ( DIR, TEXT )
-
-Print debugging information. C<DIR> denotes the direction I<true> being
-data being sent to the server. Calls C<debug_text> before printing to
-STDERR.
-
-=item debug_text ( TEXT )
-
-This method is called to print debugging information. TEXT is
-the text being sent. The method should return the text to be printed
-
-This is primarily meant for the use of modules such as FTP where passwords
-are sent, but we do not want to display them in the debugging information.
-
-=item command ( CMD [, ARGS, ... ])
-
-Send a command to the command server. All arguments a first joined with
-a space character and CRLF is appended, this string is then sent to the
-command server.
-
-Returns undef upon failure
-
-=item unsupported ()
-
-Sets the status code to 580 and the response text to 'Unsupported command'.
-Returns zero.
-
-=item response ()
-
-Obtain a response from the server. Upon success the most significant digit
-of the status code is returned. Upon failure, timeout etc., I<undef> is
-returned.
-
-=item parse_response ( TEXT )
-
-This method is called by C<response> as a method with one argument. It should
-return an array of 2 values, the 3-digit status code and a flag which is true
-when this is part of a multi-line response and this line is not the list.
-
-=item getline ()
-
-Retrieve one line, delimited by CRLF, from the remote server. Returns I<undef>
-upon failure.
-
-B<NOTE>: If you do use this method for any reason, please remember to add
-some C<debug_print> calls into your method.
-
-=item ungetline ( TEXT )
-
-Unget a line of text from the server.
-
-=item rawdatasend ( DATA )
-
-Send data to the remote server without performing any conversions. C<DATA>
-is a scalar.
-
-=item read_until_dot ()
-
-Read data from the remote server until a line consisting of a single '.'.
-Any lines starting with '..' will have one of the '.'s removed.
-
-Returns a reference to a list containing the lines, or I<undef> upon failure.
-
-=item tied_fh ()
-
-Returns a filehandle tied to the Net::Cmd object. After issuing a
-command, you may read from this filehandle using read() or <>. The
-filehandle will return EOF when the final dot is encountered.
-Similarly, you may write to the filehandle in order to send data to
-the server after issuing a command that expects data to be written.
-
-See the Net::POP3 and Net::SMTP modules for examples of this.
-
-=back
-
-=head1 EXPORTS
-
-C<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>,
-C<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results
-of C<response> and C<status>. The sixth is C<CMD_PENDING>.
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-2006 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,312 +0,0 @@
-# Net::Config.pm
-#
-# Copyright (c) 2000 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::Config;
-
-require Exporter;
-use vars qw(@ISA @EXPORT %NetConfig $VERSION $CONFIGURE $LIBNET_CFG);
-use Socket qw(inet_aton inet_ntoa);
-use strict;
-
-@EXPORT = qw(%NetConfig);
-@ISA = qw(Net::LocalCfg Exporter);
-$VERSION = "1.14";
-
-eval { local $SIG{__DIE__}; require Net::LocalCfg };
-
-%NetConfig = (
- nntp_hosts => [],
- snpp_hosts => [],
- pop3_hosts => [],
- smtp_hosts => [],
- ph_hosts => [],
- daytime_hosts => [],
- time_hosts => [],
- inet_domain => undef,
- ftp_firewall => undef,
- ftp_ext_passive => 1,
- ftp_int_passive => 1,
- test_hosts => 1,
- test_exist => 1,
-);
-
-#
-# Try to get as much configuration info as possible from InternetConfig
-#
-$^O eq 'MacOS' and eval <<TRY_INTERNET_CONFIG;
-use Mac::InternetConfig;
-
-{
-my %nc = (
- nntp_hosts => [ \$InternetConfig{ kICNNTPHost() } ],
- pop3_hosts => [ \$InternetConfig{ kICMailAccount() } =~ /\@(.*)/ ],
- smtp_hosts => [ \$InternetConfig{ kICSMTPHost() } ],
- ftp_testhost => \$InternetConfig{ kICFTPHost() } ? \$InternetConfig{ kICFTPHost()} : undef,
- ph_hosts => [ \$InternetConfig{ kICPhHost() } ],
- ftp_ext_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0,
- ftp_int_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0,
- socks_hosts =>
- \$InternetConfig{ kICUseSocks() } ? [ \$InternetConfig{ kICSocksHost() } ] : [],
- ftp_firewall =>
- \$InternetConfig{ kICUseFTPProxy() } ? [ \$InternetConfig{ kICFTPProxyHost() } ] : [],
-);
-\@NetConfig{keys %nc} = values %nc;
-}
-TRY_INTERNET_CONFIG
-
-my $file = __FILE__;
-my $ref;
-$file =~ s/Config.pm/libnet.cfg/;
-if (-f $file) {
- $ref = eval { local $SIG{__DIE__}; do $file };
- if (ref($ref) eq 'HASH') {
- %NetConfig = (%NetConfig, %{$ref});
- $LIBNET_CFG = $file;
- }
-}
-if ($< == $> and !$CONFIGURE) {
- my $home = eval { local $SIG{__DIE__}; (getpwuid($>))[7] } || $ENV{HOME};
- $home ||= $ENV{HOMEDRIVE} . ($ENV{HOMEPATH} || '') if defined $ENV{HOMEDRIVE};
- if (defined $home) {
- $file = $home . "/.libnetrc";
- $ref = eval { local $SIG{__DIE__}; do $file } if -f $file;
- %NetConfig = (%NetConfig, %{$ref})
- if ref($ref) eq 'HASH';
- }
-}
-my ($k, $v);
-while (($k, $v) = each %NetConfig) {
- $NetConfig{$k} = [$v]
- if ($k =~ /_hosts$/ and $k ne "test_hosts" and defined($v) and !ref($v));
-}
-
-# Take a hostname and determine if it is inside the firewall
-
-
-sub requires_firewall {
- shift; # ignore package
- my $host = shift;
-
- return 0 unless defined $NetConfig{'ftp_firewall'};
-
- $host = inet_aton($host) or return -1;
- $host = inet_ntoa($host);
-
- if (exists $NetConfig{'local_netmask'}) {
- my $quad = unpack("N", pack("C*", split(/\./, $host)));
- my $list = $NetConfig{'local_netmask'};
- $list = [$list] unless ref($list);
- foreach (@$list) {
- my ($net, $bits) = (m#^(\d+\.\d+\.\d+\.\d+)/(\d+)$#) or next;
- my $mask = ~0 << (32 - $bits);
- my $addr = unpack("N", pack("C*", split(/\./, $net)));
-
- return 0 if (($addr & $mask) == ($quad & $mask));
- }
- return 1;
- }
-
- return 0;
-}
-
-use vars qw(*is_external);
-*is_external = \&requires_firewall;
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::Config - Local configuration data for libnet
-
-=head1 SYNOPSYS
-
- use Net::Config qw(%NetConfig);
-
-=head1 DESCRIPTION
-
-C<Net::Config> holds configuration data for the modules in the libnet
-distribution. During installation you will be asked for these values.
-
-The configuration data is held globally in a file in the perl installation
-tree, but a user may override any of these values by providing their own. This
-can be done by having a C<.libnetrc> file in their home directory. This file
-should return a reference to a HASH containing the keys described below.
-For example
-
- # .libnetrc
- {
- nntp_hosts => [ "my_preferred_host" ],
- ph_hosts => [ "my_ph_server" ],
- }
- __END__
-
-=head1 METHODS
-
-C<Net::Config> defines the following methods. They are methods as they are
-invoked as class methods. This is because C<Net::Config> inherits from
-C<Net::LocalCfg> so you can override these methods if you want.
-
-=over 4
-
-=item requires_firewall HOST
-
-Attempts to determine if a given host is outside your firewall. Possible
-return values are.
-
- -1 Cannot lookup hostname
- 0 Host is inside firewall (or there is no ftp_firewall entry)
- 1 Host is outside the firewall
-
-This is done by using hostname lookup and the C<local_netmask> entry in
-the configuration data.
-
-=back
-
-=head1 NetConfig VALUES
-
-=over 4
-
-=item nntp_hosts
-
-=item snpp_hosts
-
-=item pop3_hosts
-
-=item smtp_hosts
-
-=item ph_hosts
-
-=item daytime_hosts
-
-=item time_hosts
-
-Each is a reference to an array of hostnames (in order of preference),
-which should be used for the given protocol
-
-=item inet_domain
-
-Your internet domain name
-
-=item ftp_firewall
-
-If you have an FTP proxy firewall (B<NOT> an HTTP or SOCKS firewall)
-then this value should be set to the firewall hostname. If your firewall
-does not listen to port 21, then this value should be set to
-C<"hostname:port"> (eg C<"hostname:99">)
-
-=item ftp_firewall_type
-
-There are many different ftp firewall products available. But unfortunately
-there is no standard for how to traverse a firewall. The list below shows the
-sequence of commands that Net::FTP will use
-
- user Username for remote host
- pass Password for remote host
- fwuser Username for firewall
- fwpass Password for firewall
- remote.host The hostname of the remote ftp server
-
-=over 4
-
-=item 0Z<>
-
-There is no firewall
-
-=item 1Z<>
-
- USER user@remote.host
- PASS pass
-
-=item 2Z<>
-
- USER fwuser
- PASS fwpass
- USER user@remote.host
- PASS pass
-
-=item 3Z<>
-
- USER fwuser
- PASS fwpass
- SITE remote.site
- USER user
- PASS pass
-
-=item 4Z<>
-
- USER fwuser
- PASS fwpass
- OPEN remote.site
- USER user
- PASS pass
-
-=item 5Z<>
-
- USER user@fwuser@remote.site
- PASS pass@fwpass
-
-=item 6Z<>
-
- USER fwuser@remote.site
- PASS fwpass
- USER user
- PASS pass
-
-=item 7Z<>
-
- USER user@remote.host
- PASS pass
- AUTH fwuser
- RESP fwpass
-
-=back
-
-=item ftp_ext_passive
-
-=item ftp_int_passive
-
-FTP servers can work in passive or active mode. Active mode is when
-you want to transfer data you have to tell the server the address and
-port to connect to. Passive mode is when the server provide the
-address and port and you establish the connection.
-
-With some firewalls active mode does not work as the server cannot
-connect to your machine (because you are behind a firewall) and the firewall
-does not re-write the command. In this case you should set C<ftp_ext_passive>
-to a I<true> value.
-
-Some servers are configured to only work in passive mode. If you have
-one of these you can force C<Net::FTP> to always transfer in passive
-mode; when not going via a firewall, by setting C<ftp_int_passive> to
-a I<true> value.
-
-=item local_netmask
-
-A reference to a list of netmask strings in the form C<"134.99.4.0/24">.
-These are used by the C<requires_firewall> function to determine if a given
-host is inside or outside your firewall.
-
-=back
-
-The following entries are used during installation & testing on the
-libnet package
-
-=over 4
-
-=item test_hosts
-
-If true then C<make test> may attempt to connect to hosts given in the
-configuration.
-
-=item test_exists
-
-If true then C<Configure> will check each hostname given that it exists
-
-=back
-
-=cut
@@ -1,354 +0,0 @@
-# Net::Domain.pm
-#
-# Copyright (c) 1995-1998 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::Domain;
-
-require Exporter;
-
-use Carp;
-use strict;
-use vars qw($VERSION @ISA @EXPORT_OK);
-use Net::Config;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(hostname hostdomain hostfqdn domainname);
-
-$VERSION = "2.23";
-
-my ($host, $domain, $fqdn) = (undef, undef, undef);
-
-# Try every conceivable way to get hostname.
-
-
-sub _hostname {
-
- # we already know it
- return $host
- if (defined $host);
-
- if ($^O eq 'MSWin32') {
- require Socket;
- my ($name, $alias, $type, $len, @addr) = gethostbyname($ENV{'COMPUTERNAME'} || 'localhost');
- while (@addr) {
- my $a = shift(@addr);
- $host = gethostbyaddr($a, Socket::AF_INET());
- last if defined $host;
- }
- if (defined($host) && index($host, '.') > 0) {
- $fqdn = $host;
- ($host, $domain) = $fqdn =~ /^([^\.]+)\.(.*)$/;
- }
- return $host;
- }
- elsif ($^O eq 'MacOS') {
- chomp($host = `hostname`);
- }
- elsif ($^O eq 'VMS') { ## multiple varieties of net s/w makes this hard
- $host = $ENV{'UCX$INET_HOST'} if defined($ENV{'UCX$INET_HOST'});
- $host = $ENV{'MULTINET_HOST_NAME'} if defined($ENV{'MULTINET_HOST_NAME'});
- if (index($host, '.') > 0) {
- $fqdn = $host;
- ($host, $domain) = $fqdn =~ /^([^\.]+)\.(.*)$/;
- }
- return $host;
- }
- else {
- local $SIG{'__DIE__'};
-
- # syscall is preferred since it avoids tainting problems
- eval {
- my $tmp = "\0" x 256; ## preload scalar
- eval {
- package main;
- require "syscall.ph";
- defined(&main::SYS_gethostname);
- }
- || eval {
- package main;
- require "sys/syscall.ph";
- defined(&main::SYS_gethostname);
- }
- and $host =
- (syscall(&main::SYS_gethostname, $tmp, 256) == 0)
- ? $tmp
- : undef;
- }
-
- # POSIX
- || eval {
- require POSIX;
- $host = (POSIX::uname())[1];
- }
-
- # trusty old hostname command
- || eval {
- chop($host = `(hostname) 2>/dev/null`); # BSD'ish
- }
-
- # sysV/POSIX uname command (may truncate)
- || eval {
- chop($host = `uname -n 2>/dev/null`); ## SYSV'ish && POSIX'ish
- }
-
- # Apollo pre-SR10
- || eval { $host = (split(/[:\. ]/, `/com/host`, 6))[0]; }
-
- || eval { $host = ""; };
- }
-
- # remove garbage
- $host =~ s/[\0\r\n]+//go;
- $host =~ s/(\A\.+|\.+\Z)//go;
- $host =~ s/\.\.+/\./go;
-
- $host;
-}
-
-
-sub _hostdomain {
-
- # we already know it
- return $domain
- if (defined $domain);
-
- local $SIG{'__DIE__'};
-
- return $domain = $NetConfig{'inet_domain'}
- if defined $NetConfig{'inet_domain'};
-
- # try looking in /etc/resolv.conf
- # putting this here and assuming that it is correct, eliminates
- # calls to gethostbyname, and therefore DNS lookups. This helps
- # those on dialup systems.
-
- local *RES;
- local ($_);
-
- if (open(RES, "/etc/resolv.conf")) {
- while (<RES>) {
- $domain = $1
- if (/\A\s*(?:domain|search)\s+(\S+)/);
- }
- close(RES);
-
- return $domain
- if (defined $domain);
- }
-
- # just try hostname and system calls
-
- my $host = _hostname();
- my (@hosts);
-
- @hosts = ($host, "localhost");
-
- unless (defined($host) && $host =~ /\./) {
- my $dom = undef;
- eval {
- my $tmp = "\0" x 256; ## preload scalar
- eval {
- package main;
- require "syscall.ph";
- }
- || eval {
- package main;
- require "sys/syscall.ph";
- }
- and $dom =
- (syscall(&main::SYS_getdomainname, $tmp, 256) == 0)
- ? $tmp
- : undef;
- };
-
- if ($^O eq 'VMS') {
- $dom ||= $ENV{'TCPIP$INET_DOMAIN'}
- || $ENV{'UCX$INET_DOMAIN'};
- }
-
- chop($dom = `domainname 2>/dev/null`)
- unless (defined $dom || $^O =~ /^(?:cygwin|MSWin32|android)/);
-
- if (defined $dom) {
- my @h = ();
- $dom =~ s/^\.+//;
- while (length($dom)) {
- push(@h, "$host.$dom");
- $dom =~ s/^[^.]+.+// or last;
- }
- unshift(@hosts, @h);
- }
- }
-
- # Attempt to locate FQDN
-
- foreach (grep { defined $_ } @hosts) {
- my @info = gethostbyname($_);
-
- next unless @info;
-
- # look at real name & aliases
- my $site;
- foreach $site ($info[0], split(/ /, $info[1])) {
- if (rindex($site, ".") > 0) {
-
- # Extract domain from FQDN
-
- ($domain = $site) =~ s/\A[^\.]+\.//;
- return $domain;
- }
- }
- }
-
- # Look for environment variable
-
- $domain ||= $ENV{LOCALDOMAIN} || $ENV{DOMAIN};
-
- if (defined $domain) {
- $domain =~ s/[\r\n\0]+//g;
- $domain =~ s/(\A\.+|\.+\Z)//g;
- $domain =~ s/\.\.+/\./g;
- }
-
- $domain;
-}
-
-
-sub domainname {
-
- return $fqdn
- if (defined $fqdn);
-
- _hostname();
-
- # *.local names are special on darwin. If we call gethostbyname below, it
- # may hang while waiting for another, non-existent computer to respond.
- if($^O eq 'darwin' && $host =~ /\.local$/) {
- return $host;
- }
-
- _hostdomain();
-
- # Assumption: If the host name does not contain a period
- # and the domain name does, then assume that they are correct
- # this helps to eliminate calls to gethostbyname, and therefore
- # eliminate DNS lookups
-
- return $fqdn = $host . "." . $domain
- if (defined $host
- and defined $domain
- and $host !~ /\./
- and $domain =~ /\./);
-
- # For hosts that have no name, just an IP address
- return $fqdn = $host if defined $host and $host =~ /^\d+(\.\d+){3}$/;
-
- my @host = defined $host ? split(/\./, $host) : ('localhost');
- my @domain = defined $domain ? split(/\./, $domain) : ();
- my @fqdn = ();
-
- # Determine from @host & @domain the FQDN
-
- my @d = @domain;
-
-LOOP:
- while (1) {
- my @h = @host;
- while (@h) {
- my $tmp = join(".", @h, @d);
- if ((gethostbyname($tmp))[0]) {
- @fqdn = (@h, @d);
- $fqdn = $tmp;
- last LOOP;
- }
- pop @h;
- }
- last unless shift @d;
- }
-
- if (@fqdn) {
- $host = shift @fqdn;
- until ((gethostbyname($host))[0]) {
- $host .= "." . shift @fqdn;
- }
- $domain = join(".", @fqdn);
- }
- else {
- undef $host;
- undef $domain;
- undef $fqdn;
- }
-
- $fqdn;
-}
-
-
-sub hostfqdn { domainname() }
-
-
-sub hostname {
- domainname()
- unless (defined $host);
- return $host;
-}
-
-
-sub hostdomain {
- domainname()
- unless (defined $domain);
- return $domain;
-}
-
-1; # Keep require happy
-
-__END__
-
-=head1 NAME
-
-Net::Domain - Attempt to evaluate the current host's internet name and domain
-
-=head1 SYNOPSIS
-
- use Net::Domain qw(hostname hostfqdn hostdomain domainname);
-
-=head1 DESCRIPTION
-
-Using various methods B<attempt> to find the Fully Qualified Domain Name (FQDN)
-of the current host. From this determine the host-name and the host-domain.
-
-Each of the functions will return I<undef> if the FQDN cannot be determined.
-
-=over 4
-
-=item hostfqdn ()
-
-Identify and return the FQDN of the current host.
-
-=item domainname ()
-
-An alias for hostfqdn ().
-
-=item hostname ()
-
-Returns the smallest part of the FQDN which can be used to identify the host.
-
-=item hostdomain ()
-
-Returns the remainder of the FQDN after the I<hostname> has been removed.
-
-=back
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>.
-Adapted from Sys::Hostname by David Sundstrom <sunds@asictest.sc.ti.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-1998 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,111 +0,0 @@
-##
-## Package to read/write on ASCII data connections
-##
-
-package Net::FTP::A;
-use strict;
-use vars qw(@ISA $buf $VERSION);
-use Carp;
-
-require Net::FTP::dataconn;
-
-@ISA = qw(Net::FTP::dataconn);
-$VERSION = "1.19";
-
-
-sub read {
- my $data = shift;
- local *buf = \$_[0];
- shift;
- my $size = shift || croak 'read($buf,$size,[$offset])';
- my $timeout = @_ ? shift: $data->timeout;
-
- if (length(${*$data}) < $size && !${*$data}{'net_ftp_eof'}) {
- my $blksize = ${*$data}{'net_ftp_blksize'};
- $blksize = $size if $size > $blksize;
-
- my $l = 0;
- my $n;
-
- READ:
- {
- my $readbuf = defined(${*$data}{'net_ftp_cr'}) ? "\015" : '';
-
- $data->can_read($timeout)
- or croak "Timeout";
-
- if ($n = sysread($data, $readbuf, $blksize, length $readbuf)) {
- ${*$data}{'net_ftp_bytesread'} += $n;
- ${*$data}{'net_ftp_cr'} =
- substr($readbuf, -1) eq "\015"
- ? chop($readbuf)
- : undef;
- }
- else {
- return undef
- unless defined $n;
-
- ${*$data}{'net_ftp_eof'} = 1;
- }
-
- $readbuf =~ s/\015\012/\n/sgo;
- ${*$data} .= $readbuf;
-
- unless (length(${*$data})) {
-
- redo READ
- if ($n > 0);
-
- $size = length(${*$data})
- if ($n == 0);
- }
- }
- }
-
- $buf = substr(${*$data}, 0, $size);
- substr(${*$data}, 0, $size) = '';
-
- length $buf;
-}
-
-
-sub write {
- my $data = shift;
- local *buf = \$_[0];
- shift;
- my $size = shift || croak 'write($buf,$size,[$timeout])';
- my $timeout = @_ ? shift: $data->timeout;
-
- my $nr = (my $tmp = substr($buf, 0, $size)) =~ tr/\r\n/\015\012/;
- $tmp =~ s/(?<!\015)\012/\015\012/sg if $nr;
- $tmp =~ s/^\015// if ${*$data}{'net_ftp_outcr'};
- ${*$data}{'net_ftp_outcr'} = substr($tmp, -1) eq "\015";
-
- # If the remote server has closed the connection we will be signal'd
- # when we write. This can happen if the disk on the remote server fills up
-
- local $SIG{PIPE} = 'IGNORE'
- unless ($SIG{PIPE} || '') eq 'IGNORE'
- or $^O eq 'MacOS';
-
- my $len = length($tmp);
- my $off = 0;
- my $wrote = 0;
-
- my $blksize = ${*$data}{'net_ftp_blksize'};
-
- while ($len) {
- $data->can_write($timeout)
- or croak "Timeout";
-
- $off += $wrote;
- $wrote = syswrite($data, substr($tmp, $off), $len > $blksize ? $blksize : $len);
- return undef
- unless defined($wrote);
- $len -= $wrote;
- }
-
- $size;
-}
-
-1;
@@ -1,8 +0,0 @@
-package Net::FTP::E;
-
-require Net::FTP::I;
-
-@ISA = qw(Net::FTP::I);
-$VERSION = "0.01";
-
-1;
@@ -1,80 +0,0 @@
-##
-## Package to read/write on BINARY data connections
-##
-
-package Net::FTP::I;
-
-use vars qw(@ISA $buf $VERSION);
-use Carp;
-
-require Net::FTP::dataconn;
-
-@ISA = qw(Net::FTP::dataconn);
-$VERSION = "1.12";
-
-
-sub read {
- my $data = shift;
- local *buf = \$_[0];
- shift;
- my $size = shift || croak 'read($buf,$size,[$timeout])';
- my $timeout = @_ ? shift: $data->timeout;
-
- my $n;
-
- if ($size > length ${*$data} and !${*$data}{'net_ftp_eof'}) {
- $data->can_read($timeout)
- or croak "Timeout";
-
- my $blksize = ${*$data}{'net_ftp_blksize'};
- $blksize = $size if $size > $blksize;
-
- unless ($n = sysread($data, ${*$data}, $blksize, length ${*$data})) {
- return undef unless defined $n;
- ${*$data}{'net_ftp_eof'} = 1;
- }
- }
-
- $buf = substr(${*$data}, 0, $size);
-
- $n = length($buf);
-
- substr(${*$data}, 0, $n) = '';
-
- ${*$data}{'net_ftp_bytesread'} += $n;
-
- $n;
-}
-
-
-sub write {
- my $data = shift;
- local *buf = \$_[0];
- shift;
- my $size = shift || croak 'write($buf,$size,[$timeout])';
- my $timeout = @_ ? shift: $data->timeout;
-
- # If the remote server has closed the connection we will be signal'd
- # when we write. This can happen if the disk on the remote server fills up
-
- local $SIG{PIPE} = 'IGNORE'
- unless ($SIG{PIPE} || '') eq 'IGNORE'
- or $^O eq 'MacOS';
- my $sent = $size;
- my $off = 0;
-
- my $blksize = ${*$data}{'net_ftp_blksize'};
- while ($sent > 0) {
- $data->can_write($timeout)
- or croak "Timeout";
-
- my $n = syswrite($data, $buf, $sent > $blksize ? $blksize : $sent, $off);
- return undef unless defined($n);
- $sent -= $n;
- $off += $n;
- }
-
- $size;
-}
-
-1;
@@ -1,8 +0,0 @@
-package Net::FTP::L;
-
-require Net::FTP::I;
-
-@ISA = qw(Net::FTP::I);
-$VERSION = "0.01";
-
-1;
@@ -1,127 +0,0 @@
-##
-## Generic data connection package
-##
-
-package Net::FTP::dataconn;
-
-use Carp;
-use vars qw(@ISA $timeout $VERSION);
-use Net::Cmd;
-use Errno;
-
-$VERSION = '0.12';
-@ISA = qw(IO::Socket::INET);
-
-
-sub reading {
- my $data = shift;
- ${*$data}{'net_ftp_bytesread'} = 0;
-}
-
-
-sub abort {
- my $data = shift;
- my $ftp = ${*$data}{'net_ftp_cmd'};
-
- # no need to abort if we have finished the xfer
- return $data->close
- if ${*$data}{'net_ftp_eof'};
-
- # for some reason if we continuously open RETR connections and not
- # read a single byte, then abort them after a while the server will
- # close our connection, this prevents the unexpected EOF on the
- # command channel -- GMB
- if (exists ${*$data}{'net_ftp_bytesread'}
- && (${*$data}{'net_ftp_bytesread'} == 0))
- {
- my $buf = "";
- my $timeout = $data->timeout;
- $data->can_read($timeout) && sysread($data, $buf, 1);
- }
-
- ${*$data}{'net_ftp_eof'} = 1; # fake
-
- $ftp->abort; # this will close me
-}
-
-
-sub _close {
- my $data = shift;
- my $ftp = ${*$data}{'net_ftp_cmd'};
-
- $data->SUPER::close();
-
- delete ${*$ftp}{'net_ftp_dataconn'}
- if defined $ftp
- && exists ${*$ftp}{'net_ftp_dataconn'}
- && $data == ${*$ftp}{'net_ftp_dataconn'};
-}
-
-
-sub close {
- my $data = shift;
- my $ftp = ${*$data}{'net_ftp_cmd'};
-
- if (exists ${*$data}{'net_ftp_bytesread'} && !${*$data}{'net_ftp_eof'}) {
- my $junk;
- eval { local($SIG{__DIE__}); $data->read($junk, 1, 0) };
- return $data->abort unless ${*$data}{'net_ftp_eof'};
- }
-
- $data->_close;
-
- return unless defined $ftp;
-
- $ftp->response() == CMD_OK
- && $ftp->message =~ /unique file name:\s*(\S*)\s*\)/
- && (${*$ftp}{'net_ftp_unique'} = $1);
-
- $ftp->status == CMD_OK;
-}
-
-
-sub _select {
- my ($data, $timeout, $do_read) = @_;
- my ($rin, $rout, $win, $wout, $tout, $nfound);
-
- vec($rin = '', fileno($data), 1) = 1;
-
- ($win, $rin) = ($rin, $win) unless $do_read;
-
- while (1) {
- $nfound = select($rout = $rin, $wout = $win, undef, $tout = $timeout);
-
- last if $nfound >= 0;
-
- croak "select: $!"
- unless $!{EINTR};
- }
-
- $nfound;
-}
-
-
-sub can_read {
- _select(@_[0, 1], 1);
-}
-
-
-sub can_write {
- _select(@_[0, 1], 0);
-}
-
-
-sub cmd {
- my $ftp = shift;
-
- ${*$ftp}{'net_ftp_cmd'};
-}
-
-
-sub bytes_read {
- my $ftp = shift;
-
- ${*$ftp}{'net_ftp_bytesread'} || 0;
-}
-
-1;
@@ -1,1861 +0,0 @@
-# Net::FTP.pm
-#
-# Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-#
-# Documentation (at end) improved 1996 by Nathan Torkington <gnat@frii.com>.
-
-package Net::FTP;
-
-require 5.001;
-
-use strict;
-use vars qw(@ISA $VERSION);
-use Carp;
-
-use Socket 1.3;
-use IO::Socket;
-use Time::Local;
-use Net::Cmd;
-use Net::Config;
-use Fcntl qw(O_WRONLY O_RDONLY O_APPEND O_CREAT O_TRUNC);
-
-$VERSION = '2.79';
-@ISA = qw(Exporter Net::Cmd IO::Socket::INET);
-
-# Someday I will "use constant", when I am not bothered to much about
-# compatibility with older releases of perl
-
-use vars qw($TELNET_IAC $TELNET_IP $TELNET_DM);
-($TELNET_IAC, $TELNET_IP, $TELNET_DM) = (255, 244, 242);
-
-
-BEGIN {
-
- # make a constant so code is fast'ish
- my $is_os390 = $^O eq 'os390';
- *trEBCDIC = sub () {$is_os390}
-}
-
-
-sub new {
- my $pkg = shift;
- my ($peer, %arg);
- if (@_ % 2) {
- $peer = shift;
- %arg = @_;
- }
- else {
- %arg = @_;
- $peer = delete $arg{Host};
- }
-
- my $host = $peer;
- my $fire = undef;
- my $fire_type = undef;
-
- if (exists($arg{Firewall}) || Net::Config->requires_firewall($peer)) {
- $fire = $arg{Firewall}
- || $ENV{FTP_FIREWALL}
- || $NetConfig{ftp_firewall}
- || undef;
-
- if (defined $fire) {
- $peer = $fire;
- delete $arg{Port};
- $fire_type = $arg{FirewallType}
- || $ENV{FTP_FIREWALL_TYPE}
- || $NetConfig{firewall_type}
- || undef;
- }
- }
-
- my $ftp = $pkg->SUPER::new(
- PeerAddr => $peer,
- PeerPort => $arg{Port} || 'ftp(21)',
- LocalAddr => $arg{'LocalAddr'},
- Proto => 'tcp',
- Timeout => defined $arg{Timeout}
- ? $arg{Timeout}
- : 120
- )
- or return undef;
-
- ${*$ftp}{'net_ftp_host'} = $host; # Remote hostname
- ${*$ftp}{'net_ftp_type'} = 'A'; # ASCII/binary/etc mode
- ${*$ftp}{'net_ftp_blksize'} = abs($arg{'BlockSize'} || 10240);
-
- ${*$ftp}{'net_ftp_localaddr'} = $arg{'LocalAddr'};
-
- ${*$ftp}{'net_ftp_firewall'} = $fire
- if (defined $fire);
- ${*$ftp}{'net_ftp_firewall_type'} = $fire_type
- if (defined $fire_type);
-
- ${*$ftp}{'net_ftp_passive'} =
- int exists $arg{Passive} ? $arg{Passive}
- : exists $ENV{FTP_PASSIVE} ? $ENV{FTP_PASSIVE}
- : defined $fire ? $NetConfig{ftp_ext_passive}
- : $NetConfig{ftp_int_passive}; # Whew! :-)
-
- $ftp->hash(exists $arg{Hash} ? $arg{Hash} : 0, 1024);
-
- $ftp->autoflush(1);
-
- $ftp->debug(exists $arg{Debug} ? $arg{Debug} : undef);
-
- unless ($ftp->response() == CMD_OK) {
- $ftp->close();
- # keep @$ if no message. Happens, when response did not start with a code.
- $@ = $ftp->message || $@;
- undef $ftp;
- }
-
- $ftp;
-}
-
-##
-## User interface methods
-##
-
-
-sub host {
- my $me = shift;
- ${*$me}{'net_ftp_host'};
-}
-
-sub passive {
- my $ftp = shift;
- return ${*$ftp}{'net_ftp_passive'} unless @_;
- ${*$ftp}{'net_ftp_passive'} = shift;
-}
-
-
-sub hash {
- my $ftp = shift; # self
-
- my ($h, $b) = @_;
- unless ($h) {
- delete ${*$ftp}{'net_ftp_hash'};
- return [\*STDERR, 0];
- }
- ($h, $b) = (ref($h) ? $h : \*STDERR, $b || 1024);
- select((select($h), $| = 1)[0]);
- $b = 512 if $b < 512;
- ${*$ftp}{'net_ftp_hash'} = [$h, $b];
-}
-
-
-sub quit {
- my $ftp = shift;
-
- $ftp->_QUIT;
- $ftp->close;
-}
-
-
-sub DESTROY { }
-
-
-sub ascii { shift->type('A', @_); }
-sub binary { shift->type('I', @_); }
-
-
-sub ebcdic {
- carp "TYPE E is unsupported, shall default to I";
- shift->type('E', @_);
-}
-
-
-sub byte {
- carp "TYPE L is unsupported, shall default to I";
- shift->type('L', @_);
-}
-
-# Allow the user to send a command directly, BE CAREFUL !!
-
-
-sub quot {
- my $ftp = shift;
- my $cmd = shift;
-
- $ftp->command(uc $cmd, @_);
- $ftp->response();
-}
-
-
-sub site {
- my $ftp = shift;
-
- $ftp->command("SITE", @_);
- $ftp->response();
-}
-
-
-sub mdtm {
- my $ftp = shift;
- my $file = shift;
-
- # Server Y2K bug workaround
- #
- # sigh; some idiotic FTP servers use ("19%d",tm.tm_year) instead of
- # ("%d",tm.tm_year+1900). This results in an extra digit in the
- # string returned. To account for this we allow an optional extra
- # digit in the year. Then if the first two digits are 19 we use the
- # remainder, otherwise we subtract 1900 from the whole year.
-
- $ftp->_MDTM($file)
- && $ftp->message =~ /((\d\d)(\d\d\d?))(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
- ? timegm($8, $7, $6, $5, $4 - 1, $2 eq '19' ? $3 : ($1 - 1900))
- : undef;
-}
-
-
-sub size {
- my $ftp = shift;
- my $file = shift;
- my $io;
- if ($ftp->supported("SIZE")) {
- return $ftp->_SIZE($file)
- ? ($ftp->message =~ /(\d+)\s*(bytes?\s*)?$/)[0]
- : undef;
- }
- elsif ($ftp->supported("STAT")) {
- my @msg;
- return undef
- unless $ftp->_STAT($file) && (@msg = $ftp->message) == 3;
- my $line;
- foreach $line (@msg) {
- return (split(/\s+/, $line))[4]
- if $line =~ /^[-rwxSsTt]{10}/;
- }
- }
- else {
- my @files = $ftp->dir($file);
- if (@files) {
- return (split(/\s+/, $1))[4]
- if $files[0] =~ /^([-rwxSsTt]{10}.*)$/;
- }
- }
- undef;
-}
-
-
-sub login {
- my ($ftp, $user, $pass, $acct) = @_;
- my ($ok, $ruser, $fwtype);
-
- unless (defined $user) {
- require Net::Netrc;
-
- my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'});
-
- ($user, $pass, $acct) = $rc->lpa()
- if ($rc);
- }
-
- $user ||= "anonymous";
- $ruser = $user;
-
- $fwtype = ${*$ftp}{'net_ftp_firewall_type'}
- || $NetConfig{'ftp_firewall_type'}
- || 0;
-
- if ($fwtype && defined ${*$ftp}{'net_ftp_firewall'}) {
- if ($fwtype == 1 || $fwtype == 7) {
- $user .= '@' . ${*$ftp}{'net_ftp_host'};
- }
- else {
- require Net::Netrc;
-
- my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
-
- my ($fwuser, $fwpass, $fwacct) = $rc ? $rc->lpa() : ();
-
- if ($fwtype == 5) {
- $user = join('@', $user, $fwuser, ${*$ftp}{'net_ftp_host'});
- $pass = $pass . '@' . $fwpass;
- }
- else {
- if ($fwtype == 2) {
- $user .= '@' . ${*$ftp}{'net_ftp_host'};
- }
- elsif ($fwtype == 6) {
- $fwuser .= '@' . ${*$ftp}{'net_ftp_host'};
- }
-
- $ok = $ftp->_USER($fwuser);
-
- return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
-
- $ok = $ftp->_PASS($fwpass || "");
-
- return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
-
- $ok = $ftp->_ACCT($fwacct)
- if defined($fwacct);
-
- if ($fwtype == 3) {
- $ok = $ftp->command("SITE", ${*$ftp}{'net_ftp_host'})->response;
- }
- elsif ($fwtype == 4) {
- $ok = $ftp->command("OPEN", ${*$ftp}{'net_ftp_host'})->response;
- }
-
- return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
- }
- }
- }
-
- $ok = $ftp->_USER($user);
-
- # Some dumb firewalls don't prefix the connection messages
- $ok = $ftp->response()
- if ($ok == CMD_OK && $ftp->code == 220 && $user =~ /\@/);
-
- if ($ok == CMD_MORE) {
- unless (defined $pass) {
- require Net::Netrc;
-
- my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'}, $ruser);
-
- ($ruser, $pass, $acct) = $rc->lpa()
- if ($rc);
-
- $pass = '-anonymous@'
- if (!defined $pass && (!defined($ruser) || $ruser =~ /^anonymous/o));
- }
-
- $ok = $ftp->_PASS($pass || "");
- }
-
- $ok = $ftp->_ACCT($acct)
- if (defined($acct) && ($ok == CMD_MORE || $ok == CMD_OK));
-
- if ($fwtype == 7 && $ok == CMD_OK && defined ${*$ftp}{'net_ftp_firewall'}) {
- my ($f, $auth, $resp) = _auth_id($ftp);
- $ftp->authorize($auth, $resp) if defined($resp);
- }
-
- $ok == CMD_OK;
-}
-
-
-sub account {
- @_ == 2 or croak 'usage: $ftp->account( ACCT )';
- my $ftp = shift;
- my $acct = shift;
- $ftp->_ACCT($acct) == CMD_OK;
-}
-
-
-sub _auth_id {
- my ($ftp, $auth, $resp) = @_;
-
- unless (defined $resp) {
- require Net::Netrc;
-
- $auth ||= eval { (getpwuid($>))[0] } || $ENV{NAME};
-
- my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}, $auth)
- || Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
-
- ($auth, $resp) = $rc->lpa()
- if ($rc);
- }
- ($ftp, $auth, $resp);
-}
-
-
-sub authorize {
- @_ >= 1 || @_ <= 3 or croak 'usage: $ftp->authorize( [AUTH [, RESP]])';
-
- my ($ftp, $auth, $resp) = &_auth_id;
-
- my $ok = $ftp->_AUTH($auth || "");
-
- $ok = $ftp->_RESP($resp || "")
- if ($ok == CMD_MORE);
-
- $ok == CMD_OK;
-}
-
-
-sub rename {
- @_ == 3 or croak 'usage: $ftp->rename(FROM, TO)';
-
- my ($ftp, $from, $to) = @_;
-
- $ftp->_RNFR($from)
- && $ftp->_RNTO($to);
-}
-
-
-sub type {
- my $ftp = shift;
- my $type = shift;
- my $oldval = ${*$ftp}{'net_ftp_type'};
-
- return $oldval
- unless (defined $type);
-
- return undef
- unless ($ftp->_TYPE($type, @_));
-
- ${*$ftp}{'net_ftp_type'} = join(" ", $type, @_);
-
- $oldval;
-}
-
-
-sub alloc {
- my $ftp = shift;
- my $size = shift;
- my $oldval = ${*$ftp}{'net_ftp_allo'};
-
- return $oldval
- unless (defined $size);
-
- return undef
- unless ($ftp->_ALLO($size, @_));
-
- ${*$ftp}{'net_ftp_allo'} = join(" ", $size, @_);
-
- $oldval;
-}
-
-
-sub abort {
- my $ftp = shift;
-
- send($ftp, pack("CCC", $TELNET_IAC, $TELNET_IP, $TELNET_IAC), MSG_OOB);
-
- $ftp->command(pack("C", $TELNET_DM) . "ABOR");
-
- ${*$ftp}{'net_ftp_dataconn'}->close()
- if defined ${*$ftp}{'net_ftp_dataconn'};
-
- $ftp->response();
-
- $ftp->status == CMD_OK;
-}
-
-
-sub get {
- my ($ftp, $remote, $local, $where) = @_;
-
- my ($loc, $len, $buf, $resp, $data);
- local *FD;
-
- my $localfd = ref($local) || ref(\$local) eq "GLOB";
-
- ($local = $remote) =~ s#^.*/##
- unless (defined $local);
-
- croak("Bad remote filename '$remote'\n")
- if $remote =~ /[\r\n]/s;
-
- ${*$ftp}{'net_ftp_rest'} = $where if defined $where;
- my $rest = ${*$ftp}{'net_ftp_rest'};
-
- delete ${*$ftp}{'net_ftp_port'};
- delete ${*$ftp}{'net_ftp_pasv'};
-
- $data = $ftp->retr($remote)
- or return undef;
-
- if ($localfd) {
- $loc = $local;
- }
- else {
- $loc = \*FD;
-
- unless (sysopen($loc, $local, O_CREAT | O_WRONLY | ($rest ? O_APPEND: O_TRUNC))) {
- carp "Cannot open Local file $local: $!\n";
- $data->abort;
- return undef;
- }
- }
-
- if ($ftp->type eq 'I' && !binmode($loc)) {
- carp "Cannot binmode Local file $local: $!\n";
- $data->abort;
- close($loc) unless $localfd;
- return undef;
- }
-
- $buf = '';
- my ($count, $hashh, $hashb, $ref) = (0);
-
- ($hashh, $hashb) = @$ref
- if ($ref = ${*$ftp}{'net_ftp_hash'});
-
- my $blksize = ${*$ftp}{'net_ftp_blksize'};
- local $\; # Just in case
-
- while (1) {
- last unless $len = $data->read($buf, $blksize);
-
- if (trEBCDIC && $ftp->type ne 'I') {
- $buf = $ftp->toebcdic($buf);
- $len = length($buf);
- }
-
- if ($hashh) {
- $count += $len;
- print $hashh "#" x (int($count / $hashb));
- $count %= $hashb;
- }
- unless (print $loc $buf) {
- carp "Cannot write to Local file $local: $!\n";
- $data->abort;
- close($loc)
- unless $localfd;
- return undef;
- }
- }
-
- print $hashh "\n" if $hashh;
-
- unless ($localfd) {
- unless (close($loc)) {
- carp "Cannot close file $local (perhaps disk space) $!\n";
- return undef;
- }
- }
-
- unless ($data->close()) # implied $ftp->response
- {
- carp "Unable to close datastream";
- return undef;
- }
-
- return $local;
-}
-
-
-sub cwd {
- @_ == 1 || @_ == 2 or croak 'usage: $ftp->cwd( [ DIR ] )';
-
- my ($ftp, $dir) = @_;
-
- $dir = "/" unless defined($dir) && $dir =~ /\S/;
-
- $dir eq ".."
- ? $ftp->_CDUP()
- : $ftp->_CWD($dir);
-}
-
-
-sub cdup {
- @_ == 1 or croak 'usage: $ftp->cdup()';
- $_[0]->_CDUP;
-}
-
-
-sub pwd {
- @_ == 1 || croak 'usage: $ftp->pwd()';
- my $ftp = shift;
-
- $ftp->_PWD();
- $ftp->_extract_path;
-}
-
-# rmdir( $ftp, $dir, [ $recurse ] )
-#
-# Removes $dir on remote host via FTP.
-# $ftp is handle for remote host
-#
-# If $recurse is TRUE, the directory and deleted recursively.
-# This means all of its contents and subdirectories.
-#
-# Initial version contributed by Dinkum Software
-#
-sub rmdir {
- @_ == 2 || @_ == 3 or croak('usage: $ftp->rmdir( DIR [, RECURSE ] )');
-
- # Pick off the args
- my ($ftp, $dir, $recurse) = @_;
- my $ok;
-
- return $ok
- if $ok = $ftp->_RMD($dir)
- or !$recurse;
-
- # Try to delete the contents
- # Get a list of all the files in the directory
- my @filelist = grep { !/^\.{1,2}$/ } $ftp->ls($dir);
-
- return undef
- unless @filelist; # failed, it is probably not a directory
-
- return $ftp->delete($dir)
- if @filelist == 1 and $dir eq $filelist[0];
-
- # Go thru and delete each file or the directory
- my $file;
- foreach $file (map { m,/, ? $_ : "$dir/$_" } @filelist) {
- next # successfully deleted the file
- if $ftp->delete($file);
-
- # Failed to delete it, assume its a directory
- # Recurse and ignore errors, the final rmdir() will
- # fail on any errors here
- return $ok
- unless $ok = $ftp->rmdir($file, 1);
- }
-
- # Directory should be empty
- # Try to remove the directory again
- # Pass results directly to caller
- # If any of the prior deletes failed, this
- # rmdir() will fail because directory is not empty
- return $ftp->_RMD($dir);
-}
-
-
-sub restart {
- @_ == 2 || croak 'usage: $ftp->restart( BYTE_OFFSET )';
-
- my ($ftp, $where) = @_;
-
- ${*$ftp}{'net_ftp_rest'} = $where;
-
- return undef;
-}
-
-
-sub mkdir {
- @_ == 2 || @_ == 3 or croak 'usage: $ftp->mkdir( DIR [, RECURSE ] )';
-
- my ($ftp, $dir, $recurse) = @_;
-
- $ftp->_MKD($dir) || $recurse
- or return undef;
-
- my $path = $dir;
-
- unless ($ftp->ok) {
- my @path = split(m#(?=/+)#, $dir);
-
- $path = "";
-
- while (@path) {
- $path .= shift @path;
-
- $ftp->_MKD($path);
-
- $path = $ftp->_extract_path($path);
- }
-
- # If the creation of the last element was not successful, see if we
- # can cd to it, if so then return path
-
- unless ($ftp->ok) {
- my ($status, $message) = ($ftp->status, $ftp->message);
- my $pwd = $ftp->pwd;
-
- if ($pwd && $ftp->cwd($dir)) {
- $path = $dir;
- $ftp->cwd($pwd);
- }
- else {
- undef $path;
- }
- $ftp->set_status($status, $message);
- }
- }
-
- $path;
-}
-
-
-sub delete {
- @_ == 2 || croak 'usage: $ftp->delete( FILENAME )';
-
- $_[0]->_DELE($_[1]);
-}
-
-
-sub put { shift->_store_cmd("stor", @_) }
-sub put_unique { shift->_store_cmd("stou", @_) }
-sub append { shift->_store_cmd("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 _store_cmd {
- my ($ftp, $cmd, $local, $remote) = @_;
- my ($loc, $sock, $len, $buf);
- local *FD;
-
- my $localfd = ref($local) || ref(\$local) eq "GLOB";
-
- if (!defined($remote) and 'STOU' ne uc($cmd)) {
- croak 'Must specify remote filename with stream input'
- if $localfd;
-
- require File::Basename;
- $remote = File::Basename::basename($local);
- }
- if (defined ${*$ftp}{'net_ftp_allo'}) {
- delete ${*$ftp}{'net_ftp_allo'};
- }
- else {
-
- # if the user hasn't already invoked the alloc method since the last
- # _store_cmd call, figure out if the local file is a regular file(not
- # a pipe, or device) and if so get the file size from stat, and send
- # an ALLO command before sending the STOR, STOU, or APPE command.
- my $size = do { local $^W; -f $local && -s _ }; # no ALLO if sending data from a pipe
- ${*$ftp}{'net_ftp_allo'} = $size if $size;
- }
- croak("Bad remote filename '$remote'\n")
- if defined($remote) and $remote =~ /[\r\n]/s;
-
- if ($localfd) {
- $loc = $local;
- }
- else {
- $loc = \*FD;
-
- unless (sysopen($loc, $local, O_RDONLY)) {
- carp "Cannot open Local file $local: $!\n";
- return undef;
- }
- }
-
- if ($ftp->type eq 'I' && !binmode($loc)) {
- carp "Cannot binmode Local file $local: $!\n";
- return undef;
- }
-
- delete ${*$ftp}{'net_ftp_port'};
- delete ${*$ftp}{'net_ftp_pasv'};
-
- $sock = $ftp->_data_cmd($cmd, grep { defined } $remote)
- or return undef;
-
- $remote = ($ftp->message =~ /\w+\s*:\s*(.*)/)[0]
- if 'STOU' eq uc $cmd;
-
- my $blksize = ${*$ftp}{'net_ftp_blksize'};
-
- my ($count, $hashh, $hashb, $ref) = (0);
-
- ($hashh, $hashb) = @$ref
- if ($ref = ${*$ftp}{'net_ftp_hash'});
-
- while (1) {
- last unless $len = read($loc, $buf = "", $blksize);
-
- if (trEBCDIC && $ftp->type ne 'I') {
- $buf = $ftp->toascii($buf);
- $len = length($buf);
- }
-
- if ($hashh) {
- $count += $len;
- print $hashh "#" x (int($count / $hashb));
- $count %= $hashb;
- }
-
- my $wlen;
- unless (defined($wlen = $sock->write($buf, $len)) && $wlen == $len) {
- $sock->abort;
- close($loc)
- unless $localfd;
- print $hashh "\n" if $hashh;
- return undef;
- }
- }
-
- print $hashh "\n" if $hashh;
-
- close($loc)
- unless $localfd;
-
- $sock->close()
- or return undef;
-
- if ('STOU' eq uc $cmd and $ftp->message =~ m/unique\s+file\s*name\s*:\s*(.*)\)|"(.*)"/) {
- require File::Basename;
- $remote = File::Basename::basename($+);
- }
-
- return $remote;
-}
-
-
-sub port {
- @_ == 1 || @_ == 2 or croak 'usage: $ftp->port([PORT])';
-
- my ($ftp, $port) = @_;
- my $ok;
-
- delete ${*$ftp}{'net_ftp_intern_port'};
-
- unless (defined $port) {
-
- # create a Listen socket at same address as the command socket
-
- ${*$ftp}{'net_ftp_listen'} ||= IO::Socket::INET->new(
- Listen => 5,
- Proto => 'tcp',
- Timeout => $ftp->timeout,
- LocalAddr => $ftp->sockhost,
- );
-
- my $listen = ${*$ftp}{'net_ftp_listen'};
-
- my ($myport, @myaddr) = ($listen->sockport, split(/\./, $listen->sockhost));
-
- $port = join(',', @myaddr, $myport >> 8, $myport & 0xff);
-
- ${*$ftp}{'net_ftp_intern_port'} = 1;
- }
-
- $ok = $ftp->_PORT($port);
-
- ${*$ftp}{'net_ftp_port'} = $port;
-
- $ok;
-}
-
-
-sub ls { shift->_list_cmd("NLST", @_); }
-sub dir { shift->_list_cmd("LIST", @_); }
-
-
-sub pasv {
- @_ == 1 or croak 'usage: $ftp->pasv()';
-
- my $ftp = shift;
-
- delete ${*$ftp}{'net_ftp_intern_port'};
-
- $ftp->_PASV && $ftp->message =~ /(\d+(,\d+)+)/
- ? ${*$ftp}{'net_ftp_pasv'} = $1
- : undef;
-}
-
-
-sub unique_name {
- my $ftp = shift;
- ${*$ftp}{'net_ftp_unique'} || undef;
-}
-
-
-sub supported {
- @_ == 2 or croak 'usage: $ftp->supported( CMD )';
- my $ftp = shift;
- my $cmd = uc shift;
- my $hash = ${*$ftp}{'net_ftp_supported'} ||= {};
-
- return $hash->{$cmd}
- if exists $hash->{$cmd};
-
- return $hash->{$cmd} = 1
- if $ftp->feature($cmd);
-
- return $hash->{$cmd} = 0
- unless $ftp->_HELP($cmd);
-
- my $text = $ftp->message;
- if ($text =~ /following\s+commands/i) {
- $text =~ s/^.*\n//;
- while ($text =~ /(\*?)(\w+)(\*?)/sg) {
- $hash->{"\U$2"} = !length("$1$3");
- }
- }
- else {
- $hash->{$cmd} = $text !~ /unimplemented/i;
- }
-
- $hash->{$cmd} ||= 0;
-}
-
-##
-## Deprecated methods
-##
-
-
-sub lsl {
- carp "Use of Net::FTP::lsl deprecated, use 'dir'"
- if $^W;
- goto &dir;
-}
-
-
-sub authorise {
- carp "Use of Net::FTP::authorise deprecated, use 'authorize'"
- if $^W;
- goto &authorize;
-}
-
-
-##
-## Private methods
-##
-
-
-sub _extract_path {
- my ($ftp, $path) = @_;
-
- # This tries to work both with and without the quote doubling
- # convention (RFC 959 requires it, but the first 3 servers I checked
- # didn't implement it). It will fail on a server which uses a quote in
- # the message which isn't a part of or surrounding the path.
- $ftp->ok
- && $ftp->message =~ /(?:^|\s)\"(.*)\"(?:$|\s)/
- && ($path = $1) =~ s/\"\"/\"/g;
-
- $path;
-}
-
-##
-## Communication methods
-##
-
-
-sub _dataconn {
- my $ftp = shift;
- my $data = undef;
- my $pkg = "Net::FTP::" . $ftp->type;
-
- eval "require " . $pkg;
-
- $pkg =~ s/ /_/g;
-
- delete ${*$ftp}{'net_ftp_dataconn'};
-
- if (defined ${*$ftp}{'net_ftp_pasv'}) {
- my @port = map { 0 + $_ } split(/,/, ${*$ftp}{'net_ftp_pasv'});
-
- $data = $pkg->new(
- PeerAddr => join(".", @port[0 .. 3]),
- PeerPort => $port[4] * 256 + $port[5],
- LocalAddr => ${*$ftp}{'net_ftp_localaddr'},
- Proto => 'tcp',
- Timeout => $ftp->timeout
- );
- }
- elsif (defined ${*$ftp}{'net_ftp_listen'}) {
- $data = ${*$ftp}{'net_ftp_listen'}->accept($pkg);
- close(delete ${*$ftp}{'net_ftp_listen'});
- }
-
- if ($data) {
- ${*$data} = "";
- $data->timeout($ftp->timeout);
- ${*$ftp}{'net_ftp_dataconn'} = $data;
- ${*$data}{'net_ftp_cmd'} = $ftp;
- ${*$data}{'net_ftp_blksize'} = ${*$ftp}{'net_ftp_blksize'};
- }
-
- $data;
-}
-
-
-sub _list_cmd {
- my $ftp = shift;
- my $cmd = uc shift;
-
- delete ${*$ftp}{'net_ftp_port'};
- delete ${*$ftp}{'net_ftp_pasv'};
-
- my $data = $ftp->_data_cmd($cmd, @_);
-
- return
- unless (defined $data);
-
- require Net::FTP::A;
- bless $data, "Net::FTP::A"; # Force ASCII mode
-
- my $databuf = '';
- my $buf = '';
- my $blksize = ${*$ftp}{'net_ftp_blksize'};
-
- while ($data->read($databuf, $blksize)) {
- $buf .= $databuf;
- }
-
- my $list = [split(/\n/, $buf)];
-
- $data->close();
-
- if (trEBCDIC) {
- for (@$list) { $_ = $ftp->toebcdic($_) }
- }
-
- wantarray
- ? @{$list}
- : $list;
-}
-
-
-sub _data_cmd {
- my $ftp = shift;
- my $cmd = uc shift;
- my $ok = 1;
- my $where = delete ${*$ftp}{'net_ftp_rest'} || 0;
- my $arg;
-
- for $arg (@_) {
- croak("Bad argument '$arg'\n")
- if $arg =~ /[\r\n]/s;
- }
-
- if ( ${*$ftp}{'net_ftp_passive'}
- && !defined ${*$ftp}{'net_ftp_pasv'}
- && !defined ${*$ftp}{'net_ftp_port'})
- {
- my $data = undef;
-
- return undef unless defined $ftp->pasv;
- $data = $ftp->_dataconn() or return undef;
-
- if ($where and !$ftp->_REST($where)) {
- my ($status, $message) = ($ftp->status, $ftp->message);
- $ftp->abort;
- $ftp->set_status($status, $message);
- return undef;
- }
-
- $ftp->command($cmd, @_);
- if (CMD_INFO == $ftp->response()) {
- $data->reading
- if $cmd =~ /RETR|LIST|NLST/;
- return $data;
- }
- $data->_close;
-
- return undef;
- }
-
- $ok = $ftp->port
- unless (defined ${*$ftp}{'net_ftp_port'}
- || defined ${*$ftp}{'net_ftp_pasv'});
-
- $ok = $ftp->_REST($where)
- if $ok && $where;
-
- return undef
- unless $ok;
-
- if ($cmd =~ /(STOR|APPE|STOU)/ and exists ${*$ftp}{net_ftp_allo}) {
- $ftp->_ALLO(delete ${*$ftp}{net_ftp_allo})
- or return undef;
- }
-
- $ftp->command($cmd, @_);
-
- return 1
- if (defined ${*$ftp}{'net_ftp_pasv'});
-
- $ok = CMD_INFO == $ftp->response();
-
- return $ok
- unless exists ${*$ftp}{'net_ftp_intern_port'};
-
- if ($ok) {
- my $data = $ftp->_dataconn();
-
- $data->reading
- if $data && $cmd =~ /RETR|LIST|NLST/;
-
- return $data;
- }
-
-
- close(delete ${*$ftp}{'net_ftp_listen'});
-
- return undef;
-}
-
-##
-## Over-ride methods (Net::Cmd)
-##
-
-
-sub debug_text { $_[2] =~ /^(pass|resp|acct)/i ? "$1 ....\n" : $_[2]; }
-
-
-sub command {
- my $ftp = shift;
-
- delete ${*$ftp}{'net_ftp_port'};
- $ftp->SUPER::command(@_);
-}
-
-
-sub response {
- my $ftp = shift;
- my $code = $ftp->SUPER::response() || 5; # assume 500 if undef
-
- delete ${*$ftp}{'net_ftp_pasv'}
- if ($code != CMD_MORE && $code != CMD_INFO);
-
- $code;
-}
-
-
-sub parse_response {
- return ($1, $2 eq "-")
- if $_[1] =~ s/^(\d\d\d)([- ]?)//o;
-
- my $ftp = shift;
-
- # Darn MS FTP server is a load of CRAP !!!!
- # Expect to see undef here.
- return ()
- unless 0 + (${*$ftp}{'net_cmd_code'} || 0);
-
- (${*$ftp}{'net_cmd_code'}, 1);
-}
-
-##
-## Allow 2 servers to talk directly
-##
-
-
-sub pasv_xfer_unique {
- my ($sftp, $sfile, $dftp, $dfile) = @_;
- $sftp->pasv_xfer($sfile, $dftp, $dfile, 1);
-}
-
-
-sub pasv_xfer {
- my ($sftp, $sfile, $dftp, $dfile, $unique) = @_;
-
- ($dfile = $sfile) =~ s#.*/##
- unless (defined $dfile);
-
- my $port = $sftp->pasv
- or return undef;
-
- $dftp->port($port)
- or return undef;
-
- return undef
- unless ($unique ? $dftp->stou($dfile) : $dftp->stor($dfile));
-
- unless ($sftp->retr($sfile) && $sftp->response == CMD_INFO) {
- $sftp->retr($sfile);
- $dftp->abort;
- $dftp->response();
- return undef;
- }
-
- $dftp->pasv_wait($sftp);
-}
-
-
-sub pasv_wait {
- @_ == 2 or croak 'usage: $ftp->pasv_wait(NON_PASV_FTP)';
-
- my ($ftp, $non_pasv) = @_;
- my ($file, $rin, $rout);
-
- vec($rin = '', fileno($ftp), 1) = 1;
- select($rout = $rin, undef, undef, undef);
-
- my $dres = $ftp->response();
- my $sres = $non_pasv->response();
-
- return undef
- unless $dres == CMD_OK && $sres == CMD_OK;
-
- return undef
- unless $ftp->ok() && $non_pasv->ok();
-
- return $1
- if $ftp->message =~ /unique file name:\s*(\S*)\s*\)/;
-
- return $1
- if $non_pasv->message =~ /unique file name:\s*(\S*)\s*\)/;
-
- return 1;
-}
-
-
-sub feature {
- @_ == 2 or croak 'usage: $ftp->feature( NAME )';
- my ($ftp, $feat) = @_;
-
- my $feature = ${*$ftp}{net_ftp_feature} ||= do {
- my @feat;
-
- # Example response
- # 211-Features:
- # MDTM
- # REST STREAM
- # SIZE
- # 211 End
-
- @feat = map { /^\s+(.*\S)/ } $ftp->message
- if $ftp->_FEAT;
-
- \@feat;
- };
-
- return grep { /^\Q$feat\E\b/i } @$feature;
-}
-
-
-sub cmd { shift->command(@_)->response() }
-
-########################################
-#
-# RFC959 commands
-#
-
-
-sub _ABOR { shift->command("ABOR")->response() == CMD_OK }
-sub _ALLO { shift->command("ALLO", @_)->response() == CMD_OK }
-sub _CDUP { shift->command("CDUP")->response() == CMD_OK }
-sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
-sub _PASV { shift->command("PASV")->response() == CMD_OK }
-sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
-sub _DELE { shift->command("DELE", @_)->response() == CMD_OK }
-sub _CWD { shift->command("CWD", @_)->response() == CMD_OK }
-sub _PORT { shift->command("PORT", @_)->response() == CMD_OK }
-sub _RMD { shift->command("RMD", @_)->response() == CMD_OK }
-sub _MKD { shift->command("MKD", @_)->response() == CMD_OK }
-sub _PWD { shift->command("PWD", @_)->response() == CMD_OK }
-sub _TYPE { shift->command("TYPE", @_)->response() == CMD_OK }
-sub _RNTO { shift->command("RNTO", @_)->response() == CMD_OK }
-sub _RESP { shift->command("RESP", @_)->response() == CMD_OK }
-sub _MDTM { shift->command("MDTM", @_)->response() == CMD_OK }
-sub _SIZE { shift->command("SIZE", @_)->response() == CMD_OK }
-sub _HELP { shift->command("HELP", @_)->response() == CMD_OK }
-sub _STAT { shift->command("STAT", @_)->response() == CMD_OK }
-sub _FEAT { shift->command("FEAT", @_)->response() == CMD_OK }
-sub _APPE { shift->command("APPE", @_)->response() == CMD_INFO }
-sub _LIST { shift->command("LIST", @_)->response() == CMD_INFO }
-sub _NLST { shift->command("NLST", @_)->response() == CMD_INFO }
-sub _RETR { shift->command("RETR", @_)->response() == CMD_INFO }
-sub _STOR { shift->command("STOR", @_)->response() == CMD_INFO }
-sub _STOU { shift->command("STOU", @_)->response() == CMD_INFO }
-sub _RNFR { shift->command("RNFR", @_)->response() == CMD_MORE }
-sub _REST { shift->command("REST", @_)->response() == CMD_MORE }
-sub _PASS { shift->command("PASS", @_)->response() }
-sub _ACCT { shift->command("ACCT", @_)->response() }
-sub _AUTH { shift->command("AUTH", @_)->response() }
-
-
-sub _USER {
- my $ftp = shift;
- my $ok = $ftp->command("USER", @_)->response();
-
- # A certain brain dead firewall :-)
- $ok = $ftp->command("user", @_)->response()
- unless $ok == CMD_MORE or $ok == CMD_OK;
-
- $ok;
-}
-
-
-sub _SMNT { shift->unsupported(@_) }
-sub _MODE { shift->unsupported(@_) }
-sub _SYST { shift->unsupported(@_) }
-sub _STRU { shift->unsupported(@_) }
-sub _REIN { shift->unsupported(@_) }
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::FTP - FTP Client class
-
-=head1 SYNOPSIS
-
- use Net::FTP;
-
- $ftp = Net::FTP->new("some.host.name", Debug => 0)
- or die "Cannot connect to some.host.name: $@";
-
- $ftp->login("anonymous",'-anonymous@')
- or die "Cannot login ", $ftp->message;
-
- $ftp->cwd("/pub")
- or die "Cannot change working directory ", $ftp->message;
-
- $ftp->get("that.file")
- or die "get failed ", $ftp->message;
-
- $ftp->quit;
-
-=head1 DESCRIPTION
-
-C<Net::FTP> is a class implementing a simple FTP client in Perl as
-described in RFC959. It provides wrappers for a subset of the RFC959
-commands.
-
-The Net::FTP class is a subclass of Net::Cmd and IO::Socket::INET.
-
-=head1 OVERVIEW
-
-FTP stands for File Transfer Protocol. It is a way of transferring
-files between networked machines. The protocol defines a client
-(whose commands are provided by this module) and a server (not
-implemented in this module). Communication is always initiated by the
-client, and the server responds with a message and a status code (and
-sometimes with data).
-
-The FTP protocol allows files to be sent to or fetched from the
-server. Each transfer involves a B<local file> (on the client) and a
-B<remote file> (on the server). In this module, the same file name
-will be used for both local and remote if only one is specified. This
-means that transferring remote file C</path/to/file> will try to put
-that file in C</path/to/file> locally, unless you specify a local file
-name.
-
-The protocol also defines several standard B<translations> which the
-file can undergo during transfer. These are ASCII, EBCDIC, binary,
-and byte. ASCII is the default type, and indicates that the sender of
-files will translate the ends of lines to a standard representation
-which the receiver will then translate back into their local
-representation. EBCDIC indicates the file being transferred is in
-EBCDIC format. Binary (also known as image) format sends the data as
-a contiguous bit stream. Byte format transfers the data as bytes, the
-values of which remain the same regardless of differences in byte size
-between the two machines (in theory - in practice you should only use
-this if you really know what you're doing).
-
-=head1 CONSTRUCTOR
-
-=over 4
-
-=item new ([ HOST ] [, OPTIONS ])
-
-This is the constructor for a new Net::FTP object. C<HOST> is the
-name of the remote host to which an FTP connection is required.
-
-C<HOST> is optional. If C<HOST> is not given then it may instead be
-passed as the C<Host> option described below.
-
-C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
-Possible options are:
-
-B<Host> - FTP host to connect to. It may be a single scalar, as defined for
-the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
-an array with hosts to try in turn. The L</host> method will return the value
-which was used to connect to the host.
-
-
-B<Firewall> - The name of a machine which acts as an FTP firewall. This can be
-overridden by an environment variable C<FTP_FIREWALL>. If specified, and the
-given host cannot be directly connected to, then the
-connection is made to the firewall machine and the string C<@hostname> is
-appended to the login identifier. This kind of setup is also referred to
-as an ftp proxy.
-
-B<FirewallType> - The type of firewall running on the machine indicated by
-B<Firewall>. This can be overridden by an environment variable
-C<FTP_FIREWALL_TYPE>. For a list of permissible types, see the description of
-ftp_firewall_type in L<Net::Config>.
-
-B<BlockSize> - This is the block size that Net::FTP will use when doing
-transfers. (defaults to 10240)
-
-B<Port> - The port number to connect to on the remote machine for the
-FTP connection
-
-B<Timeout> - Set a timeout value in seconds (defaults to 120)
-
-B<Debug> - debug level (see the debug method in L<Net::Cmd>)
-
-B<Passive> - If set to a non-zero value then all data transfers will
-be done using passive mode. If set to zero then data transfers will be
-done using active mode. If the machine is connected to the Internet
-directly, both passive and active mode should work equally well.
-Behind most firewall and NAT configurations passive mode has a better
-chance of working. However, in some rare firewall configurations,
-active mode actually works when passive mode doesn't. Some really old
-FTP servers might not implement passive transfers. If not specified,
-then the transfer mode is set by the environment variable
-C<FTP_PASSIVE> or if that one is not set by the settings done by the
-F<libnetcfg> utility. If none of these apply then passive mode is
-used.
-
-B<Hash> - If given a reference to a file handle (e.g., C<\*STDERR>),
-print hash marks (#) on that filehandle every 1024 bytes. This
-simply invokes the C<hash()> method for you, so that hash marks
-are displayed for all transfers. You can, of course, call C<hash()>
-explicitly whenever you'd like.
-
-B<LocalAddr> - Local address to use for all socket connections, this
-argument will be passed to L<IO::Socket::INET>
-
-If the constructor fails undef will be returned and an error message will
-be in $@
-
-=back
-
-=head1 METHODS
-
-Unless otherwise stated all methods return either a I<true> or I<false>
-value, with I<true> meaning that the operation was a success. When a method
-states that it returns a value, failure will be returned as I<undef> or an
-empty list.
-
-C<Net::FTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
-be used to send commands to the remote FTP server in addition to the methods
-documented here.
-
-=over 4
-
-=item login ([LOGIN [,PASSWORD [, ACCOUNT] ] ])
-
-Log into the remote FTP server with the given login information. If
-no arguments are given then the C<Net::FTP> uses the C<Net::Netrc>
-package to lookup the login information for the connected host.
-If no information is found then a login of I<anonymous> is used.
-If no password is given and the login is I<anonymous> then I<anonymous@>
-will be used for password.
-
-If the connection is via a firewall then the C<authorize> method will
-be called with no arguments.
-
-=item authorize ( [AUTH [, RESP]])
-
-This is a protocol used by some firewall ftp proxies. It is used
-to authorise the user to send data out. If both arguments are not specified
-then C<authorize> uses C<Net::Netrc> to do a lookup.
-
-=item site (ARGS)
-
-Send a SITE command to the remote server and wait for a response.
-
-Returns most significant digit of the response code.
-
-=item ascii
-
-Transfer file in ASCII. CRLF translation will be done if required
-
-=item binary
-
-Transfer file in binary mode. No transformation will be done.
-
-B<Hint>: If both server and client machines use the same line ending for
-text files, then it will be faster to transfer all files in binary mode.
-
-=item rename ( OLDNAME, NEWNAME )
-
-Rename a file on the remote FTP server from C<OLDNAME> to C<NEWNAME>. This
-is done by sending the RNFR and RNTO commands.
-
-=item delete ( FILENAME )
-
-Send a request to the server to delete C<FILENAME>.
-
-=item cwd ( [ DIR ] )
-
-Attempt to change directory to the directory given in C<$dir>. If
-C<$dir> is C<"..">, the FTP C<CDUP> command is used to attempt to
-move up one directory. If no directory is given then an attempt is made
-to change the directory to the root directory.
-
-=item cdup ()
-
-Change directory to the parent of the current directory.
-
-=item passive ( [ PASSIVE ] )
-
-Set or get if data connections will be initiated in passive mode.
-
-=item pwd ()
-
-Returns the full pathname of the current directory.
-
-=item restart ( WHERE )
-
-Set the byte offset at which to begin the next data transfer. Net::FTP simply
-records this value and uses it when during the next data transfer. For this
-reason this method will not return an error, but setting it may cause
-a subsequent data transfer to fail.
-
-=item rmdir ( DIR [, RECURSE ])
-
-Remove the directory with the name C<DIR>. If C<RECURSE> is I<true> then
-C<rmdir> will attempt to delete everything inside the directory.
-
-=item mkdir ( DIR [, RECURSE ])
-
-Create a new directory with the name C<DIR>. If C<RECURSE> is I<true> then
-C<mkdir> will attempt to create all the directories in the given path.
-
-Returns the full pathname to the new directory.
-
-=item alloc ( SIZE [, RECORD_SIZE] )
-
-The alloc command allows you to give the ftp server a hint about the size
-of the file about to be transferred using the ALLO ftp command. Some storage
-systems use this to make intelligent decisions about how to store the file.
-The C<SIZE> argument represents the size of the file in bytes. The
-C<RECORD_SIZE> argument indicates a maximum record or page size for files
-sent with a record or page structure.
-
-The size of the file will be determined, and sent to the server
-automatically for normal files so that this method need only be called if
-you are transferring data from a socket, named pipe, or other stream not
-associated with a normal file.
-
-=item ls ( [ DIR ] )
-
-Get a directory listing of C<DIR>, or the current directory.
-
-In an array context, returns a list of lines returned from the server. In
-a scalar context, returns a reference to a list.
-
-=item dir ( [ DIR ] )
-
-Get a directory listing of C<DIR>, or the current directory in long format.
-
-In an array context, returns a list of lines returned from the server. In
-a scalar context, returns a reference to a list.
-
-=item get ( REMOTE_FILE [, LOCAL_FILE [, WHERE]] )
-
-Get C<REMOTE_FILE> from the server and store locally. C<LOCAL_FILE> may be
-a filename or a filehandle. If not specified, the file will be stored in
-the current directory with the same leafname as the remote file.
-
-If C<WHERE> is given then the first C<WHERE> bytes of the file will
-not be transferred, and the remaining bytes will be appended to
-the local file if it already exists.
-
-Returns C<LOCAL_FILE>, or the generated local file name if C<LOCAL_FILE>
-is not given. If an error was encountered undef is returned.
-
-=item put ( LOCAL_FILE [, REMOTE_FILE ] )
-
-Put a file on the remote server. C<LOCAL_FILE> may be a name or a filehandle.
-If C<LOCAL_FILE> is a filehandle then C<REMOTE_FILE> must be specified. If
-C<REMOTE_FILE> is not specified then the file will be stored in the current
-directory with the same leafname as C<LOCAL_FILE>.
-
-Returns C<REMOTE_FILE>, or the generated remote filename if C<REMOTE_FILE>
-is not given.
-
-B<NOTE>: If for some reason the transfer does not complete and an error is
-returned then the contents that had been transferred will not be remove
-automatically.
-
-=item put_unique ( LOCAL_FILE [, REMOTE_FILE ] )
-
-Same as put but uses the C<STOU> command.
-
-Returns the name of the file on the server.
-
-=item append ( LOCAL_FILE [, REMOTE_FILE ] )
-
-Same as put but appends to the file on the remote server.
-
-Returns C<REMOTE_FILE>, or the generated remote filename if C<REMOTE_FILE>
-is not given.
-
-=item unique_name ()
-
-Returns the name of the last file stored on the server using the
-C<STOU> command.
-
-=item mdtm ( FILE )
-
-Returns the I<modification time> of the given file
-
-=item size ( FILE )
-
-Returns the size in bytes for the given file as stored on the remote server.
-
-B<NOTE>: The size reported is the size of the stored file on the remote server.
-If the file is subsequently transferred from the server in ASCII mode
-and the remote server and local machine have different ideas about
-"End Of Line" then the size of file on the local machine after transfer
-may be different.
-
-=item supported ( CMD )
-
-Returns TRUE if the remote server supports the given command.
-
-=item hash ( [FILEHANDLE_GLOB_REF],[ BYTES_PER_HASH_MARK] )
-
-Called without parameters, or with the first argument false, hash marks
-are suppressed. If the first argument is true but not a reference to a
-file handle glob, then \*STDERR is used. The second argument is the number
-of bytes per hash mark printed, and defaults to 1024. In all cases the
-return value is a reference to an array of two: the filehandle glob reference
-and the bytes per hash mark.
-
-=item feature ( NAME )
-
-Determine if the server supports the specified feature. The return
-value is a list of lines the server responded with to describe the
-options that it supports for the given feature. If the feature is
-unsupported then the empty list is returned.
-
- if ($ftp->feature( 'MDTM' )) {
- # Do something
- }
-
- if (grep { /\bTLS\b/ } $ftp->feature('AUTH')) {
- # Server supports TLS
- }
-
-=back
-
-The following methods can return different results depending on
-how they are called. If the user explicitly calls either
-of the C<pasv> or C<port> methods then these methods will
-return a I<true> or I<false> value. If the user does not
-call either of these methods then the result will be a
-reference to a C<Net::FTP::dataconn> based object.
-
-=over 4
-
-=item nlst ( [ DIR ] )
-
-Send an C<NLST> command to the server, with an optional parameter.
-
-=item list ( [ DIR ] )
-
-Same as C<nlst> but using the C<LIST> command
-
-=item retr ( FILE )
-
-Begin the retrieval of a file called C<FILE> from the remote server.
-
-=item stor ( FILE )
-
-Tell the server that you wish to store a file. C<FILE> is the
-name of the new file that should be created.
-
-=item stou ( FILE )
-
-Same as C<stor> but using the C<STOU> command. The name of the unique
-file which was created on the server will be available via the C<unique_name>
-method after the data connection has been closed.
-
-=item appe ( FILE )
-
-Tell the server that we want to append some data to the end of a file
-called C<FILE>. If this file does not exist then create it.
-
-=back
-
-If for some reason you want to have complete control over the data connection,
-this includes generating it and calling the C<response> method when required,
-then the user can use these methods to do so.
-
-However calling these methods only affects the use of the methods above that
-can return a data connection. They have no effect on methods C<get>, C<put>,
-C<put_unique> and those that do not require data connections.
-
-=over 4
-
-=item port ( [ PORT ] )
-
-Send a C<PORT> command to the server. If C<PORT> is specified then it is sent
-to the server. If not, then a listen socket is created and the correct information
-sent to the server.
-
-=item pasv ()
-
-Tell the server to go into passive mode. Returns the text that represents the
-port on which the server is listening, this text is in a suitable form to
-sent to another ftp server using the C<port> method.
-
-=back
-
-The following methods can be used to transfer files between two remote
-servers, providing that these two servers can connect directly to each other.
-
-=over 4
-
-=item pasv_xfer ( SRC_FILE, DEST_SERVER [, DEST_FILE ] )
-
-This method will do a file transfer between two remote ftp servers. If
-C<DEST_FILE> is omitted then the leaf name of C<SRC_FILE> will be used.
-
-=item pasv_xfer_unique ( SRC_FILE, DEST_SERVER [, DEST_FILE ] )
-
-Like C<pasv_xfer> but the file is stored on the remote server using
-the STOU command.
-
-=item pasv_wait ( NON_PASV_SERVER )
-
-This method can be used to wait for a transfer to complete between a passive
-server and a non-passive server. The method should be called on the passive
-server with the C<Net::FTP> object for the non-passive server passed as an
-argument.
-
-=item abort ()
-
-Abort the current data transfer.
-
-=item quit ()
-
-Send the QUIT command to the remote FTP server and close the socket connection.
-
-=back
-
-=head2 Methods for the adventurous
-
-=over 4
-
-=item quot (CMD [,ARGS])
-
-Send a command, that Net::FTP does not directly support, to the remote
-server and wait for a response.
-
-Returns most significant digit of the response code.
-
-B<WARNING> This call should only be used on commands that do not require
-data connections. Misuse of this method can hang the connection.
-
-=back
-
-=head1 THE dataconn CLASS
-
-Some of the methods defined in C<Net::FTP> return an object which will
-be derived from this class.The dataconn class itself is derived from
-the C<IO::Socket::INET> class, so any normal IO operations can be performed.
-However the following methods are defined in the dataconn class and IO should
-be performed using these.
-
-=over 4
-
-=item read ( BUFFER, SIZE [, TIMEOUT ] )
-
-Read C<SIZE> bytes of data from the server and place it into C<BUFFER>, also
-performing any <CRLF> translation necessary. C<TIMEOUT> is optional, if not
-given, the timeout value from the command connection will be used.
-
-Returns the number of bytes read before any <CRLF> translation.
-
-=item write ( BUFFER, SIZE [, TIMEOUT ] )
-
-Write C<SIZE> bytes of data from C<BUFFER> to the server, also
-performing any <CRLF> translation necessary. C<TIMEOUT> is optional, if not
-given, the timeout value from the command connection will be used.
-
-Returns the number of bytes written before any <CRLF> translation.
-
-=item bytes_read ()
-
-Returns the number of bytes read so far.
-
-=item abort ()
-
-Abort the current data transfer.
-
-=item close ()
-
-Close the data connection and get a response from the FTP server. Returns
-I<true> if the connection was closed successfully and the first digit of
-the response from the server was a '2'.
-
-=back
-
-=head1 UNIMPLEMENTED
-
-The following RFC959 commands have not been implemented:
-
-=over 4
-
-=item B<SMNT>
-
-Mount a different file system structure without changing login or
-accounting information.
-
-=item B<HELP>
-
-Ask the server for "helpful information" (that's what the RFC says) on
-the commands it accepts.
-
-=item B<MODE>
-
-Specifies transfer mode (stream, block or compressed) for file to be
-transferred.
-
-=item B<SYST>
-
-Request remote server system identification.
-
-=item B<STAT>
-
-Request remote server status.
-
-=item B<STRU>
-
-Specifies file structure for file to be transferred.
-
-=item B<REIN>
-
-Reinitialize the connection, flushing all I/O and account information.
-
-=back
-
-=head1 REPORTING BUGS
-
-When reporting bugs/problems please include as much information as possible.
-It may be difficult for me to reproduce the problem as almost every setup
-is different.
-
-A small script which yields the problem will probably be of help. It would
-also be useful if this script was run with the extra options C<Debug => 1>
-passed to the constructor, and the output sent with the bug report. If you
-cannot include a small script then please include a Debug trace from a
-run of your program which does yield the problem.
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 SEE ALSO
-
-L<Net::Netrc>
-L<Net::Cmd>
-
-ftp(1), ftpd(8), RFC 959
-http://www.ietf.org/rfc/rfc959.txt
-
-=head1 USE EXAMPLES
-
-For an example of the use of Net::FTP see
-
-=over 4
-
-=item http://www.csh.rit.edu/~adam/Progs/
-
-C<autoftp> is a program that can retrieve, send, or list files via
-the FTP protocol in a non-interactive manner.
-
-=back
-
-=head1 CREDITS
-
-Henry Gabryjelski <henryg@WPI.EDU> - for the suggestion of creating directories
-recursively.
-
-Nathan Torkington <gnat@frii.com> - for some input on the documentation.
-
-Roderick Schertler <roderick@gate.net> - for various inputs
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-2004 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,1154 +0,0 @@
-# Net::NNTP.pm
-#
-# Copyright (c) 1995-1997 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::NNTP;
-
-use strict;
-use vars qw(@ISA $VERSION $debug);
-use IO::Socket;
-use Net::Cmd;
-use Carp;
-use Time::Local;
-use Net::Config;
-
-$VERSION = "2.26";
-@ISA = qw(Net::Cmd IO::Socket::INET);
-
-
-sub new {
- my $self = shift;
- my $type = ref($self) || $self;
- my ($host, %arg);
- if (@_ % 2) {
- $host = shift;
- %arg = @_;
- }
- else {
- %arg = @_;
- $host = delete $arg{Host};
- }
- my $obj;
-
- $host ||= $ENV{NNTPSERVER} || $ENV{NEWSHOST};
-
- my $hosts = defined $host ? [$host] : $NetConfig{nntp_hosts};
-
- @{$hosts} = qw(news)
- unless @{$hosts};
-
- my %connect = ( Proto => 'tcp');
- my $o;
- foreach $o (qw(LocalAddr Timeout)) {
- $connect{$o} = $arg{$o} if exists $arg{$o};
- }
- $connect{Timeout} = 120 unless defined $connect{Timeout};
- $connect{PeerPort} = $arg{Port} || 'nntp(119)';
- my $h;
- foreach $h (@{$hosts}) {
- $connect{PeerAddr} = $h;
- $obj = $type->SUPER::new(%connect)
- and last;
- }
-
- return undef
- unless defined $obj;
-
- ${*$obj}{'net_nntp_host'} = $connect{PeerAddr};
-
- $obj->autoflush(1);
- $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
-
- unless ($obj->response() == CMD_OK) {
- $obj->close;
- return undef;
- }
-
- my $c = $obj->code;
- my @m = $obj->message;
-
- unless (exists $arg{Reader} && $arg{Reader} == 0) {
-
- # if server is INN and we have transfer rights the we are currently
- # talking to innd not nnrpd
- if ($obj->reader) {
-
- # If reader succeeds the we need to consider this code to determine postok
- $c = $obj->code;
- }
- else {
-
- # I want to ignore this failure, so restore the previous status.
- $obj->set_status($c, \@m);
- }
- }
-
- ${*$obj}{'net_nntp_post'} = $c == 200 ? 1 : 0;
-
- $obj;
-}
-
-
-sub host {
- my $me = shift;
- ${*$me}{'net_nntp_host'};
-}
-
-
-sub debug_text {
- my $nntp = shift;
- my $inout = shift;
- my $text = shift;
-
- if ( (ref($nntp) and $nntp->code == 350 and $text =~ /^(\S+)/)
- || ($text =~ /^(authinfo\s+pass)/io))
- {
- $text = "$1 ....\n";
- }
-
- $text;
-}
-
-
-sub postok {
- @_ == 1 or croak 'usage: $nntp->postok()';
- my $nntp = shift;
- ${*$nntp}{'net_nntp_post'} || 0;
-}
-
-
-sub article {
- @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->article( [ MSGID ], [ FH ] )';
- my $nntp = shift;
- my @fh;
-
- @fh = (pop) if @_ == 2 || (@_ && (ref($_[0]) || ref(\$_[0]) eq 'GLOB'));
-
- $nntp->_ARTICLE(@_)
- ? $nntp->read_until_dot(@fh)
- : undef;
-}
-
-
-sub articlefh {
- @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->articlefh( [ MSGID ] )';
- my $nntp = shift;
-
- return unless $nntp->_ARTICLE(@_);
- return $nntp->tied_fh;
-}
-
-
-sub authinfo {
- @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
- my ($nntp, $user, $pass) = @_;
-
- $nntp->_AUTHINFO("USER", $user) == CMD_MORE
- && $nntp->_AUTHINFO("PASS", $pass) == CMD_OK;
-}
-
-
-sub authinfo_simple {
- @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
- my ($nntp, $user, $pass) = @_;
-
- $nntp->_AUTHINFO('SIMPLE') == CMD_MORE
- && $nntp->command($user, $pass)->response == CMD_OK;
-}
-
-
-sub body {
- @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->body( [ MSGID ], [ FH ] )';
- my $nntp = shift;
- my @fh;
-
- @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
-
- $nntp->_BODY(@_)
- ? $nntp->read_until_dot(@fh)
- : undef;
-}
-
-
-sub bodyfh {
- @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->bodyfh( [ MSGID ] )';
- my $nntp = shift;
- return unless $nntp->_BODY(@_);
- return $nntp->tied_fh;
-}
-
-
-sub head {
- @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->head( [ MSGID ], [ FH ] )';
- my $nntp = shift;
- my @fh;
-
- @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
-
- $nntp->_HEAD(@_)
- ? $nntp->read_until_dot(@fh)
- : undef;
-}
-
-
-sub headfh {
- @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->headfh( [ MSGID ] )';
- my $nntp = shift;
- return unless $nntp->_HEAD(@_);
- return $nntp->tied_fh;
-}
-
-
-sub nntpstat {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->nntpstat( [ MSGID ] )';
- my $nntp = shift;
-
- $nntp->_STAT(@_) && $nntp->message =~ /(<[^>]+>)/o
- ? $1
- : undef;
-}
-
-
-sub group {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->group( [ GROUP ] )';
- my $nntp = shift;
- my $grp = ${*$nntp}{'net_nntp_group'};
-
- return $grp
- unless (@_ || wantarray);
-
- my $newgrp = shift;
-
- $newgrp = (defined($grp) and length($grp)) ? $grp : ""
- unless defined($newgrp) and length($newgrp);
-
- return
- unless $nntp->_GROUP($newgrp) and $nntp->message =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\S+)/;
-
- my ($count, $first, $last, $group) = ($1, $2, $3, $4);
-
- # group may be replied as '(current group)'
- $group = ${*$nntp}{'net_nntp_group'}
- if $group =~ /\(/;
-
- ${*$nntp}{'net_nntp_group'} = $group;
-
- wantarray
- ? ($count, $first, $last, $group)
- : $group;
-}
-
-
-sub help {
- @_ == 1 or croak 'usage: $nntp->help()';
- my $nntp = shift;
-
- $nntp->_HELP
- ? $nntp->read_until_dot
- : undef;
-}
-
-
-sub ihave {
- @_ >= 2 or croak 'usage: $nntp->ihave( MESSAGE-ID [, MESSAGE ])';
- my $nntp = shift;
- my $mid = shift;
-
- $nntp->_IHAVE($mid) && $nntp->datasend(@_)
- ? @_ == 0 || $nntp->dataend
- : undef;
-}
-
-
-sub last {
- @_ == 1 or croak 'usage: $nntp->last()';
- my $nntp = shift;
-
- $nntp->_LAST && $nntp->message =~ /(<[^>]+>)/o
- ? $1
- : undef;
-}
-
-
-sub list {
- @_ == 1 or croak 'usage: $nntp->list()';
- my $nntp = shift;
-
- $nntp->_LIST
- ? $nntp->_grouplist
- : undef;
-}
-
-
-sub newgroups {
- @_ >= 2 or croak 'usage: $nntp->newgroups( SINCE [, DISTRIBUTIONS ])';
- my $nntp = shift;
- my $time = _timestr(shift);
- my $dist = shift || "";
-
- $dist = join(",", @{$dist})
- if ref($dist);
-
- $nntp->_NEWGROUPS($time, $dist)
- ? $nntp->_grouplist
- : undef;
-}
-
-
-sub newnews {
- @_ >= 2 && @_ <= 4
- or croak 'usage: $nntp->newnews( SINCE [, GROUPS [, DISTRIBUTIONS ]])';
- my $nntp = shift;
- my $time = _timestr(shift);
- my $grp = @_ ? shift: $nntp->group;
- my $dist = shift || "";
-
- $grp ||= "*";
- $grp = join(",", @{$grp})
- if ref($grp);
-
- $dist = join(",", @{$dist})
- if ref($dist);
-
- $nntp->_NEWNEWS($grp, $time, $dist)
- ? $nntp->_articlelist
- : undef;
-}
-
-
-sub next {
- @_ == 1 or croak 'usage: $nntp->next()';
- my $nntp = shift;
-
- $nntp->_NEXT && $nntp->message =~ /(<[^>]+>)/o
- ? $1
- : undef;
-}
-
-
-sub post {
- @_ >= 1 or croak 'usage: $nntp->post( [ MESSAGE ] )';
- my $nntp = shift;
-
- $nntp->_POST() && $nntp->datasend(@_)
- ? @_ == 0 || $nntp->dataend
- : undef;
-}
-
-
-sub postfh {
- my $nntp = shift;
- return unless $nntp->_POST();
- return $nntp->tied_fh;
-}
-
-
-sub quit {
- @_ == 1 or croak 'usage: $nntp->quit()';
- my $nntp = shift;
-
- $nntp->_QUIT;
- $nntp->close;
-}
-
-
-sub slave {
- @_ == 1 or croak 'usage: $nntp->slave()';
- my $nntp = shift;
-
- $nntp->_SLAVE;
-}
-
-##
-## The following methods are not implemented by all servers
-##
-
-
-sub active {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->active( [ PATTERN ] )';
- my $nntp = shift;
-
- $nntp->_LIST('ACTIVE', @_)
- ? $nntp->_grouplist
- : undef;
-}
-
-
-sub active_times {
- @_ == 1 or croak 'usage: $nntp->active_times()';
- my $nntp = shift;
-
- $nntp->_LIST('ACTIVE.TIMES')
- ? $nntp->_grouplist
- : undef;
-}
-
-
-sub distributions {
- @_ == 1 or croak 'usage: $nntp->distributions()';
- my $nntp = shift;
-
- $nntp->_LIST('DISTRIBUTIONS')
- ? $nntp->_description
- : undef;
-}
-
-
-sub distribution_patterns {
- @_ == 1 or croak 'usage: $nntp->distributions()';
- my $nntp = shift;
-
- my $arr;
- local $_;
-
- $nntp->_LIST('DISTRIB.PATS')
- && ($arr = $nntp->read_until_dot)
- ? [grep { /^\d/ && (chomp, $_ = [split /:/]) } @$arr]
- : undef;
-}
-
-
-sub newsgroups {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->newsgroups( [ PATTERN ] )';
- my $nntp = shift;
-
- $nntp->_LIST('NEWSGROUPS', @_)
- ? $nntp->_description
- : undef;
-}
-
-
-sub overview_fmt {
- @_ == 1 or croak 'usage: $nntp->overview_fmt()';
- my $nntp = shift;
-
- $nntp->_LIST('OVERVIEW.FMT')
- ? $nntp->_articlelist
- : undef;
-}
-
-
-sub subscriptions {
- @_ == 1 or croak 'usage: $nntp->subscriptions()';
- my $nntp = shift;
-
- $nntp->_LIST('SUBSCRIPTIONS')
- ? $nntp->_articlelist
- : undef;
-}
-
-
-sub listgroup {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->listgroup( [ GROUP ] )';
- my $nntp = shift;
-
- $nntp->_LISTGROUP(@_)
- ? $nntp->_articlelist
- : undef;
-}
-
-
-sub reader {
- @_ == 1 or croak 'usage: $nntp->reader()';
- my $nntp = shift;
-
- $nntp->_MODE('READER');
-}
-
-
-sub xgtitle {
- @_ == 1 || @_ == 2 or croak 'usage: $nntp->xgtitle( [ PATTERN ] )';
- my $nntp = shift;
-
- $nntp->_XGTITLE(@_)
- ? $nntp->_description
- : undef;
-}
-
-
-sub xhdr {
- @_ >= 2 && @_ <= 4 or croak 'usage: $nntp->xhdr( HEADER, [ MESSAGE-SPEC ] )';
- my $nntp = shift;
- my $hdr = shift;
- my $arg = _msg_arg(@_);
-
- $nntp->_XHDR($hdr, $arg)
- ? $nntp->_description
- : undef;
-}
-
-
-sub xover {
- @_ == 2 || @_ == 3 or croak 'usage: $nntp->xover( MESSAGE-SPEC )';
- my $nntp = shift;
- my $arg = _msg_arg(@_);
-
- $nntp->_XOVER($arg)
- ? $nntp->_fieldlist
- : undef;
-}
-
-
-sub xpat {
- @_ == 4 || @_ == 5 or croak '$nntp->xpat( HEADER, PATTERN, MESSAGE-SPEC )';
- my $nntp = shift;
- my $hdr = shift;
- my $pat = shift;
- my $arg = _msg_arg(@_);
-
- $pat = join(" ", @$pat)
- if ref($pat);
-
- $nntp->_XPAT($hdr, $arg, $pat)
- ? $nntp->_description
- : undef;
-}
-
-
-sub xpath {
- @_ == 2 or croak 'usage: $nntp->xpath( MESSAGE-ID )';
- my ($nntp, $mid) = @_;
-
- return undef
- unless $nntp->_XPATH($mid);
-
- my $m;
- ($m = $nntp->message) =~ s/^\d+\s+//o;
- my @p = split /\s+/, $m;
-
- wantarray ? @p : $p[0];
-}
-
-
-sub xrover {
- @_ == 2 || @_ == 3 or croak 'usage: $nntp->xrover( MESSAGE-SPEC )';
- my $nntp = shift;
- my $arg = _msg_arg(@_);
-
- $nntp->_XROVER($arg)
- ? $nntp->_description
- : undef;
-}
-
-
-sub date {
- @_ == 1 or croak 'usage: $nntp->date()';
- my $nntp = shift;
-
- $nntp->_DATE
- && $nntp->message =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
- ? timegm($6, $5, $4, $3, $2 - 1, $1 - 1900)
- : undef;
-}
-
-
-##
-## Private subroutines
-##
-
-
-sub _msg_arg {
- my $spec = shift;
- my $arg = "";
-
- if (@_) {
- carp "Depriciated passing of two message numbers, " . "pass a reference"
- if $^W;
- $spec = [$spec, $_[0]];
- }
-
- if (defined $spec) {
- if (ref($spec)) {
- $arg = $spec->[0];
- if (defined $spec->[1]) {
- $arg .= "-"
- if $spec->[1] != $spec->[0];
- $arg .= $spec->[1]
- if $spec->[1] > $spec->[0];
- }
- }
- else {
- $arg = $spec;
- }
- }
-
- $arg;
-}
-
-
-sub _timestr {
- my $time = shift;
- my @g = reverse((gmtime($time))[0 .. 5]);
- $g[1] += 1;
- $g[0] %= 100;
- sprintf "%02d%02d%02d %02d%02d%02d GMT", @g;
-}
-
-
-sub _grouplist {
- my $nntp = shift;
- my $arr = $nntp->read_until_dot
- or return undef;
-
- my $hash = {};
- my $ln;
-
- foreach $ln (@$arr) {
- my @a = split(/[\s\n]+/, $ln);
- $hash->{$a[0]} = [@a[1, 2, 3]];
- }
-
- $hash;
-}
-
-
-sub _fieldlist {
- my $nntp = shift;
- my $arr = $nntp->read_until_dot
- or return undef;
-
- my $hash = {};
- my $ln;
-
- foreach $ln (@$arr) {
- my @a = split(/[\t\n]/, $ln);
- my $m = shift @a;
- $hash->{$m} = [@a];
- }
-
- $hash;
-}
-
-
-sub _articlelist {
- my $nntp = shift;
- my $arr = $nntp->read_until_dot;
-
- chomp(@$arr)
- if $arr;
-
- $arr;
-}
-
-
-sub _description {
- my $nntp = shift;
- my $arr = $nntp->read_until_dot
- or return undef;
-
- my $hash = {};
- my $ln;
-
- foreach $ln (@$arr) {
- chomp($ln);
-
- $hash->{$1} = $ln
- if $ln =~ s/^\s*(\S+)\s*//o;
- }
-
- $hash;
-
-}
-
-##
-## The commands
-##
-
-
-sub _ARTICLE { shift->command('ARTICLE', @_)->response == CMD_OK }
-sub _AUTHINFO { shift->command('AUTHINFO', @_)->response }
-sub _BODY { shift->command('BODY', @_)->response == CMD_OK }
-sub _DATE { shift->command('DATE')->response == CMD_INFO }
-sub _GROUP { shift->command('GROUP', @_)->response == CMD_OK }
-sub _HEAD { shift->command('HEAD', @_)->response == CMD_OK }
-sub _HELP { shift->command('HELP', @_)->response == CMD_INFO }
-sub _IHAVE { shift->command('IHAVE', @_)->response == CMD_MORE }
-sub _LAST { shift->command('LAST')->response == CMD_OK }
-sub _LIST { shift->command('LIST', @_)->response == CMD_OK }
-sub _LISTGROUP { shift->command('LISTGROUP', @_)->response == CMD_OK }
-sub _NEWGROUPS { shift->command('NEWGROUPS', @_)->response == CMD_OK }
-sub _NEWNEWS { shift->command('NEWNEWS', @_)->response == CMD_OK }
-sub _NEXT { shift->command('NEXT')->response == CMD_OK }
-sub _POST { shift->command('POST', @_)->response == CMD_MORE }
-sub _QUIT { shift->command('QUIT', @_)->response == CMD_OK }
-sub _SLAVE { shift->command('SLAVE', @_)->response == CMD_OK }
-sub _STAT { shift->command('STAT', @_)->response == CMD_OK }
-sub _MODE { shift->command('MODE', @_)->response == CMD_OK }
-sub _XGTITLE { shift->command('XGTITLE', @_)->response == CMD_OK }
-sub _XHDR { shift->command('XHDR', @_)->response == CMD_OK }
-sub _XPAT { shift->command('XPAT', @_)->response == CMD_OK }
-sub _XPATH { shift->command('XPATH', @_)->response == CMD_OK }
-sub _XOVER { shift->command('XOVER', @_)->response == CMD_OK }
-sub _XROVER { shift->command('XROVER', @_)->response == CMD_OK }
-sub _XTHREAD { shift->unsupported }
-sub _XSEARCH { shift->unsupported }
-sub _XINDEX { shift->unsupported }
-
-##
-## IO/perl methods
-##
-
-
-sub DESTROY {
- my $nntp = shift;
- defined(fileno($nntp)) && $nntp->quit;
-}
-
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::NNTP - NNTP Client class
-
-=head1 SYNOPSIS
-
- use Net::NNTP;
-
- $nntp = Net::NNTP->new("some.host.name");
- $nntp->quit;
-
-=head1 DESCRIPTION
-
-C<Net::NNTP> is a class implementing a simple NNTP client in Perl as described
-in RFC977.
-
-The Net::NNTP class is a subclass of Net::Cmd and IO::Socket::INET.
-
-=head1 CONSTRUCTOR
-
-=over 4
-
-=item new ( [ HOST ] [, OPTIONS ])
-
-This is the constructor for a new Net::NNTP object. C<HOST> is the
-name of the remote host to which a NNTP connection is required. If not
-given then it may be passed as the C<Host> option described below. If no host is passed
-then two environment variables are checked, first C<NNTPSERVER> then
-C<NEWSHOST>, then C<Net::Config> is checked, and if a host is not found
-then C<news> is used.
-
-C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
-Possible options are:
-
-B<Host> - NNTP host to connect to. It may be a single scalar, as defined for
-the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
-an array with hosts to try in turn. The L</host> method will return the value
-which was used to connect to the host.
-
-B<Timeout> - Maximum time, in seconds, to wait for a response from the
-NNTP server, a value of zero will cause all IO operations to block.
-(default: 120)
-
-B<Debug> - Enable the printing of debugging information to STDERR
-
-B<Reader> - If the remote server is INN then initially the connection
-will be to nnrpd, by default C<Net::NNTP> will issue a C<MODE READER> command
-so that the remote server becomes innd. If the C<Reader> option is given
-with a value of zero, then this command will not be sent and the
-connection will be left talking to nnrpd.
-
-B<LocalAddr> - If multiple IP addresses are present on the client host
-with a valid route to the destination, you can specify the address your
-C<Net::NNTP> connects from and this way override the operating system's
-pick.
-
-=back
-
-=head1 METHODS
-
-Unless otherwise stated all methods return either a I<true> or I<false>
-value, with I<true> meaning that the operation was a success. When a method
-states that it returns a value, failure will be returned as I<undef> or an
-empty list.
-
-C<Net::NNTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
-be used to send commands to the remote NNTP server in addition to the methods
-documented here.
-
-=over 4
-
-=item article ( [ MSGID|MSGNUM ], [FH] )
-
-Retrieve the header, a blank line, then the body (text) of the
-specified article.
-
-If C<FH> is specified then it is expected to be a valid filehandle
-and the result will be printed to it, on success a true value will be
-returned. If C<FH> is not specified then the return value, on success,
-will be a reference to an array containing the article requested, each
-entry in the array will contain one line of the article.
-
-If no arguments are passed then the current article in the currently
-selected newsgroup is fetched.
-
-C<MSGNUM> is a numeric id of an article in the current newsgroup, and
-will change the current article pointer. C<MSGID> is the message id of
-an article as shown in that article's header. It is anticipated that the
-client will obtain the C<MSGID> from a list provided by the C<newnews>
-command, from references contained within another article, or from the
-message-id provided in the response to some other commands.
-
-If there is an error then C<undef> will be returned.
-
-=item body ( [ MSGID|MSGNUM ], [FH] )
-
-Like C<article> but only fetches the body of the article.
-
-=item head ( [ MSGID|MSGNUM ], [FH] )
-
-Like C<article> but only fetches the headers for the article.
-
-=item articlefh ( [ MSGID|MSGNUM ] )
-
-=item bodyfh ( [ MSGID|MSGNUM ] )
-
-=item headfh ( [ MSGID|MSGNUM ] )
-
-These are similar to article(), body() and head(), but rather than
-returning the requested data directly, they return a tied filehandle
-from which to read the article.
-
-=item nntpstat ( [ MSGID|MSGNUM ] )
-
-The C<nntpstat> command is similar to the C<article> command except that no
-text is returned. When selecting by message number within a group,
-the C<nntpstat> command serves to set the "current article pointer" without
-sending text.
-
-Using the C<nntpstat> command to
-select by message-id is valid but of questionable value, since a
-selection by message-id does B<not> alter the "current article pointer".
-
-Returns the message-id of the "current article".
-
-=item group ( [ GROUP ] )
-
-Set and/or get the current group. If C<GROUP> is not given then information
-is returned on the current group.
-
-In a scalar context it returns the group name.
-
-In an array context the return value is a list containing, the number
-of articles in the group, the number of the first article, the number
-of the last article and the group name.
-
-=item ihave ( MSGID [, MESSAGE ])
-
-The C<ihave> command informs the server that the client has an article
-whose id is C<MSGID>. If the server desires a copy of that
-article, and C<MESSAGE> has been given the it will be sent.
-
-Returns I<true> if the server desires the article and C<MESSAGE> was
-successfully sent,if specified.
-
-If C<MESSAGE> is not specified then the message must be sent using the
-C<datasend> and C<dataend> methods from L<Net::Cmd>
-
-C<MESSAGE> can be either an array of lines or a reference to an array.
-
-=item last ()
-
-Set the "current article pointer" to the previous article in the current
-newsgroup.
-
-Returns the message-id of the article.
-
-=item date ()
-
-Returns the date on the remote server. This date will be in a UNIX time
-format (seconds since 1970)
-
-=item postok ()
-
-C<postok> will return I<true> if the servers initial response indicated
-that it will allow posting.
-
-=item authinfo ( USER, PASS )
-
-Authenticates to the server (using AUTHINFO USER / AUTHINFO PASS)
-using the supplied username and password. Please note that the
-password is sent in clear text to the server. This command should not
-be used with valuable passwords unless the connection to the server is
-somehow protected.
-
-=item list ()
-
-Obtain information about all the active newsgroups. The results is a reference
-to a hash where the key is a group name and each value is a reference to an
-array. The elements in this array are:- the last article number in the group,
-the first article number in the group and any information flags about the group.
-
-=item newgroups ( SINCE [, DISTRIBUTIONS ])
-
-C<SINCE> is a time value and C<DISTRIBUTIONS> is either a distribution
-pattern or a reference to a list of distribution patterns.
-The result is the same as C<list>, but the
-groups return will be limited to those created after C<SINCE> and, if
-specified, in one of the distribution areas in C<DISTRIBUTIONS>.
-
-=item newnews ( SINCE [, GROUPS [, DISTRIBUTIONS ]])
-
-C<SINCE> is a time value. C<GROUPS> is either a group pattern or a reference
-to a list of group patterns. C<DISTRIBUTIONS> is either a distribution
-pattern or a reference to a list of distribution patterns.
-
-Returns a reference to a list which contains the message-ids of all news posted
-after C<SINCE>, that are in a groups which matched C<GROUPS> and a
-distribution which matches C<DISTRIBUTIONS>.
-
-=item next ()
-
-Set the "current article pointer" to the next article in the current
-newsgroup.
-
-Returns the message-id of the article.
-
-=item post ( [ MESSAGE ] )
-
-Post a new article to the news server. If C<MESSAGE> is specified and posting
-is allowed then the message will be sent.
-
-If C<MESSAGE> is not specified then the message must be sent using the
-C<datasend> and C<dataend> methods from L<Net::Cmd>
-
-C<MESSAGE> can be either an array of lines or a reference to an array.
-
-The message, either sent via C<datasend> or as the C<MESSAGE>
-parameter, must be in the format as described by RFC822 and must
-contain From:, Newsgroups: and Subject: headers.
-
-=item postfh ()
-
-Post a new article to the news server using a tied filehandle. If
-posting is allowed, this method will return a tied filehandle that you
-can print() the contents of the article to be posted. You must
-explicitly close() the filehandle when you are finished posting the
-article, and the return value from the close() call will indicate
-whether the message was successfully posted.
-
-=item slave ()
-
-Tell the remote server that I am not a user client, but probably another
-news server.
-
-=item quit ()
-
-Quit the remote server and close the socket connection.
-
-=back
-
-=head2 Extension methods
-
-These methods use commands that are not part of the RFC977 documentation. Some
-servers may not support all of them.
-
-=over 4
-
-=item newsgroups ( [ PATTERN ] )
-
-Returns a reference to a hash where the keys are all the group names which
-match C<PATTERN>, or all of the groups if no pattern is specified, and
-each value contains the description text for the group.
-
-=item distributions ()
-
-Returns a reference to a hash where the keys are all the possible
-distribution names and the values are the distribution descriptions.
-
-=item subscriptions ()
-
-Returns a reference to a list which contains a list of groups which
-are recommended for a new user to subscribe to.
-
-=item overview_fmt ()
-
-Returns a reference to an array which contain the names of the fields returned
-by C<xover>.
-
-=item active_times ()
-
-Returns a reference to a hash where the keys are the group names and each
-value is a reference to an array containing the time the groups was created
-and an identifier, possibly an Email address, of the creator.
-
-=item active ( [ PATTERN ] )
-
-Similar to C<list> but only active groups that match the pattern are returned.
-C<PATTERN> can be a group pattern.
-
-=item xgtitle ( PATTERN )
-
-Returns a reference to a hash where the keys are all the group names which
-match C<PATTERN> and each value is the description text for the group.
-
-=item xhdr ( HEADER, MESSAGE-SPEC )
-
-Obtain the header field C<HEADER> for all the messages specified.
-
-The return value will be a reference
-to a hash where the keys are the message numbers and each value contains
-the text of the requested header for that message.
-
-=item xover ( MESSAGE-SPEC )
-
-The return value will be a reference
-to a hash where the keys are the message numbers and each value contains
-a reference to an array which contains the overview fields for that
-message.
-
-The names of the fields can be obtained by calling C<overview_fmt>.
-
-=item xpath ( MESSAGE-ID )
-
-Returns the path name to the file on the server which contains the specified
-message.
-
-=item xpat ( HEADER, PATTERN, MESSAGE-SPEC)
-
-The result is the same as C<xhdr> except the is will be restricted to
-headers where the text of the header matches C<PATTERN>
-
-=item xrover
-
-The XROVER command returns reference information for the article(s)
-specified.
-
-Returns a reference to a HASH where the keys are the message numbers and the
-values are the References: lines from the articles
-
-=item listgroup ( [ GROUP ] )
-
-Returns a reference to a list of all the active messages in C<GROUP>, or
-the current group if C<GROUP> is not specified.
-
-=item reader
-
-Tell the server that you are a reader and not another server.
-
-This is required by some servers. For example if you are connecting to
-an INN server and you have transfer permission your connection will
-be connected to the transfer daemon, not the NNTP daemon. Issuing
-this command will cause the transfer daemon to hand over control
-to the NNTP daemon.
-
-Some servers do not understand this command, but issuing it and ignoring
-the response is harmless.
-
-=back
-
-=head1 UNSUPPORTED
-
-The following NNTP command are unsupported by the package, and there are
-no plans to do so.
-
- AUTHINFO GENERIC
- XTHREAD
- XSEARCH
- XINDEX
-
-=head1 DEFINITIONS
-
-=over 4
-
-=item MESSAGE-SPEC
-
-C<MESSAGE-SPEC> is either a single message-id, a single message number, or
-a reference to a list of two message numbers.
-
-If C<MESSAGE-SPEC> is a reference to a list of two message numbers and the
-second number in a range is less than or equal to the first then the range
-represents all messages in the group after the first message number.
-
-B<NOTE> For compatibility reasons only with earlier versions of Net::NNTP
-a message spec can be passed as a list of two numbers, this is deprecated
-and a reference to the list should now be passed
-
-=item PATTERN
-
-The C<NNTP> protocol uses the C<WILDMAT> format for patterns.
-The WILDMAT format was first developed by Rich Salz based on
-the format used in the UNIX "find" command to articulate
-file names. It was developed to provide a uniform mechanism
-for matching patterns in the same manner that the UNIX shell
-matches filenames.
-
-Patterns are implicitly anchored at the
-beginning and end of each string when testing for a match.
-
-There are five pattern matching operations other than a strict
-one-to-one match between the pattern and the source to be
-checked for a match.
-
-The first is an asterisk C<*> to match any sequence of zero or more
-characters.
-
-The second is a question mark C<?> to match any single character. The
-third specifies a specific set of characters.
-
-The set is specified as a list of characters, or as a range of characters
-where the beginning and end of the range are separated by a minus (or dash)
-character, or as any combination of lists and ranges. The dash can
-also be included in the set as a character it if is the beginning
-or end of the set. This set is enclosed in square brackets. The
-close square bracket C<]> may be used in a set if it is the first
-character in the set.
-
-The fourth operation is the same as the
-logical not of the third operation and is specified the same
-way as the third with the addition of a caret character C<^> at
-the beginning of the test string just inside the open square
-bracket.
-
-The final operation uses the backslash character to
-invalidate the special meaning of an open square bracket C<[>,
-the asterisk, backslash or the question mark. Two backslashes in
-sequence will result in the evaluation of the backslash as a
-character with no special meaning.
-
-=over 4
-
-=item Examples
-
-=item C<[^]-]>
-
-matches any single character other than a close square
-bracket or a minus sign/dash.
-
-=item C<*bdc>
-
-matches any string that ends with the string "bdc"
-including the string "bdc" (without quotes).
-
-=item C<[0-9a-zA-Z]>
-
-matches any single printable alphanumeric ASCII character.
-
-=item C<a??d>
-
-matches any four character string which begins
-with a and ends with d.
-
-=back
-
-=back
-
-=head1 SEE ALSO
-
-L<Net::Cmd>
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-1997 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,333 +0,0 @@
-# Net::Netrc.pm
-#
-# Copyright (c) 1995-1998 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::Netrc;
-
-use Carp;
-use strict;
-use FileHandle;
-use vars qw($VERSION $TESTING);
-
-$VERSION = "2.14";
-
-my %netrc = ();
-
-
-sub _readrc {
- my $host = shift;
- my ($home, $file);
-
- if ($^O eq "MacOS") {
- $home = $ENV{HOME} || `pwd`;
- chomp($home);
- $file = ($home =~ /:$/ ? $home . "netrc" : $home . ":netrc");
- }
- else {
-
- # Some OS's don't have "getpwuid", so we default to $ENV{HOME}
- $home = eval { (getpwuid($>))[7] } || $ENV{HOME};
- $home ||= $ENV{HOMEDRIVE} . ($ENV{HOMEPATH} || '') if defined $ENV{HOMEDRIVE};
- if (-e $home . "/.netrc") {
- $file = $home . "/.netrc";
- }
- elsif (-e $home . "/_netrc") {
- $file = $home . "/_netrc";
- }
- else {
- return unless $TESTING;
- }
- }
-
- my ($login, $pass, $acct) = (undef, undef, undef);
- my $fh;
- local $_;
-
- $netrc{default} = undef;
-
- # OS/2 and Win32 do not handle stat in a way compatible with this check :-(
- unless ($^O eq 'os2'
- || $^O eq 'MSWin32'
- || $^O eq 'MacOS'
- || $^O =~ /^cygwin/)
- {
- my @stat = stat($file);
-
- if (@stat) {
- if ($stat[2] & 077) {
- carp "Bad permissions: $file";
- return;
- }
- if ($stat[4] != $<) {
- carp "Not owner: $file";
- return;
- }
- }
- }
-
- if ($fh = FileHandle->new($file, "r")) {
- my ($mach, $macdef, $tok, @tok) = (0, 0);
-
- while (<$fh>) {
- undef $macdef if /\A\n\Z/;
-
- if ($macdef) {
- push(@$macdef, $_);
- next;
- }
-
- s/^\s*//;
- chomp;
-
- while (length && s/^("((?:[^"]+|\\.)*)"|((?:[^\\\s]+|\\.)*))\s*//) {
- (my $tok = $+) =~ s/\\(.)/$1/g;
- push(@tok, $tok);
- }
-
- TOKEN:
- while (@tok) {
- if ($tok[0] eq "default") {
- shift(@tok);
- $mach = bless {};
- $netrc{default} = [$mach];
-
- next TOKEN;
- }
-
- last TOKEN
- unless @tok > 1;
-
- $tok = shift(@tok);
-
- if ($tok eq "machine") {
- my $host = shift @tok;
- $mach = bless {machine => $host};
-
- $netrc{$host} = []
- unless exists($netrc{$host});
- push(@{$netrc{$host}}, $mach);
- }
- elsif ($tok =~ /^(login|password|account)$/) {
- next TOKEN unless $mach;
- my $value = shift @tok;
-
- # Following line added by rmerrell to remove '/' escape char in .netrc
- $value =~ s/\/\\/\\/g;
- $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} = [];
- }
- }
- }
- $fh->close();
- }
-}
-
-
-sub lookup {
- my ($pkg, $mach, $login) = @_;
-
- _readrc()
- unless exists $netrc{default};
-
- $mach ||= 'default';
- undef $login
- if $mach eq 'default';
-
- if (exists $netrc{$mach}) {
- if (defined $login) {
- my $m;
- foreach $m (@{$netrc{$mach}}) {
- return $m
- if (exists $m->{login} && $m->{login} eq $login);
- }
- return undef;
- }
- return $netrc{$mach}->[0];
- }
-
- return $netrc{default}->[0]
- if defined $netrc{default};
-
- return undef;
-}
-
-
-sub login {
- my $me = shift;
-
- exists $me->{login}
- ? $me->{login}
- : undef;
-}
-
-
-sub account {
- my $me = shift;
-
- exists $me->{account}
- ? $me->{account}
- : undef;
-}
-
-
-sub password {
- my $me = shift;
-
- exists $me->{password}
- ? $me->{password}
- : undef;
-}
-
-
-sub lpa {
- my $me = shift;
- ($me->login, $me->password, $me->account);
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::Netrc - OO interface to users netrc file
-
-=head1 SYNOPSIS
-
- use Net::Netrc;
-
- $mach = Net::Netrc->lookup('some.machine');
- $login = $mach->login;
- ($login, $password, $account) = $mach->lpa;
-
-=head1 DESCRIPTION
-
-C<Net::Netrc> is a class implementing a simple interface to the .netrc file
-used as by the ftp program.
-
-C<Net::Netrc> also implements security checks just like the ftp program,
-these checks are, first that the .netrc file must be owned by the user and
-second the ownership permissions should be such that only the owner has
-read and write access. If these conditions are not met then a warning is
-output and the .netrc file is not read.
-
-=head1 THE .netrc FILE
-
-The .netrc file contains login and initialization information used by the
-auto-login process. It resides in the user's home directory. The following
-tokens are recognized; they may be separated by spaces, tabs, or new-lines:
-
-=over 4
-
-=item machine name
-
-Identify a remote machine name. The auto-login process searches
-the .netrc file for a machine token that matches the remote machine
-specified. Once a match is made, the subsequent .netrc tokens
-are processed, stopping when the end of file is reached or an-
-other machine or a default token is encountered.
-
-=item default
-
-This is the same as machine name except that default matches
-any name. There can be only one default token, and it must be
-after all machine tokens. This is normally used as:
-
- default login anonymous password user@site
-
-thereby giving the user automatic anonymous login to machines
-not specified in .netrc.
-
-=item login name
-
-Identify a user on the remote machine. If this token is present,
-the auto-login process will initiate a login using the
-specified name.
-
-=item password string
-
-Supply a password. If this token is present, the auto-login
-process will supply the specified string if the remote server
-requires a password as part of the login process.
-
-=item account string
-
-Supply an additional account password. If this token is present,
-the auto-login process will supply the specified string
-if the remote server requires an additional account password.
-
-=item macdef name
-
-Define a macro. C<Net::Netrc> only parses this field to be compatible
-with I<ftp>.
-
-=back
-
-=head1 CONSTRUCTOR
-
-The constructor for a C<Net::Netrc> object is not called new as it does not
-really create a new object. But instead is called C<lookup> as this is
-essentially what it does.
-
-=over 4
-
-=item lookup ( MACHINE [, LOGIN ])
-
-Lookup and return a reference to the entry for C<MACHINE>. If C<LOGIN> is given
-then the entry returned will have the given login. If C<LOGIN> is not given then
-the first entry in the .netrc file for C<MACHINE> will be returned.
-
-If a matching entry cannot be found, and a default entry exists, then a
-reference to the default entry is returned.
-
-If there is no matching entry found and there is no default defined, or
-no .netrc file is found, then C<undef> is returned.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item login ()
-
-Return the login id for the netrc entry
-
-=item password ()
-
-Return the password for the netrc entry
-
-=item account ()
-
-Return the account information for the netrc entry
-
-=item lpa ()
-
-Return a list of login, password and account information for the netrc entry
-
-=back
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 SEE ALSO
-
-L<Net::Netrc>
-L<Net::Cmd>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-1998 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,744 +0,0 @@
-# Net::POP3.pm
-#
-# Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::POP3;
-
-use strict;
-use IO::Socket;
-use vars qw(@ISA $VERSION $debug);
-use Net::Cmd;
-use Carp;
-use Net::Config;
-
-$VERSION = "2.31";
-
-@ISA = qw(Net::Cmd IO::Socket::INET);
-
-
-sub new {
- my $self = shift;
- my $type = ref($self) || $self;
- my ($host, %arg);
- if (@_ % 2) {
- $host = shift;
- %arg = @_;
- }
- else {
- %arg = @_;
- $host = delete $arg{Host};
- }
- my $hosts = defined $host ? [$host] : $NetConfig{pop3_hosts};
- my $obj;
- my @localport = exists $arg{ResvPort} ? (LocalPort => $arg{ResvPort}) : ();
-
- my $h;
- foreach $h (@{$hosts}) {
- $obj = $type->SUPER::new(
- PeerAddr => ($host = $h),
- PeerPort => $arg{Port} || 'pop3(110)',
- Proto => 'tcp',
- @localport,
- Timeout => defined $arg{Timeout}
- ? $arg{Timeout}
- : 120
- )
- and last;
- }
-
- return undef
- unless defined $obj;
-
- ${*$obj}{'net_pop3_host'} = $host;
-
- $obj->autoflush(1);
- $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
-
- unless ($obj->response() == CMD_OK) {
- $obj->close();
- return undef;
- }
-
- ${*$obj}{'net_pop3_banner'} = $obj->message;
-
- $obj;
-}
-
-
-sub host {
- my $me = shift;
- ${*$me}{'net_pop3_host'};
-}
-
-##
-## We don't want people sending me their passwords when they report problems
-## now do we :-)
-##
-
-
-sub debug_text { $_[2] =~ /^(pass|rpop)/i ? "$1 ....\n" : $_[2]; }
-
-
-sub login {
- @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->login( USER, PASS )';
- my ($me, $user, $pass) = @_;
-
- if (@_ <= 2) {
- ($user, $pass) = $me->_lookup_credentials($user);
- }
-
- $me->user($user)
- and $me->pass($pass);
-}
-
-
-sub apop {
- @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->apop( USER, PASS )';
- my ($me, $user, $pass) = @_;
- my $banner;
- my $md;
-
- if (eval { local $SIG{__DIE__}; require Digest::MD5 }) {
- $md = Digest::MD5->new();
- }
- elsif (eval { local $SIG{__DIE__}; require MD5 }) {
- $md = MD5->new();
- }
- else {
- carp "You need to install Digest::MD5 or MD5 to use the APOP command";
- return undef;
- }
-
- return undef
- unless ($banner = (${*$me}{'net_pop3_banner'} =~ /(<.*>)/)[0]);
-
- if (@_ <= 2) {
- ($user, $pass) = $me->_lookup_credentials($user);
- }
-
- $md->add($banner, $pass);
-
- return undef
- unless ($me->_APOP($user, $md->hexdigest));
-
- $me->_get_mailbox_count();
-}
-
-
-sub user {
- @_ == 2 or croak 'usage: $pop3->user( USER )';
- $_[0]->_USER($_[1]) ? 1 : undef;
-}
-
-
-sub pass {
- @_ == 2 or croak 'usage: $pop3->pass( PASS )';
-
- my ($me, $pass) = @_;
-
- return undef
- unless ($me->_PASS($pass));
-
- $me->_get_mailbox_count();
-}
-
-
-sub reset {
- @_ == 1 or croak 'usage: $obj->reset()';
-
- my $me = shift;
-
- return 0
- unless ($me->_RSET);
-
- if (defined ${*$me}{'net_pop3_mail'}) {
- local $_;
- foreach (@{${*$me}{'net_pop3_mail'}}) {
- delete $_->{'net_pop3_deleted'};
- }
- }
-}
-
-
-sub last {
- @_ == 1 or croak 'usage: $obj->last()';
-
- return undef
- unless $_[0]->_LAST && $_[0]->message =~ /(\d+)/;
-
- return $1;
-}
-
-
-sub top {
- @_ == 2 || @_ == 3 or croak 'usage: $pop3->top( MSGNUM [, NUMLINES ])';
- my $me = shift;
-
- return undef
- unless $me->_TOP($_[0], $_[1] || 0);
-
- $me->read_until_dot;
-}
-
-
-sub popstat {
- @_ == 1 or croak 'usage: $pop3->popstat()';
- my $me = shift;
-
- return ()
- unless $me->_STAT && $me->message =~ /(\d+)\D+(\d+)/;
-
- ($1 || 0, $2 || 0);
-}
-
-
-sub list {
- @_ == 1 || @_ == 2 or croak 'usage: $pop3->list( [ MSGNUM ] )';
- my $me = shift;
-
- return undef
- unless $me->_LIST(@_);
-
- if (@_) {
- $me->message =~ /\d+\D+(\d+)/;
- return $1 || undef;
- }
-
- my $info = $me->read_until_dot
- or return undef;
-
- my %hash = map { (/(\d+)\D+(\d+)/) } @$info;
-
- return \%hash;
-}
-
-
-sub get {
- @_ == 2 or @_ == 3 or croak 'usage: $pop3->get( MSGNUM [, FH ])';
- my $me = shift;
-
- return undef
- unless $me->_RETR(shift);
-
- $me->read_until_dot(@_);
-}
-
-
-sub getfh {
- @_ == 2 or croak 'usage: $pop3->getfh( MSGNUM )';
- my $me = shift;
-
- return unless $me->_RETR(shift);
- return $me->tied_fh;
-}
-
-
-sub delete {
- @_ == 2 or croak 'usage: $pop3->delete( MSGNUM )';
- my $me = shift;
- return 0 unless $me->_DELE(@_);
- ${*$me}{'net_pop3_deleted'} = 1;
-}
-
-
-sub uidl {
- @_ == 1 || @_ == 2 or croak 'usage: $pop3->uidl( [ MSGNUM ] )';
- my $me = shift;
- my $uidl;
-
- $me->_UIDL(@_)
- or return undef;
- if (@_) {
- $uidl = ($me->message =~ /\d+\s+([\041-\176]+)/)[0];
- }
- else {
- my $ref = $me->read_until_dot
- or return undef;
- my $ln;
- $uidl = {};
- foreach $ln (@$ref) {
- my ($msg, $uid) = $ln =~ /^\s*(\d+)\s+([\041-\176]+)/;
- $uidl->{$msg} = $uid;
- }
- }
- return $uidl;
-}
-
-
-sub ping {
- @_ == 2 or croak 'usage: $pop3->ping( USER )';
- my $me = shift;
-
- return () unless $me->_PING(@_) && $me->message =~ /(\d+)\D+(\d+)/;
-
- ($1 || 0, $2 || 0);
-}
-
-
-sub _lookup_credentials {
- my ($me, $user) = @_;
-
- require Net::Netrc;
-
- $user ||= eval { local $SIG{__DIE__}; (getpwuid($>))[0] }
- || $ENV{NAME}
- || $ENV{USER}
- || $ENV{LOGNAME};
-
- my $m = Net::Netrc->lookup(${*$me}{'net_pop3_host'}, $user);
- $m ||= Net::Netrc->lookup(${*$me}{'net_pop3_host'});
-
- my $pass = $m
- ? $m->password || ""
- : "";
-
- ($user, $pass);
-}
-
-
-sub _get_mailbox_count {
- my ($me) = @_;
- my $ret = ${*$me}{'net_pop3_count'} =
- ($me->message =~ /(\d+)\s+message/io) ? $1 : ($me->popstat)[0];
-
- $ret ? $ret : "0E0";
-}
-
-
-sub _STAT { shift->command('STAT' )->response() == CMD_OK }
-sub _LIST { shift->command('LIST', @_)->response() == CMD_OK }
-sub _RETR { shift->command('RETR', $_[0])->response() == CMD_OK }
-sub _DELE { shift->command('DELE', $_[0])->response() == CMD_OK }
-sub _NOOP { shift->command('NOOP' )->response() == CMD_OK }
-sub _RSET { shift->command('RSET' )->response() == CMD_OK }
-sub _QUIT { shift->command('QUIT' )->response() == CMD_OK }
-sub _TOP { shift->command( 'TOP', @_)->response() == CMD_OK }
-sub _UIDL { shift->command('UIDL', @_)->response() == CMD_OK }
-sub _USER { shift->command('USER', $_[0])->response() == CMD_OK }
-sub _PASS { shift->command('PASS', $_[0])->response() == CMD_OK }
-sub _APOP { shift->command('APOP', @_)->response() == CMD_OK }
-sub _PING { shift->command('PING', $_[0])->response() == CMD_OK }
-sub _RPOP { shift->command('RPOP', $_[0])->response() == CMD_OK }
-sub _LAST { shift->command('LAST' )->response() == CMD_OK }
-sub _CAPA { shift->command('CAPA' )->response() == CMD_OK }
-
-
-sub quit {
- my $me = shift;
-
- $me->_QUIT;
- $me->close;
-}
-
-
-sub DESTROY {
- my $me = shift;
-
- if (defined fileno($me) and ${*$me}{'net_pop3_deleted'}) {
- $me->reset;
- $me->quit;
- }
-}
-
-##
-## POP3 has weird responses, so we emulate them to look the same :-)
-##
-
-
-sub response {
- my $cmd = shift;
- my $str = $cmd->getline() or return undef;
- my $code = "500";
-
- $cmd->debug_print(0, $str)
- if ($cmd->debug);
-
- if ($str =~ s/^\+OK\s*//io) {
- $code = "200";
- }
- elsif ($str =~ s/^\+\s*//io) {
- $code = "300";
- }
- else {
- $str =~ s/^-ERR\s*//io;
- }
-
- ${*$cmd}{'net_cmd_resp'} = [$str];
- ${*$cmd}{'net_cmd_code'} = $code;
-
- substr($code, 0, 1);
-}
-
-
-sub capa {
- my $this = shift;
- my ($capa, %capabilities);
-
- # Fake a capability here
- $capabilities{APOP} = '' if ($this->banner() =~ /<.*>/);
-
- if ($this->_CAPA()) {
- $capabilities{CAPA} = 1;
- $capa = $this->read_until_dot();
- %capabilities = (%capabilities, map {/^\s*(\S+)\s*(.*)/} @$capa);
- }
- else {
-
- # Check AUTH for SASL capabilities
- if ($this->command('AUTH')->response() == CMD_OK) {
- my $mechanism = $this->read_until_dot();
- $capabilities{SASL} = join " ", map {m/([A-Z0-9_-]+)/} @{$mechanism};
- }
- }
-
- return ${*$this}{'net_pop3e_capabilities'} = \%capabilities;
-}
-
-
-sub capabilities {
- my $this = shift;
-
- ${*$this}{'net_pop3e_capabilities'} || $this->capa;
-}
-
-
-sub auth {
- my ($self, $username, $password) = @_;
-
- eval {
- require MIME::Base64;
- require Authen::SASL;
- } or $self->set_status(500, ["Need MIME::Base64 and Authen::SASL todo auth"]), return 0;
-
- my $capa = $self->capa;
- my $mechanisms = $capa->{SASL} || 'CRAM-MD5';
-
- my $sasl;
-
- if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) {
- $sasl = $username;
- my $user_mech = $sasl->mechanism || '';
- my @user_mech = split(/\s+/, $user_mech);
- my %user_mech;
- @user_mech{@user_mech} = ();
-
- my @server_mech = split(/\s+/, $mechanisms);
- my @mech = @user_mech
- ? grep { exists $user_mech{$_} } @server_mech
- : @server_mech;
- unless (@mech) {
- $self->set_status(
- 500,
- [ 'Client SASL mechanisms (',
- join(', ', @user_mech),
- ') do not match the SASL mechnism the server announces (',
- join(', ', @server_mech), ')',
- ]
- );
- return 0;
- }
-
- $sasl->mechanism(join(" ", @mech));
- }
- else {
- die "auth(username, password)" if not length $username;
- $sasl = Authen::SASL->new(
- mechanism => $mechanisms,
- callback => {
- user => $username,
- pass => $password,
- authname => $username,
- }
- );
- }
-
- # We should probably allow the user to pass the host, but I don't
- # currently know and SASL mechanisms that are used by smtp that need it
- my ($hostname) = split /:/, ${*$self}{'net_pop3_host'};
- my $client = eval { $sasl->client_new('pop', $hostname, 0) };
-
- unless ($client) {
- my $mech = $sasl->mechanism;
- $self->set_status(
- 500,
- [ " Authen::SASL failure: $@",
- '(please check if your local Authen::SASL installation',
- "supports mechanism '$mech'"
- ]
- );
- return 0;
- }
-
- my ($token) = $client->client_start
- or do {
- my $mech = $client->mechanism;
- $self->set_status(
- 500,
- [ ' Authen::SASL failure: $client->client_start ',
- "mechanism '$mech' hostname #$hostname#",
- $client->error
- ]
- );
- return 0;
- };
-
- # We don't support sasl mechanisms that encrypt the socket traffic.
- # todo that we would really need to change the ISA hierarchy
- # so we don't inherit from IO::Socket, but instead hold it in an attribute
-
- my @cmd = ("AUTH", $client->mechanism);
- my $code;
-
- push @cmd, MIME::Base64::encode_base64($token, '')
- if defined $token and length $token;
-
- while (($code = $self->command(@cmd)->response()) == CMD_MORE) {
-
- my ($token) = $client->client_step(MIME::Base64::decode_base64(($self->message)[0])) or do {
- $self->set_status(
- 500,
- [ ' Authen::SASL failure: $client->client_step ',
- "mechanism '", $client->mechanism, " hostname #$hostname#, ",
- $client->error
- ]
- );
- return 0;
- };
-
- @cmd = (MIME::Base64::encode_base64(defined $token ? $token : '', ''));
- }
-
- $code == CMD_OK;
-}
-
-
-sub banner {
- my $this = shift;
-
- return ${*$this}{'net_pop3_banner'};
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::POP3 - Post Office Protocol 3 Client class (RFC1939)
-
-=head1 SYNOPSIS
-
- use Net::POP3;
-
- # Constructors
- $pop = Net::POP3->new('pop3host');
- $pop = Net::POP3->new('pop3host', Timeout => 60);
-
- if ($pop->login($username, $password) > 0) {
- my $msgnums = $pop->list; # hashref of msgnum => size
- foreach my $msgnum (keys %$msgnums) {
- my $msg = $pop->get($msgnum);
- print @$msg;
- $pop->delete($msgnum);
- }
- }
-
- $pop->quit;
-
-=head1 DESCRIPTION
-
-This module implements a client interface to the POP3 protocol, enabling
-a perl5 application to talk to POP3 servers. This documentation assumes
-that you are familiar with the POP3 protocol described in RFC1939.
-
-A new Net::POP3 object must be created with the I<new> method. Once
-this has been done, all POP3 commands are accessed via method calls
-on the object.
-
-The Net::POP3 class is a subclass of Net::Cmd and IO::Socket::INET.
-
-=head1 CONSTRUCTOR
-
-=over 4
-
-=item new ( [ HOST ] [, OPTIONS ] )
-
-This is the constructor for a new Net::POP3 object. C<HOST> is the
-name of the remote host to which an POP3 connection is required.
-
-C<HOST> is optional. If C<HOST> is not given then it may instead be
-passed as the C<Host> option described below. If neither is given then
-the C<POP3_Hosts> specified in C<Net::Config> will be used.
-
-C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
-Possible options are:
-
-B<Host> - POP3 host to connect to. It may be a single scalar, as defined for
-the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
-an array with hosts to try in turn. The L</host> method will return the value
-which was used to connect to the host.
-
-B<ResvPort> - If given then the socket for the C<Net::POP3> object
-will be bound to the local port given using C<bind> when the socket is
-created.
-
-B<Timeout> - Maximum time, in seconds, to wait for a response from the
-POP3 server (default: 120)
-
-B<Debug> - Enable debugging information
-
-=back
-
-=head1 METHODS
-
-Unless otherwise stated all methods return either a I<true> or I<false>
-value, with I<true> meaning that the operation was a success. When a method
-states that it returns a value, failure will be returned as I<undef> or an
-empty list.
-
-C<Net::POP3> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
-be used to send commands to the remote POP3 server in addition to the methods
-documented here.
-
-=over 4
-
-=item auth ( USERNAME, PASSWORD )
-
-Attempt SASL authentication.
-
-=item user ( USER )
-
-Send the USER command.
-
-=item pass ( PASS )
-
-Send the PASS command. Returns the number of messages in the mailbox.
-
-=item login ( [ USER [, PASS ]] )
-
-Send both the USER and PASS commands. If C<PASS> is not given the
-C<Net::POP3> uses C<Net::Netrc> to lookup the password using the host
-and username. If the username is not specified then the current user name
-will be used.
-
-Returns the number of messages in the mailbox. However if there are no
-messages on the server the string C<"0E0"> will be returned. This is
-will give a true value in a boolean context, but zero in a numeric context.
-
-If there was an error authenticating the user then I<undef> will be returned.
-
-=item apop ( [ USER [, PASS ]] )
-
-Authenticate with the server identifying as C<USER> with password C<PASS>.
-Similar to L</login>, but the password is not sent in clear text.
-
-To use this method you must have the Digest::MD5 or the MD5 module installed,
-otherwise this method will return I<undef>.
-
-=item banner ()
-
-Return the sever's connection banner
-
-=item capa ()
-
-Return a reference to a hash of the capabilities of the server. APOP
-is added as a pseudo capability. Note that I've been unable to
-find a list of the standard capability values, and some appear to
-be multi-word and some are not. We make an attempt at intelligently
-parsing them, but it may not be correct.
-
-=item capabilities ()
-
-Just like capa, but only uses a cache from the last time we asked
-the server, so as to avoid asking more than once.
-
-=item top ( MSGNUM [, NUMLINES ] )
-
-Get the header and the first C<NUMLINES> of the body for the message
-C<MSGNUM>. Returns a reference to an array which contains the lines of text
-read from the server.
-
-=item list ( [ MSGNUM ] )
-
-If called with an argument the C<list> returns the size of the message
-in octets.
-
-If called without arguments a reference to a hash is returned. The
-keys will be the C<MSGNUM>'s of all undeleted messages and the values will
-be their size in octets.
-
-=item get ( MSGNUM [, FH ] )
-
-Get the message C<MSGNUM> from the remote mailbox. If C<FH> is not given
-then get returns a reference to an array which contains the lines of
-text read from the server. If C<FH> is given then the lines returned
-from the server are printed to the filehandle C<FH>.
-
-=item getfh ( MSGNUM )
-
-As per get(), but returns a tied filehandle. Reading from this
-filehandle returns the requested message. The filehandle will return
-EOF at the end of the message and should not be reused.
-
-=item last ()
-
-Returns the highest C<MSGNUM> of all the messages accessed.
-
-=item popstat ()
-
-Returns a list of two elements. These are the number of undeleted
-elements and the size of the mbox in octets.
-
-=item ping ( USER )
-
-Returns a list of two elements. These are the number of new messages
-and the total number of messages for C<USER>.
-
-=item uidl ( [ MSGNUM ] )
-
-Returns a unique identifier for C<MSGNUM> if given. If C<MSGNUM> is not
-given C<uidl> returns a reference to a hash where the keys are the
-message numbers and the values are the unique identifiers.
-
-=item delete ( MSGNUM )
-
-Mark message C<MSGNUM> to be deleted from the remote mailbox. All messages
-that are marked to be deleted will be removed from the remote mailbox
-when the server connection closed.
-
-=item reset ()
-
-Reset the status of the remote POP3 server. This includes resetting the
-status of all messages to not be deleted.
-
-=item quit ()
-
-Quit and close the connection to the remote POP3 server. Any messages marked
-as deleted will be deleted from the remote mailbox.
-
-=back
-
-=head1 NOTES
-
-If a C<Net::POP3> object goes out of scope before C<quit> method is called
-then the C<reset> method will called before the connection is closed. This
-means that any messages marked to be deleted will not be.
-
-=head1 SEE ALSO
-
-L<Net::Netrc>,
-L<Net::Cmd>
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-2003 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,883 +0,0 @@
-# Net::SMTP.pm
-#
-# Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::SMTP;
-
-require 5.001;
-
-use strict;
-use vars qw($VERSION @ISA);
-use Socket 1.3;
-use Carp;
-use IO::Socket;
-use Net::Cmd;
-use Net::Config;
-
-$VERSION = "2.34";
-
-@ISA = qw(Net::Cmd IO::Socket::INET);
-
-
-sub new {
- my $self = shift;
- my $type = ref($self) || $self;
- my ($host, %arg);
- if (@_ % 2) {
- $host = shift;
- %arg = @_;
- }
- else {
- %arg = @_;
- $host = delete $arg{Host};
- }
- my $hosts = defined $host ? $host : $NetConfig{smtp_hosts};
- my $obj;
-
- my $h;
- foreach $h (@{ref($hosts) ? $hosts : [$hosts]}) {
- $obj = $type->SUPER::new(
- PeerAddr => ($host = $h),
- PeerPort => $arg{Port} || 'smtp(25)',
- LocalAddr => $arg{LocalAddr},
- LocalPort => $arg{LocalPort},
- Proto => 'tcp',
- Timeout => defined $arg{Timeout}
- ? $arg{Timeout}
- : 120
- )
- and last;
- }
-
- return undef
- unless defined $obj;
-
- $obj->autoflush(1);
-
- $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
-
- unless ($obj->response() == CMD_OK) {
- my $err = ref($obj) . ": " . $obj->code . " " . $obj->message;
- $obj->close();
- $@ = $err;
- return undef;
- }
-
- ${*$obj}{'net_smtp_exact_addr'} = $arg{ExactAddresses};
- ${*$obj}{'net_smtp_host'} = $host;
-
- (${*$obj}{'net_smtp_banner'}) = $obj->message;
- (${*$obj}{'net_smtp_domain'}) = $obj->message =~ /\A\s*(\S+)/;
-
- unless ($obj->hello($arg{Hello} || "")) {
- my $err = ref($obj) . ": " . $obj->code . " " . $obj->message;
- $obj->close();
- $@ = $err;
- return undef;
- }
-
- $obj;
-}
-
-
-sub host {
- my $me = shift;
- ${*$me}{'net_smtp_host'};
-}
-
-##
-## User interface methods
-##
-
-
-sub banner {
- my $me = shift;
-
- return ${*$me}{'net_smtp_banner'} || undef;
-}
-
-
-sub domain {
- my $me = shift;
-
- return ${*$me}{'net_smtp_domain'} || undef;
-}
-
-
-sub etrn {
- my $self = shift;
- defined($self->supports('ETRN', 500, ["Command unknown: 'ETRN'"]))
- && $self->_ETRN(@_);
-}
-
-
-sub auth {
- my ($self, $username, $password) = @_;
-
- eval {
- require MIME::Base64;
- require Authen::SASL;
- } or $self->set_status(500, ["Need MIME::Base64 and Authen::SASL todo auth"]), return 0;
-
- my $mechanisms = $self->supports('AUTH', 500, ["Command unknown: 'AUTH'"]);
- return unless defined $mechanisms;
-
- my $sasl;
-
- if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) {
- $sasl = $username;
- $sasl->mechanism($mechanisms);
- }
- else {
- die "auth(username, password)" if not length $username;
- $sasl = Authen::SASL->new(
- mechanism => $mechanisms,
- callback => {
- user => $username,
- pass => $password,
- authname => $username,
- }
- );
- }
-
- # We should probably allow the user to pass the host, but I don't
- # currently know and SASL mechanisms that are used by smtp that need it
- my $client = $sasl->client_new('smtp', ${*$self}{'net_smtp_host'}, 0);
- my $str = $client->client_start;
-
- # We don't support sasl mechanisms that encrypt the socket traffic.
- # todo that we would really need to change the ISA hierarchy
- # so we don't inherit from IO::Socket, but instead hold it in an attribute
-
- my @cmd = ("AUTH", $client->mechanism);
- my $code;
-
- push @cmd, MIME::Base64::encode_base64($str, '')
- if defined $str and length $str;
-
- while (($code = $self->command(@cmd)->response()) == CMD_MORE) {
- @cmd = (
- MIME::Base64::encode_base64(
- $client->client_step(MIME::Base64::decode_base64(($self->message)[0])), ''
- )
- );
- }
-
- $code == CMD_OK;
-}
-
-
-sub hello {
- my $me = shift;
- my $domain = shift || "localhost.localdomain";
- my $ok = $me->_EHLO($domain);
- my @msg = $me->message;
-
- if ($ok) {
- my $h = ${*$me}{'net_smtp_esmtp'} = {};
- my $ln;
- foreach $ln (@msg) {
- $h->{uc $1} = $2
- if $ln =~ /([-\w]+)\b[= \t]*([^\n]*)/;
- }
- }
- elsif ($me->status == CMD_ERROR) {
- @msg = $me->message
- if $ok = $me->_HELO($domain);
- }
-
- return undef unless $ok;
-
- $msg[0] =~ /\A\s*(\S+)/;
- return ($1 || " ");
-}
-
-
-sub supports {
- my $self = shift;
- my $cmd = uc shift;
- return ${*$self}{'net_smtp_esmtp'}->{$cmd}
- if exists ${*$self}{'net_smtp_esmtp'}->{$cmd};
- $self->set_status(@_)
- if @_;
- return;
-}
-
-
-sub _addr {
- my $self = shift;
- my $addr = shift;
- $addr = "" unless defined $addr;
-
- if (${*$self}{'net_smtp_exact_addr'}) {
- return $1 if $addr =~ /^\s*(<.*>)\s*$/s;
- }
- else {
- return $1 if $addr =~ /(<[^>]*>)/;
- $addr =~ s/^\s+|\s+$//sg;
- }
-
- "<$addr>";
-}
-
-
-sub mail {
- my $me = shift;
- my $addr = _addr($me, shift);
- my $opts = "";
-
- if (@_) {
- my %opt = @_;
- my ($k, $v);
-
- if (exists ${*$me}{'net_smtp_esmtp'}) {
- my $esmtp = ${*$me}{'net_smtp_esmtp'};
-
- if (defined($v = delete $opt{Size})) {
- if (exists $esmtp->{SIZE}) {
- $opts .= sprintf " SIZE=%d", $v + 0;
- }
- else {
- carp 'Net::SMTP::mail: SIZE option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{Return})) {
- if (exists $esmtp->{DSN}) {
- $opts .= " RET=" . ((uc($v) eq "FULL") ? "FULL" : "HDRS");
- }
- else {
- carp 'Net::SMTP::mail: DSN option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{Bits})) {
- if ($v eq "8") {
- if (exists $esmtp->{'8BITMIME'}) {
- $opts .= " BODY=8BITMIME";
- }
- else {
- carp 'Net::SMTP::mail: 8BITMIME option not supported by host';
- }
- }
- elsif ($v eq "binary") {
- if (exists $esmtp->{'BINARYMIME'} && exists $esmtp->{'CHUNKING'}) {
- $opts .= " BODY=BINARYMIME";
- ${*$me}{'net_smtp_chunking'} = 1;
- }
- else {
- carp 'Net::SMTP::mail: BINARYMIME option not supported by host';
- }
- }
- elsif (exists $esmtp->{'8BITMIME'} or exists $esmtp->{'BINARYMIME'}) {
- $opts .= " BODY=7BIT";
- }
- else {
- carp 'Net::SMTP::mail: 8BITMIME and BINARYMIME options not supported by host';
- }
- }
-
- if (defined($v = delete $opt{Transaction})) {
- if (exists $esmtp->{CHECKPOINT}) {
- $opts .= " TRANSID=" . _addr($me, $v);
- }
- else {
- carp 'Net::SMTP::mail: CHECKPOINT option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{Envelope})) {
- if (exists $esmtp->{DSN}) {
- $v =~ s/([^\041-\176]|=|\+)/sprintf "+%02X", ord($1)/sge;
- $opts .= " ENVID=$v";
- }
- else {
- carp 'Net::SMTP::mail: DSN option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{ENVID})) {
-
- # expected to be in a format as required by RFC 3461, xtext-encoded
- if (exists $esmtp->{DSN}) {
- $opts .= " ENVID=$v";
- }
- else {
- carp 'Net::SMTP::mail: DSN option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{AUTH})) {
-
- # expected to be in a format as required by RFC 2554,
- # rfc2821-quoted and xtext-encoded, or <>
- if (exists $esmtp->{AUTH}) {
- $v = '<>' if !defined($v) || $v eq '';
- $opts .= " AUTH=$v";
- }
- else {
- carp 'Net::SMTP::mail: AUTH option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{XVERP})) {
- if (exists $esmtp->{'XVERP'}) {
- $opts .= " XVERP";
- }
- else {
- carp 'Net::SMTP::mail: XVERP option not supported by host';
- }
- }
-
- carp 'Net::SMTP::recipient: unknown option(s) ' . join(" ", keys %opt) . ' - ignored'
- if scalar keys %opt;
- }
- else {
- carp 'Net::SMTP::mail: ESMTP not supported by host - options discarded :-(';
- }
- }
-
- $me->_MAIL("FROM:" . $addr . $opts);
-}
-
-
-sub send { my $me = shift; $me->_SEND("FROM:" . _addr($me, $_[0])) }
-sub send_or_mail { my $me = shift; $me->_SOML("FROM:" . _addr($me, $_[0])) }
-sub send_and_mail { my $me = shift; $me->_SAML("FROM:" . _addr($me, $_[0])) }
-
-
-sub reset {
- my $me = shift;
-
- $me->dataend()
- if (exists ${*$me}{'net_smtp_lastch'});
-
- $me->_RSET();
-}
-
-
-sub recipient {
- my $smtp = shift;
- my $opts = "";
- my $skip_bad = 0;
-
- if (@_ && ref($_[-1])) {
- my %opt = %{pop(@_)};
- my $v;
-
- $skip_bad = delete $opt{'SkipBad'};
-
- if (exists ${*$smtp}{'net_smtp_esmtp'}) {
- my $esmtp = ${*$smtp}{'net_smtp_esmtp'};
-
- if (defined($v = delete $opt{Notify})) {
- if (exists $esmtp->{DSN}) {
- $opts .= " NOTIFY=" . join(",", map { uc $_ } @$v);
- }
- else {
- carp 'Net::SMTP::recipient: DSN option not supported by host';
- }
- }
-
- if (defined($v = delete $opt{ORcpt})) {
- if (exists $esmtp->{DSN}) {
- $opts .= " ORCPT=" . $v;
- }
- else {
- carp 'Net::SMTP::recipient: DSN option not supported by host';
- }
- }
-
- carp 'Net::SMTP::recipient: unknown option(s) ' . join(" ", keys %opt) . ' - ignored'
- if scalar keys %opt;
- }
- elsif (%opt) {
- carp 'Net::SMTP::recipient: ESMTP not supported by host - options discarded :-(';
- }
- }
-
- my @ok;
- my $addr;
- foreach $addr (@_) {
- if ($smtp->_RCPT("TO:" . _addr($smtp, $addr) . $opts)) {
- push(@ok, $addr) if $skip_bad;
- }
- elsif (!$skip_bad) {
- return 0;
- }
- }
-
- return $skip_bad ? @ok : 1;
-}
-
-BEGIN {
- *to = \&recipient;
- *cc = \&recipient;
- *bcc = \&recipient;
-}
-
-
-sub data {
- my $me = shift;
-
- if (exists ${*$me}{'net_smtp_chunking'}) {
- carp 'Net::SMTP::data: CHUNKING extension in use, must call bdat instead';
- }
- else {
- my $ok = $me->_DATA() && $me->datasend(@_);
-
- $ok && @_
- ? $me->dataend
- : $ok;
- }
-}
-
-
-sub bdat {
- my $me = shift;
-
- if (exists ${*$me}{'net_smtp_chunking'}) {
- my $data = shift;
-
- $me->_BDAT(length $data)
- && $me->rawdatasend($data)
- && $me->response() == CMD_OK;
- }
- else {
- carp 'Net::SMTP::bdat: CHUNKING extension is not in use, call data instead';
- }
-}
-
-
-sub bdatlast {
- my $me = shift;
-
- if (exists ${*$me}{'net_smtp_chunking'}) {
- my $data = shift;
-
- $me->_BDAT(length $data, "LAST")
- && $me->rawdatasend($data)
- && $me->response() == CMD_OK;
- }
- else {
- carp 'Net::SMTP::bdat: CHUNKING extension is not in use, call data instead';
- }
-}
-
-
-sub datafh {
- my $me = shift;
- return unless $me->_DATA();
- return $me->tied_fh;
-}
-
-
-sub expand {
- my $me = shift;
-
- $me->_EXPN(@_)
- ? ($me->message)
- : ();
-}
-
-
-sub verify { shift->_VRFY(@_) }
-
-
-sub help {
- my $me = shift;
-
- $me->_HELP(@_)
- ? scalar $me->message
- : undef;
-}
-
-
-sub quit {
- my $me = shift;
-
- $me->_QUIT;
- $me->close;
-}
-
-
-sub DESTROY {
-
- # ignore
-}
-
-##
-## RFC821 commands
-##
-
-
-sub _EHLO { shift->command("EHLO", @_)->response() == CMD_OK }
-sub _HELO { shift->command("HELO", @_)->response() == CMD_OK }
-sub _MAIL { shift->command("MAIL", @_)->response() == CMD_OK }
-sub _RCPT { shift->command("RCPT", @_)->response() == CMD_OK }
-sub _SEND { shift->command("SEND", @_)->response() == CMD_OK }
-sub _SAML { shift->command("SAML", @_)->response() == CMD_OK }
-sub _SOML { shift->command("SOML", @_)->response() == CMD_OK }
-sub _VRFY { shift->command("VRFY", @_)->response() == CMD_OK }
-sub _EXPN { shift->command("EXPN", @_)->response() == CMD_OK }
-sub _HELP { shift->command("HELP", @_)->response() == CMD_OK }
-sub _RSET { shift->command("RSET")->response() == CMD_OK }
-sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
-sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
-sub _DATA { shift->command("DATA")->response() == CMD_MORE }
-sub _BDAT { shift->command("BDAT", @_) }
-sub _TURN { shift->unsupported(@_); }
-sub _ETRN { shift->command("ETRN", @_)->response() == CMD_OK }
-sub _AUTH { shift->command("AUTH", @_)->response() == CMD_OK }
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::SMTP - Simple Mail Transfer Protocol Client
-
-=head1 SYNOPSIS
-
- use Net::SMTP;
-
- # Constructors
- $smtp = Net::SMTP->new('mailhost');
- $smtp = Net::SMTP->new('mailhost', Timeout => 60);
-
-=head1 DESCRIPTION
-
-This module implements a client interface to the SMTP and ESMTP
-protocol, enabling a perl5 application to talk to SMTP servers. This
-documentation assumes that you are familiar with the concepts of the
-SMTP protocol described in RFC821.
-
-A new Net::SMTP object must be created with the I<new> method. Once
-this has been done, all SMTP commands are accessed through this object.
-
-The Net::SMTP class is a subclass of Net::Cmd and IO::Socket::INET.
-
-=head1 EXAMPLES
-
-This example prints the mail domain name of the SMTP server known as mailhost:
-
- #!/usr/local/bin/perl -w
-
- use Net::SMTP;
-
- $smtp = Net::SMTP->new('mailhost');
- print $smtp->domain,"\n";
- $smtp->quit;
-
-This example sends a small message to the postmaster at the SMTP server
-known as mailhost:
-
- #!/usr/local/bin/perl -w
-
- use Net::SMTP;
-
- my $smtp = Net::SMTP->new('mailhost');
-
- $smtp->mail($ENV{USER});
- if ($smtp->to('postmaster')) {
- $smtp->data();
- $smtp->datasend("To: postmaster\n");
- $smtp->datasend("\n");
- $smtp->datasend("A simple test message\n");
- $smtp->dataend();
- } else {
- print "Error: ", $smtp->message();
- }
-
- $smtp->quit;
-
-=head1 CONSTRUCTOR
-
-=over 4
-
-=item new ( [ HOST ] [, OPTIONS ] )
-
-This is the constructor for a new Net::SMTP object. C<HOST> is the
-name of the remote host to which an SMTP connection is required.
-
-On failure C<undef> will be returned and C<$@> will contain the reason
-for the failure.
-
-C<HOST> is optional. If C<HOST> is not given then it may instead be
-passed as the C<Host> option described below. If neither is given then
-the C<SMTP_Hosts> specified in C<Net::Config> will be used.
-
-C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
-Possible options are:
-
-B<Hello> - SMTP requires that you identify yourself. This option
-specifies a string to pass as your mail domain. If not given localhost.localdomain
-will be used.
-
-B<Host> - SMTP host to connect to. It may be a single scalar (hostname[:port]),
-as defined for the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
-an array with hosts to try in turn. The L</host> method will return the value
-which was used to connect to the host.
-
-B<Port> - port to connect to. Format - C<PeerHost> from L<IO::Socket::INET> new method.
-Default - 25.
-
-B<LocalAddr> and B<LocalPort> - These parameters are passed directly
-to IO::Socket to allow binding the socket to a local port.
-
-B<Timeout> - Maximum time, in seconds, to wait for a response from the
-SMTP server (default: 120)
-
-B<ExactAddresses> - If true the all ADDRESS arguments must be as
-defined by C<addr-spec> in RFC2822. If not given, or false, then
-Net::SMTP will attempt to extract the address from the value passed.
-
-B<Debug> - Enable debugging information
-
-
-Example:
-
-
- $smtp = Net::SMTP->new('mailhost',
- Hello => 'my.mail.domain',
- Timeout => 30,
- Debug => 1,
- );
-
- # the same
- $smtp = Net::SMTP->new(
- Host => 'mailhost',
- Hello => 'my.mail.domain',
- Timeout => 30,
- Debug => 1,
- );
-
- # Connect to the default server from Net::config
- $smtp = Net::SMTP->new(
- Hello => 'my.mail.domain',
- Timeout => 30,
- );
-
-=back
-
-=head1 METHODS
-
-Unless otherwise stated all methods return either a I<true> or I<false>
-value, with I<true> meaning that the operation was a success. When a method
-states that it returns a value, failure will be returned as I<undef> or an
-empty list.
-
-C<Net::SMTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
-be used to send commands to the remote SMTP server in addition to the methods
-documented here.
-
-=over 4
-
-=item banner ()
-
-Returns the banner message which the server replied with when the
-initial connection was made.
-
-=item domain ()
-
-Returns the domain that the remote SMTP server identified itself as during
-connection.
-
-=item hello ( DOMAIN )
-
-Tell the remote server the mail domain which you are in using the EHLO
-command (or HELO if EHLO fails). Since this method is invoked
-automatically when the Net::SMTP object is constructed the user should
-normally not have to call it manually.
-
-=item host ()
-
-Returns the value used by the constructor, and passed to IO::Socket::INET,
-to connect to the host.
-
-=item etrn ( DOMAIN )
-
-Request a queue run for the DOMAIN given.
-
-=item auth ( USERNAME, PASSWORD )
-
-Attempt SASL authentication. Requires Authen::SASL module.
-
-=item mail ( ADDRESS [, OPTIONS] )
-
-=item send ( ADDRESS )
-
-=item send_or_mail ( ADDRESS )
-
-=item send_and_mail ( ADDRESS )
-
-Send the appropriate command to the server MAIL, SEND, SOML or SAML. C<ADDRESS>
-is the address of the sender. This initiates the sending of a message. The
-method C<recipient> should be called for each address that the message is to
-be sent to.
-
-The C<mail> method can some additional ESMTP OPTIONS which is passed
-in hash like fashion, using key and value pairs. Possible options are:
-
- Size => <bytes>
- Return => "FULL" | "HDRS"
- Bits => "7" | "8" | "binary"
- Transaction => <ADDRESS>
- Envelope => <ENVID> # xtext-encodes its argument
- ENVID => <ENVID> # similar to Envelope, but expects argument encoded
- XVERP => 1
- AUTH => <submitter> # encoded address according to RFC 2554
-
-The C<Return> and C<Envelope> parameters are used for DSN (Delivery
-Status Notification).
-
-The submitter address in C<AUTH> option is expected to be in a format as
-required by RFC 2554, in an RFC2821-quoted form and xtext-encoded, or <> .
-
-=item reset ()
-
-Reset the status of the server. This may be called after a message has been
-initiated, but before any data has been sent, to cancel the sending of the
-message.
-
-=item recipient ( ADDRESS [, ADDRESS, [...]] [, OPTIONS ] )
-
-Notify the server that the current message should be sent to all of the
-addresses given. Each address is sent as a separate command to the server.
-Should the sending of any address result in a failure then the process is
-aborted and a I<false> value is returned. It is up to the user to call
-C<reset> if they so desire.
-
-The C<recipient> method can also pass additional case-sensitive OPTIONS as an
-anonymous hash using key and value pairs. Possible options are:
-
- Notify => ['NEVER'] or ['SUCCESS','FAILURE','DELAY'] (see below)
- ORcpt => <ORCPT>
- SkipBad => 1 (to ignore bad addresses)
-
-If C<SkipBad> is true the C<recipient> will not return an error when a bad
-address is encountered and it will return an array of addresses that did
-succeed.
-
- $smtp->recipient($recipient1,$recipient2); # Good
- $smtp->recipient($recipient1,$recipient2, { SkipBad => 1 }); # Good
- $smtp->recipient($recipient1,$recipient2, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); # Good
- @goodrecips=$smtp->recipient(@recipients, { Notify => ['FAILURE'], SkipBad => 1 }); # Good
- $smtp->recipient("$recipient,$recipient2"); # BAD
-
-Notify is used to request Delivery Status Notifications (DSNs), but your
-SMTP/ESMTP service may not respect this request depending upon its version and
-your site's SMTP configuration.
-
-Leaving out the Notify option usually defaults an SMTP service to its default
-behavior equivalent to ['FAILURE'] notifications only, but again this may be
-dependent upon your site's SMTP configuration.
-
-The NEVER keyword must appear by itself if used within the Notify option and "requests
-that a DSN not be returned to the sender under any conditions."
-
- {Notify => ['NEVER']}
-
- $smtp->recipient(@recipients, { Notify => ['NEVER'], SkipBad => 1 }); # Good
-
-You may use any combination of these three values 'SUCCESS','FAILURE','DELAY' in
-the anonymous array reference as defined by RFC3461 (see http://www.ietf.org/rfc/rfc3461.txt
-for more information. Note: quotations in this topic from same.).
-
-A Notify parameter of 'SUCCESS' or 'FAILURE' "requests that a DSN be issued on
-successful delivery or delivery failure, respectively."
-
-A Notify parameter of 'DELAY' "indicates the sender's willingness to receive
-delayed DSNs. Delayed DSNs may be issued if delivery of a message has been
-delayed for an unusual amount of time (as determined by the Message Transfer
-Agent (MTA) at which the message is delayed), but the final delivery status
-(whether successful or failure) cannot be determined. The absence of the DELAY
-keyword in a NOTIFY parameter requests that a "delayed" DSN NOT be issued under
-any conditions."
-
- {Notify => ['SUCCESS','FAILURE','DELAY']}
-
- $smtp->recipient(@recipients, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); # Good
-
-ORcpt is also part of the SMTP DSN extension according to RFC3461.
-It is used to pass along the original recipient that the mail was first
-sent to. The machine that generates a DSN will use this address to inform
-the sender, because he can't know if recipients get rewritten by mail servers.
-It is expected to be in a format as required by RFC3461, xtext-encoded.
-
-=item to ( ADDRESS [, ADDRESS [...]] )
-
-=item cc ( ADDRESS [, ADDRESS [...]] )
-
-=item bcc ( ADDRESS [, ADDRESS [...]] )
-
-Synonyms for C<recipient>.
-
-=item data ( [ DATA ] )
-
-Initiate the sending of the data from the current message.
-
-C<DATA> may be a reference to a list or a list. If specified the contents
-of C<DATA> and a termination string C<".\r\n"> is sent to the server. And the
-result will be true if the data was accepted.
-
-If C<DATA> is not specified then the result will indicate that the server
-wishes the data to be sent. The data must then be sent using the C<datasend>
-and C<dataend> methods described in L<Net::Cmd>.
-
-=item expand ( ADDRESS )
-
-Request the server to expand the given address Returns an array
-which contains the text read from the server.
-
-=item verify ( ADDRESS )
-
-Verify that C<ADDRESS> is a legitimate mailing address.
-
-Most sites usually disable this feature in their SMTP service configuration.
-Use "Debug => 1" option under new() to see if disabled.
-
-=item help ( [ $subject ] )
-
-Request help text from the server. Returns the text or undef upon failure
-
-=item quit ()
-
-Send the QUIT command to the remote SMTP server and close the socket connection.
-
-=back
-
-=head1 ADDRESSES
-
-Net::SMTP attempts to DWIM with addresses that are passed. For
-example an application might extract The From: line from an email
-and pass that to mail(). While this may work, it is not recommended.
-The application should really use a module like L<Mail::Address>
-to extract the mail address and pass that.
-
-If C<ExactAddresses> is passed to the constructor, then addresses
-should be a valid rfc2821-quoted address, although Net::SMTP will
-accept the address surrounded by angle brackets.
-
- funny user@domain WRONG
- "funny user"@domain RIGHT, recommended
- <"funny user"@domain> OK
-
-=head1 SEE ALSO
-
-L<Net::Cmd>
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-2004 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,151 +0,0 @@
-# Net::Time.pm
-#
-# Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>. All rights reserved.
-# This program is free software; you can redistribute it and/or
-# modify it under the same terms as Perl itself.
-
-package Net::Time;
-
-use strict;
-use vars qw($VERSION @ISA @EXPORT_OK $TIMEOUT);
-use Carp;
-use IO::Socket;
-require Exporter;
-use Net::Config;
-use IO::Select;
-
-@ISA = qw(Exporter);
-@EXPORT_OK = qw(inet_time inet_daytime);
-
-$VERSION = "2.11";
-
-$TIMEOUT = 120;
-
-
-sub _socket {
- my ($pname, $pnum, $host, $proto, $timeout) = @_;
-
- $proto ||= 'udp';
-
- my $port = (getservbyname($pname, $proto))[2] || $pnum;
-
- my $hosts = defined $host ? [$host] : $NetConfig{$pname . '_hosts'};
-
- my $me;
-
- foreach $host (@$hosts) {
- $me = IO::Socket::INET->new(
- PeerAddr => $host,
- PeerPort => $port,
- Proto => $proto
- )
- and last;
- }
-
- return unless $me;
-
- $me->send("\n")
- if $proto eq 'udp';
-
- $timeout = $TIMEOUT
- unless defined $timeout;
-
- IO::Select->new($me)->can_read($timeout)
- ? $me
- : undef;
-}
-
-
-sub inet_time {
- my $s = _socket('time', 37, @_) || return undef;
- my $buf = '';
- my $offset = 0 | 0;
-
- return undef
- unless defined $s->recv($buf, length(pack("N", 0)));
-
- # unpack, we | 0 to ensure we have an unsigned
- my $time = (unpack("N", $buf))[0] | 0;
-
- # the time protocol return time in seconds since 1900, convert
- # it to a the required format
-
- if ($^O eq "MacOS") {
-
- # MacOS return seconds since 1904, 1900 was not a leap year.
- $offset = (4 * 31536000) | 0;
- }
- else {
-
- # otherwise return seconds since 1972, there were 17 leap years between
- # 1900 and 1972
- $offset = (70 * 31536000 + 17 * 86400) | 0;
- }
-
- $time - $offset;
-}
-
-
-sub inet_daytime {
- my $s = _socket('daytime', 13, @_) || return undef;
- my $buf = '';
-
- defined($s->recv($buf, 1024))
- ? $buf
- : undef;
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-Net::Time - time and daytime network client interface
-
-=head1 SYNOPSIS
-
- use Net::Time qw(inet_time inet_daytime);
-
- print inet_time(); # use default host from Net::Config
- print inet_time('localhost');
- print inet_time('localhost', 'tcp');
-
- print inet_daytime(); # use default host from Net::Config
- print inet_daytime('localhost');
- print inet_daytime('localhost', 'tcp');
-
-=head1 DESCRIPTION
-
-C<Net::Time> provides subroutines that obtain the time on a remote machine.
-
-=over 4
-
-=item inet_time ( [HOST [, PROTOCOL [, TIMEOUT]]])
-
-Obtain the time on C<HOST>, or some default host if C<HOST> is not given
-or not defined, using the protocol as defined in RFC868. The optional
-argument C<PROTOCOL> should define the protocol to use, either C<tcp> or
-C<udp>. The result will be a time value in the same units as returned
-by time() or I<undef> upon failure.
-
-=item inet_daytime ( [HOST [, PROTOCOL [, TIMEOUT]]])
-
-Obtain the time on C<HOST>, or some default host if C<HOST> is not given
-or not defined, using the protocol as defined in RFC867. The optional
-argument C<PROTOCOL> should define the protocol to use, either C<tcp> or
-C<udp>. The result will be an ASCII string or I<undef> upon failure.
-
-=back
-
-=head1 AUTHOR
-
-Graham Barr <gbarr@pobox.com>
-
-=head1 COPYRIGHT
-
-Copyright (c) 1995-2004 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,302 +0,0 @@
-=head1 NAME
-
-libnetFAQ - libnet Frequently Asked Questions
-
-=head1 DESCRIPTION
-
-=head2 Where to get this document
-
-This document is distributed with the libnet distribution, and is also
-available on the libnet web page at
-
- http://search.cpan.org/dist/libnet/
-
-=head2 How to contribute to this document
-
-You may report corrections, additions, and suggestions on the
-CPAN request tracker at
-
- http://rt.cpan.org/Dist/Display.html?Name=libnet
-
-=head1 Author and Copyright Information
-
-Copyright (c) 1997-1998 Graham Barr. All rights reserved.
-This document is free; you can redistribute it and/or modify it
-under the terms of the Artistic License.
-
-Currently maintained by Steve Hay <shay@cpan.org>.
-
-=head2 Disclaimer
-
-This information is offered in good faith and in the hope that it may
-be of use, but is not guaranteed to be correct, up to date, or suitable
-for any particular purpose whatsoever. The authors accept no liability
-in respect of this information or its use.
-
-
-=head1 Obtaining and installing libnet
-
-=head2 What is libnet ?
-
-libnet is a collection of perl5 modules which all related to network
-programming. The majority of the modules available provided the
-client side of popular server-client protocols that are used in
-the internet community.
-
-=head2 Which version of perl do I need ?
-
-libnet has been know to work with versions of perl from 5.002 onwards. However
-if your release of perl is prior to perl5.004 then you will need to
-obtain and install the IO distribution from CPAN. If you have perl5.004
-or later then you will have the IO modules in your installation already,
-but CPAN may contain updates.
-
-=head2 What other modules do I need ?
-
-The only modules you will need installed are the modules from the IO
-distribution. If you have perl5.004 or later you will already have
-these modules.
-
-=head2 What machines support libnet ?
-
-libnet itself is an entirely perl-code distribution so it should work
-on any machine that perl runs on. However IO may not work
-with some machines and earlier releases of perl. But this
-should not be the case with perl version 5.004 or later.
-
-=head2 Where can I get the latest libnet release
-
-The latest libnet release is always on CPAN, you will find it
-in
-
- http://search.cpan.org/dist/libnet/
-
-=head1 Using Net::FTP
-
-=head2 How do I download files from an FTP server ?
-
-An example taken from an article posted to comp.lang.perl.misc
-
- #!/your/path/to/perl
-
- # a module making life easier
-
- use Net::FTP;
-
- # for debugging: $ftp = Net::FTP->new('site','Debug',10);
- # open a connection and log in!
-
- $ftp = Net::FTP->new('target_site.somewhere.xxx');
- $ftp->login('username','password');
-
- # set transfer mode to binary
-
- $ftp->binary();
-
- # change the directory on the ftp site
-
- $ftp->cwd('/some/path/to/somewhere/');
-
- foreach $name ('file1', 'file2', 'file3') {
-
- # get's arguments are in the following order:
- # ftp server's filename
- # filename to save the transfer to on the local machine
- # can be simply used as get($name) if you want the same name
-
- $ftp->get($name,$name);
- }
-
- # ftp done!
-
- $ftp->quit;
-
-=head2 How do I transfer files in binary mode ?
-
-To transfer files without <LF><CR> translation Net::FTP provides
-the C<binary> method
-
- $ftp->binary;
-
-=head2 How can I get the size of a file on a remote FTP server ?
-
-=head2 How can I get the modification time of a file on a remote FTP server ?
-
-=head2 How can I change the permissions of a file on a remote server ?
-
-The FTP protocol does not have a command for changing the permissions
-of a file on the remote server. But some ftp servers may allow a chmod
-command to be issued via a SITE command, eg
-
- $ftp->quot('site chmod 0777',$filename);
-
-But this is not guaranteed to work.
-
-=head2 Can I do a reget operation like the ftp command ?
-
-=head2 How do I get a directory listing from an FTP server ?
-
-=head2 Changing directory to "" does not fail ?
-
-Passing an argument of "" to ->cwd() has the same affect of calling ->cwd()
-without any arguments. Turn on Debug (I<See below>) and you will see what is
-happening
-
- $ftp = Net::FTP->new($host, Debug => 1);
- $ftp->login;
- $ftp->cwd("");
-
-gives
-
- Net::FTP=GLOB(0x82196d8)>>> CWD /
- Net::FTP=GLOB(0x82196d8)<<< 250 CWD command successful.
-
-=head2 I am behind a SOCKS firewall, but the Firewall option does not work ?
-
-The Firewall option is only for support of one type of firewall. The type
-supported is an ftp proxy.
-
-To use Net::FTP, or any other module in the libnet distribution,
-through a SOCKS firewall you must create a socks-ified perl executable
-by compiling perl with the socks library.
-
-=head2 I am behind an FTP proxy firewall, but cannot access machines outside ?
-
-Net::FTP implements the most popular ftp proxy firewall approach. The scheme
-implemented is that where you log in to the firewall with C<user@hostname>
-
-I have heard of one other type of firewall which requires a login to the
-firewall with an account, then a second login with C<user@hostname>. You can
-still use Net::FTP to traverse these firewalls, but a more manual approach
-must be taken, eg
-
- $ftp = Net::FTP->new($firewall) or die $@;
- $ftp->login($firewall_user, $firewall_passwd) or die $ftp->message;
- $ftp->login($ext_user . '@' . $ext_host, $ext_passwd) or die $ftp->message.
-
-=head2 My ftp proxy firewall does not listen on port 21
-
-FTP servers usually listen on the same port number, port 21, as any other
-FTP server. But there is no reason why this has to be the case.
-
-If you pass a port number to Net::FTP then it assumes this is the port
-number of the final destination. By default Net::FTP will always try
-to connect to the firewall on port 21.
-
-Net::FTP uses IO::Socket to open the connection and IO::Socket allows
-the port number to be specified as part of the hostname. So this problem
-can be resolved by either passing a Firewall option like C<"hostname:1234">
-or by setting the C<ftp_firewall> option in Net::Config to be a string
-in the same form.
-
-=head2 Is it possible to change the file permissions of a file on an FTP server ?
-
-The answer to this is "maybe". The FTP protocol does not specify a command to change
-file permissions on a remote host. However many servers do allow you to run the
-chmod command via the C<SITE> command. This can be done with
-
- $ftp->site('chmod','0775',$file);
-
-=head2 I have seen scripts call a method message, but cannot find it documented ?
-
-Net::FTP, like several other packages in libnet, inherits from Net::Cmd, so
-all the methods described in Net::Cmd are also available on Net::FTP
-objects.
-
-=head2 Why does Net::FTP not implement mput and mget methods
-
-The quick answer is because they are easy to implement yourself. The long
-answer is that to write these in such a way that multiple platforms are
-supported correctly would just require too much code. Below are
-some examples how you can implement these yourself.
-
-sub mput {
- my($ftp,$pattern) = @_;
- foreach my $file (glob($pattern)) {
- $ftp->put($file) or warn $ftp->message;
- }
-}
-
-sub mget {
- my($ftp,$pattern) = @_;
- foreach my $file ($ftp->ls($pattern)) {
- $ftp->get($file) or warn $ftp->message;
- }
-}
-
-
-=head1 Using Net::SMTP
-
-=head2 Why can't the part of an Email address after the @ be used as the hostname ?
-
-The part of an Email address which follows the @ is not necessarily a hostname,
-it is a mail domain. To find the name of a host to connect for a mail domain
-you need to do a DNS MX lookup
-
-=head2 Why does Net::SMTP not do DNS MX lookups ?
-
-Net::SMTP implements the SMTP protocol. The DNS MX lookup is not part
-of this protocol.
-
-=head2 The verify method always returns true ?
-
-Well it may seem that way, but it does not. The verify method returns true
-if the command succeeded. If you pass verify an address which the
-server would normally have to forward to another machine, the command
-will succeed with something like
-
- 252 Couldn't verify <someone@there> but will attempt delivery anyway
-
-This command will fail only if you pass it an address in a domain
-the server directly delivers for, and that address does not exist.
-
-=head1 Debugging scripts
-
-=head2 How can I debug my scripts that use Net::* modules ?
-
-Most of the libnet client classes allow options to be passed to the
-constructor, in most cases one option is called C<Debug>. Passing
-this option with a non-zero value will turn on a protocol trace, which
-will be sent to STDERR. This trace can be useful to see what commands
-are being sent to the remote server and what responses are being
-received back.
-
- #!/your/path/to/perl
-
- use Net::FTP;
-
- my $ftp = new Net::FTP($host, Debug => 1);
- $ftp->login('gbarr','password');
- $ftp->quit;
-
-this script would output something like
-
- Net::FTP: Net::FTP(2.22)
- Net::FTP: Exporter
- Net::FTP: Net::Cmd(2.0801)
- Net::FTP: IO::Socket::INET
- Net::FTP: IO::Socket(1.1603)
- Net::FTP: IO::Handle(1.1504)
-
- Net::FTP=GLOB(0x8152974)<<< 220 imagine FTP server (Version wu-2.4(5) Tue Jul 29 11:17:18 CDT 1997) ready.
- Net::FTP=GLOB(0x8152974)>>> user gbarr
- Net::FTP=GLOB(0x8152974)<<< 331 Password required for gbarr.
- Net::FTP=GLOB(0x8152974)>>> PASS ....
- Net::FTP=GLOB(0x8152974)<<< 230 User gbarr logged in. Access restrictions apply.
- Net::FTP=GLOB(0x8152974)>>> QUIT
- Net::FTP=GLOB(0x8152974)<<< 221 Goodbye.
-
-The first few lines tell you the modules that Net::FTP uses and their versions,
-this is useful data to me when a user reports a bug. The last seven lines
-show the communication with the server. Each line has three parts. The first
-part is the object itself, this is useful for separating the output
-if you are using multiple objects. The second part is either C<<<<<> to
-show data coming from the server or C<>>>>> to show data
-going to the server. The remainder of the line is the command
-being sent or response being received.
-
-=head1 AUTHOR AND COPYRIGHT
-
-Copyright (c) 1997 Graham Barr.
-All rights reserved.
-
@@ -34,7 +34,7 @@ If you have a Git client, then you can checkout the latest code with
INSTALLATION
-In order to use this package you will need Perl version 5.002 or
+In order to use this package you will need Perl version 5.8.1 or
better. You install libnet, as you would install any perl module
library, by running these commands:
@@ -57,20 +57,21 @@ together with libnet. These packages should be available on CPAN
CONFIGURE
-Normally when perl Makefile.PL is run it will run Configure which will
+Normally when Makefile.PL is run it will run Configure which will
ask some questions about your system. The results of these questions
will be stored in a file called libnet.cfg which will be installed
-alongside the other perl modules in this distribution. The Makefile.PL
+alongside the other perl modules in this distribution. Makefile.PL
will run Configure in an interactive mode unless these exists a file
-called libnet.cfg in the build directory.
+called libnet.cfg in the build directory or Makefile.PL itself is being
+run non-interactively or via cpan, cpanp or cpanm.
If you are on a system which cannot run this script you can create an
empty file to make Makefile.PL skip running Configure. If you want to
-keep your existing settings and not run interactivly the simple run
+keep your existing settings and not run interactivly then simply run
- Configure -d
+ perl Configure -d
-before running the Makefile.PL.
+before running Makefile.PL.
DOCUMENTATION
@@ -93,9 +94,9 @@ SUPPORT
Questions about how to use this library should be directed to the
comp.lang.perl.modules USENET Newsgroup. Bug reports and suggestions
-for improvements can be reported on the CPAN request tracker at
+for improvements can be reported on the CPAN Request Tracker at
- http://rt.cpan.org/Dist/Display.html?Name=libnet
+ http://rt.cpan.org/Public/Bug//Report.html?Queue=libnet
Most of the modules in this library have an option to output a debug
transcript to STDERR. When reporting bugs/problems please, if possible,
@@ -103,11 +104,11 @@ include a transcript of a run.
COPYRIGHT
- (C) 1996-2007 Graham Barr. All rights reserved.
+ Versions up to 1.22_01 (C) 1996-2007 Graham Barr. All rights reserved.
+ Changes in Version 1.22_02 onwards Copyright (C) 2013-2014 Steve Hay.
+ All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
-Currently maintained by Steve Hay <shay@cpan.org>.
-
Share and Enjoy!
@@ -1,19 +1,24 @@
-#!/usr/local/bin/perl
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
use blib;
-use Net::FTP;
use Getopt::Long;
+use Net::FTP;
-$opt_debug = undef;
-$opt_firewall = undef;
+our $opt_debug = undef;
+our $opt_firewall = undef;
GetOptions(qw(debug firewall=s));
-@firewall = defined $opt_firewall ? (Firewall => $opt_firewall) : ();
+my @firewall = defined $opt_firewall ? (Firewall => $opt_firewall) : ();
-foreach $host (@ARGV)
+foreach my $host (@ARGV)
{
- $ftp = Net::FTP->new($host, @firewall, Debug => $opt_debug ? 1 : 0);
+ my $ftp = Net::FTP->new($host, @firewall, Debug => $opt_debug ? 1 : 0);
$ftp->login();
print $ftp->pwd,"\n";
$ftp->quit;
@@ -1,9 +0,0 @@
-#!/usr/local/bin/perl
-
-use Net::DummyInetd;
-use Net::SMTP;
-
-$p = new Net::DummyInetd qw(/usr/lib/sendmail -ba -bs);
-
-$smtp = Net::SMTP->new('localhost', Port => $p->port, Debug => 7);
-$smtp->quit;
@@ -1,17 +1,23 @@
-#!/usr/local/bin/perl
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
use blib;
use Getopt::Long;
use Net::NNTP;
-$opt_debug = undef;
+our $opt_debug = undef;
GetOptions(qw(debug));
-@groups = @ARGV;
+my @groups = @ARGV;
-$nntp = Net::NNTP->new('news', Debug => $opt_debug ? 1 : 0);
+my $nntp = Net::NNTP->new('news', Debug => $opt_debug ? 1 : 0);
+my $subs;
if($subs = $nntp->newsgroups)
{
print join("\n",(keys %$subs)[0 .. 10]),"\n";
@@ -21,14 +27,14 @@ if($subs = $nntp->newsgroups)
warn $nntp->message;
}
-foreach $group (@groups)
+foreach my $group (@groups)
{
- $new = $nntp->newnews(time - 3600, lc $group);
+ my $news = $nntp->newnews(time - 3600, lc $group);
- if(ref($new) && scalar(@$new))
+ if(ref($news) && scalar(@$news))
{
- print@{$news}[0..3],"\n"
- if $news = $nntp->article($new->[-1]);
+ print @{$news}[0..3],"\n"
+ if $news = $nntp->article($news->[-1]);
warn $nntp->message
unless $news;
@@ -1,4 +1,4 @@
-#!/usr/bin/perl5
+#!perl
### Subject: Re: Fuller example of Net::NNTP?
### Date: Tue, 4 Feb 1997 10:37:58 -0800
@@ -20,9 +20,14 @@
### work on every system
###
+use 5.008001;
+
+use strict;
+use warnings;
+
use Net::NNTP;
-$BaseDir = '/usr/usenet';
+my $BaseDir = '/usr/usenet';
chdir($BaseDir) or die "Could not cd to $BaseDir\n";
# Format of grouplist is:
@@ -30,18 +35,21 @@ chdir($BaseDir) or die "Could not cd to $BaseDir\n";
# expirationdays is the number of days to leave the articles around;
# set it to 0 if you want the articles to stay forever
# If the groupname starts with a #, it is skipped
-open(GROUPLIST, 'grouplist.txt') or die "Could not open grouplist.txt\n";
-while(<GROUPLIST>) {
- $Line = $_; chomp($Line);
+my $GroupList;
+open($GroupList, '<', 'grouplist.txt') or die "Could not open grouplist.txt\n";
+my @Groups;
+while(<$GroupList>) {
+ my $Line = $_; chomp($Line);
if($Line eq '') { next }; # Skip blank lines
if(substr($Line, 0, 1) eq '#') { next }; # Skip comments
push(@Groups, $Line)
}
+close $GroupList;
-$NntpPtr = Net::NNTP->new('news.server.com');
+my $NntpPtr = Net::NNTP->new('news.server.com');
-foreach $GroupLine (@Groups) {
- ($GroupName, $GroupExp) = split(/\s/, $GroupLine, 2);
+foreach my $GroupLine (@Groups) {
+ my($GroupName, $GroupExp) = split(/\s/, $GroupLine, 2);
# Process the expiration first (still to be done...)
# See if this is a new group
@@ -51,29 +59,32 @@ foreach $GroupLine (@Groups) {
}
chdir("$BaseDir/$GroupName") or die "Couldn't chdir to $GroupName\n";
# Find the last article in the directory
- @AllInDir = <*>; @RevSortedAllInDir = reverse(sort(@AllInDir));
- $LenArr = @RevSortedAllInDir;
+ my @AllInDir = glob('*'); my @RevSortedAllInDir = reverse(sort(@AllInDir));
+ my $LenArr = @RevSortedAllInDir;
+ my $NumLastInDir;
if($LenArr > 0) { $NumLastInDir = $RevSortedAllInDir[0] }
else { $NumLastInDir = 0 }
- ($NumArt, $NumFirst, $NumLast, $XGroupName) =
-$NntpPtr->group($GroupName);
+ my($NumArt, $NumFirst, $NumLast, $XGroupName) =
+ $NntpPtr->group($GroupName);
if($NumLast == $NumLastInDir) { next } # No new articles
if($NumLast < $NumLastInDir)
{ die "In $GroupName, the last number was $NumLast, but the " .
" last number in the directory was $NumLastInDir\n" }
# Figure out which article to start from
+ my $GetArtNum;
if($NumLastInDir == 0) { $GetArtNum = $NumFirst }
else { $GetArtNum = $NumLastInDir + 1 }
# Now read each of the new articles
while(1) { # Loop until "last" is called
- $ArtRef = $NntpPtr->article($GetArtNum);
- @ArtArr = @$ArtRef; $ArtArrLen = @ArtArr;
+ my $ArtRef = $NntpPtr->article($GetArtNum);
+ my @ArtArr = @$ArtRef; my $ArtArrLen = @ArtArr;
if($ArtArrLen > 0 ) { # Skip article numbers that had 0 len
- open(OUT, ">$GetArtNum") or
+ my $Out;
+ open($Out, '>', $GetArtNum) or
die "Could not create $GroupName/$GetArtNum\n";
- print OUT @$ArtRef; close(OUT);
+ print $Out @$ArtRef; close($Out);
}
# Check if we're at the end
@@ -1,23 +1,28 @@
-#!/usr/local/bin/perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
use blib;
-use Net::POP3;
use Getopt::Long;
+use Net::POP3;
-$opt_debug = 0;
-$opt_user = undef;
+my $opt_debug = 0;
+my $opt_user = undef;
GetOptions(qw(debug user=s));
-$pop = Net::POP3->new('backup3', Debug => $opt_debug ? 6 : 0);
+my $pop = Net::POP3->new('backup3', Debug => $opt_debug ? 6 : 0);
-$user = $opt_user || $ENV{USER} || $ENV{LOGNAME};
+my $user = $opt_user || $ENV{USER} || $ENV{LOGNAME};
-$count = $pop->login($user);
+my $count = $pop->login($user);
if($count)
{
- $m = $pop->get(1);
+ my $m = $pop->get(1);
print @$m if $m;
}
@@ -1,8 +1,13 @@
-#!/usr/local/bin/perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
use blib;
-use Net::SMTP;
use Getopt::Long;
+use Net::SMTP;
=head1 NAME
@@ -38,9 +43,9 @@ Send the message to C<USERNAME>
=cut
-$opt_debug = undef;
-$opt_user = undef;
-$opt_help = undef;
+my $opt_debug = undef;
+my $opt_user = undef;
+my $opt_help = undef;
GetOptions(qw(debug user=s help));
exec("pod2text $0")
@@ -48,9 +53,9 @@ exec("pod2text $0")
Net::SMTP->debug(1) if $opt_debug;
-$smtp = Net::SMTP->new("mailhost");
+my $smtp = Net::SMTP->new("mailhost");
-$user = $opt_user || $ENV{USER} || $ENV{LOGNAME};
+my $user = $opt_user || $ENV{USER} || $ENV{LOGNAME};
$smtp->mail($user) && $smtp->to($user);
$smtp->reset;
@@ -59,7 +64,8 @@ if($smtp->mail($user) && $smtp->to($user))
{
$smtp->data();
- map { s/-USER-/$user/g } @data=<DATA>;
+ my @data;
+ map { s/-USER-/$user/g } @data=<DATA>; ## no critic (ControlStructures::ProhibitMutatingListFunctions)
$smtp->datasend(@data);
$smtp->dataend;
@@ -1,4 +1,9 @@
-#!/usr/local/bin/perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
use blib;
use Net::Time qw(inet_time inet_daytime);
@@ -1,37 +0,0 @@
-#!/bin/sh
-#
-# This script is included in this distribution for the benefit
-# of those users who cannot use MakeMaker and make to install.
-# Run this script with
-#
-# perl install-nomake [-s <directory>]
-#
-# -s Install into this directory instead of the system site_lib
-# (This directory must already exist)
-#
-
-eval 'exec perl -x -S $0 ${1+"$@"}'
- if $running_under_some_shell;
-
-#! -*- perl -*-
-
-use Config;
-use File::Spec;
-use Getopt::Long;
-use ExtUtils::Install qw(install);
-
-my $sitelib = $Config{'sitelibexp'};
-
-GetOptions(
- 'sitelib=s' => \$sitelib
-);
-
-die "$sitelib is not a directory: $!"
- unless -d $sitelib;
-
-my $src = File::Spec->curdir;
-
-die "This script must be run from the libnet distribution directory: $!\n"
- unless -f File::Spec->catfile($src, qw(Net FTP.pm));
-
-install( { $src => $sitelib }, 1,0,0);
@@ -0,0 +1,876 @@
+# Net::Cmd.pm
+#
+# Versions up to 2.29_1 Copyright (c) 1995-2006 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.29_2 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::Cmd;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Exporter;
+use Symbol 'gensym';
+
+BEGIN {
+ if ($^O eq 'os390') {
+ require Convert::EBCDIC;
+
+ # Convert::EBCDIC->import;
+ }
+}
+
+BEGIN {
+ if (!eval { require utf8 }) {
+ *is_utf8 = sub { 0 };
+ }
+ elsif (eval { utf8::is_utf8(undef); 1 }) {
+ *is_utf8 = \&utf8::is_utf8;
+ }
+ elsif (eval { require Encode; Encode::is_utf8(undef); 1 }) {
+ *is_utf8 = \&Encode::is_utf8;
+ }
+ else {
+ *is_utf8 = sub { $_[0] =~ /[^\x00-\xff]/ };
+ }
+}
+
+our $VERSION = "3.04";
+our @ISA = qw(Exporter);
+our @EXPORT = qw(CMD_INFO CMD_OK CMD_MORE CMD_REJECT CMD_ERROR CMD_PENDING);
+
+use constant CMD_INFO => 1;
+use constant CMD_OK => 2;
+use constant CMD_MORE => 3;
+use constant CMD_REJECT => 4;
+use constant CMD_ERROR => 5;
+use constant CMD_PENDING => 0;
+
+use constant DEF_REPLY_CODE => 421;
+
+my %debug = ();
+
+my $tr = $^O eq 'os390' ? Convert::EBCDIC->new() : undef;
+
+sub toebcdic {
+ my $cmd = shift;
+
+ unless (exists ${*$cmd}{'net_cmd_asciipeer'}) {
+ my $string = $_[0];
+ my $ebcdicstr = $tr->toebcdic($string);
+ ${*$cmd}{'net_cmd_asciipeer'} = $string !~ /^\d+/ && $ebcdicstr =~ /^\d+/;
+ }
+
+ ${*$cmd}{'net_cmd_asciipeer'}
+ ? $tr->toebcdic($_[0])
+ : $_[0];
+}
+
+
+sub toascii {
+ my $cmd = shift;
+ ${*$cmd}{'net_cmd_asciipeer'}
+ ? $tr->toascii($_[0])
+ : $_[0];
+}
+
+
+sub _print_isa {
+ no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
+
+ my $pkg = shift;
+ my $cmd = $pkg;
+
+ $debug{$pkg} ||= 0;
+
+ my %done = ();
+ my @do = ($pkg);
+ my %spc = ($pkg, "");
+
+ while ($pkg = shift @do) {
+ next if defined $done{$pkg};
+
+ $done{$pkg} = 1;
+
+ my $v =
+ defined ${"${pkg}::VERSION"}
+ ? "(" . ${"${pkg}::VERSION"} . ")"
+ : "";
+
+ my $spc = $spc{$pkg};
+ $cmd->debug_print(1, "${spc}${pkg}${v}\n");
+
+ if (@{"${pkg}::ISA"}) {
+ @spc{@{"${pkg}::ISA"}} = (" " . $spc{$pkg}) x @{"${pkg}::ISA"};
+ unshift(@do, @{"${pkg}::ISA"});
+ }
+ }
+}
+
+
+sub debug {
+ @_ == 1 or @_ == 2 or croak 'usage: $obj->debug([LEVEL])';
+
+ my ($cmd, $level) = @_;
+ my $pkg = ref($cmd) || $cmd;
+ my $oldval = 0;
+
+ if (ref($cmd)) {
+ $oldval = ${*$cmd}{'net_cmd_debug'} || 0;
+ }
+ else {
+ $oldval = $debug{$pkg} || 0;
+ }
+
+ return $oldval
+ unless @_ == 2;
+
+ $level = $debug{$pkg} || 0
+ unless defined $level;
+
+ _print_isa($pkg)
+ if ($level && !exists $debug{$pkg});
+
+ if (ref($cmd)) {
+ ${*$cmd}{'net_cmd_debug'} = $level;
+ }
+ else {
+ $debug{$pkg} = $level;
+ }
+
+ $oldval;
+}
+
+
+sub message {
+ @_ == 1 or croak 'usage: $obj->message()';
+
+ my $cmd = shift;
+
+ wantarray
+ ? @{${*$cmd}{'net_cmd_resp'}}
+ : join("", @{${*$cmd}{'net_cmd_resp'}});
+}
+
+
+sub debug_text { $_[2] }
+
+
+sub debug_print {
+ my ($cmd, $out, $text) = @_;
+ print STDERR $cmd, ($out ? '>>> ' : '<<< '), $cmd->debug_text($out, $text);
+}
+
+
+sub code {
+ @_ == 1 or croak 'usage: $obj->code()';
+
+ my $cmd = shift;
+
+ ${*$cmd}{'net_cmd_code'} = $cmd->DEF_REPLY_CODE
+ unless exists ${*$cmd}{'net_cmd_code'};
+
+ ${*$cmd}{'net_cmd_code'};
+}
+
+
+sub status {
+ @_ == 1 or croak 'usage: $obj->status()';
+
+ my $cmd = shift;
+
+ substr(${*$cmd}{'net_cmd_code'}, 0, 1);
+}
+
+
+sub set_status {
+ @_ == 3 or croak 'usage: $obj->set_status(CODE, MESSAGE)';
+
+ my $cmd = shift;
+ my ($code, $resp) = @_;
+
+ $resp = defined $resp ? [$resp] : []
+ unless ref($resp);
+
+ (${*$cmd}{'net_cmd_code'}, ${*$cmd}{'net_cmd_resp'}) = ($code, $resp);
+
+ 1;
+}
+
+
+
+sub _set_status_timeout {
+ my $cmd = shift;
+ my $pkg = ref($cmd) || $cmd;
+
+ $cmd->set_status($cmd->DEF_REPLY_CODE, "[$pkg] Timeout");
+ carp(ref($cmd) . ": " . (caller(1))[3] . "(): timeout") if $cmd->debug;
+}
+
+sub _set_status_closed {
+ my $cmd = shift;
+ my $pkg = ref($cmd) || $cmd;
+
+ $cmd->set_status($cmd->DEF_REPLY_CODE, "[$pkg] Connection closed");
+ carp(ref($cmd) . ": " . (caller(1))[3]
+ . "(): unexpected EOF on command channel: $!") if $cmd->debug;
+}
+
+sub _is_closed {
+ my $cmd = shift;
+ if (!defined fileno($cmd)) {
+ $cmd->_set_status_closed;
+ return 1;
+ }
+ return 0;
+}
+
+sub command {
+ my $cmd = shift;
+
+ return $cmd
+ if $cmd->_is_closed;
+
+ $cmd->dataend()
+ if (exists ${*$cmd}{'net_cmd_last_ch'});
+
+ if (scalar(@_)) {
+ local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
+
+ my $str = join(
+ " ",
+ map {
+ /\n/
+ ? do { my $n = $_; $n =~ tr/\n/ /; $n }
+ : $_;
+ } @_
+ );
+ $str = $cmd->toascii($str) if $tr;
+ $str .= "\015\012";
+
+ my $len = length $str;
+ my $swlen;
+
+ $cmd->debug_print(1, $str)
+ if ($cmd->debug);
+
+ unless (defined($swlen = syswrite($cmd,$str,$len)) && $swlen == $len) {
+ $cmd->close;
+ $cmd->_set_status_closed;
+ return $cmd;
+ }
+ }
+
+ $cmd;
+}
+
+
+sub ok {
+ @_ == 1 or croak 'usage: $obj->ok()';
+
+ my $code = $_[0]->code;
+ 0 < $code && $code < 400;
+}
+
+
+sub unsupported {
+ my $cmd = shift;
+
+ $cmd->set_status(580, 'Unsupported command');
+
+ 0;
+}
+
+
+sub getline {
+ my $cmd = shift;
+
+ ${*$cmd}{'net_cmd_lines'} ||= [];
+
+ return shift @{${*$cmd}{'net_cmd_lines'}}
+ if scalar(@{${*$cmd}{'net_cmd_lines'}});
+
+ my $partial = defined(${*$cmd}{'net_cmd_partial'}) ? ${*$cmd}{'net_cmd_partial'} : "";
+
+ return
+ if $cmd->_is_closed;
+
+ my $fd = fileno($cmd);
+ my $rin = "";
+ vec($rin, $fd, 1) = 1;
+
+ my $buf;
+
+ until (scalar(@{${*$cmd}{'net_cmd_lines'}})) {
+ my $timeout = $cmd->timeout || undef;
+ my $rout;
+
+ my $select_ret = select($rout = $rin, undef, undef, $timeout);
+ if ($select_ret > 0) {
+ unless (sysread($cmd, $buf = "", 1024)) {
+ $cmd->close;
+ $cmd->_set_status_closed;
+ return;
+ }
+
+ substr($buf, 0, 0) = $partial; ## prepend from last sysread
+
+ my @buf = split(/\015?\012/, $buf, -1); ## break into lines
+
+ $partial = pop @buf;
+
+ push(@{${*$cmd}{'net_cmd_lines'}}, map {"$_\n"} @buf);
+
+ }
+ else {
+ $cmd->_set_status_timeout;
+ return;
+ }
+ }
+
+ ${*$cmd}{'net_cmd_partial'} = $partial;
+
+ if ($tr) {
+ foreach my $ln (@{${*$cmd}{'net_cmd_lines'}}) {
+ $ln = $cmd->toebcdic($ln);
+ }
+ }
+
+ shift @{${*$cmd}{'net_cmd_lines'}};
+}
+
+
+sub ungetline {
+ my ($cmd, $str) = @_;
+
+ ${*$cmd}{'net_cmd_lines'} ||= [];
+ unshift(@{${*$cmd}{'net_cmd_lines'}}, $str);
+}
+
+
+sub parse_response {
+ return ()
+ unless $_[1] =~ s/^(\d\d\d)(.?)//o;
+ ($1, $2 eq "-");
+}
+
+
+sub response {
+ my $cmd = shift;
+ my ($code, $more) = (undef) x 2;
+
+ $cmd->set_status($cmd->DEF_REPLY_CODE, undef); # initialize the response
+
+ while (1) {
+ my $str = $cmd->getline();
+
+ return CMD_ERROR
+ unless defined($str);
+
+ $cmd->debug_print(0, $str)
+ if ($cmd->debug);
+
+ ($code, $more) = $cmd->parse_response($str);
+ unless (defined $code) {
+ carp("$cmd: response(): parse error in '$str'") if ($cmd->debug);
+ $cmd->ungetline($str);
+ $@ = $str; # $@ used as tunneling hack
+ return CMD_ERROR;
+ }
+
+ ${*$cmd}{'net_cmd_code'} = $code;
+
+ push(@{${*$cmd}{'net_cmd_resp'}}, $str);
+
+ last unless ($more);
+ }
+
+ return unless defined $code;
+ substr($code, 0, 1);
+}
+
+
+sub read_until_dot {
+ my $cmd = shift;
+ my $fh = shift;
+ my $arr = [];
+
+ while (1) {
+ my $str = $cmd->getline() or return;
+
+ $cmd->debug_print(0, $str)
+ if ($cmd->debug & 4);
+
+ last if ($str =~ /^\.\r?\n/o);
+
+ $str =~ s/^\.\././o;
+
+ if (defined $fh) {
+ print $fh $str;
+ }
+ else {
+ push(@$arr, $str);
+ }
+ }
+
+ $arr;
+}
+
+
+sub datasend {
+ my $cmd = shift;
+ my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
+ my $line = join("", @$arr);
+
+ # encode to individual utf8 bytes if
+ # $line is a string (in internal UTF-8)
+ utf8::encode($line) if is_utf8($line);
+
+ return 0
+ if $cmd->_is_closed;
+
+ my $last_ch = ${*$cmd}{'net_cmd_last_ch'};
+
+ # We have not send anything yet, so last_ch = "\012" means we are at the start of a line
+ $last_ch = ${*$cmd}{'net_cmd_last_ch'} = "\012" unless defined $last_ch;
+
+ return 1 unless length $line;
+
+ if ($cmd->debug) {
+ foreach my $b (split(/\n/, $line)) {
+ $cmd->debug_print(1, "$b\n");
+ }
+ }
+
+ $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015";
+
+ my $first_ch = '';
+
+ if ($last_ch eq "\015") {
+ # Remove \012 so it does not get prefixed with another \015 below
+ # and escape the . if there is one following it because the fixup
+ # below will not find it
+ $first_ch = "\012" if $line =~ s/^\012(\.?)/$1$1/;
+ }
+ elsif ($last_ch eq "\012") {
+ # Fixup below will not find the . as the first character of the buffer
+ $first_ch = "." if $line =~ /^\./;
+ }
+
+ $line =~ s/\015?\012(\.?)/\015\012$1$1/sg;
+
+ substr($line, 0, 0) = $first_ch;
+
+ ${*$cmd}{'net_cmd_last_ch'} = substr($line, -1, 1);
+
+ my $len = length($line);
+ my $offset = 0;
+ my $win = "";
+ vec($win, fileno($cmd), 1) = 1;
+ my $timeout = $cmd->timeout || undef;
+
+ local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
+
+ while ($len) {
+ my $wout;
+ my $s = select(undef, $wout = $win, undef, $timeout);
+ if ((defined $s and $s > 0) or -f $cmd) # -f for testing on win32
+ {
+ my $w = syswrite($cmd, $line, $len, $offset);
+ unless (defined($w) && $w == $len) {
+ $cmd->close;
+ $cmd->_set_status_closed;
+ return;
+ }
+ $len -= $w;
+ $offset += $w;
+ }
+ else {
+ $cmd->_set_status_timeout;
+ return;
+ }
+ }
+
+ 1;
+}
+
+
+sub rawdatasend {
+ my $cmd = shift;
+ my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
+ my $line = join("", @$arr);
+
+ return 0
+ if $cmd->_is_closed;
+
+ return 1
+ unless length($line);
+
+ if ($cmd->debug) {
+ my $b = "$cmd>>> ";
+ print STDERR $b, join("\n$b", split(/\n/, $line)), "\n";
+ }
+
+ my $len = length($line);
+ my $offset = 0;
+ my $win = "";
+ vec($win, fileno($cmd), 1) = 1;
+ my $timeout = $cmd->timeout || undef;
+
+ local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
+ while ($len) {
+ my $wout;
+ if (select(undef, $wout = $win, undef, $timeout) > 0) {
+ my $w = syswrite($cmd, $line, $len, $offset);
+ unless (defined($w) && $w == $len) {
+ $cmd->close;
+ $cmd->_set_status_closed;
+ return;
+ }
+ $len -= $w;
+ $offset += $w;
+ }
+ else {
+ $cmd->_set_status_timeout;
+ return;
+ }
+ }
+
+ 1;
+}
+
+
+sub dataend {
+ my $cmd = shift;
+
+ return 0
+ if $cmd->_is_closed;
+
+ my $ch = ${*$cmd}{'net_cmd_last_ch'};
+ my $tosend;
+
+ if (!defined $ch) {
+ return 1;
+ }
+ elsif ($ch ne "\012") {
+ $tosend = "\015\012";
+ }
+
+ $tosend .= ".\015\012";
+
+ local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
+
+ $cmd->debug_print(1, ".\n")
+ if ($cmd->debug);
+
+ my $len = length $tosend;
+ my $w = syswrite($cmd, $tosend, $len);
+ unless (defined($w) && $w == $len)
+ {
+ $cmd->close;
+ $cmd->_set_status_closed;
+ return 0;
+ }
+
+ delete ${*$cmd}{'net_cmd_last_ch'};
+
+ $cmd->response() == CMD_OK;
+}
+
+# read and write to tied filehandle
+sub tied_fh {
+ my $cmd = shift;
+ ${*$cmd}{'net_cmd_readbuf'} = '';
+ my $fh = gensym();
+ tie *$fh, ref($cmd), $cmd;
+ return $fh;
+}
+
+# tie to myself
+sub TIEHANDLE {
+ my $class = shift;
+ my $cmd = shift;
+ return $cmd;
+}
+
+# Tied filehandle read. Reads requested data length, returning
+# end-of-file when the dot is encountered.
+sub READ {
+ my $cmd = shift;
+ my ($len, $offset) = @_[1, 2];
+ return unless exists ${*$cmd}{'net_cmd_readbuf'};
+ my $done = 0;
+ while (!$done and length(${*$cmd}{'net_cmd_readbuf'}) < $len) {
+ ${*$cmd}{'net_cmd_readbuf'} .= $cmd->getline() or return;
+ $done++ if ${*$cmd}{'net_cmd_readbuf'} =~ s/^\.\r?\n\Z//m;
+ }
+
+ $_[0] = '';
+ substr($_[0], $offset + 0) = substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len);
+ substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len) = '';
+ delete ${*$cmd}{'net_cmd_readbuf'} if $done;
+
+ return length $_[0];
+}
+
+
+sub READLINE {
+ my $cmd = shift;
+
+ # in this context, we use the presence of readbuf to
+ # indicate that we have not yet reached the eof
+ return unless exists ${*$cmd}{'net_cmd_readbuf'};
+ my $line = $cmd->getline;
+ return if $line =~ /^\.\r?\n/;
+ $line;
+}
+
+
+sub PRINT {
+ my $cmd = shift;
+ my ($buf, $len, $offset) = @_;
+ $len ||= length($buf);
+ $offset += 0;
+ return unless $cmd->datasend(substr($buf, $offset, $len));
+ ${*$cmd}{'net_cmd_sending'}++; # flag that we should call dataend()
+ return $len;
+}
+
+
+sub CLOSE {
+ my $cmd = shift;
+ my $r = exists(${*$cmd}{'net_cmd_sending'}) ? $cmd->dataend : 1;
+ delete ${*$cmd}{'net_cmd_readbuf'};
+ delete ${*$cmd}{'net_cmd_sending'};
+ $r;
+}
+
+1;
+
+__END__
+
+
+=head1 NAME
+
+Net::Cmd - Network Command class (as used by FTP, SMTP etc)
+
+=head1 SYNOPSIS
+
+ use Net::Cmd;
+
+ @ISA = qw(Net::Cmd);
+
+=head1 DESCRIPTION
+
+C<Net::Cmd> is a collection of methods that can be inherited by a sub class
+of C<IO::Handle>. These methods implement the functionality required for a
+command based protocol, for example FTP and SMTP.
+
+=head1 USER METHODS
+
+These methods provide a user interface to the C<Net::Cmd> object.
+
+=over 4
+
+=item debug ( VALUE )
+
+Set the level of debug information for this object. If C<VALUE> is not given
+then the current state is returned. Otherwise the state is changed to
+C<VALUE> and the previous state returned.
+
+Different packages
+may implement different levels of debug but a non-zero value results in
+copies of all commands and responses also being sent to STDERR.
+
+If C<VALUE> is C<undef> then the debug level will be set to the default
+debug level for the class.
+
+This method can also be called as a I<static> method to set/get the default
+debug level for a given class.
+
+=item message ()
+
+Returns the text message returned from the last command. In a scalar
+context it returns a single string, in a list context it will return
+each line as a separate element. (See L<PSEUDO RESPONSES> below.)
+
+=item code ()
+
+Returns the 3-digit code from the last command. If a command is pending
+then the value 0 is returned. (See L<PSEUDO RESPONSES> below.)
+
+=item ok ()
+
+Returns non-zero if the last code value was greater than zero and
+less than 400. This holds true for most command servers. Servers
+where this does not hold may override this method.
+
+=item status ()
+
+Returns the most significant digit of the current status code. If a command
+is pending then C<CMD_PENDING> is returned.
+
+=item datasend ( DATA )
+
+Send data to the remote server, converting LF to CRLF. Any line starting
+with a '.' will be prefixed with another '.'.
+C<DATA> may be an array or a reference to an array.
+
+=item dataend ()
+
+End the sending of data to the remote server. This is done by ensuring that
+the data already sent ends with CRLF then sending '.CRLF' to end the
+transmission. Once this data has been sent C<dataend> calls C<response> and
+returns true if C<response> returns CMD_OK.
+
+=back
+
+=head1 CLASS METHODS
+
+These methods are not intended to be called by the user, but used or
+over-ridden by a sub-class of C<Net::Cmd>
+
+=over 4
+
+=item debug_print ( DIR, TEXT )
+
+Print debugging information. C<DIR> denotes the direction I<true> being
+data being sent to the server. Calls C<debug_text> before printing to
+STDERR.
+
+=item debug_text ( DIR, TEXT )
+
+This method is called to print debugging information. TEXT is
+the text being sent. The method should return the text to be printed.
+
+This is primarily meant for the use of modules such as FTP where passwords
+are sent, but we do not want to display them in the debugging information.
+
+=item command ( CMD [, ARGS, ... ])
+
+Send a command to the command server. All arguments are first joined with
+a space character and CRLF is appended, this string is then sent to the
+command server.
+
+Returns undef upon failure.
+
+=item unsupported ()
+
+Sets the status code to 580 and the response text to 'Unsupported command'.
+Returns zero.
+
+=item response ()
+
+Obtain a response from the server. Upon success the most significant digit
+of the status code is returned. Upon failure, timeout etc., I<CMD_ERROR> is
+returned.
+
+=item parse_response ( TEXT )
+
+This method is called by C<response> as a method with one argument. It should
+return an array of 2 values, the 3-digit status code and a flag which is true
+when this is part of a multi-line response and this line is not the last.
+
+=item getline ()
+
+Retrieve one line, delimited by CRLF, from the remote server. Returns I<undef>
+upon failure.
+
+B<NOTE>: If you do use this method for any reason, please remember to add
+some C<debug_print> calls into your method.
+
+=item ungetline ( TEXT )
+
+Unget a line of text from the server.
+
+=item rawdatasend ( DATA )
+
+Send data to the remote server without performing any conversions. C<DATA>
+is a scalar.
+
+=item read_until_dot ()
+
+Read data from the remote server until a line consisting of a single '.'.
+Any lines starting with '..' will have one of the '.'s removed.
+
+Returns a reference to a list containing the lines, or I<undef> upon failure.
+
+=item tied_fh ()
+
+Returns a filehandle tied to the Net::Cmd object. After issuing a
+command, you may read from this filehandle using read() or <>. The
+filehandle will return EOF when the final dot is encountered.
+Similarly, you may write to the filehandle in order to send data to
+the server after issuing a command that expects data to be written.
+
+See the Net::POP3 and Net::SMTP modules for examples of this.
+
+=back
+
+=head1 PSEUDO RESPONSES
+
+Normally the values returned by C<message()> and C<code()> are
+obtained from the remote server, but in a few circumstances, as
+detailed below, C<Net::Cmd> will return values that it sets. You
+can alter this behavior by overriding DEF_REPLY_CODE() to specify
+a different default reply code, or overriding one of the specific
+error handling methods below.
+
+=over 4
+
+=item Initial value
+
+Before any command has executed or if an unexpected error occurs
+C<code()> will return "421" (temporary connection failure) and
+C<message()> will return undef.
+
+=item Connection closed
+
+If the underlying C<IO::Handle> is closed, or if there are
+any read or write failures, the file handle will be forced closed,
+and C<code()> will return "421" (temporary connection failure)
+and C<message()> will return "[$pkg] Connection closed"
+(where $pkg is the name of the class that subclassed C<Net::Cmd>).
+The _set_status_closed() method can be overridden to set a different
+message (by calling set_status()) or otherwise trap this error.
+
+=item Timeout
+
+If there is a read or write timeout C<code()> will return "421"
+(temporary connection failure) and C<message()> will return
+"[$pkg] Timeout" (where $pkg is the name of the class
+that subclassed C<Net::Cmd>). The _set_status_timeout() method
+can be overridden to set a different message (by calling set_status())
+or otherwise trap this error.
+
+=back
+
+=head1 EXPORTS
+
+C<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>,
+C<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results
+of C<response> and C<status>. The sixth is C<CMD_PENDING>.
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.29_1 Copyright (c) 1995-2006 Graham Barr. All rights reserved.
+Changes in Version 2.29_2 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,322 @@
+# Net::Config.pm
+#
+# Versions up to 1.11 Copyright (c) 2000 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 1.11_01 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::Config;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Exporter;
+use Socket qw(inet_aton inet_ntoa);
+
+our @EXPORT = qw(%NetConfig);
+our @ISA = qw(Net::LocalCfg Exporter);
+our $VERSION = "3.04";
+
+our($CONFIGURE, $LIBNET_CFG);
+
+eval { local $SIG{__DIE__}; require Net::LocalCfg };
+
+our %NetConfig = (
+ nntp_hosts => [],
+ snpp_hosts => [],
+ pop3_hosts => [],
+ smtp_hosts => [],
+ ph_hosts => [],
+ daytime_hosts => [],
+ time_hosts => [],
+ inet_domain => undef,
+ ftp_firewall => undef,
+ ftp_ext_passive => 1,
+ ftp_int_passive => 1,
+ test_hosts => 1,
+ test_exist => 1,
+);
+
+#
+# Try to get as much configuration info as possible from InternetConfig
+#
+{
+## no critic (BuiltinFunctions::ProhibitStringyEval)
+$^O eq 'MacOS' and eval <<TRY_INTERNET_CONFIG;
+use Mac::InternetConfig;
+
+{
+my %nc = (
+ nntp_hosts => [ \$InternetConfig{ kICNNTPHost() } ],
+ pop3_hosts => [ \$InternetConfig{ kICMailAccount() } =~ /\@(.*)/ ],
+ smtp_hosts => [ \$InternetConfig{ kICSMTPHost() } ],
+ ftp_testhost => \$InternetConfig{ kICFTPHost() } ? \$InternetConfig{ kICFTPHost()} : undef,
+ ph_hosts => [ \$InternetConfig{ kICPhHost() } ],
+ ftp_ext_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0,
+ ftp_int_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0,
+ socks_hosts =>
+ \$InternetConfig{ kICUseSocks() } ? [ \$InternetConfig{ kICSocksHost() } ] : [],
+ ftp_firewall =>
+ \$InternetConfig{ kICUseFTPProxy() } ? [ \$InternetConfig{ kICFTPProxyHost() } ] : [],
+);
+\@NetConfig{keys %nc} = values %nc;
+}
+TRY_INTERNET_CONFIG
+}
+
+my $file = __FILE__;
+my $ref;
+$file =~ s/Config.pm/libnet.cfg/;
+if (-f $file) {
+ $ref = eval { local $SIG{__DIE__}; do $file };
+ if (ref($ref) eq 'HASH') {
+ %NetConfig = (%NetConfig, %{$ref});
+ $LIBNET_CFG = $file;
+ }
+}
+if ($< == $> and !$CONFIGURE) {
+ my $home = eval { local $SIG{__DIE__}; (getpwuid($>))[7] } || $ENV{HOME};
+ $home ||= $ENV{HOMEDRIVE} . ($ENV{HOMEPATH} || '') if defined $ENV{HOMEDRIVE};
+ if (defined $home) {
+ $file = $home . "/.libnetrc";
+ $ref = eval { local $SIG{__DIE__}; do $file } if -f $file;
+ %NetConfig = (%NetConfig, %{$ref})
+ if ref($ref) eq 'HASH';
+ }
+}
+my ($k, $v);
+while (($k, $v) = each %NetConfig) {
+ $NetConfig{$k} = [$v]
+ if ($k =~ /_hosts$/ and $k ne "test_hosts" and defined($v) and !ref($v));
+}
+
+# Take a hostname and determine if it is inside the firewall
+
+
+sub requires_firewall {
+ shift; # ignore package
+ my $host = shift;
+
+ return 0 unless defined $NetConfig{'ftp_firewall'};
+
+ $host = inet_aton($host) or return -1;
+ $host = inet_ntoa($host);
+
+ if (exists $NetConfig{'local_netmask'}) {
+ my $quad = unpack("N", pack("C*", split(/\./, $host)));
+ my $list = $NetConfig{'local_netmask'};
+ $list = [$list] unless ref($list);
+ foreach (@$list) {
+ my ($net, $bits) = (m#^(\d+\.\d+\.\d+\.\d+)/(\d+)$#) or next;
+ my $mask = ~0 << (32 - $bits);
+ my $addr = unpack("N", pack("C*", split(/\./, $net)));
+
+ return 0 if (($addr & $mask) == ($quad & $mask));
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+*is_external = \&requires_firewall;
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::Config - Local configuration data for libnet
+
+=head1 SYNOPSYS
+
+ use Net::Config qw(%NetConfig);
+
+=head1 DESCRIPTION
+
+C<Net::Config> holds configuration data for the modules in the libnet
+distribution. During installation you will be asked for these values.
+
+The configuration data is held globally in a file in the perl installation
+tree, but a user may override any of these values by providing their own. This
+can be done by having a C<.libnetrc> file in their home directory. This file
+should return a reference to a HASH containing the keys described below.
+For example
+
+ # .libnetrc
+ {
+ nntp_hosts => [ "my_preferred_host" ],
+ ph_hosts => [ "my_ph_server" ],
+ }
+ __END__
+
+=head1 METHODS
+
+C<Net::Config> defines the following methods. They are methods as they are
+invoked as class methods. This is because C<Net::Config> inherits from
+C<Net::LocalCfg> so you can override these methods if you want.
+
+=over 4
+
+=item requires_firewall ( HOST )
+
+Attempts to determine if a given host is outside your firewall. Possible
+return values are.
+
+ -1 Cannot lookup hostname
+ 0 Host is inside firewall (or there is no ftp_firewall entry)
+ 1 Host is outside the firewall
+
+This is done by using hostname lookup and the C<local_netmask> entry in
+the configuration data.
+
+=back
+
+=head1 NetConfig VALUES
+
+=over 4
+
+=item nntp_hosts
+
+=item snpp_hosts
+
+=item pop3_hosts
+
+=item smtp_hosts
+
+=item ph_hosts
+
+=item daytime_hosts
+
+=item time_hosts
+
+Each is a reference to an array of hostnames (in order of preference),
+which should be used for the given protocol
+
+=item inet_domain
+
+Your internet domain name
+
+=item ftp_firewall
+
+If you have an FTP proxy firewall (B<NOT> an HTTP or SOCKS firewall)
+then this value should be set to the firewall hostname. If your firewall
+does not listen to port 21, then this value should be set to
+C<"hostname:port"> (eg C<"hostname:99">)
+
+=item ftp_firewall_type
+
+There are many different ftp firewall products available. But unfortunately
+there is no standard for how to traverse a firewall. The list below shows the
+sequence of commands that Net::FTP will use
+
+ user Username for remote host
+ pass Password for remote host
+ fwuser Username for firewall
+ fwpass Password for firewall
+ remote.host The hostname of the remote ftp server
+
+=over 4
+
+=item 0Z<>
+
+There is no firewall
+
+=item 1Z<>
+
+ USER user@remote.host
+ PASS pass
+
+=item 2Z<>
+
+ USER fwuser
+ PASS fwpass
+ USER user@remote.host
+ PASS pass
+
+=item 3Z<>
+
+ USER fwuser
+ PASS fwpass
+ SITE remote.site
+ USER user
+ PASS pass
+
+=item 4Z<>
+
+ USER fwuser
+ PASS fwpass
+ OPEN remote.site
+ USER user
+ PASS pass
+
+=item 5Z<>
+
+ USER user@fwuser@remote.site
+ PASS pass@fwpass
+
+=item 6Z<>
+
+ USER fwuser@remote.site
+ PASS fwpass
+ USER user
+ PASS pass
+
+=item 7Z<>
+
+ USER user@remote.host
+ PASS pass
+ AUTH fwuser
+ RESP fwpass
+
+=back
+
+=item ftp_ext_passive
+
+=item ftp_int_passive
+
+FTP servers can work in passive or active mode. Active mode is when
+you want to transfer data you have to tell the server the address and
+port to connect to. Passive mode is when the server provide the
+address and port and you establish the connection.
+
+With some firewalls active mode does not work as the server cannot
+connect to your machine (because you are behind a firewall) and the firewall
+does not re-write the command. In this case you should set C<ftp_ext_passive>
+to a I<true> value.
+
+Some servers are configured to only work in passive mode. If you have
+one of these you can force C<Net::FTP> to always transfer in passive
+mode; when not going via a firewall, by setting C<ftp_int_passive> to
+a I<true> value.
+
+=item local_netmask
+
+A reference to a list of netmask strings in the form C<"134.99.4.0/24">.
+These are used by the C<requires_firewall> function to determine if a given
+host is inside or outside your firewall.
+
+=back
+
+The following entries are used during installation & testing on the
+libnet package
+
+=over 4
+
+=item test_hosts
+
+If true then C<make test> may attempt to connect to hosts given in the
+configuration.
+
+=item test_exists
+
+If true then C<Configure> will check each hostname given that it exists
+
+=back
+
+=cut
@@ -0,0 +1,362 @@
+# Net::Domain.pm
+#
+# Versions up to 2.21 Copyright (c) 1995-1998 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.22 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+# reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::Domain;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Exporter;
+use Net::Config;
+
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(hostname hostdomain hostfqdn domainname);
+our $VERSION = "3.04";
+
+my ($host, $domain, $fqdn) = (undef, undef, undef);
+
+# Try every conceivable way to get hostname.
+
+
+sub _hostname {
+
+ # we already know it
+ return $host
+ if (defined $host);
+
+ if ($^O eq 'MSWin32') {
+ require Socket;
+ my ($name, $alias, $type, $len, @addr) = gethostbyname($ENV{'COMPUTERNAME'} || 'localhost');
+ while (@addr) {
+ my $a = shift(@addr);
+ $host = gethostbyaddr($a, Socket::AF_INET());
+ last if defined $host;
+ }
+ if (defined($host) && index($host, '.') > 0) {
+ $fqdn = $host;
+ ($host, $domain) = $fqdn =~ /^([^.]+)\.(.*)$/;
+ }
+ return $host;
+ }
+ elsif ($^O eq 'MacOS') {
+ chomp($host = `hostname`);
+ }
+ elsif ($^O eq 'VMS') { ## multiple varieties of net s/w makes this hard
+ $host = $ENV{'UCX$INET_HOST'} if defined($ENV{'UCX$INET_HOST'});
+ $host = $ENV{'MULTINET_HOST_NAME'} if defined($ENV{'MULTINET_HOST_NAME'});
+ if (index($host, '.') > 0) {
+ $fqdn = $host;
+ ($host, $domain) = $fqdn =~ /^([^.]+)\.(.*)$/;
+ }
+ return $host;
+ }
+ else {
+ local $SIG{'__DIE__'};
+
+ # syscall is preferred since it avoids tainting problems
+ eval {
+ my $tmp = "\0" x 256; ## preload scalar
+ eval {
+ package main;
+ require "syscall.ph"; ## no critic (Modules::RequireBarewordIncludes)
+ defined(&main::SYS_gethostname);
+ }
+ || eval {
+ package main;
+ require "sys/syscall.ph"; ## no critic (Modules::RequireBarewordIncludes)
+ defined(&main::SYS_gethostname);
+ }
+ and $host =
+ (syscall(&main::SYS_gethostname, $tmp, 256) == 0)
+ ? $tmp
+ : undef;
+ }
+
+ # POSIX
+ || eval {
+ require POSIX;
+ $host = (POSIX::uname())[1];
+ }
+
+ # trusty old hostname command
+ || eval {
+ chop($host = `(hostname) 2>/dev/null`); # BSD'ish
+ }
+
+ # sysV/POSIX uname command (may truncate)
+ || eval {
+ chop($host = `uname -n 2>/dev/null`); ## SYSV'ish && POSIX'ish
+ }
+
+ # Apollo pre-SR10
+ || eval { $host = (split(/[:. ]/, `/com/host`, 6))[0]; }
+
+ || eval { $host = ""; };
+ }
+
+ # remove garbage
+ $host =~ s/[\0\r\n]+//go;
+ $host =~ s/(\A\.+|\.+\Z)//go;
+ $host =~ s/\.\.+/\./go;
+
+ $host;
+}
+
+
+sub _hostdomain {
+
+ # we already know it
+ return $domain
+ if (defined $domain);
+
+ local $SIG{'__DIE__'};
+
+ return $domain = $NetConfig{'inet_domain'}
+ if defined $NetConfig{'inet_domain'};
+
+ # try looking in /etc/resolv.conf
+ # putting this here and assuming that it is correct, eliminates
+ # calls to gethostbyname, and therefore DNS lookups. This helps
+ # those on dialup systems.
+
+ local ($_);
+
+ if (open(my $res, '<', "/etc/resolv.conf")) {
+ while (<$res>) {
+ $domain = $1
+ if (/\A\s*(?:domain|search)\s+(\S+)/);
+ }
+ close($res);
+
+ return $domain
+ if (defined $domain);
+ }
+
+ # just try hostname and system calls
+
+ my $host = _hostname();
+ my (@hosts);
+
+ @hosts = ($host, "localhost");
+
+ unless (defined($host) && $host =~ /\./) {
+ my $dom = undef;
+ eval {
+ my $tmp = "\0" x 256; ## preload scalar
+ eval {
+ package main;
+ require "syscall.ph"; ## no critic (Modules::RequireBarewordIncludes)
+ }
+ || eval {
+ package main;
+ require "sys/syscall.ph"; ## no critic (Modules::RequireBarewordIncludes)
+ }
+ and $dom =
+ (syscall(&main::SYS_getdomainname, $tmp, 256) == 0)
+ ? $tmp
+ : undef;
+ };
+
+ if ($^O eq 'VMS') {
+ $dom ||= $ENV{'TCPIP$INET_DOMAIN'}
+ || $ENV{'UCX$INET_DOMAIN'};
+ }
+
+ chop($dom = `domainname 2>/dev/null`)
+ unless (defined $dom || $^O =~ /^(?:cygwin|MSWin32|android)/);
+
+ if (defined $dom) {
+ my @h = ();
+ $dom =~ s/^\.+//;
+ while (length($dom)) {
+ push(@h, "$host.$dom");
+ $dom =~ s/^[^.]+.+// or last;
+ }
+ unshift(@hosts, @h);
+ }
+ }
+
+ # Attempt to locate FQDN
+
+ foreach (grep { defined $_ } @hosts) {
+ my @info = gethostbyname($_);
+
+ next unless @info;
+
+ # look at real name & aliases
+ foreach my $site ($info[0], split(/ /, $info[1])) {
+ if (rindex($site, ".") > 0) {
+
+ # Extract domain from FQDN
+
+ ($domain = $site) =~ s/\A[^.]+\.//;
+ return $domain;
+ }
+ }
+ }
+
+ # Look for environment variable
+
+ $domain ||= $ENV{LOCALDOMAIN} || $ENV{DOMAIN};
+
+ if (defined $domain) {
+ $domain =~ s/[\r\n\0]+//g;
+ $domain =~ s/(\A\.+|\.+\Z)//g;
+ $domain =~ s/\.\.+/\./g;
+ }
+
+ $domain;
+}
+
+
+sub domainname {
+
+ return $fqdn
+ if (defined $fqdn);
+
+ _hostname();
+
+ # *.local names are special on darwin. If we call gethostbyname below, it
+ # may hang while waiting for another, non-existent computer to respond.
+ if($^O eq 'darwin' && $host =~ /\.local$/) {
+ return $host;
+ }
+
+ _hostdomain();
+
+ # Assumption: If the host name does not contain a period
+ # and the domain name does, then assume that they are correct
+ # this helps to eliminate calls to gethostbyname, and therefore
+ # eliminate DNS lookups
+
+ return $fqdn = $host . "." . $domain
+ if (defined $host
+ and defined $domain
+ and $host !~ /\./
+ and $domain =~ /\./);
+
+ # For hosts that have no name, just an IP address
+ return $fqdn = $host if defined $host and $host =~ /^\d+(\.\d+){3}$/;
+
+ my @host = defined $host ? split(/\./, $host) : ('localhost');
+ my @domain = defined $domain ? split(/\./, $domain) : ();
+ my @fqdn = ();
+
+ # Determine from @host & @domain the FQDN
+
+ my @d = @domain;
+
+LOOP:
+ while (1) {
+ my @h = @host;
+ while (@h) {
+ my $tmp = join(".", @h, @d);
+ if ((gethostbyname($tmp))[0]) {
+ @fqdn = (@h, @d);
+ $fqdn = $tmp;
+ last LOOP;
+ }
+ pop @h;
+ }
+ last unless shift @d;
+ }
+
+ if (@fqdn) {
+ $host = shift @fqdn;
+ until ((gethostbyname($host))[0]) {
+ $host .= "." . shift @fqdn;
+ }
+ $domain = join(".", @fqdn);
+ }
+ else {
+ undef $host;
+ undef $domain;
+ undef $fqdn;
+ }
+
+ $fqdn;
+}
+
+
+sub hostfqdn { domainname() }
+
+
+sub hostname {
+ domainname()
+ unless (defined $host);
+ return $host;
+}
+
+
+sub hostdomain {
+ domainname()
+ unless (defined $domain);
+ return $domain;
+}
+
+1; # Keep require happy
+
+__END__
+
+=head1 NAME
+
+Net::Domain - Attempt to evaluate the current host's internet name and domain
+
+=head1 SYNOPSIS
+
+ use Net::Domain qw(hostname hostfqdn hostdomain domainname);
+
+=head1 DESCRIPTION
+
+Using various methods B<attempt> to find the Fully Qualified Domain Name (FQDN)
+of the current host. From this determine the host-name and the host-domain.
+
+Each of the functions will return I<undef> if the FQDN cannot be determined.
+
+=over 4
+
+=item hostfqdn ()
+
+Identify and return the FQDN of the current host.
+
+=item domainname ()
+
+An alias for hostfqdn ().
+
+=item hostname ()
+
+Returns the smallest part of the FQDN which can be used to identify the host.
+
+=item hostdomain ()
+
+Returns the remainder of the FQDN after the I<hostname> has been removed.
+
+=back
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>.
+Adapted from Sys::Hostname by David Sundstrom E<lt>F<sunds@asictest.sc.ti.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.21 Copyright (c) 1995-1998 Graham Barr. All rights reserved.
+Changes in Version 2.22 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,115 @@
+##
+## Package to read/write on ASCII data connections
+##
+
+package Net::FTP::A;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Net::FTP::dataconn;
+
+our @ISA = qw(Net::FTP::dataconn);
+our $VERSION = "3.04";
+
+our $buf;
+
+sub read {
+ my $data = shift;
+ local *buf = \$_[0];
+ shift;
+ my $size = shift || croak 'read($buf,$size,[$offset])';
+ my $timeout = @_ ? shift: $data->timeout;
+
+ if (length(${*$data}) < $size && !${*$data}{'net_ftp_eof'}) {
+ my $blksize = ${*$data}{'net_ftp_blksize'};
+ $blksize = $size if $size > $blksize;
+
+ my $l = 0;
+ my $n;
+
+ READ:
+ {
+ my $readbuf = defined(${*$data}{'net_ftp_cr'}) ? "\015" : '';
+
+ $data->can_read($timeout)
+ or croak "Timeout";
+
+ if ($n = sysread($data, $readbuf, $blksize, length $readbuf)) {
+ ${*$data}{'net_ftp_bytesread'} += $n;
+ ${*$data}{'net_ftp_cr'} =
+ substr($readbuf, -1) eq "\015"
+ ? chop($readbuf)
+ : undef;
+ }
+ else {
+ return
+ unless defined $n;
+
+ ${*$data}{'net_ftp_eof'} = 1;
+ }
+
+ $readbuf =~ s/\015\012/\n/sgo;
+ ${*$data} .= $readbuf;
+
+ unless (length(${*$data})) {
+
+ redo READ
+ if ($n > 0);
+
+ $size = length(${*$data})
+ if ($n == 0);
+ }
+ }
+ }
+
+ $buf = substr(${*$data}, 0, $size);
+ substr(${*$data}, 0, $size) = '';
+
+ length $buf;
+}
+
+
+sub write {
+ my $data = shift;
+ local *buf = \$_[0];
+ shift;
+ my $size = shift || croak 'write($buf,$size,[$timeout])';
+ my $timeout = @_ ? shift: $data->timeout;
+
+ my $nr = (my $tmp = substr($buf, 0, $size)) =~ tr/\r\n/\015\012/;
+ $tmp =~ s/(?<!\015)\012/\015\012/sg if $nr;
+ $tmp =~ s/^\015// if ${*$data}{'net_ftp_outcr'};
+ ${*$data}{'net_ftp_outcr'} = substr($tmp, -1) eq "\015";
+
+ # If the remote server has closed the connection we will be signal'd
+ # when we write. This can happen if the disk on the remote server fills up
+
+ local $SIG{PIPE} = 'IGNORE'
+ unless ($SIG{PIPE} || '') eq 'IGNORE'
+ or $^O eq 'MacOS';
+
+ my $len = length($tmp);
+ my $off = 0;
+ my $wrote = 0;
+
+ my $blksize = ${*$data}{'net_ftp_blksize'};
+
+ while ($len) {
+ $data->can_write($timeout)
+ or croak "Timeout";
+
+ $off += $wrote;
+ $wrote = syswrite($data, substr($tmp, $off), $len > $blksize ? $blksize : $len);
+ return
+ unless defined($wrote);
+ $len -= $wrote;
+ }
+
+ $size;
+}
+
+1;
@@ -0,0 +1,13 @@
+package Net::FTP::E;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Net::FTP::I;
+
+our @ISA = qw(Net::FTP::I);
+our $VERSION = "3.04";
+
+1;
@@ -0,0 +1,84 @@
+##
+## Package to read/write on BINARY data connections
+##
+
+package Net::FTP::I;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Net::FTP::dataconn;
+
+our @ISA = qw(Net::FTP::dataconn);
+our $VERSION = "3.04";
+
+our $buf;
+
+sub read {
+ my $data = shift;
+ local *buf = \$_[0];
+ shift;
+ my $size = shift || croak 'read($buf,$size,[$timeout])';
+ my $timeout = @_ ? shift: $data->timeout;
+
+ my $n;
+
+ if ($size > length ${*$data} and !${*$data}{'net_ftp_eof'}) {
+ $data->can_read($timeout)
+ or croak "Timeout";
+
+ my $blksize = ${*$data}{'net_ftp_blksize'};
+ $blksize = $size if $size > $blksize;
+
+ unless ($n = sysread($data, ${*$data}, $blksize, length ${*$data})) {
+ return unless defined $n;
+ ${*$data}{'net_ftp_eof'} = 1;
+ }
+ }
+
+ $buf = substr(${*$data}, 0, $size);
+
+ $n = length($buf);
+
+ substr(${*$data}, 0, $n) = '';
+
+ ${*$data}{'net_ftp_bytesread'} += $n;
+
+ $n;
+}
+
+
+sub write {
+ my $data = shift;
+ local *buf = \$_[0];
+ shift;
+ my $size = shift || croak 'write($buf,$size,[$timeout])';
+ my $timeout = @_ ? shift: $data->timeout;
+
+ # If the remote server has closed the connection we will be signal'd
+ # when we write. This can happen if the disk on the remote server fills up
+
+ local $SIG{PIPE} = 'IGNORE'
+ unless ($SIG{PIPE} || '') eq 'IGNORE'
+ or $^O eq 'MacOS';
+ my $sent = $size;
+ my $off = 0;
+
+ my $blksize = ${*$data}{'net_ftp_blksize'};
+ while ($sent > 0) {
+ $data->can_write($timeout)
+ or croak "Timeout";
+
+ my $n = syswrite($data, $buf, $sent > $blksize ? $blksize : $sent, $off);
+ return unless defined($n);
+ $sent -= $n;
+ $off += $n;
+ }
+
+ $size;
+}
+
+1;
@@ -0,0 +1,13 @@
+package Net::FTP::L;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Net::FTP::I;
+
+our @ISA = qw(Net::FTP::I);
+our $VERSION = "3.04";
+
+1;
@@ -0,0 +1,182 @@
+##
+## Generic data connection package
+##
+
+package Net::FTP::dataconn;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Errno;
+use Net::Cmd;
+
+our $VERSION = '3.04';
+
+$Net::FTP::IOCLASS or die "please load Net::FTP before Net::FTP::dataconn";
+our @ISA = $Net::FTP::IOCLASS;
+
+sub reading {
+ my $data = shift;
+ ${*$data}{'net_ftp_bytesread'} = 0;
+}
+
+
+sub abort {
+ my $data = shift;
+ my $ftp = ${*$data}{'net_ftp_cmd'};
+
+ # no need to abort if we have finished the xfer
+ return $data->close
+ if ${*$data}{'net_ftp_eof'};
+
+ # for some reason if we continuously open RETR connections and not
+ # read a single byte, then abort them after a while the server will
+ # close our connection, this prevents the unexpected EOF on the
+ # command channel -- GMB
+ if (exists ${*$data}{'net_ftp_bytesread'}
+ && (${*$data}{'net_ftp_bytesread'} == 0))
+ {
+ my $buf = "";
+ my $timeout = $data->timeout;
+ $data->can_read($timeout) && sysread($data, $buf, 1);
+ }
+
+ ${*$data}{'net_ftp_eof'} = 1; # fake
+
+ $ftp->abort; # this will close me
+}
+
+
+sub _close {
+ my $data = shift;
+ my $ftp = ${*$data}{'net_ftp_cmd'};
+
+ $data->SUPER::close();
+
+ delete ${*$ftp}{'net_ftp_dataconn'}
+ if defined $ftp
+ && exists ${*$ftp}{'net_ftp_dataconn'}
+ && $data == ${*$ftp}{'net_ftp_dataconn'};
+}
+
+
+sub close {
+ my $data = shift;
+ my $ftp = ${*$data}{'net_ftp_cmd'};
+
+ if (exists ${*$data}{'net_ftp_bytesread'} && !${*$data}{'net_ftp_eof'}) {
+ my $junk;
+ eval { local($SIG{__DIE__}); $data->read($junk, 1, 0) };
+ return $data->abort unless ${*$data}{'net_ftp_eof'};
+ }
+
+ $data->_close;
+
+ return unless defined $ftp;
+
+ $ftp->response() == CMD_OK
+ && $ftp->message =~ /unique file name:\s*(\S*)\s*\)/
+ && (${*$ftp}{'net_ftp_unique'} = $1);
+
+ $ftp->status == CMD_OK;
+}
+
+
+sub _select {
+ my ($data, $timeout, $do_read) = @_;
+ my ($rin, $rout, $win, $wout, $tout, $nfound);
+
+ vec($rin = '', fileno($data), 1) = 1;
+
+ ($win, $rin) = ($rin, $win) unless $do_read;
+
+ while (1) {
+ $nfound = select($rout = $rin, $wout = $win, undef, $tout = $timeout);
+
+ last if $nfound >= 0;
+
+ croak "select: $!"
+ unless $!{EINTR};
+ }
+
+ $nfound;
+}
+
+
+sub can_read {
+ _select(@_[0, 1], 1);
+}
+
+
+sub can_write {
+ _select(@_[0, 1], 0);
+}
+
+
+sub cmd {
+ my $ftp = shift;
+
+ ${*$ftp}{'net_ftp_cmd'};
+}
+
+
+sub bytes_read {
+ my $ftp = shift;
+
+ ${*$ftp}{'net_ftp_bytesread'} || 0;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::FTP::dataconn - FTP Client data connection class
+
+=head1 DESCRIPTION
+
+Some of the methods defined in C<Net::FTP> return an object which will
+be derived from this class. The dataconn class itself is derived from
+the C<IO::Socket::INET> class, so any normal IO operations can be performed.
+However the following methods are defined in the dataconn class and IO should
+be performed using these.
+
+=over 4
+
+=item read ( BUFFER, SIZE [, TIMEOUT ] )
+
+Read C<SIZE> bytes of data from the server and place it into C<BUFFER>, also
+performing any <CRLF> translation necessary. C<TIMEOUT> is optional, if not
+given, the timeout value from the command connection will be used.
+
+Returns the number of bytes read before any <CRLF> translation.
+
+=item write ( BUFFER, SIZE [, TIMEOUT ] )
+
+Write C<SIZE> bytes of data from C<BUFFER> to the server, also
+performing any <CRLF> translation necessary. C<TIMEOUT> is optional, if not
+given, the timeout value from the command connection will be used.
+
+Returns the number of bytes written before any <CRLF> translation.
+
+=item bytes_read ()
+
+Returns the number of bytes read so far.
+
+=item abort ()
+
+Abort the current data transfer.
+
+=item close ()
+
+Close the data connection and get a response from the FTP server. Returns
+I<true> if the connection was closed successfully and the first digit of
+the response from the server was a '2'.
+
+=back
+
+=cut
@@ -0,0 +1,2020 @@
+# Net::FTP.pm
+#
+# Versions up to 2.77_2 Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.77_3 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# Documentation (at end) improved 1996 by Nathan Torkington <gnat@frii.com>.
+
+package Net::FTP;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Fcntl qw(O_WRONLY O_RDONLY O_APPEND O_CREAT O_TRUNC);
+use IO::Socket;
+use Net::Cmd;
+use Net::Config;
+use Socket;
+use Time::Local;
+
+our $VERSION = '3.04';
+
+our $IOCLASS;
+BEGIN {
+ # Code for detecting if we can use SSL
+ my $ssl_class = eval {
+ require IO::Socket::SSL;
+ # first version with default CA on most platforms
+ no warnings 'numeric';
+ IO::Socket::SSL->VERSION(2.007);
+ } && 'IO::Socket::SSL';
+
+ my $nossl_warn = !$ssl_class &&
+ 'To use SSL please install IO::Socket::SSL with version>=2.007';
+
+ # Code for detecting if we can use IPv6
+ my $inet6_class = eval {
+ require IO::Socket::IP;
+ no warnings 'numeric';
+ IO::Socket::IP->VERSION(0.20);
+ } && 'IO::Socket::IP' || eval {
+ require IO::Socket::INET6;
+ no warnings 'numeric';
+ IO::Socket::INET6->VERSION(2.62);
+ } && 'IO::Socket::INET6';
+
+ sub can_ssl { $ssl_class };
+ sub can_inet6 { $inet6_class };
+
+ $IOCLASS = $ssl_class || $inet6_class || 'IO::Socket::INET';
+}
+
+our @ISA = ('Exporter','Net::Cmd',$IOCLASS);
+
+use constant TELNET_IAC => 255;
+use constant TELNET_IP => 244;
+use constant TELNET_DM => 242;
+
+use constant EBCDIC => $^O eq 'os390';
+
+sub new {
+ my $pkg = shift;
+ my ($peer, %arg);
+ if (@_ % 2) {
+ $peer = shift;
+ %arg = @_;
+ }
+ else {
+ %arg = @_;
+ $peer = delete $arg{Host};
+ }
+
+ my $host = $peer;
+ my $fire = undef;
+ my $fire_type = undef;
+
+ if (exists($arg{Firewall}) || Net::Config->requires_firewall($peer)) {
+ $fire = $arg{Firewall}
+ || $ENV{FTP_FIREWALL}
+ || $NetConfig{ftp_firewall}
+ || undef;
+
+ if (defined $fire) {
+ $peer = $fire;
+ delete $arg{Port};
+ $fire_type = $arg{FirewallType}
+ || $ENV{FTP_FIREWALL_TYPE}
+ || $NetConfig{firewall_type}
+ || undef;
+ }
+ }
+
+ my %tlsargs;
+ if (can_ssl()) {
+ # for name verification strip port from domain:port, ipv4:port, [ipv6]:port
+ (my $hostname = $host) =~s{(?<!:):\d+$}{};
+ %tlsargs = (
+ SSL_verifycn_scheme => 'ftp',
+ SSL_verifycn_name => $hostname,
+ # use SNI if supported by IO::Socket::SSL
+ $pkg->can_client_sni ? (SSL_hostname => $hostname):(),
+ # reuse SSL session of control connection in data connections
+ SSL_session_cache => Net::FTP::_SSL_SingleSessionCache->new,
+ );
+ # user defined SSL arg
+ $tlsargs{$_} = $arg{$_} for(grep { m{^SSL_} } keys %arg);
+
+ } elsif ($arg{SSL}) {
+ croak("IO::Socket::SSL >= 2.007 needed for SSL support");
+ }
+
+ my $ftp = $pkg->SUPER::new(
+ PeerAddr => $peer,
+ PeerPort => $arg{Port} || ($arg{SSL} ? 'ftps(990)' : 'ftp(21)'),
+ LocalAddr => $arg{'LocalAddr'},
+ Proto => 'tcp',
+ Timeout => defined $arg{Timeout} ? $arg{Timeout} : 120,
+ %tlsargs,
+ $arg{SSL} ? ():( SSL_startHandshake => 0 ),
+ ) or return;
+
+ ${*$ftp}{'net_ftp_host'} = $host; # Remote hostname
+ ${*$ftp}{'net_ftp_type'} = 'A'; # ASCII/binary/etc mode
+ ${*$ftp}{'net_ftp_blksize'} = abs($arg{'BlockSize'} || 10240);
+
+ ${*$ftp}{'net_ftp_localaddr'} = $arg{'LocalAddr'};
+
+ ${*$ftp}{'net_ftp_firewall'} = $fire
+ if (defined $fire);
+ ${*$ftp}{'net_ftp_firewall_type'} = $fire_type
+ if (defined $fire_type);
+
+ ${*$ftp}{'net_ftp_passive'} =
+ int exists $arg{Passive} ? $arg{Passive}
+ : exists $ENV{FTP_PASSIVE} ? $ENV{FTP_PASSIVE}
+ : defined $fire ? $NetConfig{ftp_ext_passive}
+ : $NetConfig{ftp_int_passive}; # Whew! :-)
+
+ ${*$ftp}{net_ftp_tlsargs} = \%tlsargs if %tlsargs;
+ if ($arg{SSL}) {
+ ${*$ftp}{net_ftp_tlsprot} = 'P';
+ ${*$ftp}{net_ftp_tlsdirect} = 1;
+ }
+
+ $ftp->hash(exists $arg{Hash} ? $arg{Hash} : 0, 1024);
+
+ $ftp->autoflush(1);
+
+ $ftp->debug(exists $arg{Debug} ? $arg{Debug} : undef);
+
+ unless ($ftp->response() == CMD_OK) {
+ $ftp->close();
+ # keep @$ if no message. Happens, when response did not start with a code.
+ $@ = $ftp->message || $@;
+ undef $ftp;
+ }
+
+ $ftp;
+}
+
+##
+## User interface methods
+##
+
+
+sub host {
+ my $me = shift;
+ ${*$me}{'net_ftp_host'};
+}
+
+sub passive {
+ my $ftp = shift;
+ return ${*$ftp}{'net_ftp_passive'} unless @_;
+ ${*$ftp}{'net_ftp_passive'} = shift;
+}
+
+
+sub hash {
+ my $ftp = shift; # self
+
+ my ($h, $b) = @_;
+ unless ($h) {
+ delete ${*$ftp}{'net_ftp_hash'};
+ return [\*STDERR, 0];
+ }
+ ($h, $b) = (ref($h) ? $h : \*STDERR, $b || 1024);
+ select((select($h), $| = 1)[0]);
+ $b = 512 if $b < 512;
+ ${*$ftp}{'net_ftp_hash'} = [$h, $b];
+}
+
+
+sub quit {
+ my $ftp = shift;
+
+ $ftp->_QUIT;
+ $ftp->close;
+}
+
+
+sub DESTROY { }
+
+
+sub ascii { shift->type('A', @_); }
+sub binary { shift->type('I', @_); }
+
+
+sub ebcdic {
+ carp "TYPE E is unsupported, shall default to I";
+ shift->type('E', @_);
+}
+
+
+sub byte {
+ carp "TYPE L is unsupported, shall default to I";
+ shift->type('L', @_);
+}
+
+# Allow the user to send a command directly, BE CAREFUL !!
+
+
+sub quot {
+ my $ftp = shift;
+ my $cmd = shift;
+
+ $ftp->command(uc $cmd, @_);
+ $ftp->response();
+}
+
+
+sub site {
+ my $ftp = shift;
+
+ $ftp->command("SITE", @_);
+ $ftp->response();
+}
+
+
+sub mdtm {
+ my $ftp = shift;
+ my $file = shift;
+
+ # Server Y2K bug workaround
+ #
+ # sigh; some idiotic FTP servers use ("19%d",tm.tm_year) instead of
+ # ("%d",tm.tm_year+1900). This results in an extra digit in the
+ # string returned. To account for this we allow an optional extra
+ # digit in the year. Then if the first two digits are 19 we use the
+ # remainder, otherwise we subtract 1900 from the whole year.
+
+ $ftp->_MDTM($file)
+ && $ftp->message =~ /((\d\d)(\d\d\d?))(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
+ ? timegm($8, $7, $6, $5, $4 - 1, $2 eq '19' ? $3 : ($1 - 1900))
+ : undef;
+}
+
+
+sub size {
+ my $ftp = shift;
+ my $file = shift;
+ my $io;
+ if ($ftp->supported("SIZE")) {
+ return $ftp->_SIZE($file)
+ ? ($ftp->message =~ /(\d+)\s*(bytes?\s*)?$/)[0]
+ : undef;
+ }
+ elsif ($ftp->supported("STAT")) {
+ my @msg;
+ return
+ unless $ftp->_STAT($file) && (@msg = $ftp->message) == 3;
+ foreach my $line (@msg) {
+ return (split(/\s+/, $line))[4]
+ if $line =~ /^[-rwxSsTt]{10}/;
+ }
+ }
+ else {
+ my @files = $ftp->dir($file);
+ if (@files) {
+ return (split(/\s+/, $1))[4]
+ if $files[0] =~ /^([-rwxSsTt]{10}.*)$/;
+ }
+ }
+ undef;
+}
+
+
+sub starttls {
+ my $ftp = shift;
+ can_ssl() or croak("IO::Socket::SSL >= 2.007 needed for SSL support");
+ $ftp->is_SSL and croak("called starttls within SSL session");
+ $ftp->_AUTH('TLS') == CMD_OK or return;
+
+ $ftp->connect_SSL or return;
+ $ftp->prot('P');
+ return 1;
+}
+
+sub prot {
+ my ($ftp,$prot) = @_;
+ $prot eq 'C' or $prot eq 'P' or croak("prot must by C or P");
+ $ftp->_PBSZ(0) or return;
+ $ftp->_PROT($prot) or return;
+ ${*$ftp}{net_ftp_tlsprot} = $prot;
+ return 1;
+}
+
+sub stoptls {
+ my $ftp = shift;
+ $ftp->is_SSL or croak("called stoptls outside SSL session");
+ ${*$ftp}{net_ftp_tlsdirect} and croak("cannot stoptls direct SSL session");
+ $ftp->_CCC() or return;
+ $ftp->stop_SSL();
+ return 1;
+}
+
+sub login {
+ my ($ftp, $user, $pass, $acct) = @_;
+ my ($ok, $ruser, $fwtype);
+
+ unless (defined $user) {
+ require Net::Netrc;
+
+ my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'});
+
+ ($user, $pass, $acct) = $rc->lpa()
+ if ($rc);
+ }
+
+ $user ||= "anonymous";
+ $ruser = $user;
+
+ $fwtype = ${*$ftp}{'net_ftp_firewall_type'}
+ || $NetConfig{'ftp_firewall_type'}
+ || 0;
+
+ if ($fwtype && defined ${*$ftp}{'net_ftp_firewall'}) {
+ if ($fwtype == 1 || $fwtype == 7) {
+ $user .= '@' . ${*$ftp}{'net_ftp_host'};
+ }
+ else {
+ require Net::Netrc;
+
+ my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
+
+ my ($fwuser, $fwpass, $fwacct) = $rc ? $rc->lpa() : ();
+
+ if ($fwtype == 5) {
+ $user = join('@', $user, $fwuser, ${*$ftp}{'net_ftp_host'});
+ $pass = $pass . '@' . $fwpass;
+ }
+ else {
+ if ($fwtype == 2) {
+ $user .= '@' . ${*$ftp}{'net_ftp_host'};
+ }
+ elsif ($fwtype == 6) {
+ $fwuser .= '@' . ${*$ftp}{'net_ftp_host'};
+ }
+
+ $ok = $ftp->_USER($fwuser);
+
+ return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
+
+ $ok = $ftp->_PASS($fwpass || "");
+
+ return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
+
+ $ok = $ftp->_ACCT($fwacct)
+ if defined($fwacct);
+
+ if ($fwtype == 3) {
+ $ok = $ftp->command("SITE", ${*$ftp}{'net_ftp_host'})->response;
+ }
+ elsif ($fwtype == 4) {
+ $ok = $ftp->command("OPEN", ${*$ftp}{'net_ftp_host'})->response;
+ }
+
+ return 0 unless $ok == CMD_OK || $ok == CMD_MORE;
+ }
+ }
+ }
+
+ $ok = $ftp->_USER($user);
+
+ # Some dumb firewalls don't prefix the connection messages
+ $ok = $ftp->response()
+ if ($ok == CMD_OK && $ftp->code == 220 && $user =~ /\@/);
+
+ if ($ok == CMD_MORE) {
+ unless (defined $pass) {
+ require Net::Netrc;
+
+ my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'}, $ruser);
+
+ ($ruser, $pass, $acct) = $rc->lpa()
+ if ($rc);
+
+ $pass = '-anonymous@'
+ if (!defined $pass && (!defined($ruser) || $ruser =~ /^anonymous/o));
+ }
+
+ $ok = $ftp->_PASS($pass || "");
+ }
+
+ $ok = $ftp->_ACCT($acct)
+ if (defined($acct) && ($ok == CMD_MORE || $ok == CMD_OK));
+
+ if ($fwtype == 7 && $ok == CMD_OK && defined ${*$ftp}{'net_ftp_firewall'}) {
+ my ($f, $auth, $resp) = _auth_id($ftp);
+ $ftp->authorize($auth, $resp) if defined($resp);
+ }
+
+ $ok == CMD_OK;
+}
+
+
+sub account {
+ @_ == 2 or croak 'usage: $ftp->account( ACCT )';
+ my $ftp = shift;
+ my $acct = shift;
+ $ftp->_ACCT($acct) == CMD_OK;
+}
+
+
+sub _auth_id {
+ my ($ftp, $auth, $resp) = @_;
+
+ unless (defined $resp) {
+ require Net::Netrc;
+
+ $auth ||= eval { (getpwuid($>))[0] } || $ENV{NAME};
+
+ my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}, $auth)
+ || Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'});
+
+ ($auth, $resp) = $rc->lpa()
+ if ($rc);
+ }
+ ($ftp, $auth, $resp);
+}
+
+
+sub authorize {
+ @_ >= 1 || @_ <= 3 or croak 'usage: $ftp->authorize( [AUTH [, RESP]])';
+
+ my ($ftp, $auth, $resp) = &_auth_id;
+
+ my $ok = $ftp->_AUTH($auth || "");
+
+ $ok = $ftp->_RESP($resp || "")
+ if ($ok == CMD_MORE);
+
+ $ok == CMD_OK;
+}
+
+
+sub rename {
+ @_ == 3 or croak 'usage: $ftp->rename(FROM, TO)';
+
+ my ($ftp, $from, $to) = @_;
+
+ $ftp->_RNFR($from)
+ && $ftp->_RNTO($to);
+}
+
+
+sub type {
+ my $ftp = shift;
+ my $type = shift;
+ my $oldval = ${*$ftp}{'net_ftp_type'};
+
+ return $oldval
+ unless (defined $type);
+
+ return
+ unless ($ftp->_TYPE($type, @_));
+
+ ${*$ftp}{'net_ftp_type'} = join(" ", $type, @_);
+
+ $oldval;
+}
+
+
+sub alloc {
+ my $ftp = shift;
+ my $size = shift;
+ my $oldval = ${*$ftp}{'net_ftp_allo'};
+
+ return $oldval
+ unless (defined $size);
+
+ return
+ unless ($ftp->supported("ALLO") and $ftp->_ALLO($size, @_));
+
+ ${*$ftp}{'net_ftp_allo'} = join(" ", $size, @_);
+
+ $oldval;
+}
+
+
+sub abort {
+ my $ftp = shift;
+
+ send($ftp, pack("CCC", TELNET_IAC, TELNET_IP, TELNET_IAC), MSG_OOB);
+
+ $ftp->command(pack("C", TELNET_DM) . "ABOR");
+
+ ${*$ftp}{'net_ftp_dataconn'}->close()
+ if defined ${*$ftp}{'net_ftp_dataconn'};
+
+ $ftp->response();
+
+ $ftp->status == CMD_OK;
+}
+
+
+sub get {
+ my ($ftp, $remote, $local, $where) = @_;
+
+ my ($loc, $len, $buf, $resp, $data);
+ local *FD;
+
+ my $localfd = ref($local) || ref(\$local) eq "GLOB";
+
+ ($local = $remote) =~ s#^.*/##
+ unless (defined $local);
+
+ croak("Bad remote filename '$remote'\n")
+ if $remote =~ /[\r\n]/s;
+
+ ${*$ftp}{'net_ftp_rest'} = $where if defined $where;
+ my $rest = ${*$ftp}{'net_ftp_rest'};
+
+ delete ${*$ftp}{'net_ftp_port'};
+ delete ${*$ftp}{'net_ftp_pasv'};
+
+ $data = $ftp->retr($remote)
+ or return;
+
+ if ($localfd) {
+ $loc = $local;
+ }
+ else {
+ $loc = \*FD;
+
+ unless (sysopen($loc, $local, O_CREAT | O_WRONLY | ($rest ? O_APPEND: O_TRUNC))) {
+ carp "Cannot open Local file $local: $!\n";
+ $data->abort;
+ return;
+ }
+ }
+
+ if ($ftp->type eq 'I' && !binmode($loc)) {
+ carp "Cannot binmode Local file $local: $!\n";
+ $data->abort;
+ close($loc) unless $localfd;
+ return;
+ }
+
+ $buf = '';
+ my ($count, $hashh, $hashb, $ref) = (0);
+
+ ($hashh, $hashb) = @$ref
+ if ($ref = ${*$ftp}{'net_ftp_hash'});
+
+ my $blksize = ${*$ftp}{'net_ftp_blksize'};
+ local $\; # Just in case
+
+ while (1) {
+ last unless $len = $data->read($buf, $blksize);
+
+ if (EBCDIC && $ftp->type ne 'I') {
+ $buf = $ftp->toebcdic($buf);
+ $len = length($buf);
+ }
+
+ if ($hashh) {
+ $count += $len;
+ print $hashh "#" x (int($count / $hashb));
+ $count %= $hashb;
+ }
+ unless (print $loc $buf) {
+ carp "Cannot write to Local file $local: $!\n";
+ $data->abort;
+ close($loc)
+ unless $localfd;
+ return;
+ }
+ }
+
+ print $hashh "\n" if $hashh;
+
+ unless ($localfd) {
+ unless (close($loc)) {
+ carp "Cannot close file $local (perhaps disk space) $!\n";
+ return;
+ }
+ }
+
+ unless ($data->close()) # implied $ftp->response
+ {
+ carp "Unable to close datastream";
+ return;
+ }
+
+ return $local;
+}
+
+
+sub cwd {
+ @_ == 1 || @_ == 2 or croak 'usage: $ftp->cwd( [ DIR ] )';
+
+ my ($ftp, $dir) = @_;
+
+ $dir = "/" unless defined($dir) && $dir =~ /\S/;
+
+ $dir eq ".."
+ ? $ftp->_CDUP()
+ : $ftp->_CWD($dir);
+}
+
+
+sub cdup {
+ @_ == 1 or croak 'usage: $ftp->cdup()';
+ $_[0]->_CDUP;
+}
+
+
+sub pwd {
+ @_ == 1 || croak 'usage: $ftp->pwd()';
+ my $ftp = shift;
+
+ $ftp->_PWD();
+ $ftp->_extract_path;
+}
+
+# rmdir( $ftp, $dir, [ $recurse ] )
+#
+# Removes $dir on remote host via FTP.
+# $ftp is handle for remote host
+#
+# If $recurse is TRUE, the directory and deleted recursively.
+# This means all of its contents and subdirectories.
+#
+# Initial version contributed by Dinkum Software
+#
+sub rmdir {
+ @_ == 2 || @_ == 3 or croak('usage: $ftp->rmdir( DIR [, RECURSE ] )');
+
+ # Pick off the args
+ my ($ftp, $dir, $recurse) = @_;
+ my $ok;
+
+ return $ok
+ if $ok = $ftp->_RMD($dir)
+ or !$recurse;
+
+ # Try to delete the contents
+ # Get a list of all the files in the directory
+ my @filelist = grep { !/^\.{1,2}$/ } $ftp->ls($dir);
+
+ return
+ unless @filelist; # failed, it is probably not a directory
+
+ return $ftp->delete($dir)
+ if @filelist == 1 and $dir eq $filelist[0];
+
+ # Go thru and delete each file or the directory
+ foreach my $file (map { m,/, ? $_ : "$dir/$_" } @filelist) {
+ next # successfully deleted the file
+ if $ftp->delete($file);
+
+ # Failed to delete it, assume its a directory
+ # Recurse and ignore errors, the final rmdir() will
+ # fail on any errors here
+ return $ok
+ unless $ok = $ftp->rmdir($file, 1);
+ }
+
+ # Directory should be empty
+ # Try to remove the directory again
+ # Pass results directly to caller
+ # If any of the prior deletes failed, this
+ # rmdir() will fail because directory is not empty
+ return $ftp->_RMD($dir);
+}
+
+
+sub restart {
+ @_ == 2 || croak 'usage: $ftp->restart( BYTE_OFFSET )';
+
+ my ($ftp, $where) = @_;
+
+ ${*$ftp}{'net_ftp_rest'} = $where;
+
+ return;
+}
+
+
+sub mkdir {
+ @_ == 2 || @_ == 3 or croak 'usage: $ftp->mkdir( DIR [, RECURSE ] )';
+
+ my ($ftp, $dir, $recurse) = @_;
+
+ $ftp->_MKD($dir) || $recurse
+ or return;
+
+ my $path = $dir;
+
+ unless ($ftp->ok) {
+ my @path = split(m#(?=/+)#, $dir);
+
+ $path = "";
+
+ while (@path) {
+ $path .= shift @path;
+
+ $ftp->_MKD($path);
+
+ $path = $ftp->_extract_path($path);
+ }
+
+ # If the creation of the last element was not successful, see if we
+ # can cd to it, if so then return path
+
+ unless ($ftp->ok) {
+ my ($status, $message) = ($ftp->status, $ftp->message);
+ my $pwd = $ftp->pwd;
+
+ if ($pwd && $ftp->cwd($dir)) {
+ $path = $dir;
+ $ftp->cwd($pwd);
+ }
+ else {
+ undef $path;
+ }
+ $ftp->set_status($status, $message);
+ }
+ }
+
+ $path;
+}
+
+
+sub delete {
+ @_ == 2 || croak 'usage: $ftp->delete( FILENAME )';
+
+ $_[0]->_DELE($_[1]);
+}
+
+
+sub put { shift->_store_cmd("stor", @_) }
+sub put_unique { shift->_store_cmd("stou", @_) }
+sub append { shift->_store_cmd("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 _store_cmd {
+ my ($ftp, $cmd, $local, $remote) = @_;
+ my ($loc, $sock, $len, $buf);
+ local *FD;
+
+ my $localfd = ref($local) || ref(\$local) eq "GLOB";
+
+ if (!defined($remote) and 'STOU' ne uc($cmd)) {
+ croak 'Must specify remote filename with stream input'
+ if $localfd;
+
+ require File::Basename;
+ $remote = File::Basename::basename($local);
+ }
+ if (defined ${*$ftp}{'net_ftp_allo'}) {
+ delete ${*$ftp}{'net_ftp_allo'};
+ }
+ else {
+
+ # if the user hasn't already invoked the alloc method since the last
+ # _store_cmd call, figure out if the local file is a regular file(not
+ # a pipe, or device) and if so get the file size from stat, and send
+ # an ALLO command before sending the STOR, STOU, or APPE command.
+ my $size = do { local $^W; -f $local && -s _ }; # no ALLO if sending data from a pipe
+ ${*$ftp}{'net_ftp_allo'} = $size if $size;
+ }
+ croak("Bad remote filename '$remote'\n")
+ if defined($remote) and $remote =~ /[\r\n]/s;
+
+ if ($localfd) {
+ $loc = $local;
+ }
+ else {
+ $loc = \*FD;
+
+ unless (sysopen($loc, $local, O_RDONLY)) {
+ carp "Cannot open Local file $local: $!\n";
+ return;
+ }
+ }
+
+ if ($ftp->type eq 'I' && !binmode($loc)) {
+ carp "Cannot binmode Local file $local: $!\n";
+ return;
+ }
+
+ delete ${*$ftp}{'net_ftp_port'};
+ delete ${*$ftp}{'net_ftp_pasv'};
+
+ $sock = $ftp->_data_cmd($cmd, grep { defined } $remote)
+ or return;
+
+ $remote = ($ftp->message =~ /\w+\s*:\s*(.*)/)[0]
+ if 'STOU' eq uc $cmd;
+
+ my $blksize = ${*$ftp}{'net_ftp_blksize'};
+
+ my ($count, $hashh, $hashb, $ref) = (0);
+
+ ($hashh, $hashb) = @$ref
+ if ($ref = ${*$ftp}{'net_ftp_hash'});
+
+ while (1) {
+ last unless $len = read($loc, $buf = "", $blksize);
+
+ if (EBCDIC && $ftp->type ne 'I') {
+ $buf = $ftp->toascii($buf);
+ $len = length($buf);
+ }
+
+ if ($hashh) {
+ $count += $len;
+ print $hashh "#" x (int($count / $hashb));
+ $count %= $hashb;
+ }
+
+ my $wlen;
+ unless (defined($wlen = $sock->write($buf, $len)) && $wlen == $len) {
+ $sock->abort;
+ close($loc)
+ unless $localfd;
+ print $hashh "\n" if $hashh;
+ return;
+ }
+ }
+
+ print $hashh "\n" if $hashh;
+
+ close($loc)
+ unless $localfd;
+
+ $sock->close()
+ or return;
+
+ if ('STOU' eq uc $cmd and $ftp->message =~ m/unique\s+file\s*name\s*:\s*(.*)\)|"(.*)"/) {
+ require File::Basename;
+ $remote = File::Basename::basename($+);
+ }
+
+ return $remote;
+}
+
+
+sub port {
+ @_ == 1 || @_ == 2 or croak 'usage: $self->port([PORT])';
+ return _eprt('PORT',@_);
+}
+
+sub eprt {
+ @_ == 1 || @_ == 2 or croak 'usage: $self->eprt([PORT])';
+ return _eprt('EPRT',@_);
+}
+
+sub _eprt {
+ my ($cmd,$ftp,$port) = @_;
+ delete ${*$ftp}{net_ftp_intern_port};
+ unless ($port) {
+ my $listen = ${*$ftp}{net_ftp_listen} ||= $IOCLASS->new(
+ Listen => 1,
+ Timeout => $ftp->timeout,
+ LocalAddr => $ftp->sockhost,
+ can_ssl() ? (
+ %{ ${*$ftp}{net_ftp_tlsargs} },
+ SSL_startHandshake => 0,
+ ):(),
+ );
+ ${*$ftp}{net_ftp_intern_port} = 1;
+ my $fam = ($listen->sockdomain == AF_INET) ? 1:2;
+ if ( $cmd eq 'EPRT' || $fam == 2 ) {
+ $port = "|$fam|".$listen->sockhost."|".$listen->sockport."|";
+ $cmd = 'EPRT';
+ } else {
+ my $p = $listen->sockport;
+ $port = join(',',split(m{\.},$listen->sockhost),$p >> 8,$p & 0xff);
+ }
+ } elsif (ref($port) eq 'ARRAY') {
+ $port = join(',',split(m{\.},@$port[0]),@$port[1] >> 8,@$port[1] & 0xff);
+ }
+ my $ok = $cmd eq 'EPRT' ? $ftp->_EPRT($port) : $ftp->_PORT($port);
+ ${*$ftp}{net_ftp_port} = $port if $ok;
+ return $ok;
+}
+
+
+sub ls { shift->_list_cmd("NLST", @_); }
+sub dir { shift->_list_cmd("LIST", @_); }
+
+
+sub pasv {
+ my $ftp = shift;
+ @_ and croak 'usage: $ftp->port()';
+ return $ftp->epsv if $ftp->sockdomain != AF_INET;
+ delete ${*$ftp}{net_ftp_intern_port};
+
+ if ( $ftp->_PASV &&
+ $ftp->message =~ m{(\d+,\d+,\d+,\d+),(\d+),(\d+)} ) {
+ my $port = 256 * $2 + $3;
+ ( my $ip = $1 ) =~s{,}{.}g;
+ return ${*$ftp}{net_ftp_pasv} = [ $ip,$port ];
+ }
+ return;
+}
+
+sub epsv {
+ my $ftp = shift;
+ @_ and croak 'usage: $ftp->epsv()';
+ delete ${*$ftp}{net_ftp_intern_port};
+
+ $ftp->_EPSV && $ftp->message =~ m{\(([\x33-\x7e])\1\1(\d+)\1\)}
+ ? ${*$ftp}{net_ftp_pasv} = [ $ftp->peerhost, $2 ]
+ : undef;
+}
+
+
+sub unique_name {
+ my $ftp = shift;
+ ${*$ftp}{'net_ftp_unique'} || undef;
+}
+
+
+sub supported {
+ @_ == 2 or croak 'usage: $ftp->supported( CMD )';
+ my $ftp = shift;
+ my $cmd = uc shift;
+ my $hash = ${*$ftp}{'net_ftp_supported'} ||= {};
+
+ return $hash->{$cmd}
+ if exists $hash->{$cmd};
+
+ return $hash->{$cmd} = 1
+ if $ftp->feature($cmd);
+
+ return $hash->{$cmd} = 0
+ unless $ftp->_HELP($cmd);
+
+ my $text = $ftp->message;
+ if ($text =~ /following.+commands/i) {
+ $text =~ s/^.*\n//;
+ while ($text =~ /(\*?)(\w+)(\*?)/sg) {
+ $hash->{"\U$2"} = !length("$1$3");
+ }
+ }
+ else {
+ $hash->{$cmd} = $text !~ /unimplemented/i;
+ }
+
+ $hash->{$cmd} ||= 0;
+}
+
+##
+## Deprecated methods
+##
+
+
+sub lsl {
+ carp "Use of Net::FTP::lsl deprecated, use 'dir'"
+ if $^W;
+ goto &dir;
+}
+
+
+sub authorise {
+ carp "Use of Net::FTP::authorise deprecated, use 'authorize'"
+ if $^W;
+ goto &authorize;
+}
+
+
+##
+## Private methods
+##
+
+
+sub _extract_path {
+ my ($ftp, $path) = @_;
+
+ # This tries to work both with and without the quote doubling
+ # convention (RFC 959 requires it, but the first 3 servers I checked
+ # didn't implement it). It will fail on a server which uses a quote in
+ # the message which isn't a part of or surrounding the path.
+ $ftp->ok
+ && $ftp->message =~ /(?:^|\s)\"(.*)\"(?:$|\s)/
+ && ($path = $1) =~ s/\"\"/\"/g;
+
+ $path;
+}
+
+##
+## Communication methods
+##
+
+
+sub _dataconn {
+ my $ftp = shift;
+ my $pkg = "Net::FTP::" . $ftp->type;
+ eval "require " . $pkg ## no critic (BuiltinFunctions::ProhibitStringyEval)
+ or croak("cannot load $pkg required for type ".$ftp->type);
+ $pkg =~ s/ /_/g;
+ delete ${*$ftp}{net_ftp_dataconn};
+
+ my $conn;
+ my $pasv = ${*$ftp}{net_ftp_pasv};
+ if ($pasv) {
+ $conn = $pkg->new(
+ PeerAddr => $pasv->[0],
+ PeerPort => $pasv->[1],
+ LocalAddr => ${*$ftp}{net_ftp_localaddr},
+ Timeout => $ftp->timeout,
+ can_ssl() ? (
+ SSL_startHandshake => 0,
+ $ftp->is_SSL ? (
+ SSL_reuse_ctx => $ftp,
+ SSL_verifycn_name => ${*$ftp}{net_ftp_tlsargs}{SSL_verifycn_name},
+ # This will cause the use of SNI if supported by IO::Socket::SSL.
+ $ftp->can_client_sni ? (
+ SSL_hostname => ${*$ftp}{net_ftp_tlsargs}{SSL_hostname}
+ ):(),
+ ) :( %{${*$ftp}{net_ftp_tlsargs}} ),
+ ):(),
+ ) or return;
+ } elsif (my $listen = delete ${*$ftp}{net_ftp_listen}) {
+ $conn = $listen->accept($pkg) or return;
+ $conn->timeout($ftp->timeout);
+ close($listen);
+ } else {
+ croak("no listener in active mode");
+ }
+
+ if (( ${*$ftp}{net_ftp_tlsprot} || '') eq 'P') {
+ if ($conn->connect_SSL) {
+ # SSL handshake ok
+ } else {
+ carp("failed to ssl upgrade dataconn: $IO::Socket::SSL::SSL_ERROR");
+ return;
+ }
+ }
+
+ ${*$ftp}{net_ftp_dataconn} = $conn;
+ ${*$conn} = "";
+ ${*$conn}{net_ftp_cmd} = $ftp;
+ ${*$conn}{net_ftp_blksize} = ${*$ftp}{net_ftp_blksize};
+ return $conn;
+}
+
+
+sub _list_cmd {
+ my $ftp = shift;
+ my $cmd = uc shift;
+
+ delete ${*$ftp}{'net_ftp_port'};
+ delete ${*$ftp}{'net_ftp_pasv'};
+
+ my $data = $ftp->_data_cmd($cmd, @_);
+
+ return
+ unless (defined $data);
+
+ require Net::FTP::A;
+ bless $data, "Net::FTP::A"; # Force ASCII mode
+
+ my $databuf = '';
+ my $buf = '';
+ my $blksize = ${*$ftp}{'net_ftp_blksize'};
+
+ while ($data->read($databuf, $blksize)) {
+ $buf .= $databuf;
+ }
+
+ my $list = [split(/\n/, $buf)];
+
+ $data->close();
+
+ if (EBCDIC) {
+ for (@$list) { $_ = $ftp->toebcdic($_) }
+ }
+
+ wantarray
+ ? @{$list}
+ : $list;
+}
+
+
+sub _data_cmd {
+ my $ftp = shift;
+ my $cmd = uc shift;
+ my $ok = 1;
+ my $where = delete ${*$ftp}{'net_ftp_rest'} || 0;
+ my $arg;
+
+ for my $arg (@_) {
+ croak("Bad argument '$arg'\n")
+ if $arg =~ /[\r\n]/s;
+ }
+
+ if ( ${*$ftp}{'net_ftp_passive'}
+ && !defined ${*$ftp}{'net_ftp_pasv'}
+ && !defined ${*$ftp}{'net_ftp_port'})
+ {
+ return unless defined $ftp->pasv;
+
+ if ($where and !$ftp->_REST($where)) {
+ my ($status, $message) = ($ftp->status, $ftp->message);
+ $ftp->abort;
+ $ftp->set_status($status, $message);
+ return;
+ }
+
+ # first send command, then open data connection
+ # otherwise the peer might not do a full accept (with SSL
+ # handshake if PROT P)
+ $ftp->command($cmd, @_);
+ my $data = $ftp->_dataconn();
+ if (CMD_INFO == $ftp->response()) {
+ $data->reading
+ if $data && $cmd =~ /RETR|LIST|NLST/;
+ return $data;
+ }
+ $data->_close if $data;
+
+ return;
+ }
+
+ $ok = $ftp->port
+ unless (defined ${*$ftp}{'net_ftp_port'}
+ || defined ${*$ftp}{'net_ftp_pasv'});
+
+ $ok = $ftp->_REST($where)
+ if $ok && $where;
+
+ return
+ unless $ok;
+
+ if ($cmd =~ /(STOR|APPE|STOU)/ and exists ${*$ftp}{net_ftp_allo} and
+ $ftp->supported("ALLO"))
+ {
+ $ftp->_ALLO(delete ${*$ftp}{net_ftp_allo})
+ or return;
+ }
+
+ $ftp->command($cmd, @_);
+
+ return 1
+ if (defined ${*$ftp}{'net_ftp_pasv'});
+
+ $ok = CMD_INFO == $ftp->response();
+
+ return $ok
+ unless exists ${*$ftp}{'net_ftp_intern_port'};
+
+ if ($ok) {
+ my $data = $ftp->_dataconn();
+
+ $data->reading
+ if $data && $cmd =~ /RETR|LIST|NLST/;
+
+ return $data;
+ }
+
+
+ close(delete ${*$ftp}{'net_ftp_listen'});
+
+ return;
+}
+
+##
+## Over-ride methods (Net::Cmd)
+##
+
+
+sub debug_text { $_[2] =~ /^(pass|resp|acct)/i ? "$1 ....\n" : $_[2]; }
+
+
+sub command {
+ my $ftp = shift;
+
+ delete ${*$ftp}{'net_ftp_port'};
+ $ftp->SUPER::command(@_);
+}
+
+
+sub response {
+ my $ftp = shift;
+ my $code = $ftp->SUPER::response() || 5; # assume 500 if undef
+
+ delete ${*$ftp}{'net_ftp_pasv'}
+ if ($code != CMD_MORE && $code != CMD_INFO);
+
+ $code;
+}
+
+
+sub parse_response {
+ return ($1, $2 eq "-")
+ if $_[1] =~ s/^(\d\d\d)([- ]?)//o;
+
+ my $ftp = shift;
+
+ # Darn MS FTP server is a load of CRAP !!!!
+ # Expect to see undef here.
+ return ()
+ unless 0 + (${*$ftp}{'net_cmd_code'} || 0);
+
+ (${*$ftp}{'net_cmd_code'}, 1);
+}
+
+##
+## Allow 2 servers to talk directly
+##
+
+
+sub pasv_xfer_unique {
+ my ($sftp, $sfile, $dftp, $dfile) = @_;
+ $sftp->pasv_xfer($sfile, $dftp, $dfile, 1);
+}
+
+
+sub pasv_xfer {
+ my ($sftp, $sfile, $dftp, $dfile, $unique) = @_;
+
+ ($dfile = $sfile) =~ s#.*/##
+ unless (defined $dfile);
+
+ my $port = $sftp->pasv
+ or return;
+
+ $dftp->port($port)
+ or return;
+
+ return
+ unless ($unique ? $dftp->stou($dfile) : $dftp->stor($dfile));
+
+ unless ($sftp->retr($sfile) && $sftp->response == CMD_INFO) {
+ $sftp->retr($sfile);
+ $dftp->abort;
+ $dftp->response();
+ return;
+ }
+
+ $dftp->pasv_wait($sftp);
+}
+
+
+sub pasv_wait {
+ @_ == 2 or croak 'usage: $ftp->pasv_wait(NON_PASV_FTP)';
+
+ my ($ftp, $non_pasv) = @_;
+ my ($file, $rin, $rout);
+
+ vec($rin = '', fileno($ftp), 1) = 1;
+ select($rout = $rin, undef, undef, undef);
+
+ my $dres = $ftp->response();
+ my $sres = $non_pasv->response();
+
+ return
+ unless $dres == CMD_OK && $sres == CMD_OK;
+
+ return
+ unless $ftp->ok() && $non_pasv->ok();
+
+ return $1
+ if $ftp->message =~ /unique file name:\s*(\S*)\s*\)/;
+
+ return $1
+ if $non_pasv->message =~ /unique file name:\s*(\S*)\s*\)/;
+
+ return 1;
+}
+
+
+sub feature {
+ @_ == 2 or croak 'usage: $ftp->feature( NAME )';
+ my ($ftp, $feat) = @_;
+
+ my $feature = ${*$ftp}{net_ftp_feature} ||= do {
+ my @feat;
+
+ # Example response
+ # 211-Features:
+ # MDTM
+ # REST STREAM
+ # SIZE
+ # 211 End
+
+ @feat = map { /^\s+(.*\S)/ } $ftp->message
+ if $ftp->_FEAT;
+
+ \@feat;
+ };
+
+ return grep { /^\Q$feat\E\b/i } @$feature;
+}
+
+
+sub cmd { shift->command(@_)->response() }
+
+########################################
+#
+# RFC959 + RFC2428 + RFC4217 commands
+#
+
+
+sub _ABOR { shift->command("ABOR")->response() == CMD_OK }
+sub _ALLO { shift->command("ALLO", @_)->response() == CMD_OK }
+sub _CDUP { shift->command("CDUP")->response() == CMD_OK }
+sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
+sub _PASV { shift->command("PASV")->response() == CMD_OK }
+sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
+sub _DELE { shift->command("DELE", @_)->response() == CMD_OK }
+sub _CWD { shift->command("CWD", @_)->response() == CMD_OK }
+sub _PORT { shift->command("PORT", @_)->response() == CMD_OK }
+sub _RMD { shift->command("RMD", @_)->response() == CMD_OK }
+sub _MKD { shift->command("MKD", @_)->response() == CMD_OK }
+sub _PWD { shift->command("PWD", @_)->response() == CMD_OK }
+sub _TYPE { shift->command("TYPE", @_)->response() == CMD_OK }
+sub _RNTO { shift->command("RNTO", @_)->response() == CMD_OK }
+sub _RESP { shift->command("RESP", @_)->response() == CMD_OK }
+sub _MDTM { shift->command("MDTM", @_)->response() == CMD_OK }
+sub _SIZE { shift->command("SIZE", @_)->response() == CMD_OK }
+sub _HELP { shift->command("HELP", @_)->response() == CMD_OK }
+sub _STAT { shift->command("STAT", @_)->response() == CMD_OK }
+sub _FEAT { shift->command("FEAT", @_)->response() == CMD_OK }
+sub _PBSZ { shift->command("PBSZ", @_)->response() == CMD_OK }
+sub _PROT { shift->command("PROT", @_)->response() == CMD_OK }
+sub _CCC { shift->command("CCC", @_)->response() == CMD_OK }
+sub _EPRT { shift->command("EPRT", @_)->response() == CMD_OK }
+sub _EPSV { shift->command("EPSV", @_)->response() == CMD_OK }
+sub _APPE { shift->command("APPE", @_)->response() == CMD_INFO }
+sub _LIST { shift->command("LIST", @_)->response() == CMD_INFO }
+sub _NLST { shift->command("NLST", @_)->response() == CMD_INFO }
+sub _RETR { shift->command("RETR", @_)->response() == CMD_INFO }
+sub _STOR { shift->command("STOR", @_)->response() == CMD_INFO }
+sub _STOU { shift->command("STOU", @_)->response() == CMD_INFO }
+sub _RNFR { shift->command("RNFR", @_)->response() == CMD_MORE }
+sub _REST { shift->command("REST", @_)->response() == CMD_MORE }
+sub _PASS { shift->command("PASS", @_)->response() }
+sub _ACCT { shift->command("ACCT", @_)->response() }
+sub _AUTH { shift->command("AUTH", @_)->response() }
+
+
+sub _USER {
+ my $ftp = shift;
+ my $ok = $ftp->command("USER", @_)->response();
+
+ # A certain brain dead firewall :-)
+ $ok = $ftp->command("user", @_)->response()
+ unless $ok == CMD_MORE or $ok == CMD_OK;
+
+ $ok;
+}
+
+
+sub _SMNT { shift->unsupported(@_) }
+sub _MODE { shift->unsupported(@_) }
+sub _SYST { shift->unsupported(@_) }
+sub _STRU { shift->unsupported(@_) }
+sub _REIN { shift->unsupported(@_) }
+
+{
+ # Session Cache with single entry
+ # used to make sure that we reuse same session for control and data channels
+ package Net::FTP::_SSL_SingleSessionCache;
+ sub new { my $x; return bless \$x,shift }
+ sub add_session {
+ my ($cache,$key,$session) = @_;
+ Net::SSLeay::SESSION_free($$cache) if $$cache;
+ $$cache = $session;
+ }
+ sub get_session {
+ my $cache = shift;
+ return $$cache
+ }
+ sub DESTROY {
+ my $cache = shift;
+ Net::SSLeay::SESSION_free($$cache) if $$cache;
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::FTP - FTP Client class
+
+=head1 SYNOPSIS
+
+ use Net::FTP;
+
+ $ftp = Net::FTP->new("some.host.name", Debug => 0)
+ or die "Cannot connect to some.host.name: $@";
+
+ $ftp->login("anonymous",'-anonymous@')
+ or die "Cannot login ", $ftp->message;
+
+ $ftp->cwd("/pub")
+ or die "Cannot change working directory ", $ftp->message;
+
+ $ftp->get("that.file")
+ or die "get failed ", $ftp->message;
+
+ $ftp->quit;
+
+=head1 DESCRIPTION
+
+C<Net::FTP> is a class implementing a simple FTP client in Perl as
+described in RFC959. It provides wrappers for a subset of the RFC959
+commands.
+
+The Net::FTP class is a subclass of Net::Cmd and IO::Socket::INET.
+
+=head1 OVERVIEW
+
+FTP stands for File Transfer Protocol. It is a way of transferring
+files between networked machines. The protocol defines a client
+(whose commands are provided by this module) and a server (not
+implemented in this module). Communication is always initiated by the
+client, and the server responds with a message and a status code (and
+sometimes with data).
+
+The FTP protocol allows files to be sent to or fetched from the
+server. Each transfer involves a B<local file> (on the client) and a
+B<remote file> (on the server). In this module, the same file name
+will be used for both local and remote if only one is specified. This
+means that transferring remote file C</path/to/file> will try to put
+that file in C</path/to/file> locally, unless you specify a local file
+name.
+
+The protocol also defines several standard B<translations> which the
+file can undergo during transfer. These are ASCII, EBCDIC, binary,
+and byte. ASCII is the default type, and indicates that the sender of
+files will translate the ends of lines to a standard representation
+which the receiver will then translate back into their local
+representation. EBCDIC indicates the file being transferred is in
+EBCDIC format. Binary (also known as image) format sends the data as
+a contiguous bit stream. Byte format transfers the data as bytes, the
+values of which remain the same regardless of differences in byte size
+between the two machines (in theory - in practice you should only use
+this if you really know what you're doing). This class does not support
+the EBCDIC or byte formats, and will default to binary instead if they
+are attempted.
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ([ HOST ] [, OPTIONS ])
+
+This is the constructor for a new Net::FTP object. C<HOST> is the
+name of the remote host to which an FTP connection is required.
+
+C<HOST> is optional. If C<HOST> is not given then it may instead be
+passed as the C<Host> option described below.
+
+C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
+Possible options are:
+
+B<Host> - FTP host to connect to. It may be a single scalar, as defined for
+the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
+an array with hosts to try in turn. The L</host> method will return the value
+which was used to connect to the host.
+
+B<Firewall> - The name of a machine which acts as an FTP firewall. This can be
+overridden by an environment variable C<FTP_FIREWALL>. If specified, and the
+given host cannot be directly connected to, then the
+connection is made to the firewall machine and the string C<@hostname> is
+appended to the login identifier. This kind of setup is also referred to
+as an ftp proxy.
+
+B<FirewallType> - The type of firewall running on the machine indicated by
+B<Firewall>. This can be overridden by an environment variable
+C<FTP_FIREWALL_TYPE>. For a list of permissible types, see the description of
+ftp_firewall_type in L<Net::Config>.
+
+B<BlockSize> - This is the block size that Net::FTP will use when doing
+transfers. (defaults to 10240)
+
+B<Port> - The port number to connect to on the remote machine for the
+FTP connection
+
+B<SSL> - If the connection should be done from start with SSL, contrary to later
+upgrade with C<starttls>.
+
+B<SSL_*> - SSL arguments which will be applied when upgrading the control or
+data connection to SSL. You can use SSL arguments as documented in
+L<IO::Socket::SSL>, but it will usually use the right arguments already.
+
+B<Timeout> - Set a timeout value in seconds (defaults to 120)
+
+B<Debug> - debug level (see the debug method in L<Net::Cmd>)
+
+B<Passive> - If set to a non-zero value then all data transfers will
+be done using passive mode. If set to zero then data transfers will be
+done using active mode. If the machine is connected to the Internet
+directly, both passive and active mode should work equally well.
+Behind most firewall and NAT configurations passive mode has a better
+chance of working. However, in some rare firewall configurations,
+active mode actually works when passive mode doesn't. Some really old
+FTP servers might not implement passive transfers. If not specified,
+then the transfer mode is set by the environment variable
+C<FTP_PASSIVE> or if that one is not set by the settings done by the
+F<libnetcfg> utility. If none of these apply then passive mode is
+used.
+
+B<Hash> - If given a reference to a file handle (e.g., C<\*STDERR>),
+print hash marks (#) on that filehandle every 1024 bytes. This
+simply invokes the C<hash()> method for you, so that hash marks
+are displayed for all transfers. You can, of course, call C<hash()>
+explicitly whenever you'd like.
+
+B<LocalAddr> - Local address to use for all socket connections, this
+argument will be passed to L<IO::Socket::INET>
+
+If the constructor fails undef will be returned and an error message will
+be in $@
+
+=back
+
+=head1 METHODS
+
+Unless otherwise stated all methods return either a I<true> or I<false>
+value, with I<true> meaning that the operation was a success. When a method
+states that it returns a value, failure will be returned as I<undef> or an
+empty list.
+
+C<Net::FTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
+be used to send commands to the remote FTP server in addition to the methods
+documented here.
+
+=over 4
+
+=item login ([LOGIN [,PASSWORD [, ACCOUNT] ] ])
+
+Log into the remote FTP server with the given login information. If
+no arguments are given then the C<Net::FTP> uses the C<Net::Netrc>
+package to lookup the login information for the connected host.
+If no information is found then a login of I<anonymous> is used.
+If no password is given and the login is I<anonymous> then I<anonymous@>
+will be used for password.
+
+If the connection is via a firewall then the C<authorize> method will
+be called with no arguments.
+
+=item starttls ()
+
+Upgrade existing plain connection to SSL.
+The SSL arguments have to be given in C<new> already because they are needed for
+data connections too.
+
+=item stoptls ()
+
+Downgrade existing SSL connection back to plain.
+This is needed to work with some FTP helpers at firewalls, which need to see the
+PORT and PASV commands and responses to dynamically open the necessary ports.
+In this case C<starttls> is usually only done to protect the authorization.
+
+=item prot ( LEVEL )
+
+Set what type of data channel protection the client and server will be using.
+Only C<LEVEL>s "C" (clear) and "P" (private) are supported.
+
+=item host ()
+
+Returns the value used by the constructor, and passed to IO::Socket::INET,
+to connect to the host.
+
+=item account( ACCT )
+
+Set a string identifying the user's account.
+
+=item authorize ( [AUTH [, RESP]])
+
+This is a protocol used by some firewall ftp proxies. It is used
+to authorise the user to send data out. If both arguments are not specified
+then C<authorize> uses C<Net::Netrc> to do a lookup.
+
+=item site (ARGS)
+
+Send a SITE command to the remote server and wait for a response.
+
+Returns most significant digit of the response code.
+
+=item ascii ()
+
+Transfer file in ASCII. CRLF translation will be done if required
+
+=item binary ()
+
+Transfer file in binary mode. No transformation will be done.
+
+B<Hint>: If both server and client machines use the same line ending for
+text files, then it will be faster to transfer all files in binary mode.
+
+=item type ( [ TYPE ] )
+
+Set or get if files will be transferred in ASCII or binary mode.
+
+=item rename ( OLDNAME, NEWNAME )
+
+Rename a file on the remote FTP server from C<OLDNAME> to C<NEWNAME>. This
+is done by sending the RNFR and RNTO commands.
+
+=item delete ( FILENAME )
+
+Send a request to the server to delete C<FILENAME>.
+
+=item cwd ( [ DIR ] )
+
+Attempt to change directory to the directory given in C<$dir>. If
+C<$dir> is C<"..">, the FTP C<CDUP> command is used to attempt to
+move up one directory. If no directory is given then an attempt is made
+to change the directory to the root directory.
+
+=item cdup ()
+
+Change directory to the parent of the current directory.
+
+=item passive ( [ PASSIVE ] )
+
+Set or get if data connections will be initiated in passive mode.
+
+=item pwd ()
+
+Returns the full pathname of the current directory.
+
+=item restart ( WHERE )
+
+Set the byte offset at which to begin the next data transfer. Net::FTP simply
+records this value and uses it when during the next data transfer. For this
+reason this method will not return an error, but setting it may cause
+a subsequent data transfer to fail.
+
+=item rmdir ( DIR [, RECURSE ])
+
+Remove the directory with the name C<DIR>. If C<RECURSE> is I<true> then
+C<rmdir> will attempt to delete everything inside the directory.
+
+=item mkdir ( DIR [, RECURSE ])
+
+Create a new directory with the name C<DIR>. If C<RECURSE> is I<true> then
+C<mkdir> will attempt to create all the directories in the given path.
+
+Returns the full pathname to the new directory.
+
+=item alloc ( SIZE [, RECORD_SIZE] )
+
+The alloc command allows you to give the ftp server a hint about the size
+of the file about to be transferred using the ALLO ftp command. Some storage
+systems use this to make intelligent decisions about how to store the file.
+The C<SIZE> argument represents the size of the file in bytes. The
+C<RECORD_SIZE> argument indicates a maximum record or page size for files
+sent with a record or page structure.
+
+The size of the file will be determined, and sent to the server
+automatically for normal files so that this method need only be called if
+you are transferring data from a socket, named pipe, or other stream not
+associated with a normal file.
+
+=item ls ( [ DIR ] )
+
+Get a directory listing of C<DIR>, or the current directory.
+
+In an array context, returns a list of lines returned from the server. In
+a scalar context, returns a reference to a list.
+
+=item dir ( [ DIR ] )
+
+Get a directory listing of C<DIR>, or the current directory in long format.
+
+In an array context, returns a list of lines returned from the server. In
+a scalar context, returns a reference to a list.
+
+=item get ( REMOTE_FILE [, LOCAL_FILE [, WHERE]] )
+
+Get C<REMOTE_FILE> from the server and store locally. C<LOCAL_FILE> may be
+a filename or a filehandle. If not specified, the file will be stored in
+the current directory with the same leafname as the remote file.
+
+If C<WHERE> is given then the first C<WHERE> bytes of the file will
+not be transferred, and the remaining bytes will be appended to
+the local file if it already exists.
+
+Returns C<LOCAL_FILE>, or the generated local file name if C<LOCAL_FILE>
+is not given. If an error was encountered undef is returned.
+
+=item put ( LOCAL_FILE [, REMOTE_FILE ] )
+
+Put a file on the remote server. C<LOCAL_FILE> may be a name or a filehandle.
+If C<LOCAL_FILE> is a filehandle then C<REMOTE_FILE> must be specified. If
+C<REMOTE_FILE> is not specified then the file will be stored in the current
+directory with the same leafname as C<LOCAL_FILE>.
+
+Returns C<REMOTE_FILE>, or the generated remote filename if C<REMOTE_FILE>
+is not given.
+
+B<NOTE>: If for some reason the transfer does not complete and an error is
+returned then the contents that had been transferred will not be remove
+automatically.
+
+=item put_unique ( LOCAL_FILE [, REMOTE_FILE ] )
+
+Same as put but uses the C<STOU> command.
+
+Returns the name of the file on the server.
+
+=item append ( LOCAL_FILE [, REMOTE_FILE ] )
+
+Same as put but appends to the file on the remote server.
+
+Returns C<REMOTE_FILE>, or the generated remote filename if C<REMOTE_FILE>
+is not given.
+
+=item unique_name ()
+
+Returns the name of the last file stored on the server using the
+C<STOU> command.
+
+=item mdtm ( FILE )
+
+Returns the I<modification time> of the given file
+
+=item size ( FILE )
+
+Returns the size in bytes for the given file as stored on the remote server.
+
+B<NOTE>: The size reported is the size of the stored file on the remote server.
+If the file is subsequently transferred from the server in ASCII mode
+and the remote server and local machine have different ideas about
+"End Of Line" then the size of file on the local machine after transfer
+may be different.
+
+=item supported ( CMD )
+
+Returns TRUE if the remote server supports the given command.
+
+=item hash ( [FILEHANDLE_GLOB_REF],[ BYTES_PER_HASH_MARK] )
+
+Called without parameters, or with the first argument false, hash marks
+are suppressed. If the first argument is true but not a reference to a
+file handle glob, then \*STDERR is used. The second argument is the number
+of bytes per hash mark printed, and defaults to 1024. In all cases the
+return value is a reference to an array of two: the filehandle glob reference
+and the bytes per hash mark.
+
+=item feature ( NAME )
+
+Determine if the server supports the specified feature. The return
+value is a list of lines the server responded with to describe the
+options that it supports for the given feature. If the feature is
+unsupported then the empty list is returned.
+
+ if ($ftp->feature( 'MDTM' )) {
+ # Do something
+ }
+
+ if (grep { /\bTLS\b/ } $ftp->feature('AUTH')) {
+ # Server supports TLS
+ }
+
+=back
+
+The following methods can return different results depending on
+how they are called. If the user explicitly calls either
+of the C<pasv> or C<port> methods then these methods will
+return a I<true> or I<false> value. If the user does not
+call either of these methods then the result will be a
+reference to a C<Net::FTP::dataconn> based object.
+
+=over 4
+
+=item nlst ( [ DIR ] )
+
+Send an C<NLST> command to the server, with an optional parameter.
+
+=item list ( [ DIR ] )
+
+Same as C<nlst> but using the C<LIST> command
+
+=item retr ( FILE )
+
+Begin the retrieval of a file called C<FILE> from the remote server.
+
+=item stor ( FILE )
+
+Tell the server that you wish to store a file. C<FILE> is the
+name of the new file that should be created.
+
+=item stou ( FILE )
+
+Same as C<stor> but using the C<STOU> command. The name of the unique
+file which was created on the server will be available via the C<unique_name>
+method after the data connection has been closed.
+
+=item appe ( FILE )
+
+Tell the server that we want to append some data to the end of a file
+called C<FILE>. If this file does not exist then create it.
+
+=back
+
+If for some reason you want to have complete control over the data connection,
+this includes generating it and calling the C<response> method when required,
+then the user can use these methods to do so.
+
+However calling these methods only affects the use of the methods above that
+can return a data connection. They have no effect on methods C<get>, C<put>,
+C<put_unique> and those that do not require data connections.
+
+=over 4
+
+=item port ( [ PORT ] )
+=item eprt ( [ PORT ] )
+
+Send a C<PORT> (IPv4) or C<EPRT> (IPv6) command to the server. If C<PORT> is
+specified then it is sent to the server. If not, then a listen socket is created
+and the correct information sent to the server.
+
+=item pasv ()
+=item epsv ()
+
+Tell the server to go into passive mode (C<pasv> for IPv4, C<epsv> for IPv6).
+Returns the text that represents the port on which the server is listening, this
+text is in a suitable form to send to another ftp server using the C<port> or
+C<eprt> method.
+
+=back
+
+The following methods can be used to transfer files between two remote
+servers, providing that these two servers can connect directly to each other.
+
+=over 4
+
+=item pasv_xfer ( SRC_FILE, DEST_SERVER [, DEST_FILE ] )
+
+This method will do a file transfer between two remote ftp servers. If
+C<DEST_FILE> is omitted then the leaf name of C<SRC_FILE> will be used.
+
+=item pasv_xfer_unique ( SRC_FILE, DEST_SERVER [, DEST_FILE ] )
+
+Like C<pasv_xfer> but the file is stored on the remote server using
+the STOU command.
+
+=item pasv_wait ( NON_PASV_SERVER )
+
+This method can be used to wait for a transfer to complete between a passive
+server and a non-passive server. The method should be called on the passive
+server with the C<Net::FTP> object for the non-passive server passed as an
+argument.
+
+=item abort ()
+
+Abort the current data transfer.
+
+=item quit ()
+
+Send the QUIT command to the remote FTP server and close the socket connection.
+
+=back
+
+=head2 Methods for the adventurous
+
+=over 4
+
+=item quot (CMD [,ARGS])
+
+Send a command, that Net::FTP does not directly support, to the remote
+server and wait for a response.
+
+Returns most significant digit of the response code.
+
+B<WARNING> This call should only be used on commands that do not require
+data connections. Misuse of this method can hang the connection.
+
+=item can_inet6 ()
+
+Returns whether we can use IPv6.
+
+=item can_ssl ()
+
+Returns whether we can use SSL.
+
+=back
+
+=head1 THE dataconn CLASS
+
+Some of the methods defined in C<Net::FTP> return an object which will
+be derived from the C<Net::FTP::dataconn> class. See L<Net::FTP::dataconn> for
+more details.
+
+=head1 UNIMPLEMENTED
+
+The following RFC959 commands have not been implemented:
+
+=over 4
+
+=item B<SMNT>
+
+Mount a different file system structure without changing login or
+accounting information.
+
+=item B<HELP>
+
+Ask the server for "helpful information" (that's what the RFC says) on
+the commands it accepts.
+
+=item B<MODE>
+
+Specifies transfer mode (stream, block or compressed) for file to be
+transferred.
+
+=item B<SYST>
+
+Request remote server system identification.
+
+=item B<STAT>
+
+Request remote server status.
+
+=item B<STRU>
+
+Specifies file structure for file to be transferred.
+
+=item B<REIN>
+
+Reinitialize the connection, flushing all I/O and account information.
+
+=back
+
+=head1 REPORTING BUGS
+
+When reporting bugs/problems please include as much information as possible.
+It may be difficult for me to reproduce the problem as almost every setup
+is different.
+
+A small script which yields the problem will probably be of help. It would
+also be useful if this script was run with the extra options C<Debug => 1>
+passed to the constructor, and the output sent with the bug report. If you
+cannot include a small script then please include a Debug trace from a
+run of your program which does yield the problem.
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 SEE ALSO
+
+L<Net::Netrc>,
+L<Net::Cmd>,
+L<IO::Socket::SSL>
+
+ftp(1), ftpd(8), RFC 959, RFC 2428, RFC 4217
+http://www.ietf.org/rfc/rfc959.txt
+http://www.ietf.org/rfc/rfc2428.txt
+http://www.ietf.org/rfc/rfc4217.txt
+
+=head1 USE EXAMPLES
+
+For an example of the use of Net::FTP see
+
+=over 4
+
+=item http://www.csh.rit.edu/~adam/Progs/
+
+C<autoftp> is a program that can retrieve, send, or list files via
+the FTP protocol in a non-interactive manner.
+
+=back
+
+=head1 CREDITS
+
+Henry Gabryjelski <henryg@WPI.EDU> - for the suggestion of creating directories
+recursively.
+
+Nathan Torkington <gnat@frii.com> - for some input on the documentation.
+
+Roderick Schertler <roderick@gate.net> - for various inputs
+
+=head1 COPYRIGHT
+
+Versions up to 2.77_2 Copyright (c) 1995-2004 Graham Barr. All rights reserved.
+Changes in Version 2.77_3 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,1287 @@
+# Net::NNTP.pm
+#
+# Versions up to 2.24_1 Copyright (c) 1995-1997 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.25 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+# reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::NNTP;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::Socket;
+use Net::Cmd;
+use Net::Config;
+use Time::Local;
+
+our $VERSION = "3.04";
+
+# Code for detecting if we can use SSL
+my $ssl_class = eval {
+ require IO::Socket::SSL;
+ # first version with default CA on most platforms
+ no warnings 'numeric';
+ IO::Socket::SSL->VERSION(2.007);
+} && 'IO::Socket::SSL';
+
+my $nossl_warn = !$ssl_class &&
+ 'To use SSL please install IO::Socket::SSL with version>=2.007';
+
+# Code for detecting if we can use IPv6
+my $inet6_class = eval {
+ require IO::Socket::IP;
+ no warnings 'numeric';
+ IO::Socket::IP->VERSION(0.20);
+} && 'IO::Socket::IP' || eval {
+ require IO::Socket::INET6;
+ no warnings 'numeric';
+ IO::Socket::INET6->VERSION(2.62);
+} && 'IO::Socket::INET6';
+
+sub can_ssl { $ssl_class };
+sub can_inet6 { $inet6_class };
+
+our @ISA = ('Net::Cmd', $inet6_class || 'IO::Socket::INET');
+
+
+sub new {
+ my $self = shift;
+ my $type = ref($self) || $self;
+ my ($host, %arg);
+ if (@_ % 2) {
+ $host = shift;
+ %arg = @_;
+ }
+ else {
+ %arg = @_;
+ $host = delete $arg{Host};
+ }
+ my $obj;
+
+ $host ||= $ENV{NNTPSERVER} || $ENV{NEWSHOST};
+
+ my $hosts = defined $host ? [$host] : $NetConfig{nntp_hosts};
+
+ @{$hosts} = qw(news)
+ unless @{$hosts};
+
+ my %connect = ( Proto => 'tcp');
+
+ if ($arg{SSL}) {
+ # SSL from start
+ die $nossl_warn if ! $ssl_class;
+ $arg{Port} ||= 563;
+ $connect{$_} = $arg{$_} for(grep { m{^SSL_} } keys %arg);
+ }
+
+ foreach my $o (qw(LocalAddr Timeout)) {
+ $connect{$o} = $arg{$o} if exists $arg{$o};
+ }
+ $connect{Timeout} = 120 unless defined $connect{Timeout};
+ $connect{PeerPort} = $arg{Port} || 'nntp(119)';
+ foreach my $h (@{$hosts}) {
+ $connect{PeerAddr} = $h;
+ $obj = $type->SUPER::new(%connect) or next;
+ ${*$obj}{'net_nntp_host'} = $h;
+ ${*$obj}{'net_nntp_arg'} = \%arg;
+ if ($arg{SSL}) {
+ Net::NNTP::_SSL->start_SSL($obj,%arg) or next;
+ }
+ last:
+ }
+
+ return
+ unless defined $obj;
+
+ $obj->autoflush(1);
+ $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
+
+ unless ($obj->response() == CMD_OK) {
+ $obj->close;
+ return;
+ }
+
+ my $c = $obj->code;
+ my @m = $obj->message;
+
+ unless (exists $arg{Reader} && $arg{Reader} == 0) {
+
+ # if server is INN and we have transfer rights the we are currently
+ # talking to innd not nnrpd
+ if ($obj->reader) {
+
+ # If reader succeeds the we need to consider this code to determine postok
+ $c = $obj->code;
+ }
+ else {
+
+ # I want to ignore this failure, so restore the previous status.
+ $obj->set_status($c, \@m);
+ }
+ }
+
+ ${*$obj}{'net_nntp_post'} = $c == 200 ? 1 : 0;
+
+ $obj;
+}
+
+
+sub host {
+ my $me = shift;
+ ${*$me}{'net_nntp_host'};
+}
+
+
+sub debug_text {
+ my $nntp = shift;
+ my $inout = shift;
+ my $text = shift;
+
+ if ( (ref($nntp) and $nntp->code == 350 and $text =~ /^(\S+)/)
+ || ($text =~ /^(authinfo\s+pass)/io))
+ {
+ $text = "$1 ....\n";
+ }
+
+ $text;
+}
+
+
+sub postok {
+ @_ == 1 or croak 'usage: $nntp->postok()';
+ my $nntp = shift;
+ ${*$nntp}{'net_nntp_post'} || 0;
+}
+
+
+sub starttls {
+ my $self = shift;
+ $ssl_class or die $nossl_warn;
+ $self->_STARTTLS or return;
+ Net::NNTP::_SSL->start_SSL($self,
+ %{ ${*$self}{'net_nntp_arg'} }, # (ssl) args given in new
+ @_ # more (ssl) args
+ ) or return;
+ return 1;
+}
+
+
+sub article {
+ @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->article( [ MSGID ], [ FH ] )';
+ my $nntp = shift;
+ my @fh;
+
+ @fh = (pop) if @_ == 2 || (@_ && (ref($_[0]) || ref(\$_[0]) eq 'GLOB'));
+
+ $nntp->_ARTICLE(@_)
+ ? $nntp->read_until_dot(@fh)
+ : undef;
+}
+
+
+sub articlefh {
+ @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->articlefh( [ MSGID ] )';
+ my $nntp = shift;
+
+ return unless $nntp->_ARTICLE(@_);
+ return $nntp->tied_fh;
+}
+
+
+sub authinfo {
+ @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
+ my ($nntp, $user, $pass) = @_;
+
+ $nntp->_AUTHINFO("USER", $user) == CMD_MORE
+ && $nntp->_AUTHINFO("PASS", $pass) == CMD_OK;
+}
+
+
+sub authinfo_simple {
+ @_ == 3 or croak 'usage: $nntp->authinfo( USER, PASS )';
+ my ($nntp, $user, $pass) = @_;
+
+ $nntp->_AUTHINFO('SIMPLE') == CMD_MORE
+ && $nntp->command($user, $pass)->response == CMD_OK;
+}
+
+
+sub body {
+ @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->body( [ MSGID ], [ FH ] )';
+ my $nntp = shift;
+ my @fh;
+
+ @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
+
+ $nntp->_BODY(@_)
+ ? $nntp->read_until_dot(@fh)
+ : undef;
+}
+
+
+sub bodyfh {
+ @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->bodyfh( [ MSGID ] )';
+ my $nntp = shift;
+ return unless $nntp->_BODY(@_);
+ return $nntp->tied_fh;
+}
+
+
+sub head {
+ @_ >= 1 && @_ <= 3 or croak 'usage: $nntp->head( [ MSGID ], [ FH ] )';
+ my $nntp = shift;
+ my @fh;
+
+ @fh = (pop) if @_ == 2 || (@_ && ref($_[0]) || ref(\$_[0]) eq 'GLOB');
+
+ $nntp->_HEAD(@_)
+ ? $nntp->read_until_dot(@fh)
+ : undef;
+}
+
+
+sub headfh {
+ @_ >= 1 && @_ <= 2 or croak 'usage: $nntp->headfh( [ MSGID ] )';
+ my $nntp = shift;
+ return unless $nntp->_HEAD(@_);
+ return $nntp->tied_fh;
+}
+
+
+sub nntpstat {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->nntpstat( [ MSGID ] )';
+ my $nntp = shift;
+
+ $nntp->_STAT(@_) && $nntp->message =~ /(<[^>]+>)/o
+ ? $1
+ : undef;
+}
+
+
+sub group {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->group( [ GROUP ] )';
+ my $nntp = shift;
+ my $grp = ${*$nntp}{'net_nntp_group'};
+
+ return $grp
+ unless (@_ || wantarray);
+
+ my $newgrp = shift;
+
+ $newgrp = (defined($grp) and length($grp)) ? $grp : ""
+ unless defined($newgrp) and length($newgrp);
+
+ return
+ unless $nntp->_GROUP($newgrp) and $nntp->message =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\S+)/;
+
+ my ($count, $first, $last, $group) = ($1, $2, $3, $4);
+
+ # group may be replied as '(current group)'
+ $group = ${*$nntp}{'net_nntp_group'}
+ if $group =~ /\(/;
+
+ ${*$nntp}{'net_nntp_group'} = $group;
+
+ wantarray
+ ? ($count, $first, $last, $group)
+ : $group;
+}
+
+
+sub help {
+ @_ == 1 or croak 'usage: $nntp->help()';
+ my $nntp = shift;
+
+ $nntp->_HELP
+ ? $nntp->read_until_dot
+ : undef;
+}
+
+
+sub ihave {
+ @_ >= 2 or croak 'usage: $nntp->ihave( MESSAGE-ID [, MESSAGE ])';
+ my $nntp = shift;
+ my $mid = shift;
+
+ $nntp->_IHAVE($mid) && $nntp->datasend(@_)
+ ? @_ == 0 || $nntp->dataend
+ : undef;
+}
+
+
+sub last {
+ @_ == 1 or croak 'usage: $nntp->last()';
+ my $nntp = shift;
+
+ $nntp->_LAST && $nntp->message =~ /(<[^>]+>)/o
+ ? $1
+ : undef;
+}
+
+
+sub list {
+ @_ == 1 or croak 'usage: $nntp->list()';
+ my $nntp = shift;
+
+ $nntp->_LIST
+ ? $nntp->_grouplist
+ : undef;
+}
+
+
+sub newgroups {
+ @_ >= 2 or croak 'usage: $nntp->newgroups( SINCE [, DISTRIBUTIONS ])';
+ my $nntp = shift;
+ my $time = _timestr(shift);
+ my $dist = shift || "";
+
+ $dist = join(",", @{$dist})
+ if ref($dist);
+
+ $nntp->_NEWGROUPS($time, $dist)
+ ? $nntp->_grouplist
+ : undef;
+}
+
+
+sub newnews {
+ @_ >= 2 && @_ <= 4
+ or croak 'usage: $nntp->newnews( SINCE [, GROUPS [, DISTRIBUTIONS ]])';
+ my $nntp = shift;
+ my $time = _timestr(shift);
+ my $grp = @_ ? shift: $nntp->group;
+ my $dist = shift || "";
+
+ $grp ||= "*";
+ $grp = join(",", @{$grp})
+ if ref($grp);
+
+ $dist = join(",", @{$dist})
+ if ref($dist);
+
+ $nntp->_NEWNEWS($grp, $time, $dist)
+ ? $nntp->_articlelist
+ : undef;
+}
+
+
+sub next {
+ @_ == 1 or croak 'usage: $nntp->next()';
+ my $nntp = shift;
+
+ $nntp->_NEXT && $nntp->message =~ /(<[^>]+>)/o
+ ? $1
+ : undef;
+}
+
+
+sub post {
+ @_ >= 1 or croak 'usage: $nntp->post( [ MESSAGE ] )';
+ my $nntp = shift;
+
+ $nntp->_POST() && $nntp->datasend(@_)
+ ? @_ == 0 || $nntp->dataend
+ : undef;
+}
+
+
+sub postfh {
+ my $nntp = shift;
+ return unless $nntp->_POST();
+ return $nntp->tied_fh;
+}
+
+
+sub quit {
+ @_ == 1 or croak 'usage: $nntp->quit()';
+ my $nntp = shift;
+
+ $nntp->_QUIT;
+ $nntp->close;
+}
+
+
+sub slave {
+ @_ == 1 or croak 'usage: $nntp->slave()';
+ my $nntp = shift;
+
+ $nntp->_SLAVE;
+}
+
+##
+## The following methods are not implemented by all servers
+##
+
+
+sub active {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->active( [ PATTERN ] )';
+ my $nntp = shift;
+
+ $nntp->_LIST('ACTIVE', @_)
+ ? $nntp->_grouplist
+ : undef;
+}
+
+
+sub active_times {
+ @_ == 1 or croak 'usage: $nntp->active_times()';
+ my $nntp = shift;
+
+ $nntp->_LIST('ACTIVE.TIMES')
+ ? $nntp->_grouplist
+ : undef;
+}
+
+
+sub distributions {
+ @_ == 1 or croak 'usage: $nntp->distributions()';
+ my $nntp = shift;
+
+ $nntp->_LIST('DISTRIBUTIONS')
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub distribution_patterns {
+ @_ == 1 or croak 'usage: $nntp->distributions()';
+ my $nntp = shift;
+
+ my $arr;
+ local $_;
+
+ ## no critic (ControlStructures::ProhibitMutatingListFunctions)
+ $nntp->_LIST('DISTRIB.PATS')
+ && ($arr = $nntp->read_until_dot)
+ ? [grep { /^\d/ && (chomp, $_ = [split /:/]) } @$arr]
+ : undef;
+}
+
+
+sub newsgroups {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->newsgroups( [ PATTERN ] )';
+ my $nntp = shift;
+
+ $nntp->_LIST('NEWSGROUPS', @_)
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub overview_fmt {
+ @_ == 1 or croak 'usage: $nntp->overview_fmt()';
+ my $nntp = shift;
+
+ $nntp->_LIST('OVERVIEW.FMT')
+ ? $nntp->_articlelist
+ : undef;
+}
+
+
+sub subscriptions {
+ @_ == 1 or croak 'usage: $nntp->subscriptions()';
+ my $nntp = shift;
+
+ $nntp->_LIST('SUBSCRIPTIONS')
+ ? $nntp->_articlelist
+ : undef;
+}
+
+
+sub listgroup {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->listgroup( [ GROUP ] )';
+ my $nntp = shift;
+
+ $nntp->_LISTGROUP(@_)
+ ? $nntp->_articlelist
+ : undef;
+}
+
+
+sub reader {
+ @_ == 1 or croak 'usage: $nntp->reader()';
+ my $nntp = shift;
+
+ $nntp->_MODE('READER');
+}
+
+
+sub xgtitle {
+ @_ == 1 || @_ == 2 or croak 'usage: $nntp->xgtitle( [ PATTERN ] )';
+ my $nntp = shift;
+
+ $nntp->_XGTITLE(@_)
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub xhdr {
+ @_ >= 2 && @_ <= 4 or croak 'usage: $nntp->xhdr( HEADER, [ MESSAGE-SPEC ] )';
+ my $nntp = shift;
+ my $hdr = shift;
+ my $arg = _msg_arg(@_);
+
+ $nntp->_XHDR($hdr, $arg)
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub xover {
+ @_ == 2 || @_ == 3 or croak 'usage: $nntp->xover( MESSAGE-SPEC )';
+ my $nntp = shift;
+ my $arg = _msg_arg(@_);
+
+ $nntp->_XOVER($arg)
+ ? $nntp->_fieldlist
+ : undef;
+}
+
+
+sub xpat {
+ @_ == 4 || @_ == 5 or croak '$nntp->xpat( HEADER, PATTERN, MESSAGE-SPEC )';
+ my $nntp = shift;
+ my $hdr = shift;
+ my $pat = shift;
+ my $arg = _msg_arg(@_);
+
+ $pat = join(" ", @$pat)
+ if ref($pat);
+
+ $nntp->_XPAT($hdr, $arg, $pat)
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub xpath {
+ @_ == 2 or croak 'usage: $nntp->xpath( MESSAGE-ID )';
+ my ($nntp, $mid) = @_;
+
+ return
+ unless $nntp->_XPATH($mid);
+
+ my $m;
+ ($m = $nntp->message) =~ s/^\d+\s+//o;
+ my @p = split /\s+/, $m;
+
+ wantarray ? @p : $p[0];
+}
+
+
+sub xrover {
+ @_ == 2 || @_ == 3 or croak 'usage: $nntp->xrover( MESSAGE-SPEC )';
+ my $nntp = shift;
+ my $arg = _msg_arg(@_);
+
+ $nntp->_XROVER($arg)
+ ? $nntp->_description
+ : undef;
+}
+
+
+sub date {
+ @_ == 1 or croak 'usage: $nntp->date()';
+ my $nntp = shift;
+
+ $nntp->_DATE
+ && $nntp->message =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/
+ ? timegm($6, $5, $4, $3, $2 - 1, $1 - 1900)
+ : undef;
+}
+
+
+##
+## Private subroutines
+##
+
+
+sub _msg_arg {
+ my $spec = shift;
+ my $arg = "";
+
+ if (@_) {
+ carp "Depriciated passing of two message numbers, " . "pass a reference"
+ if $^W;
+ $spec = [$spec, $_[0]];
+ }
+
+ if (defined $spec) {
+ if (ref($spec)) {
+ $arg = $spec->[0];
+ if (defined $spec->[1]) {
+ $arg .= "-"
+ if $spec->[1] != $spec->[0];
+ $arg .= $spec->[1]
+ if $spec->[1] > $spec->[0];
+ }
+ }
+ else {
+ $arg = $spec;
+ }
+ }
+
+ $arg;
+}
+
+
+sub _timestr {
+ my $time = shift;
+ my @g = reverse((gmtime($time))[0 .. 5]);
+ $g[1] += 1;
+ $g[0] %= 100;
+ sprintf "%02d%02d%02d %02d%02d%02d GMT", @g;
+}
+
+
+sub _grouplist {
+ my $nntp = shift;
+ my $arr = $nntp->read_until_dot
+ or return;
+
+ my $hash = {};
+
+ foreach my $ln (@$arr) {
+ my @a = split(/[\s\n]+/, $ln);
+ $hash->{$a[0]} = [@a[1, 2, 3]];
+ }
+
+ $hash;
+}
+
+
+sub _fieldlist {
+ my $nntp = shift;
+ my $arr = $nntp->read_until_dot
+ or return;
+
+ my $hash = {};
+
+ foreach my $ln (@$arr) {
+ my @a = split(/[\t\n]/, $ln);
+ my $m = shift @a;
+ $hash->{$m} = [@a];
+ }
+
+ $hash;
+}
+
+
+sub _articlelist {
+ my $nntp = shift;
+ my $arr = $nntp->read_until_dot;
+
+ chomp(@$arr)
+ if $arr;
+
+ $arr;
+}
+
+
+sub _description {
+ my $nntp = shift;
+ my $arr = $nntp->read_until_dot
+ or return;
+
+ my $hash = {};
+
+ foreach my $ln (@$arr) {
+ chomp($ln);
+
+ $hash->{$1} = $ln
+ if $ln =~ s/^\s*(\S+)\s*//o;
+ }
+
+ $hash;
+
+}
+
+##
+## The commands
+##
+
+
+sub _ARTICLE { shift->command('ARTICLE', @_)->response == CMD_OK }
+sub _AUTHINFO { shift->command('AUTHINFO', @_)->response }
+sub _BODY { shift->command('BODY', @_)->response == CMD_OK }
+sub _DATE { shift->command('DATE')->response == CMD_INFO }
+sub _GROUP { shift->command('GROUP', @_)->response == CMD_OK }
+sub _HEAD { shift->command('HEAD', @_)->response == CMD_OK }
+sub _HELP { shift->command('HELP', @_)->response == CMD_INFO }
+sub _IHAVE { shift->command('IHAVE', @_)->response == CMD_MORE }
+sub _LAST { shift->command('LAST')->response == CMD_OK }
+sub _LIST { shift->command('LIST', @_)->response == CMD_OK }
+sub _LISTGROUP { shift->command('LISTGROUP', @_)->response == CMD_OK }
+sub _NEWGROUPS { shift->command('NEWGROUPS', @_)->response == CMD_OK }
+sub _NEWNEWS { shift->command('NEWNEWS', @_)->response == CMD_OK }
+sub _NEXT { shift->command('NEXT')->response == CMD_OK }
+sub _POST { shift->command('POST', @_)->response == CMD_MORE }
+sub _QUIT { shift->command('QUIT', @_)->response == CMD_OK }
+sub _SLAVE { shift->command('SLAVE', @_)->response == CMD_OK }
+sub _STARTTLS { shift->command("STARTTLS")->response() == CMD_MORE }
+sub _STAT { shift->command('STAT', @_)->response == CMD_OK }
+sub _MODE { shift->command('MODE', @_)->response == CMD_OK }
+sub _XGTITLE { shift->command('XGTITLE', @_)->response == CMD_OK }
+sub _XHDR { shift->command('XHDR', @_)->response == CMD_OK }
+sub _XPAT { shift->command('XPAT', @_)->response == CMD_OK }
+sub _XPATH { shift->command('XPATH', @_)->response == CMD_OK }
+sub _XOVER { shift->command('XOVER', @_)->response == CMD_OK }
+sub _XROVER { shift->command('XROVER', @_)->response == CMD_OK }
+sub _XTHREAD { shift->unsupported }
+sub _XSEARCH { shift->unsupported }
+sub _XINDEX { shift->unsupported }
+
+##
+## IO/perl methods
+##
+
+
+sub DESTROY {
+ my $nntp = shift;
+ defined(fileno($nntp)) && $nntp->quit;
+}
+
+{
+ package Net::NNTP::_SSL;
+ our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::NNTP' );
+ sub starttls { die "NNTP connection is already in SSL mode" }
+ sub start_SSL {
+ my ($class,$nntp,%arg) = @_;
+ delete @arg{ grep { !m{^SSL_} } keys %arg };
+ ( $arg{SSL_verifycn_name} ||= $nntp->host )
+ =~s{(?<!:):[\w()]+$}{}; # strip port
+ $arg{SSL_hostname} = $arg{SSL_verifycn_name}
+ if ! defined $arg{SSL_hostname} && $class->can_client_sni;
+ my $ok = $class->SUPER::start_SSL($nntp,
+ SSL_verifycn_scheme => 'nntp',
+ %arg
+ );
+ $@ = $ssl_class->errstr if !$ok;
+ return $ok;
+ }
+}
+
+
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::NNTP - NNTP Client class
+
+=head1 SYNOPSIS
+
+ use Net::NNTP;
+
+ $nntp = Net::NNTP->new("some.host.name");
+ $nntp->quit;
+
+ # start with SSL, e.g. nntps
+ $nntp = Net::NNTP->new("some.host.name", SSL => 1);
+
+ # start with plain and upgrade to SSL
+ $nntp = Net::NNTP->new("some.host.name");
+ $nntp->starttls;
+
+
+=head1 DESCRIPTION
+
+C<Net::NNTP> is a class implementing a simple NNTP client in Perl as described
+in RFC977 and RFC4642.
+
+The Net::NNTP class is a subclass of Net::Cmd and IO::Socket::INET.
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ( [ HOST ] [, OPTIONS ])
+
+This is the constructor for a new Net::NNTP object. C<HOST> is the
+name of the remote host to which a NNTP connection is required. If not
+given then it may be passed as the C<Host> option described below. If no host is passed
+then two environment variables are checked, first C<NNTPSERVER> then
+C<NEWSHOST>, then C<Net::Config> is checked, and if a host is not found
+then C<news> is used.
+
+C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
+Possible options are:
+
+B<Host> - NNTP host to connect to. It may be a single scalar, as defined for
+the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
+an array with hosts to try in turn. The L</host> method will return the value
+which was used to connect to the host.
+
+B<Port> - port to connect to.
+Default - 119 for plain NNTP and 563 for immediate SSL (nntps).
+
+B<SSL> - If the connection should be done from start with SSL, contrary to later
+upgrade with C<starttls>.
+You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
+usually use the right arguments already.
+
+B<Timeout> - Maximum time, in seconds, to wait for a response from the
+NNTP server, a value of zero will cause all IO operations to block.
+(default: 120)
+
+B<Debug> - Enable the printing of debugging information to STDERR
+
+B<Reader> - If the remote server is INN then initially the connection
+will be to nnrpd, by default C<Net::NNTP> will issue a C<MODE READER> command
+so that the remote server becomes innd. If the C<Reader> option is given
+with a value of zero, then this command will not be sent and the
+connection will be left talking to nnrpd.
+
+B<LocalAddr> - If multiple IP addresses are present on the client host
+with a valid route to the destination, you can specify the address your
+C<Net::NNTP> connects from and this way override the operating system's
+pick.
+
+=back
+
+=head1 METHODS
+
+Unless otherwise stated all methods return either a I<true> or I<false>
+value, with I<true> meaning that the operation was a success. When a method
+states that it returns a value, failure will be returned as I<undef> or an
+empty list.
+
+C<Net::NNTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
+be used to send commands to the remote NNTP server in addition to the methods
+documented here.
+
+=over 4
+
+=item host ()
+
+Returns the value used by the constructor, and passed to IO::Socket::INET,
+to connect to the host.
+
+=item starttls ()
+
+Upgrade existing plain connection to SSL.
+Any arguments necessary for SSL must be given in C<new> already.
+
+=item article ( [ MSGID|MSGNUM ], [FH] )
+
+Retrieve the header, a blank line, then the body (text) of the
+specified article.
+
+If C<FH> is specified then it is expected to be a valid filehandle
+and the result will be printed to it, on success a true value will be
+returned. If C<FH> is not specified then the return value, on success,
+will be a reference to an array containing the article requested, each
+entry in the array will contain one line of the article.
+
+If no arguments are passed then the current article in the currently
+selected newsgroup is fetched.
+
+C<MSGNUM> is a numeric id of an article in the current newsgroup, and
+will change the current article pointer. C<MSGID> is the message id of
+an article as shown in that article's header. It is anticipated that the
+client will obtain the C<MSGID> from a list provided by the C<newnews>
+command, from references contained within another article, or from the
+message-id provided in the response to some other commands.
+
+If there is an error then C<undef> will be returned.
+
+=item body ( [ MSGID|MSGNUM ], [FH] )
+
+Like C<article> but only fetches the body of the article.
+
+=item head ( [ MSGID|MSGNUM ], [FH] )
+
+Like C<article> but only fetches the headers for the article.
+
+=item articlefh ( [ MSGID|MSGNUM ] )
+
+=item bodyfh ( [ MSGID|MSGNUM ] )
+
+=item headfh ( [ MSGID|MSGNUM ] )
+
+These are similar to article(), body() and head(), but rather than
+returning the requested data directly, they return a tied filehandle
+from which to read the article.
+
+=item nntpstat ( [ MSGID|MSGNUM ] )
+
+The C<nntpstat> command is similar to the C<article> command except that no
+text is returned. When selecting by message number within a group,
+the C<nntpstat> command serves to set the "current article pointer" without
+sending text.
+
+Using the C<nntpstat> command to
+select by message-id is valid but of questionable value, since a
+selection by message-id does B<not> alter the "current article pointer".
+
+Returns the message-id of the "current article".
+
+=item group ( [ GROUP ] )
+
+Set and/or get the current group. If C<GROUP> is not given then information
+is returned on the current group.
+
+In a scalar context it returns the group name.
+
+In an array context the return value is a list containing, the number
+of articles in the group, the number of the first article, the number
+of the last article and the group name.
+
+=item help ( )
+
+Request help text (a short summary of commands that are understood by this
+implementation) from the server. Returns the text or undef upon failure.
+
+=item ihave ( MSGID [, MESSAGE ])
+
+The C<ihave> command informs the server that the client has an article
+whose id is C<MSGID>. If the server desires a copy of that
+article, and C<MESSAGE> has been given the it will be sent.
+
+Returns I<true> if the server desires the article and C<MESSAGE> was
+successfully sent,if specified.
+
+If C<MESSAGE> is not specified then the message must be sent using the
+C<datasend> and C<dataend> methods from L<Net::Cmd>
+
+C<MESSAGE> can be either an array of lines or a reference to an array.
+
+=item last ()
+
+Set the "current article pointer" to the previous article in the current
+newsgroup.
+
+Returns the message-id of the article.
+
+=item date ()
+
+Returns the date on the remote server. This date will be in a UNIX time
+format (seconds since 1970)
+
+=item postok ()
+
+C<postok> will return I<true> if the servers initial response indicated
+that it will allow posting.
+
+=item authinfo ( USER, PASS )
+
+Authenticates to the server (using the original AUTHINFO USER / AUTHINFO PASS
+form, defined in RFC2980) using the supplied username and password. Please
+note that the password is sent in clear text to the server. This command
+should not be used with valuable passwords unless the connection to the server
+is somehow protected.
+
+=item authinfo_simple ( USER, PASS )
+
+Authenticates to the server (using the proposed NNTP V2 AUTHINFO SIMPLE form,
+defined and deprecated in RFC2980) using the supplied username and password.
+As with L</authinfo> the password is sent in clear text.
+
+=item list ()
+
+Obtain information about all the active newsgroups. The results is a reference
+to a hash where the key is a group name and each value is a reference to an
+array. The elements in this array are:- the last article number in the group,
+the first article number in the group and any information flags about the group.
+
+=item newgroups ( SINCE [, DISTRIBUTIONS ])
+
+C<SINCE> is a time value and C<DISTRIBUTIONS> is either a distribution
+pattern or a reference to a list of distribution patterns.
+The result is the same as C<list>, but the
+groups return will be limited to those created after C<SINCE> and, if
+specified, in one of the distribution areas in C<DISTRIBUTIONS>.
+
+=item newnews ( SINCE [, GROUPS [, DISTRIBUTIONS ]])
+
+C<SINCE> is a time value. C<GROUPS> is either a group pattern or a reference
+to a list of group patterns. C<DISTRIBUTIONS> is either a distribution
+pattern or a reference to a list of distribution patterns.
+
+Returns a reference to a list which contains the message-ids of all news posted
+after C<SINCE>, that are in a groups which matched C<GROUPS> and a
+distribution which matches C<DISTRIBUTIONS>.
+
+=item next ()
+
+Set the "current article pointer" to the next article in the current
+newsgroup.
+
+Returns the message-id of the article.
+
+=item post ( [ MESSAGE ] )
+
+Post a new article to the news server. If C<MESSAGE> is specified and posting
+is allowed then the message will be sent.
+
+If C<MESSAGE> is not specified then the message must be sent using the
+C<datasend> and C<dataend> methods from L<Net::Cmd>
+
+C<MESSAGE> can be either an array of lines or a reference to an array.
+
+The message, either sent via C<datasend> or as the C<MESSAGE>
+parameter, must be in the format as described by RFC822 and must
+contain From:, Newsgroups: and Subject: headers.
+
+=item postfh ()
+
+Post a new article to the news server using a tied filehandle. If
+posting is allowed, this method will return a tied filehandle that you
+can print() the contents of the article to be posted. You must
+explicitly close() the filehandle when you are finished posting the
+article, and the return value from the close() call will indicate
+whether the message was successfully posted.
+
+=item slave ()
+
+Tell the remote server that I am not a user client, but probably another
+news server.
+
+=item quit ()
+
+Quit the remote server and close the socket connection.
+
+=item can_inet6 ()
+
+Returns whether we can use IPv6.
+
+=item can_ssl ()
+
+Returns whether we can use SSL.
+
+=back
+
+=head2 Extension methods
+
+These methods use commands that are not part of the RFC977 documentation. Some
+servers may not support all of them.
+
+=over 4
+
+=item newsgroups ( [ PATTERN ] )
+
+Returns a reference to a hash where the keys are all the group names which
+match C<PATTERN>, or all of the groups if no pattern is specified, and
+each value contains the description text for the group.
+
+=item distributions ()
+
+Returns a reference to a hash where the keys are all the possible
+distribution names and the values are the distribution descriptions.
+
+=item distribution_patterns ()
+
+Returns a reference to an array where each element, itself an array
+reference, consists of the three fields of a line of the distrib.pats list
+maintained by some NNTP servers, namely: a weight, a wildmat and a value
+which the client may use to construct a Distribution header.
+
+=item subscriptions ()
+
+Returns a reference to a list which contains a list of groups which
+are recommended for a new user to subscribe to.
+
+=item overview_fmt ()
+
+Returns a reference to an array which contain the names of the fields returned
+by C<xover>.
+
+=item active_times ()
+
+Returns a reference to a hash where the keys are the group names and each
+value is a reference to an array containing the time the groups was created
+and an identifier, possibly an Email address, of the creator.
+
+=item active ( [ PATTERN ] )
+
+Similar to C<list> but only active groups that match the pattern are returned.
+C<PATTERN> can be a group pattern.
+
+=item xgtitle ( PATTERN )
+
+Returns a reference to a hash where the keys are all the group names which
+match C<PATTERN> and each value is the description text for the group.
+
+=item xhdr ( HEADER, MESSAGE-SPEC )
+
+Obtain the header field C<HEADER> for all the messages specified.
+
+The return value will be a reference
+to a hash where the keys are the message numbers and each value contains
+the text of the requested header for that message.
+
+=item xover ( MESSAGE-SPEC )
+
+The return value will be a reference
+to a hash where the keys are the message numbers and each value contains
+a reference to an array which contains the overview fields for that
+message.
+
+The names of the fields can be obtained by calling C<overview_fmt>.
+
+=item xpath ( MESSAGE-ID )
+
+Returns the path name to the file on the server which contains the specified
+message.
+
+=item xpat ( HEADER, PATTERN, MESSAGE-SPEC)
+
+The result is the same as C<xhdr> except the is will be restricted to
+headers where the text of the header matches C<PATTERN>
+
+=item xrover ()
+
+The XROVER command returns reference information for the article(s)
+specified.
+
+Returns a reference to a HASH where the keys are the message numbers and the
+values are the References: lines from the articles
+
+=item listgroup ( [ GROUP ] )
+
+Returns a reference to a list of all the active messages in C<GROUP>, or
+the current group if C<GROUP> is not specified.
+
+=item reader ()
+
+Tell the server that you are a reader and not another server.
+
+This is required by some servers. For example if you are connecting to
+an INN server and you have transfer permission your connection will
+be connected to the transfer daemon, not the NNTP daemon. Issuing
+this command will cause the transfer daemon to hand over control
+to the NNTP daemon.
+
+Some servers do not understand this command, but issuing it and ignoring
+the response is harmless.
+
+=back
+
+=head1 UNSUPPORTED
+
+The following NNTP command are unsupported by the package, and there are
+no plans to do so.
+
+ AUTHINFO GENERIC
+ XTHREAD
+ XSEARCH
+ XINDEX
+
+=head1 DEFINITIONS
+
+=over 4
+
+=item MESSAGE-SPEC
+
+C<MESSAGE-SPEC> is either a single message-id, a single message number, or
+a reference to a list of two message numbers.
+
+If C<MESSAGE-SPEC> is a reference to a list of two message numbers and the
+second number in a range is less than or equal to the first then the range
+represents all messages in the group after the first message number.
+
+B<NOTE> For compatibility reasons only with earlier versions of Net::NNTP
+a message spec can be passed as a list of two numbers, this is deprecated
+and a reference to the list should now be passed
+
+=item PATTERN
+
+The C<NNTP> protocol uses the C<WILDMAT> format for patterns.
+The WILDMAT format was first developed by Rich Salz based on
+the format used in the UNIX "find" command to articulate
+file names. It was developed to provide a uniform mechanism
+for matching patterns in the same manner that the UNIX shell
+matches filenames.
+
+Patterns are implicitly anchored at the
+beginning and end of each string when testing for a match.
+
+There are five pattern matching operations other than a strict
+one-to-one match between the pattern and the source to be
+checked for a match.
+
+The first is an asterisk C<*> to match any sequence of zero or more
+characters.
+
+The second is a question mark C<?> to match any single character. The
+third specifies a specific set of characters.
+
+The set is specified as a list of characters, or as a range of characters
+where the beginning and end of the range are separated by a minus (or dash)
+character, or as any combination of lists and ranges. The dash can
+also be included in the set as a character it if is the beginning
+or end of the set. This set is enclosed in square brackets. The
+close square bracket C<]> may be used in a set if it is the first
+character in the set.
+
+The fourth operation is the same as the
+logical not of the third operation and is specified the same
+way as the third with the addition of a caret character C<^> at
+the beginning of the test string just inside the open square
+bracket.
+
+The final operation uses the backslash character to
+invalidate the special meaning of an open square bracket C<[>,
+the asterisk, backslash or the question mark. Two backslashes in
+sequence will result in the evaluation of the backslash as a
+character with no special meaning.
+
+=over 4
+
+=item Examples
+
+=item C<[^]-]>
+
+matches any single character other than a close square
+bracket or a minus sign/dash.
+
+=item C<*bdc>
+
+matches any string that ends with the string "bdc"
+including the string "bdc" (without quotes).
+
+=item C<[0-9a-zA-Z]>
+
+matches any single printable alphanumeric ASCII character.
+
+=item C<a??d>
+
+matches any four character string which begins
+with a and ends with d.
+
+=back
+
+=back
+
+=head1 SEE ALSO
+
+L<Net::Cmd>,
+L<IO::Socket::SSL>
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.24_1 Copyright (c) 1995-1997 Graham Barr. All rights reserved.
+Changes in Version 2.25 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,345 @@
+# Net::Netrc.pm
+#
+# Versions up to 2.13 Copyright (c) 1995-1998 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.13_01 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::Netrc;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use FileHandle;
+
+our $VERSION = "3.04";
+
+our $TESTING;
+
+my %netrc = ();
+
+sub _readrc {
+ my($class, $host) = @_;
+ my ($home, $file);
+
+ if ($^O eq "MacOS") {
+ $home = $ENV{HOME} || `pwd`;
+ chomp($home);
+ $file = ($home =~ /:$/ ? $home . "netrc" : $home . ":netrc");
+ }
+ else {
+
+ # Some OS's don't have "getpwuid", so we default to $ENV{HOME}
+ $home = eval { (getpwuid($>))[7] } || $ENV{HOME};
+ $home ||= $ENV{HOMEDRIVE} . ($ENV{HOMEPATH} || '') if defined $ENV{HOMEDRIVE};
+ if (-e $home . "/.netrc") {
+ $file = $home . "/.netrc";
+ }
+ elsif (-e $home . "/_netrc") {
+ $file = $home . "/_netrc";
+ }
+ else {
+ return unless $TESTING;
+ }
+ }
+
+ my ($login, $pass, $acct) = (undef, undef, undef);
+ my $fh;
+ local $_;
+
+ $netrc{default} = undef;
+
+ # OS/2 and Win32 do not handle stat in a way compatible with this check :-(
+ unless ($^O eq 'os2'
+ || $^O eq 'MSWin32'
+ || $^O eq 'MacOS'
+ || $^O =~ /^cygwin/)
+ {
+ my @stat = stat($file);
+
+ if (@stat) {
+ if ($stat[2] & 077) { ## no critic (ValuesAndExpressions::ProhibitLeadingZeros)
+ carp "Bad permissions: $file";
+ return;
+ }
+ if ($stat[4] != $<) {
+ carp "Not owner: $file";
+ return;
+ }
+ }
+ }
+
+ if ($fh = FileHandle->new($file, "r")) {
+ my ($mach, $macdef, $tok, @tok) = (0, 0);
+
+ while (<$fh>) {
+ undef $macdef if /\A\n\Z/;
+
+ if ($macdef) {
+ push(@$macdef, $_);
+ next;
+ }
+
+ s/^\s*//;
+ chomp;
+
+ while (length && s/^("((?:[^"]+|\\.)*)"|((?:[^\\\s]+|\\.)*))\s*//) {
+ (my $tok = $+) =~ s/\\(.)/$1/g;
+ push(@tok, $tok);
+ }
+
+ TOKEN:
+ while (@tok) {
+ if ($tok[0] eq "default") {
+ shift(@tok);
+ $mach = bless {}, $class;
+ $netrc{default} = [$mach];
+
+ next TOKEN;
+ }
+
+ last TOKEN
+ unless @tok > 1;
+
+ $tok = shift(@tok);
+
+ if ($tok eq "machine") {
+ my $host = shift @tok;
+ $mach = bless {machine => $host}, $class;
+
+ $netrc{$host} = []
+ unless exists($netrc{$host});
+ push(@{$netrc{$host}}, $mach);
+ }
+ elsif ($tok =~ /^(login|password|account)$/) {
+ next TOKEN unless $mach;
+ my $value = shift @tok;
+
+ # Following line added by rmerrell to remove '/' escape char in .netrc
+ $value =~ s/\/\\/\\/g;
+ $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} = [];
+ }
+ }
+ }
+ $fh->close();
+ }
+}
+
+
+sub lookup {
+ my ($class, $mach, $login) = @_;
+
+ $class->_readrc()
+ unless exists $netrc{default};
+
+ $mach ||= 'default';
+ undef $login
+ if $mach eq 'default';
+
+ if (exists $netrc{$mach}) {
+ if (defined $login) {
+ foreach my $m (@{$netrc{$mach}}) {
+ return $m
+ if (exists $m->{login} && $m->{login} eq $login);
+ }
+ return;
+ }
+ return $netrc{$mach}->[0];
+ }
+
+ return $netrc{default}->[0]
+ if defined $netrc{default};
+
+ return;
+}
+
+
+sub login {
+ my $me = shift;
+
+ exists $me->{login}
+ ? $me->{login}
+ : undef;
+}
+
+
+sub account {
+ my $me = shift;
+
+ exists $me->{account}
+ ? $me->{account}
+ : undef;
+}
+
+
+sub password {
+ my $me = shift;
+
+ exists $me->{password}
+ ? $me->{password}
+ : undef;
+}
+
+
+sub lpa {
+ my $me = shift;
+ ($me->login, $me->password, $me->account);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::Netrc - OO interface to users netrc file
+
+=head1 SYNOPSIS
+
+ use Net::Netrc;
+
+ $mach = Net::Netrc->lookup('some.machine');
+ $login = $mach->login;
+ ($login, $password, $account) = $mach->lpa;
+
+=head1 DESCRIPTION
+
+C<Net::Netrc> is a class implementing a simple interface to the .netrc file
+used as by the ftp program.
+
+C<Net::Netrc> also implements security checks just like the ftp program,
+these checks are, first that the .netrc file must be owned by the user and
+second the ownership permissions should be such that only the owner has
+read and write access. If these conditions are not met then a warning is
+output and the .netrc file is not read.
+
+=head1 THE .netrc FILE
+
+The .netrc file contains login and initialization information used by the
+auto-login process. It resides in the user's home directory. The following
+tokens are recognized; they may be separated by spaces, tabs, or new-lines:
+
+=over 4
+
+=item machine name
+
+Identify a remote machine name. The auto-login process searches
+the .netrc file for a machine token that matches the remote machine
+specified. Once a match is made, the subsequent .netrc tokens
+are processed, stopping when the end of file is reached or an-
+other machine or a default token is encountered.
+
+=item default
+
+This is the same as machine name except that default matches
+any name. There can be only one default token, and it must be
+after all machine tokens. This is normally used as:
+
+ default login anonymous password user@site
+
+thereby giving the user automatic anonymous login to machines
+not specified in .netrc.
+
+=item login name
+
+Identify a user on the remote machine. If this token is present,
+the auto-login process will initiate a login using the
+specified name.
+
+=item password string
+
+Supply a password. If this token is present, the auto-login
+process will supply the specified string if the remote server
+requires a password as part of the login process.
+
+=item account string
+
+Supply an additional account password. If this token is present,
+the auto-login process will supply the specified string
+if the remote server requires an additional account password.
+
+=item macdef name
+
+Define a macro. C<Net::Netrc> only parses this field to be compatible
+with I<ftp>.
+
+=back
+
+=head1 CONSTRUCTOR
+
+The constructor for a C<Net::Netrc> object is not called new as it does not
+really create a new object. But instead is called C<lookup> as this is
+essentially what it does.
+
+=over 4
+
+=item lookup ( MACHINE [, LOGIN ])
+
+Lookup and return a reference to the entry for C<MACHINE>. If C<LOGIN> is given
+then the entry returned will have the given login. If C<LOGIN> is not given then
+the first entry in the .netrc file for C<MACHINE> will be returned.
+
+If a matching entry cannot be found, and a default entry exists, then a
+reference to the default entry is returned.
+
+If there is no matching entry found and there is no default defined, or
+no .netrc file is found, then C<undef> is returned.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item login ()
+
+Return the login id for the netrc entry
+
+=item password ()
+
+Return the password for the netrc entry
+
+=item account ()
+
+Return the account information for the netrc entry
+
+=item lpa ()
+
+Return a list of login, password and account information for the netrc entry
+
+=back
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 SEE ALSO
+
+L<Net::Netrc>,
+L<Net::Cmd>
+
+=head1 COPYRIGHT
+
+Versions up to 2.13 Copyright (c) 1995-1998 Graham Barr. All rights reserved.
+Changes in Version 2.13_01 onwards Copyright (C) 2013-2014 Steve Hay. All
+rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,848 @@
+# Net::POP3.pm
+#
+# Versions up to 2.29 Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.29_01 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::POP3;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::Socket;
+use Net::Cmd;
+use Net::Config;
+
+our $VERSION = "3.04";
+
+# Code for detecting if we can use SSL
+my $ssl_class = eval {
+ require IO::Socket::SSL;
+ # first version with default CA on most platforms
+ no warnings 'numeric';
+ IO::Socket::SSL->VERSION(2.007);
+} && 'IO::Socket::SSL';
+
+my $nossl_warn = !$ssl_class &&
+ 'To use SSL please install IO::Socket::SSL with version>=2.007';
+
+# Code for detecting if we can use IPv6
+my $inet6_class = eval {
+ require IO::Socket::IP;
+ no warnings 'numeric';
+ IO::Socket::IP->VERSION(0.20);
+} && 'IO::Socket::IP' || eval {
+ require IO::Socket::INET6;
+ no warnings 'numeric';
+ IO::Socket::INET6->VERSION(2.62);
+} && 'IO::Socket::INET6';
+
+sub can_ssl { $ssl_class };
+sub can_inet6 { $inet6_class };
+
+our @ISA = ('Net::Cmd', $inet6_class || 'IO::Socket::INET');
+
+sub new {
+ my $self = shift;
+ my $type = ref($self) || $self;
+ my ($host, %arg);
+ if (@_ % 2) {
+ $host = shift;
+ %arg = @_;
+ }
+ else {
+ %arg = @_;
+ $host = delete $arg{Host};
+ }
+ my $hosts = defined $host ? [$host] : $NetConfig{pop3_hosts};
+ my $obj;
+ my @localport = exists $arg{ResvPort} ? (LocalPort => $arg{ResvPort}) : ();
+
+ if ($arg{SSL}) {
+ # SSL from start
+ die $nossl_warn if !$ssl_class;
+ $arg{Port} ||= 995;
+ }
+
+ $arg{Timeout} = 120 if ! defined $arg{Timeout};
+
+ foreach my $h (@{$hosts}) {
+ $obj = $type->SUPER::new(
+ PeerAddr => ($host = $h),
+ PeerPort => $arg{Port} || 'pop3(110)',
+ Proto => 'tcp',
+ @localport,
+ Timeout => $arg{Timeout},
+ )
+ and last;
+ }
+
+ return
+ unless defined $obj;
+
+ ${*$obj}{'net_pop3_arg'} = \%arg;
+ ${*$obj}{'net_pop3_host'} = $host;
+ if ($arg{SSL}) {
+ Net::POP3::_SSL->start_SSL($obj,%arg) or return;
+ }
+
+ $obj->autoflush(1);
+ $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
+
+ unless ($obj->response() == CMD_OK) {
+ $obj->close();
+ return;
+ }
+
+ ${*$obj}{'net_pop3_banner'} = $obj->message;
+
+ $obj;
+}
+
+
+sub host {
+ my $me = shift;
+ ${*$me}{'net_pop3_host'};
+}
+
+##
+## We don't want people sending me their passwords when they report problems
+## now do we :-)
+##
+
+
+sub debug_text { $_[2] =~ /^(pass|rpop)/i ? "$1 ....\n" : $_[2]; }
+
+
+sub login {
+ @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->login( USER, PASS )';
+ my ($me, $user, $pass) = @_;
+
+ if (@_ <= 2) {
+ ($user, $pass) = $me->_lookup_credentials($user);
+ }
+
+ $me->user($user)
+ and $me->pass($pass);
+}
+
+sub starttls {
+ my $self = shift;
+ $ssl_class or die $nossl_warn;
+ $self->_STLS or return;
+ Net::POP3::_SSL->start_SSL($self,
+ %{ ${*$self}{'net_pop3_arg'} }, # (ssl) args given in new
+ @_ # more (ssl) args
+ ) or return;
+ return 1;
+}
+
+sub apop {
+ @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->apop( USER, PASS )';
+ my ($me, $user, $pass) = @_;
+ my $banner;
+ my $md;
+
+ if (eval { local $SIG{__DIE__}; require Digest::MD5 }) {
+ $md = Digest::MD5->new();
+ }
+ elsif (eval { local $SIG{__DIE__}; require MD5 }) {
+ $md = MD5->new();
+ }
+ else {
+ carp "You need to install Digest::MD5 or MD5 to use the APOP command";
+ return;
+ }
+
+ return
+ unless ($banner = (${*$me}{'net_pop3_banner'} =~ /(<.*>)/)[0]);
+
+ if (@_ <= 2) {
+ ($user, $pass) = $me->_lookup_credentials($user);
+ }
+
+ $md->add($banner, $pass);
+
+ return
+ unless ($me->_APOP($user, $md->hexdigest));
+
+ $me->_get_mailbox_count();
+}
+
+
+sub user {
+ @_ == 2 or croak 'usage: $pop3->user( USER )';
+ $_[0]->_USER($_[1]) ? 1 : undef;
+}
+
+
+sub pass {
+ @_ == 2 or croak 'usage: $pop3->pass( PASS )';
+
+ my ($me, $pass) = @_;
+
+ return
+ unless ($me->_PASS($pass));
+
+ $me->_get_mailbox_count();
+}
+
+
+sub reset {
+ @_ == 1 or croak 'usage: $obj->reset()';
+
+ my $me = shift;
+
+ return 0
+ unless ($me->_RSET);
+
+ if (defined ${*$me}{'net_pop3_mail'}) {
+ local $_;
+ foreach (@{${*$me}{'net_pop3_mail'}}) {
+ delete $_->{'net_pop3_deleted'};
+ }
+ }
+}
+
+
+sub last {
+ @_ == 1 or croak 'usage: $obj->last()';
+
+ return
+ unless $_[0]->_LAST && $_[0]->message =~ /(\d+)/;
+
+ return $1;
+}
+
+
+sub top {
+ @_ == 2 || @_ == 3 or croak 'usage: $pop3->top( MSGNUM [, NUMLINES ])';
+ my $me = shift;
+
+ return
+ unless $me->_TOP($_[0], $_[1] || 0);
+
+ $me->read_until_dot;
+}
+
+
+sub popstat {
+ @_ == 1 or croak 'usage: $pop3->popstat()';
+ my $me = shift;
+
+ return ()
+ unless $me->_STAT && $me->message =~ /(\d+)\D+(\d+)/;
+
+ ($1 || 0, $2 || 0);
+}
+
+
+sub list {
+ @_ == 1 || @_ == 2 or croak 'usage: $pop3->list( [ MSGNUM ] )';
+ my $me = shift;
+
+ return
+ unless $me->_LIST(@_);
+
+ if (@_) {
+ $me->message =~ /\d+\D+(\d+)/;
+ return $1 || undef;
+ }
+
+ my $info = $me->read_until_dot
+ or return;
+
+ my %hash = map { (/(\d+)\D+(\d+)/) } @$info;
+
+ return \%hash;
+}
+
+
+sub get {
+ @_ == 2 or @_ == 3 or croak 'usage: $pop3->get( MSGNUM [, FH ])';
+ my $me = shift;
+
+ return
+ unless $me->_RETR(shift);
+
+ $me->read_until_dot(@_);
+}
+
+
+sub getfh {
+ @_ == 2 or croak 'usage: $pop3->getfh( MSGNUM )';
+ my $me = shift;
+
+ return unless $me->_RETR(shift);
+ return $me->tied_fh;
+}
+
+
+sub delete {
+ @_ == 2 or croak 'usage: $pop3->delete( MSGNUM )';
+ my $me = shift;
+ return 0 unless $me->_DELE(@_);
+ ${*$me}{'net_pop3_deleted'} = 1;
+}
+
+
+sub uidl {
+ @_ == 1 || @_ == 2 or croak 'usage: $pop3->uidl( [ MSGNUM ] )';
+ my $me = shift;
+ my $uidl;
+
+ $me->_UIDL(@_)
+ or return;
+ if (@_) {
+ $uidl = ($me->message =~ /\d+\s+([\041-\176]+)/)[0];
+ }
+ else {
+ my $ref = $me->read_until_dot
+ or return;
+ $uidl = {};
+ foreach my $ln (@$ref) {
+ my ($msg, $uid) = $ln =~ /^\s*(\d+)\s+([\041-\176]+)/;
+ $uidl->{$msg} = $uid;
+ }
+ }
+ return $uidl;
+}
+
+
+sub ping {
+ @_ == 2 or croak 'usage: $pop3->ping( USER )';
+ my $me = shift;
+
+ return () unless $me->_PING(@_) && $me->message =~ /(\d+)\D+(\d+)/;
+
+ ($1 || 0, $2 || 0);
+}
+
+
+sub _lookup_credentials {
+ my ($me, $user) = @_;
+
+ require Net::Netrc;
+
+ $user ||= eval { local $SIG{__DIE__}; (getpwuid($>))[0] }
+ || $ENV{NAME}
+ || $ENV{USER}
+ || $ENV{LOGNAME};
+
+ my $m = Net::Netrc->lookup(${*$me}{'net_pop3_host'}, $user);
+ $m ||= Net::Netrc->lookup(${*$me}{'net_pop3_host'});
+
+ my $pass = $m
+ ? $m->password || ""
+ : "";
+
+ ($user, $pass);
+}
+
+
+sub _get_mailbox_count {
+ my ($me) = @_;
+ my $ret = ${*$me}{'net_pop3_count'} =
+ ($me->message =~ /(\d+)\s+message/io) ? $1 : ($me->popstat)[0];
+
+ $ret ? $ret : "0E0";
+}
+
+
+sub _STAT { shift->command('STAT' )->response() == CMD_OK }
+sub _LIST { shift->command('LIST', @_)->response() == CMD_OK }
+sub _RETR { shift->command('RETR', $_[0])->response() == CMD_OK }
+sub _DELE { shift->command('DELE', $_[0])->response() == CMD_OK }
+sub _NOOP { shift->command('NOOP' )->response() == CMD_OK }
+sub _RSET { shift->command('RSET' )->response() == CMD_OK }
+sub _QUIT { shift->command('QUIT' )->response() == CMD_OK }
+sub _TOP { shift->command( 'TOP', @_)->response() == CMD_OK }
+sub _UIDL { shift->command('UIDL', @_)->response() == CMD_OK }
+sub _USER { shift->command('USER', $_[0])->response() == CMD_OK }
+sub _PASS { shift->command('PASS', $_[0])->response() == CMD_OK }
+sub _APOP { shift->command('APOP', @_)->response() == CMD_OK }
+sub _PING { shift->command('PING', $_[0])->response() == CMD_OK }
+sub _RPOP { shift->command('RPOP', $_[0])->response() == CMD_OK }
+sub _LAST { shift->command('LAST' )->response() == CMD_OK }
+sub _CAPA { shift->command('CAPA' )->response() == CMD_OK }
+sub _STLS { shift->command("STLS", )->response() == CMD_OK }
+
+
+sub quit {
+ my $me = shift;
+
+ $me->_QUIT;
+ $me->close;
+}
+
+
+sub DESTROY {
+ my $me = shift;
+
+ if (defined fileno($me) and ${*$me}{'net_pop3_deleted'}) {
+ $me->reset;
+ $me->quit;
+ }
+}
+
+##
+## POP3 has weird responses, so we emulate them to look the same :-)
+##
+
+
+sub response {
+ my $cmd = shift;
+ my $str = $cmd->getline() or return;
+ my $code = "500";
+
+ $cmd->debug_print(0, $str)
+ if ($cmd->debug);
+
+ if ($str =~ s/^\+OK\s*//io) {
+ $code = "200";
+ }
+ elsif ($str =~ s/^\+\s*//io) {
+ $code = "300";
+ }
+ else {
+ $str =~ s/^-ERR\s*//io;
+ }
+
+ ${*$cmd}{'net_cmd_resp'} = [$str];
+ ${*$cmd}{'net_cmd_code'} = $code;
+
+ substr($code, 0, 1);
+}
+
+
+sub capa {
+ my $this = shift;
+ my ($capa, %capabilities);
+
+ # Fake a capability here
+ $capabilities{APOP} = '' if ($this->banner() =~ /<.*>/);
+
+ if ($this->_CAPA()) {
+ $capabilities{CAPA} = 1;
+ $capa = $this->read_until_dot();
+ %capabilities = (%capabilities, map {/^\s*(\S+)\s*(.*)/} @$capa);
+ }
+ else {
+
+ # Check AUTH for SASL capabilities
+ if ($this->command('AUTH')->response() == CMD_OK) {
+ my $mechanism = $this->read_until_dot();
+ $capabilities{SASL} = join " ", map {m/([A-Z0-9_-]+)/} @{$mechanism};
+ }
+ }
+
+ return ${*$this}{'net_pop3e_capabilities'} = \%capabilities;
+}
+
+
+sub capabilities {
+ my $this = shift;
+
+ ${*$this}{'net_pop3e_capabilities'} || $this->capa;
+}
+
+
+sub auth {
+ my ($self, $username, $password) = @_;
+
+ eval {
+ require MIME::Base64;
+ require Authen::SASL;
+ } or $self->set_status(500, ["Need MIME::Base64 and Authen::SASL todo auth"]), return 0;
+
+ my $capa = $self->capa;
+ my $mechanisms = $capa->{SASL} || 'CRAM-MD5';
+
+ my $sasl;
+
+ if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) {
+ $sasl = $username;
+ my $user_mech = $sasl->mechanism || '';
+ my @user_mech = split(/\s+/, $user_mech);
+ my %user_mech;
+ @user_mech{@user_mech} = ();
+
+ my @server_mech = split(/\s+/, $mechanisms);
+ my @mech = @user_mech
+ ? grep { exists $user_mech{$_} } @server_mech
+ : @server_mech;
+ unless (@mech) {
+ $self->set_status(
+ 500,
+ [ 'Client SASL mechanisms (',
+ join(', ', @user_mech),
+ ') do not match the SASL mechnism the server announces (',
+ join(', ', @server_mech), ')',
+ ]
+ );
+ return 0;
+ }
+
+ $sasl->mechanism(join(" ", @mech));
+ }
+ else {
+ die "auth(username, password)" if not length $username;
+ $sasl = Authen::SASL->new(
+ mechanism => $mechanisms,
+ callback => {
+ user => $username,
+ pass => $password,
+ authname => $username,
+ }
+ );
+ }
+
+ # We should probably allow the user to pass the host, but I don't
+ # currently know and SASL mechanisms that are used by smtp that need it
+ my ($hostname) = split /:/, ${*$self}{'net_pop3_host'};
+ my $client = eval { $sasl->client_new('pop', $hostname, 0) };
+
+ unless ($client) {
+ my $mech = $sasl->mechanism;
+ $self->set_status(
+ 500,
+ [ " Authen::SASL failure: $@",
+ '(please check if your local Authen::SASL installation',
+ "supports mechanism '$mech'"
+ ]
+ );
+ return 0;
+ }
+
+ my ($token) = $client->client_start
+ or do {
+ my $mech = $client->mechanism;
+ $self->set_status(
+ 500,
+ [ ' Authen::SASL failure: $client->client_start ',
+ "mechanism '$mech' hostname #$hostname#",
+ $client->error
+ ]
+ );
+ return 0;
+ };
+
+ # We don't support sasl mechanisms that encrypt the socket traffic.
+ # todo that we would really need to change the ISA hierarchy
+ # so we don't inherit from IO::Socket, but instead hold it in an attribute
+
+ my @cmd = ("AUTH", $client->mechanism);
+ my $code;
+
+ push @cmd, MIME::Base64::encode_base64($token, '')
+ if defined $token and length $token;
+
+ while (($code = $self->command(@cmd)->response()) == CMD_MORE) {
+
+ my ($token) = $client->client_step(MIME::Base64::decode_base64(($self->message)[0])) or do {
+ $self->set_status(
+ 500,
+ [ ' Authen::SASL failure: $client->client_step ',
+ "mechanism '", $client->mechanism, " hostname #$hostname#, ",
+ $client->error
+ ]
+ );
+ return 0;
+ };
+
+ @cmd = (MIME::Base64::encode_base64(defined $token ? $token : '', ''));
+ }
+
+ $code == CMD_OK;
+}
+
+
+sub banner {
+ my $this = shift;
+
+ return ${*$this}{'net_pop3_banner'};
+}
+
+{
+ package Net::POP3::_SSL;
+ our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::POP3' );
+ sub starttls { die "POP3 connection is already in SSL mode" }
+ sub start_SSL {
+ my ($class,$pop3,%arg) = @_;
+ delete @arg{ grep { !m{^SSL_} } keys %arg };
+ ( $arg{SSL_verifycn_name} ||= $pop3->host )
+ =~s{(?<!:):[\w()]+$}{}; # strip port
+ $arg{SSL_hostname} = $arg{SSL_verifycn_name}
+ if ! defined $arg{SSL_hostname} && $class->can_client_sni;
+ $arg{SSL_verifycn_scheme} ||= 'pop3';
+ my $ok = $class->SUPER::start_SSL($pop3,%arg);
+ $@ = $ssl_class->errstr if !$ok;
+ return $ok;
+ }
+}
+
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::POP3 - Post Office Protocol 3 Client class (RFC1939)
+
+=head1 SYNOPSIS
+
+ use Net::POP3;
+
+ # Constructors
+ $pop = Net::POP3->new('pop3host');
+ $pop = Net::POP3->new('pop3host', Timeout => 60);
+ $pop = Net::POP3->new('pop3host', SSL => 1, Timeout => 60);
+
+ if ($pop->login($username, $password) > 0) {
+ my $msgnums = $pop->list; # hashref of msgnum => size
+ foreach my $msgnum (keys %$msgnums) {
+ my $msg = $pop->get($msgnum);
+ print @$msg;
+ $pop->delete($msgnum);
+ }
+ }
+
+ $pop->quit;
+
+=head1 DESCRIPTION
+
+This module implements a client interface to the POP3 protocol, enabling
+a perl5 application to talk to POP3 servers. This documentation assumes
+that you are familiar with the POP3 protocol described in RFC1939.
+
+A new Net::POP3 object must be created with the I<new> method. Once
+this has been done, all POP3 commands are accessed via method calls
+on the object.
+
+The Net::POP3 class is a subclass of Net::Cmd and IO::Socket::INET.
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ( [ HOST ] [, OPTIONS ] )
+
+This is the constructor for a new Net::POP3 object. C<HOST> is the
+name of the remote host to which an POP3 connection is required.
+
+C<HOST> is optional. If C<HOST> is not given then it may instead be
+passed as the C<Host> option described below. If neither is given then
+the C<POP3_Hosts> specified in C<Net::Config> will be used.
+
+C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
+Possible options are:
+
+B<Host> - POP3 host to connect to. It may be a single scalar, as defined for
+the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
+an array with hosts to try in turn. The L</host> method will return the value
+which was used to connect to the host.
+
+B<Port> - port to connect to.
+Default - 110 for plain POP3 and 995 for POP3s (direct SSL).
+
+B<SSL> - If the connection should be done from start with SSL, contrary to later
+upgrade with C<starttls>.
+You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
+usually use the right arguments already.
+
+B<ResvPort> - If given then the socket for the C<Net::POP3> object
+will be bound to the local port given using C<bind> when the socket is
+created.
+
+B<Timeout> - Maximum time, in seconds, to wait for a response from the
+POP3 server (default: 120)
+
+B<Debug> - Enable debugging information
+
+=back
+
+=head1 METHODS
+
+Unless otherwise stated all methods return either a I<true> or I<false>
+value, with I<true> meaning that the operation was a success. When a method
+states that it returns a value, failure will be returned as I<undef> or an
+empty list.
+
+C<Net::POP3> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
+be used to send commands to the remote POP3 server in addition to the methods
+documented here.
+
+=over 4
+
+=item host ()
+
+Returns the value used by the constructor, and passed to IO::Socket::INET,
+to connect to the host.
+
+=item auth ( USERNAME, PASSWORD )
+
+Attempt SASL authentication.
+
+=item user ( USER )
+
+Send the USER command.
+
+=item pass ( PASS )
+
+Send the PASS command. Returns the number of messages in the mailbox.
+
+=item login ( [ USER [, PASS ]] )
+
+Send both the USER and PASS commands. If C<PASS> is not given the
+C<Net::POP3> uses C<Net::Netrc> to lookup the password using the host
+and username. If the username is not specified then the current user name
+will be used.
+
+Returns the number of messages in the mailbox. However if there are no
+messages on the server the string C<"0E0"> will be returned. This is
+will give a true value in a boolean context, but zero in a numeric context.
+
+If there was an error authenticating the user then I<undef> will be returned.
+
+=item starttls ( SSLARGS )
+
+Upgrade existing plain connection to SSL.
+You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
+usually use the right arguments already.
+
+=item apop ( [ USER [, PASS ]] )
+
+Authenticate with the server identifying as C<USER> with password C<PASS>.
+Similar to L</login>, but the password is not sent in clear text.
+
+To use this method you must have the Digest::MD5 or the MD5 module installed,
+otherwise this method will return I<undef>.
+
+=item banner ()
+
+Return the sever's connection banner
+
+=item capa ()
+
+Return a reference to a hash of the capabilities of the server. APOP
+is added as a pseudo capability. Note that I've been unable to
+find a list of the standard capability values, and some appear to
+be multi-word and some are not. We make an attempt at intelligently
+parsing them, but it may not be correct.
+
+=item capabilities ()
+
+Just like capa, but only uses a cache from the last time we asked
+the server, so as to avoid asking more than once.
+
+=item top ( MSGNUM [, NUMLINES ] )
+
+Get the header and the first C<NUMLINES> of the body for the message
+C<MSGNUM>. Returns a reference to an array which contains the lines of text
+read from the server.
+
+=item list ( [ MSGNUM ] )
+
+If called with an argument the C<list> returns the size of the message
+in octets.
+
+If called without arguments a reference to a hash is returned. The
+keys will be the C<MSGNUM>'s of all undeleted messages and the values will
+be their size in octets.
+
+=item get ( MSGNUM [, FH ] )
+
+Get the message C<MSGNUM> from the remote mailbox. If C<FH> is not given
+then get returns a reference to an array which contains the lines of
+text read from the server. If C<FH> is given then the lines returned
+from the server are printed to the filehandle C<FH>.
+
+=item getfh ( MSGNUM )
+
+As per get(), but returns a tied filehandle. Reading from this
+filehandle returns the requested message. The filehandle will return
+EOF at the end of the message and should not be reused.
+
+=item last ()
+
+Returns the highest C<MSGNUM> of all the messages accessed.
+
+=item popstat ()
+
+Returns a list of two elements. These are the number of undeleted
+elements and the size of the mbox in octets.
+
+=item ping ( USER )
+
+Returns a list of two elements. These are the number of new messages
+and the total number of messages for C<USER>.
+
+=item uidl ( [ MSGNUM ] )
+
+Returns a unique identifier for C<MSGNUM> if given. If C<MSGNUM> is not
+given C<uidl> returns a reference to a hash where the keys are the
+message numbers and the values are the unique identifiers.
+
+=item delete ( MSGNUM )
+
+Mark message C<MSGNUM> to be deleted from the remote mailbox. All messages
+that are marked to be deleted will be removed from the remote mailbox
+when the server connection closed.
+
+=item reset ()
+
+Reset the status of the remote POP3 server. This includes resetting the
+status of all messages to not be deleted.
+
+=item quit ()
+
+Quit and close the connection to the remote POP3 server. Any messages marked
+as deleted will be deleted from the remote mailbox.
+
+=item can_inet6 ()
+
+Returns whether we can use IPv6.
+
+=item can_ssl ()
+
+Returns whether we can use SSL.
+
+=back
+
+=head1 NOTES
+
+If a C<Net::POP3> object goes out of scope before C<quit> method is called
+then the C<reset> method will called before the connection is closed. This
+means that any messages marked to be deleted will not be.
+
+=head1 SEE ALSO
+
+L<Net::Netrc>,
+L<Net::Cmd>,
+L<IO::Socket::SSL>
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.29 Copyright (c) 1995-2004 Graham Barr. All rights reserved.
+Changes in Version 2.29_01 onwards Copyright (C) 2013-2014 Steve Hay. All
+rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,1019 @@
+# Net::SMTP.pm
+#
+# Versions up to 2.31_1 Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.31_2 onwards Copyright (C) 2013-2014 Steve Hay. All
+# rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::SMTP;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use IO::Socket;
+use Net::Cmd;
+use Net::Config;
+use Socket;
+
+our $VERSION = "3.04";
+
+# Code for detecting if we can use SSL
+my $ssl_class = eval {
+ require IO::Socket::SSL;
+ # first version with default CA on most platforms
+ no warnings 'numeric';
+ IO::Socket::SSL->VERSION(2.007);
+} && 'IO::Socket::SSL';
+
+my $nossl_warn = !$ssl_class &&
+ 'To use SSL please install IO::Socket::SSL with version>=2.007';
+
+# Code for detecting if we can use IPv6
+my $inet6_class = eval {
+ require IO::Socket::IP;
+ no warnings 'numeric';
+ IO::Socket::IP->VERSION(0.20);
+} && 'IO::Socket::IP' || eval {
+ require IO::Socket::INET6;
+ no warnings 'numeric';
+ IO::Socket::INET6->VERSION(2.62);
+} && 'IO::Socket::INET6';
+
+sub can_ssl { $ssl_class };
+sub can_inet6 { $inet6_class };
+
+our @ISA = ('Net::Cmd', $inet6_class || 'IO::Socket::INET');
+
+sub new {
+ my $self = shift;
+ my $type = ref($self) || $self;
+ my ($host, %arg);
+ if (@_ % 2) {
+ $host = shift;
+ %arg = @_;
+ }
+ else {
+ %arg = @_;
+ $host = delete $arg{Host};
+ }
+
+ if ($arg{SSL}) {
+ # SSL from start
+ die $nossl_warn if !$ssl_class;
+ $arg{Port} ||= 465;
+ }
+
+ my $hosts = defined $host ? $host : $NetConfig{smtp_hosts};
+ my $obj;
+
+ $arg{Timeout} = 120 if ! defined $arg{Timeout};
+
+ foreach my $h (@{ref($hosts) ? $hosts : [$hosts]}) {
+ $obj = $type->SUPER::new(
+ PeerAddr => ($host = $h),
+ PeerPort => $arg{Port} || 'smtp(25)',
+ LocalAddr => $arg{LocalAddr},
+ LocalPort => $arg{LocalPort},
+ Proto => 'tcp',
+ Timeout => $arg{Timeout}
+ )
+ and last;
+ }
+
+ return
+ unless defined $obj;
+
+ ${*$obj}{'net_smtp_arg'} = \%arg;
+ ${*$obj}{'net_smtp_host'} = $host;
+
+ if ($arg{SSL}) {
+ Net::SMTP::_SSL->start_SSL($obj,%arg)
+ or return;
+ }
+
+ $obj->autoflush(1);
+
+ $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
+
+ unless ($obj->response() == CMD_OK) {
+ my $err = ref($obj) . ": " . $obj->code . " " . $obj->message;
+ $obj->close();
+ $@ = $err;
+ return;
+ }
+
+ ${*$obj}{'net_smtp_exact_addr'} = $arg{ExactAddresses};
+
+ (${*$obj}{'net_smtp_banner'}) = $obj->message;
+ (${*$obj}{'net_smtp_domain'}) = $obj->message =~ /\A\s*(\S+)/;
+
+ unless ($obj->hello($arg{Hello} || "")) {
+ my $err = ref($obj) . ": " . $obj->code . " " . $obj->message;
+ $obj->close();
+ $@ = $err;
+ return;
+ }
+
+ $obj;
+}
+
+
+sub host {
+ my $me = shift;
+ ${*$me}{'net_smtp_host'};
+}
+
+##
+## User interface methods
+##
+
+
+sub banner {
+ my $me = shift;
+
+ return ${*$me}{'net_smtp_banner'} || undef;
+}
+
+
+sub domain {
+ my $me = shift;
+
+ return ${*$me}{'net_smtp_domain'} || undef;
+}
+
+
+sub etrn {
+ my $self = shift;
+ defined($self->supports('ETRN', 500, ["Command unknown: 'ETRN'"]))
+ && $self->_ETRN(@_);
+}
+
+
+sub auth {
+ my ($self, $username, $password) = @_;
+
+ eval {
+ require MIME::Base64;
+ require Authen::SASL;
+ } or $self->set_status(500, ["Need MIME::Base64 and Authen::SASL todo auth"]), return 0;
+
+ my $mechanisms = $self->supports('AUTH', 500, ["Command unknown: 'AUTH'"]);
+ return unless defined $mechanisms;
+
+ my $sasl;
+
+ if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) {
+ $sasl = $username;
+ my $requested_mechanisms = $sasl->mechanism();
+ if (! defined($requested_mechanisms) || $requested_mechanisms eq '') {
+ $sasl->mechanism($mechanisms);
+ }
+ }
+ else {
+ die "auth(username, password)" if not length $username;
+ $sasl = Authen::SASL->new(
+ mechanism => $mechanisms,
+ callback => {
+ user => $username,
+ pass => $password,
+ authname => $username,
+ },
+ debug => $self->debug
+ );
+ }
+
+ my $client;
+ my $str;
+ do {
+ if ($client) {
+ # $client mechanism failed, so we need to exclude this mechanism from list
+ my $failed_mechanism = $client->mechanism;
+ $self->debug_text("Auth mechanism failed: $failed_mechanism")
+ if $self->debug;
+ $mechanisms =~ s/\b\Q$failed_mechanism\E\b//;
+ last unless $mechanisms =~ /\S/;
+ $sasl->mechanism($mechanisms);
+ }
+
+ # We should probably allow the user to pass the host, but I don't
+ # currently know and SASL mechanisms that are used by smtp that need it
+
+ $client = $sasl->client_new('smtp', ${*$self}{'net_smtp_host'}, 0);
+ $str = $client->client_start;
+ } while (!defined $str);
+
+ # We don't support sasl mechanisms that encrypt the socket traffic.
+ # todo that we would really need to change the ISA hierarchy
+ # so we don't inherit from IO::Socket, but instead hold it in an attribute
+
+ my @cmd = ("AUTH", $client->mechanism);
+ my $code;
+
+ push @cmd, MIME::Base64::encode_base64($str, '')
+ if defined $str and length $str;
+
+ while (($code = $self->command(@cmd)->response()) == CMD_MORE) {
+ @cmd = (
+ MIME::Base64::encode_base64(
+ $client->client_step(MIME::Base64::decode_base64(($self->message)[0])), ''
+ )
+ );
+ }
+
+ $code == CMD_OK;
+}
+
+
+sub hello {
+ my $me = shift;
+ my $domain = shift || "localhost.localdomain";
+ my $ok = $me->_EHLO($domain);
+ my @msg = $me->message;
+
+ if ($ok) {
+ my $h = ${*$me}{'net_smtp_esmtp'} = {};
+ foreach my $ln (@msg) {
+ $h->{uc $1} = $2
+ if $ln =~ /([-\w]+)\b[= \t]*([^\n]*)/;
+ }
+ }
+ elsif ($me->status == CMD_ERROR) {
+ @msg = $me->message
+ if $ok = $me->_HELO($domain);
+ }
+
+ return unless $ok;
+ ${*$me}{net_smtp_hello_domain} = $domain;
+
+ $msg[0] =~ /\A\s*(\S+)/;
+ return ($1 || " ");
+}
+
+sub starttls {
+ my $self = shift;
+ $ssl_class or die $nossl_warn;
+ $self->_STARTTLS or return;
+ Net::SMTP::_SSL->start_SSL($self,
+ %{ ${*$self}{'net_smtp_arg'} }, # (ssl) args given in new
+ @_ # more (ssl) args
+ ) or return;
+
+ # another hello after starttls to read new ESMTP capabilities
+ return $self->hello(${*$self}{net_smtp_hello_domain});
+}
+
+
+sub supports {
+ my $self = shift;
+ my $cmd = uc shift;
+ return ${*$self}{'net_smtp_esmtp'}->{$cmd}
+ if exists ${*$self}{'net_smtp_esmtp'}->{$cmd};
+ $self->set_status(@_)
+ if @_;
+ return;
+}
+
+
+sub _addr {
+ my $self = shift;
+ my $addr = shift;
+ $addr = "" unless defined $addr;
+
+ if (${*$self}{'net_smtp_exact_addr'}) {
+ return $1 if $addr =~ /^\s*(<.*>)\s*$/s;
+ }
+ else {
+ return $1 if $addr =~ /(<[^>]*>)/;
+ $addr =~ s/^\s+|\s+$//sg;
+ }
+
+ "<$addr>";
+}
+
+
+sub mail {
+ my $me = shift;
+ my $addr = _addr($me, shift);
+ my $opts = "";
+
+ if (@_) {
+ my %opt = @_;
+ my ($k, $v);
+
+ if (exists ${*$me}{'net_smtp_esmtp'}) {
+ my $esmtp = ${*$me}{'net_smtp_esmtp'};
+
+ if (defined($v = delete $opt{Size})) {
+ if (exists $esmtp->{SIZE}) {
+ $opts .= sprintf " SIZE=%d", $v + 0;
+ }
+ else {
+ carp 'Net::SMTP::mail: SIZE option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{Return})) {
+ if (exists $esmtp->{DSN}) {
+ $opts .= " RET=" . ((uc($v) eq "FULL") ? "FULL" : "HDRS");
+ }
+ else {
+ carp 'Net::SMTP::mail: DSN option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{Bits})) {
+ if ($v eq "8") {
+ if (exists $esmtp->{'8BITMIME'}) {
+ $opts .= " BODY=8BITMIME";
+ }
+ else {
+ carp 'Net::SMTP::mail: 8BITMIME option not supported by host';
+ }
+ }
+ elsif ($v eq "binary") {
+ if (exists $esmtp->{'BINARYMIME'} && exists $esmtp->{'CHUNKING'}) {
+ $opts .= " BODY=BINARYMIME";
+ ${*$me}{'net_smtp_chunking'} = 1;
+ }
+ else {
+ carp 'Net::SMTP::mail: BINARYMIME option not supported by host';
+ }
+ }
+ elsif (exists $esmtp->{'8BITMIME'} or exists $esmtp->{'BINARYMIME'}) {
+ $opts .= " BODY=7BIT";
+ }
+ else {
+ carp 'Net::SMTP::mail: 8BITMIME and BINARYMIME options not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{Transaction})) {
+ if (exists $esmtp->{CHECKPOINT}) {
+ $opts .= " TRANSID=" . _addr($me, $v);
+ }
+ else {
+ carp 'Net::SMTP::mail: CHECKPOINT option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{Envelope})) {
+ if (exists $esmtp->{DSN}) {
+ $v =~ s/([^\041-\176]|=|\+)/sprintf "+%02X", ord($1)/sge;
+ $opts .= " ENVID=$v";
+ }
+ else {
+ carp 'Net::SMTP::mail: DSN option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{ENVID})) {
+
+ # expected to be in a format as required by RFC 3461, xtext-encoded
+ if (exists $esmtp->{DSN}) {
+ $opts .= " ENVID=$v";
+ }
+ else {
+ carp 'Net::SMTP::mail: DSN option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{AUTH})) {
+
+ # expected to be in a format as required by RFC 2554,
+ # rfc2821-quoted and xtext-encoded, or <>
+ if (exists $esmtp->{AUTH}) {
+ $v = '<>' if !defined($v) || $v eq '';
+ $opts .= " AUTH=$v";
+ }
+ else {
+ carp 'Net::SMTP::mail: AUTH option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{XVERP})) {
+ if (exists $esmtp->{'XVERP'}) {
+ $opts .= " XVERP";
+ }
+ else {
+ carp 'Net::SMTP::mail: XVERP option not supported by host';
+ }
+ }
+
+ carp 'Net::SMTP::recipient: unknown option(s) ' . join(" ", keys %opt) . ' - ignored'
+ if scalar keys %opt;
+ }
+ else {
+ carp 'Net::SMTP::mail: ESMTP not supported by host - options discarded :-(';
+ }
+ }
+
+ $me->_MAIL("FROM:" . $addr . $opts);
+}
+
+
+sub send { my $me = shift; $me->_SEND("FROM:" . _addr($me, $_[0])) }
+sub send_or_mail { my $me = shift; $me->_SOML("FROM:" . _addr($me, $_[0])) }
+sub send_and_mail { my $me = shift; $me->_SAML("FROM:" . _addr($me, $_[0])) }
+
+
+sub reset {
+ my $me = shift;
+
+ $me->dataend()
+ if (exists ${*$me}{'net_smtp_lastch'});
+
+ $me->_RSET();
+}
+
+
+sub recipient {
+ my $smtp = shift;
+ my $opts = "";
+ my $skip_bad = 0;
+
+ if (@_ && ref($_[-1])) {
+ my %opt = %{pop(@_)};
+ my $v;
+
+ $skip_bad = delete $opt{'SkipBad'};
+
+ if (exists ${*$smtp}{'net_smtp_esmtp'}) {
+ my $esmtp = ${*$smtp}{'net_smtp_esmtp'};
+
+ if (defined($v = delete $opt{Notify})) {
+ if (exists $esmtp->{DSN}) {
+ $opts .= " NOTIFY=" . join(",", map { uc $_ } @$v);
+ }
+ else {
+ carp 'Net::SMTP::recipient: DSN option not supported by host';
+ }
+ }
+
+ if (defined($v = delete $opt{ORcpt})) {
+ if (exists $esmtp->{DSN}) {
+ $opts .= " ORCPT=" . $v;
+ }
+ else {
+ carp 'Net::SMTP::recipient: DSN option not supported by host';
+ }
+ }
+
+ carp 'Net::SMTP::recipient: unknown option(s) ' . join(" ", keys %opt) . ' - ignored'
+ if scalar keys %opt;
+ }
+ elsif (%opt) {
+ carp 'Net::SMTP::recipient: ESMTP not supported by host - options discarded :-(';
+ }
+ }
+
+ my @ok;
+ foreach my $addr (@_) {
+ if ($smtp->_RCPT("TO:" . _addr($smtp, $addr) . $opts)) {
+ push(@ok, $addr) if $skip_bad;
+ }
+ elsif (!$skip_bad) {
+ return 0;
+ }
+ }
+
+ return $skip_bad ? @ok : 1;
+}
+
+BEGIN {
+ *to = \&recipient;
+ *cc = \&recipient;
+ *bcc = \&recipient;
+}
+
+
+sub data {
+ my $me = shift;
+
+ if (exists ${*$me}{'net_smtp_chunking'}) {
+ carp 'Net::SMTP::data: CHUNKING extension in use, must call bdat instead';
+ }
+ else {
+ my $ok = $me->_DATA() && $me->datasend(@_);
+
+ $ok && @_
+ ? $me->dataend
+ : $ok;
+ }
+}
+
+
+sub bdat {
+ my $me = shift;
+
+ if (exists ${*$me}{'net_smtp_chunking'}) {
+ my $data = shift;
+
+ $me->_BDAT(length $data)
+ && $me->rawdatasend($data)
+ && $me->response() == CMD_OK;
+ }
+ else {
+ carp 'Net::SMTP::bdat: CHUNKING extension is not in use, call data instead';
+ }
+}
+
+
+sub bdatlast {
+ my $me = shift;
+
+ if (exists ${*$me}{'net_smtp_chunking'}) {
+ my $data = shift;
+
+ $me->_BDAT(length $data, "LAST")
+ && $me->rawdatasend($data)
+ && $me->response() == CMD_OK;
+ }
+ else {
+ carp 'Net::SMTP::bdat: CHUNKING extension is not in use, call data instead';
+ }
+}
+
+
+sub datafh {
+ my $me = shift;
+ return unless $me->_DATA();
+ return $me->tied_fh;
+}
+
+
+sub expand {
+ my $me = shift;
+
+ $me->_EXPN(@_)
+ ? ($me->message)
+ : ();
+}
+
+
+sub verify { shift->_VRFY(@_) }
+
+
+sub help {
+ my $me = shift;
+
+ $me->_HELP(@_)
+ ? scalar $me->message
+ : undef;
+}
+
+
+sub quit {
+ my $me = shift;
+
+ $me->_QUIT;
+ $me->close;
+}
+
+
+sub DESTROY {
+
+ # ignore
+}
+
+##
+## RFC821 commands
+##
+
+
+sub _EHLO { shift->command("EHLO", @_)->response() == CMD_OK }
+sub _HELO { shift->command("HELO", @_)->response() == CMD_OK }
+sub _MAIL { shift->command("MAIL", @_)->response() == CMD_OK }
+sub _RCPT { shift->command("RCPT", @_)->response() == CMD_OK }
+sub _SEND { shift->command("SEND", @_)->response() == CMD_OK }
+sub _SAML { shift->command("SAML", @_)->response() == CMD_OK }
+sub _SOML { shift->command("SOML", @_)->response() == CMD_OK }
+sub _VRFY { shift->command("VRFY", @_)->response() == CMD_OK }
+sub _EXPN { shift->command("EXPN", @_)->response() == CMD_OK }
+sub _HELP { shift->command("HELP", @_)->response() == CMD_OK }
+sub _RSET { shift->command("RSET")->response() == CMD_OK }
+sub _NOOP { shift->command("NOOP")->response() == CMD_OK }
+sub _QUIT { shift->command("QUIT")->response() == CMD_OK }
+sub _DATA { shift->command("DATA")->response() == CMD_MORE }
+sub _BDAT { shift->command("BDAT", @_) }
+sub _TURN { shift->unsupported(@_); }
+sub _ETRN { shift->command("ETRN", @_)->response() == CMD_OK }
+sub _AUTH { shift->command("AUTH", @_)->response() == CMD_OK }
+sub _STARTTLS { shift->command("STARTTLS")->response() == CMD_OK }
+
+
+{
+ package Net::SMTP::_SSL;
+ our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::SMTP' );
+ sub starttls { die "SMTP connection is already in SSL mode" }
+ sub start_SSL {
+ my ($class,$smtp,%arg) = @_;
+ delete @arg{ grep { !m{^SSL_} } keys %arg };
+ ( $arg{SSL_verifycn_name} ||= $smtp->host )
+ =~s{(?<!:):[\w()]+$}{}; # strip port
+ $arg{SSL_hostname} = $arg{SSL_verifycn_name}
+ if ! defined $arg{SSL_hostname} && $class->can_client_sni;
+ $arg{SSL_verifycn_scheme} ||= 'smtp';
+ my $ok = $class->SUPER::start_SSL($smtp,%arg);
+ $@ = $ssl_class->errstr if !$ok;
+ return $ok;
+ }
+}
+
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::SMTP - Simple Mail Transfer Protocol Client
+
+=head1 SYNOPSIS
+
+ use Net::SMTP;
+
+ # Constructors
+ $smtp = Net::SMTP->new('mailhost');
+ $smtp = Net::SMTP->new('mailhost', Timeout => 60);
+
+=head1 DESCRIPTION
+
+This module implements a client interface to the SMTP and ESMTP
+protocol, enabling a perl5 application to talk to SMTP servers. This
+documentation assumes that you are familiar with the concepts of the
+SMTP protocol described in RFC821.
+
+A new Net::SMTP object must be created with the I<new> method. Once
+this has been done, all SMTP commands are accessed through this object.
+
+The Net::SMTP class is a subclass of Net::Cmd and IO::Socket::INET.
+
+=head1 EXAMPLES
+
+This example prints the mail domain name of the SMTP server known as mailhost:
+
+ #!/usr/local/bin/perl -w
+
+ use Net::SMTP;
+
+ $smtp = Net::SMTP->new('mailhost');
+ print $smtp->domain,"\n";
+ $smtp->quit;
+
+This example sends a small message to the postmaster at the SMTP server
+known as mailhost:
+
+ #!/usr/local/bin/perl -w
+
+ use Net::SMTP;
+
+ my $smtp = Net::SMTP->new('mailhost');
+
+ $smtp->mail($ENV{USER});
+ if ($smtp->to('postmaster')) {
+ $smtp->data();
+ $smtp->datasend("To: postmaster\n");
+ $smtp->datasend("\n");
+ $smtp->datasend("A simple test message\n");
+ $smtp->dataend();
+ } else {
+ print "Error: ", $smtp->message();
+ }
+
+ $smtp->quit;
+
+=head1 CONSTRUCTOR
+
+=over 4
+
+=item new ( [ HOST ] [, OPTIONS ] )
+
+This is the constructor for a new Net::SMTP object. C<HOST> is the
+name of the remote host to which an SMTP connection is required.
+
+On failure C<undef> will be returned and C<$@> will contain the reason
+for the failure.
+
+C<HOST> is optional. If C<HOST> is not given then it may instead be
+passed as the C<Host> option described below. If neither is given then
+the C<SMTP_Hosts> specified in C<Net::Config> will be used.
+
+C<OPTIONS> are passed in a hash like fashion, using key and value pairs.
+Possible options are:
+
+B<Hello> - SMTP requires that you identify yourself. This option
+specifies a string to pass as your mail domain. If not given localhost.localdomain
+will be used.
+
+B<Host> - SMTP host to connect to. It may be a single scalar (hostname[:port]),
+as defined for the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to
+an array with hosts to try in turn. The L</host> method will return the value
+which was used to connect to the host.
+Format - C<PeerHost> from L<IO::Socket::INET> new method.
+
+B<Port> - port to connect to.
+Default - 25 for plain SMTP and 465 for immediate SSL.
+
+B<SSL> - If the connection should be done from start with SSL, contrary to later
+upgrade with C<starttls>.
+You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
+usually use the right arguments already.
+
+B<LocalAddr> and B<LocalPort> - These parameters are passed directly
+to IO::Socket to allow binding the socket to a local port.
+
+B<Timeout> - Maximum time, in seconds, to wait for a response from the
+SMTP server (default: 120)
+
+B<ExactAddresses> - If true the all ADDRESS arguments must be as
+defined by C<addr-spec> in RFC2822. If not given, or false, then
+Net::SMTP will attempt to extract the address from the value passed.
+
+B<Debug> - Enable debugging information
+
+
+Example:
+
+
+ $smtp = Net::SMTP->new('mailhost',
+ Hello => 'my.mail.domain',
+ Timeout => 30,
+ Debug => 1,
+ );
+
+ # the same
+ $smtp = Net::SMTP->new(
+ Host => 'mailhost',
+ Hello => 'my.mail.domain',
+ Timeout => 30,
+ Debug => 1,
+ );
+
+ # the same with direct SSL
+ $smtp = Net::SMTP->new('mailhost',
+ Hello => 'my.mail.domain',
+ Timeout => 30,
+ Debug => 1,
+ SSL => 1,
+ );
+
+ # Connect to the default server from Net::config
+ $smtp = Net::SMTP->new(
+ Hello => 'my.mail.domain',
+ Timeout => 30,
+ );
+
+=back
+
+=head1 METHODS
+
+Unless otherwise stated all methods return either a I<true> or I<false>
+value, with I<true> meaning that the operation was a success. When a method
+states that it returns a value, failure will be returned as I<undef> or an
+empty list.
+
+C<Net::SMTP> inherits from C<Net::Cmd> so methods defined in C<Net::Cmd> may
+be used to send commands to the remote SMTP server in addition to the methods
+documented here.
+
+=over 4
+
+=item banner ()
+
+Returns the banner message which the server replied with when the
+initial connection was made.
+
+=item domain ()
+
+Returns the domain that the remote SMTP server identified itself as during
+connection.
+
+=item hello ( DOMAIN )
+
+Tell the remote server the mail domain which you are in using the EHLO
+command (or HELO if EHLO fails). Since this method is invoked
+automatically when the Net::SMTP object is constructed the user should
+normally not have to call it manually.
+
+=item host ()
+
+Returns the value used by the constructor, and passed to IO::Socket::INET,
+to connect to the host.
+
+=item etrn ( DOMAIN )
+
+Request a queue run for the DOMAIN given.
+
+=item starttls ( SSLARGS )
+
+Upgrade existing plain connection to SSL.
+You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will
+usually use the right arguments already.
+
+=item auth ( USERNAME, PASSWORD )
+
+Attempt SASL authentication. Requires Authen::SASL module.
+
+=item mail ( ADDRESS [, OPTIONS] )
+
+=item send ( ADDRESS )
+
+=item send_or_mail ( ADDRESS )
+
+=item send_and_mail ( ADDRESS )
+
+Send the appropriate command to the server MAIL, SEND, SOML or SAML. C<ADDRESS>
+is the address of the sender. This initiates the sending of a message. The
+method C<recipient> should be called for each address that the message is to
+be sent to.
+
+The C<mail> method can some additional ESMTP OPTIONS which is passed
+in hash like fashion, using key and value pairs. Possible options are:
+
+ Size => <bytes>
+ Return => "FULL" | "HDRS"
+ Bits => "7" | "8" | "binary"
+ Transaction => <ADDRESS>
+ Envelope => <ENVID> # xtext-encodes its argument
+ ENVID => <ENVID> # similar to Envelope, but expects argument encoded
+ XVERP => 1
+ AUTH => <submitter> # encoded address according to RFC 2554
+
+The C<Return> and C<Envelope> parameters are used for DSN (Delivery
+Status Notification).
+
+The submitter address in C<AUTH> option is expected to be in a format as
+required by RFC 2554, in an RFC2821-quoted form and xtext-encoded, or <> .
+
+=item reset ()
+
+Reset the status of the server. This may be called after a message has been
+initiated, but before any data has been sent, to cancel the sending of the
+message.
+
+=item recipient ( ADDRESS [, ADDRESS, [...]] [, OPTIONS ] )
+
+Notify the server that the current message should be sent to all of the
+addresses given. Each address is sent as a separate command to the server.
+Should the sending of any address result in a failure then the process is
+aborted and a I<false> value is returned. It is up to the user to call
+C<reset> if they so desire.
+
+The C<recipient> method can also pass additional case-sensitive OPTIONS as an
+anonymous hash using key and value pairs. Possible options are:
+
+ Notify => ['NEVER'] or ['SUCCESS','FAILURE','DELAY'] (see below)
+ ORcpt => <ORCPT>
+ SkipBad => 1 (to ignore bad addresses)
+
+If C<SkipBad> is true the C<recipient> will not return an error when a bad
+address is encountered and it will return an array of addresses that did
+succeed.
+
+ $smtp->recipient($recipient1,$recipient2); # Good
+ $smtp->recipient($recipient1,$recipient2, { SkipBad => 1 }); # Good
+ $smtp->recipient($recipient1,$recipient2, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); # Good
+ @goodrecips=$smtp->recipient(@recipients, { Notify => ['FAILURE'], SkipBad => 1 }); # Good
+ $smtp->recipient("$recipient,$recipient2"); # BAD
+
+Notify is used to request Delivery Status Notifications (DSNs), but your
+SMTP/ESMTP service may not respect this request depending upon its version and
+your site's SMTP configuration.
+
+Leaving out the Notify option usually defaults an SMTP service to its default
+behavior equivalent to ['FAILURE'] notifications only, but again this may be
+dependent upon your site's SMTP configuration.
+
+The NEVER keyword must appear by itself if used within the Notify option and "requests
+that a DSN not be returned to the sender under any conditions."
+
+ {Notify => ['NEVER']}
+
+ $smtp->recipient(@recipients, { Notify => ['NEVER'], SkipBad => 1 }); # Good
+
+You may use any combination of these three values 'SUCCESS','FAILURE','DELAY' in
+the anonymous array reference as defined by RFC3461 (see http://www.ietf.org/rfc/rfc3461.txt
+for more information. Note: quotations in this topic from same.).
+
+A Notify parameter of 'SUCCESS' or 'FAILURE' "requests that a DSN be issued on
+successful delivery or delivery failure, respectively."
+
+A Notify parameter of 'DELAY' "indicates the sender's willingness to receive
+delayed DSNs. Delayed DSNs may be issued if delivery of a message has been
+delayed for an unusual amount of time (as determined by the Message Transfer
+Agent (MTA) at which the message is delayed), but the final delivery status
+(whether successful or failure) cannot be determined. The absence of the DELAY
+keyword in a NOTIFY parameter requests that a "delayed" DSN NOT be issued under
+any conditions."
+
+ {Notify => ['SUCCESS','FAILURE','DELAY']}
+
+ $smtp->recipient(@recipients, { Notify => ['FAILURE','DELAY'], SkipBad => 1 }); # Good
+
+ORcpt is also part of the SMTP DSN extension according to RFC3461.
+It is used to pass along the original recipient that the mail was first
+sent to. The machine that generates a DSN will use this address to inform
+the sender, because he can't know if recipients get rewritten by mail servers.
+It is expected to be in a format as required by RFC3461, xtext-encoded.
+
+=item to ( ADDRESS [, ADDRESS [...]] )
+
+=item cc ( ADDRESS [, ADDRESS [...]] )
+
+=item bcc ( ADDRESS [, ADDRESS [...]] )
+
+Synonyms for C<recipient>.
+
+=item data ( [ DATA ] )
+
+Initiate the sending of the data from the current message.
+
+C<DATA> may be a reference to a list or a list. If specified the contents
+of C<DATA> and a termination string C<".\r\n"> is sent to the server. And the
+result will be true if the data was accepted.
+
+If C<DATA> is not specified then the result will indicate that the server
+wishes the data to be sent. The data must then be sent using the C<datasend>
+and C<dataend> methods described in L<Net::Cmd>.
+
+=item bdat ( DATA )
+
+=item bdatlast ( DATA )
+
+Use the alternate DATA command "BDAT" of the data chunking service extension
+defined in RFC1830 for efficiently sending large MIME messages.
+
+=item expand ( ADDRESS )
+
+Request the server to expand the given address Returns an array
+which contains the text read from the server.
+
+=item verify ( ADDRESS )
+
+Verify that C<ADDRESS> is a legitimate mailing address.
+
+Most sites usually disable this feature in their SMTP service configuration.
+Use "Debug => 1" option under new() to see if disabled.
+
+=item help ( [ $subject ] )
+
+Request help text from the server. Returns the text or undef upon failure
+
+=item quit ()
+
+Send the QUIT command to the remote SMTP server and close the socket connection.
+
+=item can_inet6 ()
+
+Returns whether we can use IPv6.
+
+=item can_ssl ()
+
+Returns whether we can use SSL.
+
+=back
+
+=head1 ADDRESSES
+
+Net::SMTP attempts to DWIM with addresses that are passed. For
+example an application might extract The From: line from an email
+and pass that to mail(). While this may work, it is not recommended.
+The application should really use a module like L<Mail::Address>
+to extract the mail address and pass that.
+
+If C<ExactAddresses> is passed to the constructor, then addresses
+should be a valid rfc2821-quoted address, although Net::SMTP will
+accept the address surrounded by angle brackets.
+
+ funny user@domain WRONG
+ "funny user"@domain RIGHT, recommended
+ <"funny user"@domain> OK
+
+=head1 SEE ALSO
+
+L<Net::Cmd>,
+L<IO::Socket::SSL>
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.31_1 Copyright (c) 1995-2004 Graham Barr. All rights reserved.
+Changes in Version 2.31_2 onwards Copyright (C) 2013-2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,162 @@
+# Net::Time.pm
+#
+# Versions up to 2.10 Copyright (c) 1995-2004 Graham Barr <gbarr@pobox.com>.
+# All rights reserved.
+# Changes in Version 2.11 onwards Copyright (C) 2014 Steve Hay. All rights
+# reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::Time;
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Carp;
+use Exporter;
+use IO::Select;
+use IO::Socket;
+use Net::Config;
+
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(inet_time inet_daytime);
+
+our $VERSION = "3.04";
+
+our $TIMEOUT = 120;
+
+sub _socket {
+ my ($pname, $pnum, $host, $proto, $timeout) = @_;
+
+ $proto ||= 'udp';
+
+ my $port = (getservbyname($pname, $proto))[2] || $pnum;
+
+ my $hosts = defined $host ? [$host] : $NetConfig{$pname . '_hosts'};
+
+ my $me;
+
+ foreach my $addr (@$hosts) {
+ $me = IO::Socket::INET->new(
+ PeerAddr => $addr,
+ PeerPort => $port,
+ Proto => $proto
+ )
+ and last;
+ }
+
+ return unless $me;
+
+ $me->send("\n")
+ if $proto eq 'udp';
+
+ $timeout = $TIMEOUT
+ unless defined $timeout;
+
+ IO::Select->new($me)->can_read($timeout)
+ ? $me
+ : undef;
+}
+
+
+sub inet_time {
+ my $s = _socket('time', 37, @_) || return;
+ my $buf = '';
+ my $offset = 0 | 0;
+
+ return
+ unless defined $s->recv($buf, length(pack("N", 0)));
+
+ # unpack, we | 0 to ensure we have an unsigned
+ my $time = (unpack("N", $buf))[0] | 0;
+
+ # the time protocol return time in seconds since 1900, convert
+ # it to a the required format
+
+ if ($^O eq "MacOS") {
+
+ # MacOS return seconds since 1904, 1900 was not a leap year.
+ $offset = (4 * 31536000) | 0;
+ }
+ else {
+
+ # otherwise return seconds since 1972, there were 17 leap years between
+ # 1900 and 1972
+ $offset = (70 * 31536000 + 17 * 86400) | 0;
+ }
+
+ $time - $offset;
+}
+
+
+sub inet_daytime {
+ my $s = _socket('daytime', 13, @_) || return;
+ my $buf = '';
+
+ defined($s->recv($buf, 1024))
+ ? $buf
+ : undef;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Net::Time - time and daytime network client interface
+
+=head1 SYNOPSIS
+
+ use Net::Time qw(inet_time inet_daytime);
+
+ print inet_time(); # use default host from Net::Config
+ print inet_time('localhost');
+ print inet_time('localhost', 'tcp');
+
+ print inet_daytime(); # use default host from Net::Config
+ print inet_daytime('localhost');
+ print inet_daytime('localhost', 'tcp');
+
+=head1 DESCRIPTION
+
+C<Net::Time> provides subroutines that obtain the time on a remote machine.
+
+=over 4
+
+=item inet_time ( [HOST [, PROTOCOL [, TIMEOUT]]])
+
+Obtain the time on C<HOST>, or some default host if C<HOST> is not given
+or not defined, using the protocol as defined in RFC868. The optional
+argument C<PROTOCOL> should define the protocol to use, either C<tcp> or
+C<udp>. The result will be a time value in the same units as returned
+by time() or I<undef> upon failure.
+
+=item inet_daytime ( [HOST [, PROTOCOL [, TIMEOUT]]])
+
+Obtain the time on C<HOST>, or some default host if C<HOST> is not given
+or not defined, using the protocol as defined in RFC867. The optional
+argument C<PROTOCOL> should define the protocol to use, either C<tcp> or
+C<udp>. The result will be an ASCII string or I<undef> upon failure.
+
+=back
+
+=head1 AUTHOR
+
+Graham Barr E<lt>F<gbarr@pobox.com>E<gt>
+
+Steve Hay E<lt>F<shay@cpan.org>E<gt> is now maintaining libnet as of version
+1.22_02
+
+=head1 COPYRIGHT
+
+Versions up to 2.11 Copyright (c) 1995-2004 Graham Barr. All rights reserved.
+Changes in Version 2.11 onwards Copyright (C) 2014 Steve Hay. All rights
+reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
@@ -0,0 +1,302 @@
+=head1 NAME
+
+libnetFAQ - libnet Frequently Asked Questions
+
+=head1 DESCRIPTION
+
+=head2 Where to get this document
+
+This document is distributed with the libnet distribution, and is also
+available on the libnet web page at
+
+ http://search.cpan.org/dist/libnet/
+
+=head2 How to contribute to this document
+
+You may report corrections, additions, and suggestions on the
+CPAN Request Tracker at
+
+ http://rt.cpan.org/Public/Bug/Report.html?Queue=libnet
+
+=head1 Author and Copyright Information
+
+Copyright (c) 1997-1998 Graham Barr. All rights reserved.
+This document is free; you can redistribute it and/or modify it
+under the terms of the Artistic License.
+
+Currently maintained by Steve Hay <shay@cpan.org>.
+
+=head2 Disclaimer
+
+This information is offered in good faith and in the hope that it may
+be of use, but is not guaranteed to be correct, up to date, or suitable
+for any particular purpose whatsoever. The authors accept no liability
+in respect of this information or its use.
+
+
+=head1 Obtaining and installing libnet
+
+=head2 What is libnet ?
+
+libnet is a collection of perl5 modules which all related to network
+programming. The majority of the modules available provided the
+client side of popular server-client protocols that are used in
+the internet community.
+
+=head2 Which version of perl do I need ?
+
+This version of libnet requires Perl 5.8.1 or higher.
+
+=head2 What other modules do I need ?
+
+No non-core modules are required for normal use, except on os390,
+which requires Convert::EBCDIC.
+
+Authen::SASL is required for AUTH support.
+
+IO::Socket::SSL version 2.007 or higher is required for SSL support.
+
+IO::Socket::IP version 0.20 or IO::Socket::INET6 version 2.62 is
+required for IPv6 support.
+
+=head2 What machines support libnet ?
+
+libnet itself is an entirely perl-code distribution so it should work
+on any machine that perl runs on.
+
+=head2 Where can I get the latest libnet release
+
+The latest libnet release is always on CPAN, you will find it
+in
+
+ http://search.cpan.org/dist/libnet/
+
+=head1 Using Net::FTP
+
+=head2 How do I download files from an FTP server ?
+
+An example taken from an article posted to comp.lang.perl.misc
+
+ #!/your/path/to/perl
+
+ # a module making life easier
+
+ use Net::FTP;
+
+ # for debugging: $ftp = Net::FTP->new('site','Debug',10);
+ # open a connection and log in!
+
+ $ftp = Net::FTP->new('target_site.somewhere.xxx');
+ $ftp->login('username','password');
+
+ # set transfer mode to binary
+
+ $ftp->binary();
+
+ # change the directory on the ftp site
+
+ $ftp->cwd('/some/path/to/somewhere/');
+
+ foreach $name ('file1', 'file2', 'file3') {
+
+ # get's arguments are in the following order:
+ # ftp server's filename
+ # filename to save the transfer to on the local machine
+ # can be simply used as get($name) if you want the same name
+
+ $ftp->get($name,$name);
+ }
+
+ # ftp done!
+
+ $ftp->quit;
+
+=head2 How do I transfer files in binary mode ?
+
+To transfer files without <LF><CR> translation Net::FTP provides
+the C<binary> method
+
+ $ftp->binary;
+
+=head2 How can I get the size of a file on a remote FTP server ?
+
+=head2 How can I get the modification time of a file on a remote FTP server ?
+
+=head2 How can I change the permissions of a file on a remote server ?
+
+The FTP protocol does not have a command for changing the permissions
+of a file on the remote server. But some ftp servers may allow a chmod
+command to be issued via a SITE command, eg
+
+ $ftp->quot('site chmod 0777',$filename);
+
+But this is not guaranteed to work.
+
+=head2 Can I do a reget operation like the ftp command ?
+
+=head2 How do I get a directory listing from an FTP server ?
+
+=head2 Changing directory to "" does not fail ?
+
+Passing an argument of "" to ->cwd() has the same affect of calling ->cwd()
+without any arguments. Turn on Debug (I<See below>) and you will see what is
+happening
+
+ $ftp = Net::FTP->new($host, Debug => 1);
+ $ftp->login;
+ $ftp->cwd("");
+
+gives
+
+ Net::FTP=GLOB(0x82196d8)>>> CWD /
+ Net::FTP=GLOB(0x82196d8)<<< 250 CWD command successful.
+
+=head2 I am behind a SOCKS firewall, but the Firewall option does not work ?
+
+The Firewall option is only for support of one type of firewall. The type
+supported is an ftp proxy.
+
+To use Net::FTP, or any other module in the libnet distribution,
+through a SOCKS firewall you must create a socks-ified perl executable
+by compiling perl with the socks library.
+
+=head2 I am behind an FTP proxy firewall, but cannot access machines outside ?
+
+Net::FTP implements the most popular ftp proxy firewall approach. The scheme
+implemented is that where you log in to the firewall with C<user@hostname>
+
+I have heard of one other type of firewall which requires a login to the
+firewall with an account, then a second login with C<user@hostname>. You can
+still use Net::FTP to traverse these firewalls, but a more manual approach
+must be taken, eg
+
+ $ftp = Net::FTP->new($firewall) or die $@;
+ $ftp->login($firewall_user, $firewall_passwd) or die $ftp->message;
+ $ftp->login($ext_user . '@' . $ext_host, $ext_passwd) or die $ftp->message.
+
+=head2 My ftp proxy firewall does not listen on port 21
+
+FTP servers usually listen on the same port number, port 21, as any other
+FTP server. But there is no reason why this has to be the case.
+
+If you pass a port number to Net::FTP then it assumes this is the port
+number of the final destination. By default Net::FTP will always try
+to connect to the firewall on port 21.
+
+Net::FTP uses IO::Socket to open the connection and IO::Socket allows
+the port number to be specified as part of the hostname. So this problem
+can be resolved by either passing a Firewall option like C<"hostname:1234">
+or by setting the C<ftp_firewall> option in Net::Config to be a string
+in the same form.
+
+=head2 Is it possible to change the file permissions of a file on an FTP server ?
+
+The answer to this is "maybe". The FTP protocol does not specify a command to change
+file permissions on a remote host. However many servers do allow you to run the
+chmod command via the C<SITE> command. This can be done with
+
+ $ftp->site('chmod','0775',$file);
+
+=head2 I have seen scripts call a method message, but cannot find it documented ?
+
+Net::FTP, like several other packages in libnet, inherits from Net::Cmd, so
+all the methods described in Net::Cmd are also available on Net::FTP
+objects.
+
+=head2 Why does Net::FTP not implement mput and mget methods
+
+The quick answer is because they are easy to implement yourself. The long
+answer is that to write these in such a way that multiple platforms are
+supported correctly would just require too much code. Below are
+some examples how you can implement these yourself.
+
+sub mput {
+ my($ftp,$pattern) = @_;
+ foreach my $file (glob($pattern)) {
+ $ftp->put($file) or warn $ftp->message;
+ }
+}
+
+sub mget {
+ my($ftp,$pattern) = @_;
+ foreach my $file ($ftp->ls($pattern)) {
+ $ftp->get($file) or warn $ftp->message;
+ }
+}
+
+
+=head1 Using Net::SMTP
+
+=head2 Why can't the part of an Email address after the @ be used as the hostname ?
+
+The part of an Email address which follows the @ is not necessarily a hostname,
+it is a mail domain. To find the name of a host to connect for a mail domain
+you need to do a DNS MX lookup
+
+=head2 Why does Net::SMTP not do DNS MX lookups ?
+
+Net::SMTP implements the SMTP protocol. The DNS MX lookup is not part
+of this protocol.
+
+=head2 The verify method always returns true ?
+
+Well it may seem that way, but it does not. The verify method returns true
+if the command succeeded. If you pass verify an address which the
+server would normally have to forward to another machine, the command
+will succeed with something like
+
+ 252 Couldn't verify <someone@there> but will attempt delivery anyway
+
+This command will fail only if you pass it an address in a domain
+the server directly delivers for, and that address does not exist.
+
+=head1 Debugging scripts
+
+=head2 How can I debug my scripts that use Net::* modules ?
+
+Most of the libnet client classes allow options to be passed to the
+constructor, in most cases one option is called C<Debug>. Passing
+this option with a non-zero value will turn on a protocol trace, which
+will be sent to STDERR. This trace can be useful to see what commands
+are being sent to the remote server and what responses are being
+received back.
+
+ #!/your/path/to/perl
+
+ use Net::FTP;
+
+ my $ftp = new Net::FTP($host, Debug => 1);
+ $ftp->login('gbarr','password');
+ $ftp->quit;
+
+this script would output something like
+
+ Net::FTP: Net::FTP(2.22)
+ Net::FTP: Exporter
+ Net::FTP: Net::Cmd(2.0801)
+ Net::FTP: IO::Socket::INET
+ Net::FTP: IO::Socket(1.1603)
+ Net::FTP: IO::Handle(1.1504)
+
+ Net::FTP=GLOB(0x8152974)<<< 220 imagine FTP server (Version wu-2.4(5) Tue Jul 29 11:17:18 CDT 1997) ready.
+ Net::FTP=GLOB(0x8152974)>>> user gbarr
+ Net::FTP=GLOB(0x8152974)<<< 331 Password required for gbarr.
+ Net::FTP=GLOB(0x8152974)>>> PASS ....
+ Net::FTP=GLOB(0x8152974)<<< 230 User gbarr logged in. Access restrictions apply.
+ Net::FTP=GLOB(0x8152974)>>> QUIT
+ Net::FTP=GLOB(0x8152974)<<< 221 Goodbye.
+
+The first few lines tell you the modules that Net::FTP uses and their versions,
+this is useful data to me when a user reports a bug. The last seven lines
+show the communication with the server. Each line has three parts. The first
+part is the object itself, this is useful for separating the output
+if you are using multiple objects. The second part is either C<<<<<> to
+show data coming from the server or C<>>>>> to show data
+going to the server. The remainder of the line is the command
+being sent or response being received.
+
+=head1 AUTHOR AND COPYRIGHT
+
+Copyright (c) 1997 Graham Barr.
+All rights reserved.
+
@@ -0,0 +1,46 @@
+#!perl
+#===============================================================================
+#
+# t/changes.t
+#
+# DESCRIPTION
+# Test script to check CPAN::Changes conformance.
+#
+# COPYRIGHT
+# Copyright (C) 2014 Steve Hay. All rights reserved.
+#
+# LICENCE
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License, as specified in the LICENCE file.
+#
+#===============================================================================
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Test::More;
+
+#===============================================================================
+# MAIN PROGRAM
+#===============================================================================
+
+MAIN: {
+ plan skip_all => 'Author testing only' unless $ENV{AUTHOR_TESTING};
+
+ my $ok = eval {
+ require Test::CPAN::Changes;
+ Test::CPAN::Changes->import();
+ 1;
+ };
+
+ if (not $ok) {
+ plan skip_all => 'Test::CPAN::Changes required to test Changes';
+ }
+ else {
+ changes_ok();
+ }
+}
+
+#===============================================================================
@@ -1,16 +1,17 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- if ($ENV{PERL_CORE}) {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
undef *{Socket::inet_aton};
undef *{Socket::inet_ntoa};
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
$INC{'Socket.pm'} = 1;
@@ -20,7 +21,7 @@ package Socket;
sub import {
my $pkg = caller();
- no strict 'refs';
+ no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
*{ $pkg . '::inet_aton' } = \&inet_aton;
*{ $pkg . '::inet_ntoa' } = \&inet_ntoa;
}
@@ -0,0 +1,46 @@
+#!perl
+#===============================================================================
+#
+# t/critic.t
+#
+# DESCRIPTION
+# Test script to check Perl::Critic conformance.
+#
+# COPYRIGHT
+# Copyright (C) 2014 Steve Hay. All rights reserved.
+#
+# LICENCE
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License, as specified in the LICENCE file.
+#
+#===============================================================================
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Test::More;
+
+#===============================================================================
+# MAIN PROGRAM
+#===============================================================================
+
+MAIN: {
+ plan skip_all => 'Author testing only' unless $ENV{AUTHOR_TESTING};
+
+ my $ok = eval {
+ require Test::Perl::Critic;
+ Test::Perl::Critic->import(-profile => '');
+ 1;
+ };
+
+ if (not $ok) {
+ plan skip_all => 'Test::Perl::Critic required to test with Perl::Critic';
+ }
+ else {
+ all_critic_ok('.');
+ }
+}
+
+#===============================================================================
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- if ($ENV{PERL_CORE}) {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -18,7 +19,7 @@ BEGIN {
use IO::File;
use Net::Cmd;
- @ISA = qw(Net::Cmd IO::File);
+ our @ISA = qw(Net::Cmd IO::File);
sub timeout { 0 }
@@ -0,0 +1,173 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Net::FTP;
+use Test::More;
+use File::Temp;
+use IO::Socket::INET;
+
+my $server = 'test.rebex.net';
+my $debug = 0;
+
+plan skip_all => "no SSL support" if ! Net::FTP->can_ssl;
+require IO::Socket::SSL;
+
+
+# first try to connect w/o ftp
+# plain
+diag( "connect inet to $server:21" );
+IO::Socket::INET->new( "$server:21" ) or do {
+ plan skip_all => "$server:21 not reachable";
+};
+
+# ssl to the right host
+diag( "connect inet to $server:990" );
+my $sock = IO::Socket::INET->new( "$server:990") or do {
+ plan skip_all => "$server:990 not reachable";
+};
+
+# now we need CAs
+my $cafh = File::Temp->new( UNLINK => 0, SUFFIX => '.crt' );
+my %sslargs = ( SSL_ca_file => $cafh->filename );
+print $cafh <DATA>;
+close($cafh);
+
+diag( "upgrade to ssl $server:990" );
+IO::Socket::SSL->start_SSL($sock,
+ SSL_verify_mode => 1,
+ SSL_verifycn_name => $server,
+ SSL_verifycn_scheme => 'ftp',
+ %sslargs,
+) or do {
+ plan skip_all => "$server:990 not upgradable to SSL: ".
+ $IO::Socket::SSL::SSL_ERROR;
+};
+
+plan tests => 9;
+
+# first direct SSL
+diag( "connect ftp over ssl to $server" );
+my $ftp = Net::FTP->new($server,
+ SSL => 1,
+ %sslargs,
+ Debug => $debug,
+ Passive => 1,
+);
+ok($ftp,"ftp ssl connect $server");
+$ftp->login("anonymous",'net-sslglue-ftp@test.perl')
+ or die "login to $server failed";
+diag("logged in");
+# check that we can talk on connection
+ok(~~$ftp->ls,"directory listing protected");
+$ftp->prot('C');
+ok(~~$ftp->ls,"directory listing clear");
+
+# then TLS upgrade inside plain connection
+$ftp = Net::FTP->new($server,
+ Passive => 1,
+ Debug => $debug,
+ %sslargs
+);
+ok($ftp,"ftp plain connect $server");
+my $ok = $ftp->starttls;
+ok($ok,"ssl upgrade");
+$ftp->login("anonymous",'net-sslglue-ftp@test.perl')
+ or die "login to $server failed";
+diag("logged in");
+# check that we can talk on connection
+ok(~~$ftp->ls,"directory listing protected");
+$ftp->prot('C');
+ok(~~$ftp->ls,"directory listing clear");
+$ok = $ftp->stoptls;
+ok($ok,"ssl downgrade");
+ok(~~$ftp->ls,"directory listing after downgrade");
+
+
+__DATA__
+# Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 2 Primary Intermediate Server CA
+# Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh
+G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8
+H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL
+hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE
+d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa
+8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAnQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+
+yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0ULpLA4Gmj0lRPM4EOU
+7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox
+uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntN
+rCEnME5ZRmqTF8rIOS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4
+NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hItuO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6Si
+BeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjHeOBiS0N9PN7WmrQQ
+oUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P
+4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y
+6NjAbx/rJnWfk/x7G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd6
+0Y+NWZP8P3PXLrQsldiL98l+x/ydrHIEH9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3v
+qhykguAzx/Q=
+-----END CERTIFICATE-----
+# Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+# Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+-----BEGIN CERTIFICATE-----
+MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
+MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
+U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
+cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
+pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
+OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
+Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
+Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
+HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
+Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
+Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
+26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
+AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
+ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
+ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
+aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
+YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
+c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
+d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
+CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
+wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
+Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
+0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
+pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
+CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
+P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
+1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
+KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
+JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
+8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
+fyWl8kgAwKQB2j8=
+-----END CERTIFICATE-----
@@ -0,0 +1,58 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Net::POP3;
+use Test::More;
+
+my $host = 'pop.gmx.net';
+my $debug = 0;
+
+plan skip_all => "no SSL support" if ! Net::POP3->can_ssl;
+{
+no warnings 'once';
+plan skip_all => "no verified SSL connection to $host:995 - $@" if ! eval {
+ IO::Socket::SSL->new(PeerAddr => "$host:995", Timeout => 10)
+ || die($IO::Socket::SSL::SSL_ERROR||$!);
+};
+}
+
+plan tests => 2;
+
+SKIP: {
+ diag( "connect inet to $host:110" );
+ skip "no inet connect to $host:110",1
+ if ! IO::Socket::INET->new(PeerAddr => "$host:110", Timeout => 10);
+ my $pop3 = Net::POP3->new($host, Debug => $debug, Timeout => 10)
+ or skip "normal POP3 failed: $@",1;
+ skip "no STARTTLS support",1 if $pop3->message !~/STARTTLS/;
+
+ if (!$pop3->starttls) {
+ fail("starttls failed: ".$pop3->code." $@")
+ } else {
+ # we now should have access to SSL stuff
+ my $cipher = eval { $pop3->get_cipher };
+ if (!$cipher) {
+ fail("after starttls: not an SSL object");
+ } elsif ( $pop3->quit ) {
+ pass("starttls + quit ok, cipher=$cipher");
+ } else {
+ fail("quit after starttls failed: ".$pop3->code);
+ }
+ }
+}
+
+
+my $pop3 = Net::POP3->new($host, SSL => 1, Timeout => 10, Debug => $debug);
+# we now should have access to SSL stuff
+my $cipher = eval { $pop3->get_cipher };
+if (!$cipher) {
+ fail("after ssl connect: not an SSL object");
+} elsif ( $pop3->quit ) {
+ pass("ssl connect ok, cipher=$cipher");
+} else {
+ fail("quit after direct ssl failed: ".$pop3->code);
+}
@@ -0,0 +1,57 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Net::SMTP;
+use Test::More;
+
+my $host = 'mail.gmx.net';
+my $debug = 0;
+
+plan skip_all => "no SSL support" if ! Net::SMTP->can_ssl;
+{
+no warnings 'once';
+plan skip_all => "no verified SSL connection to $host:465 - $@" if ! eval {
+ IO::Socket::SSL->new("$host:465")
+ || die($IO::Socket::SSL::SSL_ERROR||$!);
+};
+}
+
+plan tests => 2;
+
+SKIP: {
+ diag( "connect inet to $host:25" );
+ skip "no inet connect to $host:25",1 if ! IO::Socket::INET->new("$host:25");
+ my $smtp = Net::SMTP->new($host, Debug => $debug)
+ or skip "normal SMTP failed: $@",1;
+ skip "no STARTTLS support",1 if $smtp->message !~/STARTTLS/;
+
+ if (!$smtp->starttls) {
+ fail("starttls failed: ".$smtp->code." $@")
+ } else {
+ # we now should have access to SSL stuff
+ my $cipher = eval { $smtp->get_cipher };
+ if (!$cipher) {
+ fail("after starttls: not an SSL object");
+ } elsif ( $smtp->quit ) {
+ pass("starttls + quit ok, cipher=$cipher");
+ } else {
+ fail("quit after starttls failed: ".$smtp->code);
+ }
+ }
+}
+
+
+my $smtp = Net::SMTP->new($host, SSL => 1, Debug => $debug);
+# we now should have access to SSL stuff
+my $cipher = eval { $smtp->get_cipher };
+if (!$cipher) {
+ fail("after ssl connect: not an SSL object");
+} elsif ( $smtp->quit ) {
+ pass("ssl connect ok, cipher=$cipher");
+} else {
+ fail("quit after direct ssl failed: ".$smtp->code);
+}
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- unless (-d 'blib') {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # Skip: no Socket module\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # Skip: EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -29,7 +30,7 @@ unless($NetConfig{test_hosts}) {
my $t = 1;
print "1..7\n";
-$ftp = Net::FTP->new($NetConfig{ftp_testhost})
+my $ftp = Net::FTP->new($NetConfig{ftp_testhost})
or (print("not ok 1\n"), exit);
printf "ok %d\n",$t++;
@@ -49,12 +50,14 @@ $ftp->cwd('/pub') or do {
print "not ";
};
+my $data;
if ($data = $ftp->stor('libnet.tst')) {
my $text = "abc\ndef\nqwe\n";
printf "ok %d\n",$t++;
$data->write($text,length $text);
$data->close;
$data = $ftp->retr('libnet.tst');
+ my $buf;
$data->read($buf,length $text);
$data->close;
print "not " unless $text eq $buf;
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- unless (-d 'blib') {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -23,7 +24,7 @@ unless($NetConfig{test_hosts}) {
print "1..5\n";
-$domain = domainname();
+my $domain = domainname();
if(defined $domain && $domain ne "") {
print "ok 1 - defined, non-empty domainname\n";
@@ -52,7 +53,7 @@ my @dummy = grep { defined hostname() and hostname() eq $_ } @domain;
: print "not ok 3\n";
my $name = hostname();
-my $domain = hostdomain();
+$domain = hostdomain();
if(defined $domain && defined $name && $name ne "" && $domain ne "") {
hostfqdn() eq $name . "." . $domain ? print "ok 4\n" : print "not ok 4\n";
domainname() eq $name . "." . $domain ? print "ok 5\n" : print "not ok 5\n";} else {
@@ -1,3 +1,7 @@
+use 5.008001;
+
+use strict;
+use warnings;
my $number = 0;
sub ok {
@@ -1,20 +1,19 @@
-#!./perl
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- if ($ENV{PERL_CORE}) {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
-use strict;
-
use Cwd;
print "1..20\n";
@@ -48,7 +47,7 @@ $Net::Netrc::TESTING=$Net::Netrc::TESTING=1;
SKIP: {
skip('incompatible stat() handling for OS', 4), next SKIP
- if ($^O =~ /os2|win32|macos|cygwin/i or $] < 5.005);
+ if $^O =~ /os2|win32|macos|cygwin/i;
my $warn;
local $SIG{__WARN__} = sub {
@@ -56,8 +55,8 @@ SKIP: {
};
# add write access for group/other
- $stat[2] = 077;
- ok( !defined(Net::Netrc::_readrc()),
+ $stat[2] = 077; ## no critic (ValuesAndExpressions::ProhibitLeadingZeros)
+ ok( !defined(Net::Netrc->_readrc()),
'_readrc() should not read world-writable file' );
ok( scalar($warn =~ /^Bad permissions:/),
'... and should warn about it' );
@@ -66,7 +65,7 @@ SKIP: {
$stat[2] = 0;
if ($<) {
- ok( !defined(Net::Netrc::_readrc()),
+ ok( !defined(Net::Netrc->_readrc()),
'_readrc() should not read file owned by someone else' );
ok( scalar($warn =~ /^Not owner:/),
'... and should warn about it' );
@@ -93,7 +92,7 @@ macdef
LINES
# having set several lines and the uid, this should succeed
-is( Net::Netrc::_readrc(), 1, '_readrc() should succeed now' );
+is( Net::Netrc->_readrc(), 1, '_readrc() should succeed now' );
# on 'foo', the login is 'nigol'
is( Net::Netrc->lookup('foo')->{login}, 'nigol',
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- unless (-d 'blib') {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -26,13 +27,13 @@ print "1..4\n";
my $i = 1;
-$nntp = Net::NNTP->new(Debug => 0)
+my $nntp = Net::NNTP->new(Debug => 0)
or (print("not ok 1\n"), exit);
print "ok 1\n";
-my $grp;
-foreach $grp (qw(test alt.test control news.announce.newusers)) {
+my @grp;
+foreach my $grp (qw(test alt.test control news.announce.newusers)) {
@grp = $nntp->group($grp);
last if @grp;
}
@@ -0,0 +1,66 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::NNTP;
+use Test::More;
+
+my $debug = 0; # Net::NNTP->new( Debug => .. )
+
+my $inet6class = Net::NNTP->can_inet6;
+plan skip_all => "no IPv6 support found in Net::NNTP" if ! $inet6class;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = $inet6class->new(
+ LocalAddr => '::1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on ::1: $!" if ! $srv;
+my $host = $srv->sockhost;
+my $port = $srv->sockport;
+diag("server on $host port $port");
+
+plan tests => 1;
+
+defined( my $pid = fork()) or die "fork failed: $!";
+exit(nntp_server()) if ! $pid;
+
+my $cl = Net::NNTP->new(Host => $host, Port => $port,, Debug => $debug);
+diag("created Net::NNTP object");
+if (!$cl) {
+ fail("IPv6 NNTP connect failed");
+} else {
+ $cl->quit;
+ pass("IPv6 success");
+}
+wait;
+
+sub nntp_server {
+ my $ssl = shift;
+ my $cl = $srv->accept or die "accept failed: $!";
+ print $cl "200 nntp.example.com\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "205 bye\r\n";
+ last;
+ } elsif ( $cmd eq 'MODE' ) {
+ print $cl "201 Posting denied\r\n";
+ } else {
+ diag("received unknown command: $cmd");
+ print "500 unknown cmd\r\n";
+ }
+ }
+ diag("NNTP dialog done");
+}
@@ -0,0 +1,129 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::NNTP;
+use Test::More;
+
+my $debug = 0; # Net::NNTP Debug => ..
+
+my $parent = 0;
+
+plan skip_all => "no SSL support found in Net::NNTP" if ! Net::NNTP->can_ssl;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = IO::Socket::INET->new(
+ LocalAddr => '127.0.0.1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on localhost: $!" if ! $srv;
+my $host = $srv->sockhost;
+my $port = $srv->sockport;
+
+plan tests => 2;
+
+require IO::Socket::SSL::Utils;
+my ($ca,$key) = IO::Socket::SSL::Utils::CERT_create( CA => 1 );
+my ($fh,$cafile) = tempfile();
+print $fh IO::Socket::SSL::Utils::PEM_cert2string($ca);
+close($fh);
+
+$parent = $$;
+END { unlink($cafile) if $$ == $parent }
+
+my ($cert) = IO::Socket::SSL::Utils::CERT_create(
+ subject => { CN => 'nntp.example.com' },
+ issuer_cert => $ca, issuer_key => $key,
+ key => $key
+);
+
+test(1); # direct ssl
+test(0); # starttls
+
+
+sub test {
+ my $ssl = shift;
+ defined( my $pid = fork()) or die "fork failed: $!";
+ exit(nntp_server($ssl)) if ! $pid;
+ nntp_client($ssl);
+ wait;
+}
+
+
+sub nntp_client {
+ my $ssl = shift;
+ my %sslopt = (
+ SSL_verifycn_name => 'nntp.example.com',
+ SSL_ca_file => $cafile
+ );
+ $sslopt{SSL} = 1 if $ssl;
+ my $cl = Net::NNTP->new(
+ Host => $host,
+ Port => $port,
+ Debug => $debug,
+ %sslopt,
+ );
+ diag("created Net::NNTP object");
+ if (!$cl) {
+ fail( ($ssl ? "SSL ":"" )."NNTP connect failed");
+ } elsif ($ssl) {
+ $cl->quit;
+ pass("SSL NNTP connect success");
+ } elsif ( ! $cl->starttls ) {
+ no warnings 'once';
+ fail("starttls failed: $IO::Socket::SSL::SSL_ERROR");
+ } else {
+ $cl->quit;
+ pass("starttls success");
+ }
+}
+
+sub nntp_server {
+ my $ssl = shift;
+ my $cl = $srv->accept or die "accept failed: $!";
+ my %sslargs = (
+ SSL_server => 1,
+ SSL_cert => $cert,
+ SSL_key => $key,
+ );
+ if ( $ssl ) {
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ }
+
+ print $cl "200 nntp.example.com\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "205 bye\r\n";
+ last;
+ } elsif ( $cmd eq 'MODE' ) {
+ print $cl "201 Posting denied\r\n";
+ } elsif ( ! $ssl and $cmd eq 'STARTTLS' ) {
+ print $cl "382 Continue with TLS negotiation\r\n";
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ $ssl = 1;
+ } else {
+ diag("received unknown command: $cmd");
+ print "500 unknown cmd\r\n";
+ }
+ }
+
+ diag("NNTP dialog done");
+}
@@ -0,0 +1,49 @@
+#!perl
+#===============================================================================
+#
+# t/pod.t
+#
+# DESCRIPTION
+# Test script to check POD.
+#
+# COPYRIGHT
+# Copyright (C) 2014 Steve Hay. All rights reserved.
+#
+# LICENCE
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License, as specified in the LICENCE file.
+#
+#===============================================================================
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Test::More;
+
+#===============================================================================
+# MAIN PROGRAM
+#===============================================================================
+
+MAIN: {
+ plan skip_all => 'Author testing only' unless $ENV{AUTHOR_TESTING};
+
+ my $ok = eval {
+ require Test::Pod;
+ Test::Pod->import();
+ 1;
+ };
+
+ if (not $ok) {
+ plan skip_all => 'Test::Pod required to test POD';
+ }
+ elsif ($Test::Pod::VERSION < 1.00) {
+ plan skip_all => 'Test::Pod 1.00 or higher required to test POD';
+ }
+ else {
+ all_pod_files_ok();
+ }
+}
+
+#===============================================================================
@@ -0,0 +1,77 @@
+#!perl
+#===============================================================================
+#
+# t/pod_coverage.t
+#
+# DESCRIPTION
+# Test script to check POD coverage.
+#
+# COPYRIGHT
+# Copyright (C) 2014 Steve Hay. All rights reserved.
+#
+# LICENCE
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License, as specified in the LICENCE file.
+#
+#===============================================================================
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Test::More;
+
+#===============================================================================
+# MAIN PROGRAM
+#===============================================================================
+
+MAIN: {
+ plan skip_all => 'Author testing only' unless $ENV{AUTHOR_TESTING};
+
+ my $ok = eval {
+ require Test::Pod::Coverage;
+ Test::Pod::Coverage->import();
+ 1;
+ };
+
+ if (not $ok) {
+ plan skip_all => 'Test::Pod::Coverage required to test POD coverage';
+ }
+ elsif ($Test::Pod::Coverage::VERSION < 0.08) {
+ plan skip_all => 'Test::Pod::Coverage 0.08 or higher required to test POD coverage';
+ }
+ else {
+ plan tests => 12;
+ my $params = { coverage_class => qw(Pod::Coverage::CountParents) };
+ pod_coverage_ok('Net::Cmd', {
+ %$params,
+ also_private => [qw(is_utf8 toascii toebcdic set_status)]
+ });
+ pod_coverage_ok('Net::Config', {
+ %$params,
+ also_private => [qw(is_external)]
+ });
+ pod_coverage_ok('Net::Domain', $params);
+ pod_coverage_ok('Net::FTP', {
+ %$params,
+ also_private => [qw(authorise lsl ebcdic byte cmd)]
+ });
+ pod_coverage_ok('Net::Netrc', $params);
+ pod_coverage_ok('Net::NNTP', $params);
+ pod_coverage_ok('Net::POP3', $params);
+ pod_coverage_ok('Net::SMTP', {
+ %$params,
+ also_private => [qw(datafh supports)]
+ });
+ pod_coverage_ok('Net::Time', $params);
+ pod_coverage_ok('Net::FTP::A', $params);
+ pod_coverage_ok('Net::FTP::dataconn', {
+ %$params,
+ also_private => [qw(can_read can_write cmd reading)]
+ });
+ pod_coverage_ok('Net::FTP::I', $params);
+ }
+}
+
+#===============================================================================
@@ -0,0 +1,66 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::POP3;
+use Test::More;
+
+my $debug = 0; # Net::POP3->new( Debug => .. )
+
+my $inet6class = Net::POP3->can_inet6;
+plan skip_all => "no IPv6 support found in Net::POP3" if ! $inet6class;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = $inet6class->new(
+ LocalAddr => '::1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on ::1: $!" if ! $srv;
+my $saddr = "[".$srv->sockhost."]".':'.$srv->sockport;
+diag("server on $saddr");
+
+plan tests => 1;
+
+defined( my $pid = fork()) or die "fork failed: $!";
+exit(pop3_server()) if ! $pid;
+
+my $cl = Net::POP3->new($saddr, Debug => $debug);
+diag("created Net::POP3 object");
+if (!$cl) {
+ fail("IPv6 POP3 connect failed");
+} else {
+ $cl->quit;
+ pass("IPv6 success");
+}
+wait;
+
+sub pop3_server {
+ my $cl = $srv->accept or die "accept failed: $!";
+ print $cl "+OK localhost ready\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "+OK bye\r\n";
+ last;
+ } elsif ( $cmd eq 'CAPA' ) {
+ print $cl "+OK\r\n".
+ ".\r\n";
+ } else {
+ diag("received unknown command: $cmd");
+ print "-ERR unknown cmd\r\n";
+ }
+ }
+
+ diag("POP3 dialog done");
+}
@@ -0,0 +1,125 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::POP3;
+use Test::More;
+
+my $debug = 0; # Net::POP3 Debug => ..
+
+my $parent = 0;
+
+plan skip_all => "no SSL support found in Net::POP3" if ! Net::POP3->can_ssl;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = IO::Socket::INET->new(
+ LocalAddr => '127.0.0.1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on localhost: $!" if ! $srv;
+my $saddr = $srv->sockhost.':'.$srv->sockport;
+
+plan tests => 2;
+
+require IO::Socket::SSL::Utils;
+my ($ca,$key) = IO::Socket::SSL::Utils::CERT_create( CA => 1 );
+my ($fh,$cafile) = tempfile();
+print $fh IO::Socket::SSL::Utils::PEM_cert2string($ca);
+close($fh);
+
+$parent = $$;
+END { unlink($cafile) if $$ == $parent }
+
+my ($cert) = IO::Socket::SSL::Utils::CERT_create(
+ subject => { CN => 'pop3.example.com' },
+ issuer_cert => $ca, issuer_key => $key,
+ key => $key
+);
+
+test(1); # direct ssl
+test(0); # starttls
+
+
+sub test {
+ my $ssl = shift;
+ defined( my $pid = fork()) or die "fork failed: $!";
+ exit(pop3_server($ssl)) if ! $pid;
+ pop3_client($ssl);
+ wait;
+}
+
+
+sub pop3_client {
+ my $ssl = shift;
+ my %sslopt = (
+ SSL_verifycn_name => 'pop3.example.com',
+ SSL_ca_file => $cafile
+ );
+ $sslopt{SSL} = 1 if $ssl;
+ my $cl = Net::POP3->new($saddr, %sslopt, Debug => $debug);
+ diag("created Net::POP3 object");
+ if (!$cl) {
+ fail( ($ssl ? "SSL ":"" )."POP3 connect failed");
+ } elsif ($ssl) {
+ $cl->quit;
+ pass("SSL POP3 connect success");
+ } elsif ( ! $cl->starttls ) {
+ no warnings 'once';
+ fail("starttls failed: $IO::Socket::SSL::SSL_ERROR");
+ } else {
+ $cl->quit;
+ pass("starttls success");
+ }
+}
+
+sub pop3_server {
+ my $ssl = shift;
+ my $cl = $srv->accept or die "accept failed: $!";
+ my %sslargs = (
+ SSL_server => 1,
+ SSL_cert => $cert,
+ SSL_key => $key,
+ );
+ if ( $ssl ) {
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ }
+
+ print $cl "+OK localhost ready\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "+OK bye\r\n";
+ last;
+ } elsif ( $cmd eq 'CAPA' ) {
+ print $cl "+OK\r\n".
+ ( $ssl ? "" : "STLS\r\n" ).
+ ".\r\n";
+ } elsif ( ! $ssl and $cmd eq 'STLS' ) {
+ print $cl "+OK starting ssl\r\n";
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ $ssl = 1;
+ } else {
+ diag("received unknown command: $cmd");
+ print "-ERR unknown cmd\r\n";
+ }
+ }
+
+ diag("POP3 dialog done");
+}
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- unless (-d 'blib') {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- unless (-d 'blib') {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
}
@@ -25,7 +26,7 @@ print "1..3\n";
my $i = 1;
-$smtp = Net::SMTP->new(Debug => 0)
+my $smtp = Net::SMTP->new(Debug => 0)
or (print("not ok 1\n"), exit);
print "ok 1\n";
@@ -0,0 +1,68 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::SMTP;
+use Test::More;
+
+my $debug = 0; # Net::SMTP->new( Debug => .. )
+
+my $inet6class = Net::SMTP->can_inet6;
+plan skip_all => "no IPv6 support found in Net::SMTP" if ! $inet6class;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = $inet6class->new(
+ LocalAddr => '::1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on ::1: $!" if ! $srv;
+my $saddr = "[".$srv->sockhost."]".':'.$srv->sockport;
+diag("server on $saddr");
+
+plan tests => 1;
+
+defined( my $pid = fork()) or die "fork failed: $!";
+exit(smtp_server()) if ! $pid;
+
+my $cl = Net::SMTP->new($saddr, Debug => $debug);
+diag("created Net::SMTP object");
+if (!$cl) {
+ fail("IPv6 SMTP connect failed");
+} else {
+ $cl->quit;
+ pass("IPv6 success");
+}
+wait;
+
+sub smtp_server {
+ my $cl = $srv->accept or die "accept failed: $!";
+ print $cl "220 welcome\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "250 bye\r\n";
+ last;
+ } elsif ( $cmd eq 'HELO' ) {
+ print $cl "250 localhost\r\n";
+ } elsif ( $cmd eq 'EHLO' ) {
+ print $cl "250-localhost\r\n".
+ "250 HELP\r\n";
+ } else {
+ diag("received unknown command: $cmd");
+ print "500 unknown cmd\r\n";
+ }
+ }
+
+ diag("SMTP dialog done");
+}
@@ -0,0 +1,127 @@
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
+
+use Config;
+use File::Temp 'tempfile';
+use Net::SMTP;
+use Test::More;
+
+my $debug = 0; # Net::SMTP Debug => ..
+
+my $parent = 0;
+
+plan skip_all => "no SSL support found in Net::SMTP" if ! Net::SMTP->can_ssl;
+
+plan skip_all => "fork not supported on this platform"
+ unless $Config::Config{d_fork} || $Config::Config{d_pseudofork} ||
+ (($^O eq 'MSWin32' || $^O eq 'NetWare') and
+ $Config::Config{useithreads} and
+ $Config::Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/);
+
+my $srv = IO::Socket::INET->new(
+ LocalAddr => '127.0.0.1',
+ Listen => 10
+);
+plan skip_all => "cannot create listener on localhost: $!" if ! $srv;
+my $saddr = $srv->sockhost.':'.$srv->sockport;
+
+plan tests => 2;
+
+require IO::Socket::SSL::Utils;
+my ($ca,$key) = IO::Socket::SSL::Utils::CERT_create( CA => 1 );
+my ($fh,$cafile) = tempfile();
+print $fh IO::Socket::SSL::Utils::PEM_cert2string($ca);
+close($fh);
+
+$parent = $$;
+END { unlink($cafile) if $$ == $parent }
+
+my ($cert) = IO::Socket::SSL::Utils::CERT_create(
+ subject => { CN => 'smtp.example.com' },
+ issuer_cert => $ca, issuer_key => $key,
+ key => $key
+);
+
+test(1); # direct ssl
+test(0); # starttls
+
+
+sub test {
+ my $ssl = shift;
+ defined( my $pid = fork()) or die "fork failed: $!";
+ exit(smtp_server($ssl)) if ! $pid;
+ smtp_client($ssl);
+ wait;
+}
+
+
+sub smtp_client {
+ my $ssl = shift;
+ my %sslopt = (
+ SSL_verifycn_name => 'smtp.example.com',
+ SSL_ca_file => $cafile
+ );
+ $sslopt{SSL} = 1 if $ssl;
+ my $cl = Net::SMTP->new($saddr, %sslopt, Debug => $debug);
+ diag("created Net::SMTP object");
+ if (!$cl) {
+ fail( ($ssl ? "SSL ":"" )."SMTP connect failed");
+ } elsif ($ssl) {
+ $cl->quit;
+ pass("SSL SMTP connect success");
+ } elsif ( ! $cl->starttls ) {
+ no warnings 'once';
+ fail("starttls failed: $IO::Socket::SSL::SSL_ERROR");
+ } else {
+ $cl->quit;
+ pass("starttls success");
+ }
+}
+
+sub smtp_server {
+ my $ssl = shift;
+ my $cl = $srv->accept or die "accept failed: $!";
+ my %sslargs = (
+ SSL_server => 1,
+ SSL_cert => $cert,
+ SSL_key => $key,
+ );
+ if ( $ssl ) {
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ }
+
+ print $cl "220 welcome\r\n";
+ while (<$cl>) {
+ my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_;
+ $cmd = uc($cmd);
+ if ($cmd eq 'QUIT' ) {
+ print $cl "250 bye\r\n";
+ last;
+ } elsif ( $cmd eq 'HELO' ) {
+ print $cl "250 localhost\r\n";
+ } elsif ( $cmd eq 'EHLO' ) {
+ print $cl "250-localhost\r\n".
+ ( $ssl ? "" : "250-STARTTLS\r\n" ).
+ "250 HELP\r\n";
+ } elsif ( ! $ssl and $cmd eq 'STARTTLS' ) {
+ print $cl "250 starting ssl\r\n";
+ if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) {
+ diag("initial ssl handshake with client failed");
+ return;
+ }
+ $ssl = 1;
+ } else {
+ diag("received unknown command: $cmd");
+ print "500 unknown cmd\r\n";
+ }
+ }
+
+ diag("SMTP dialog done");
+}
@@ -1,14 +1,15 @@
-#!./perl -w
+#!perl
+
+use 5.008001;
+
+use strict;
+use warnings;
BEGIN {
- if ($ENV{PERL_CORE}) {
- chdir 't' if -d 't';
- @INC = '../lib';
- }
- if (!eval "require Socket") {
+ if (!eval { require Socket; 1 }) {
print "1..0 # no Socket\n"; exit 0;
}
- if (ord('A') == 193 && !eval "require Convert::EBCDIC") {
+ if (ord('A') == 193 && !eval { require Convert::EBCDIC; 1 }) {
print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0;
}
$INC{'IO/Socket.pm'} = 1;