The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#!/apps/perl5/bin/perl -w
=head1 NAME
cpan-upload - upload one or more files to CPAN, using PAUSE
=head1 SYNOPSIS
cpan-upload [options] file1 .. fileN
=head1 DESCRIPTION
B<cpan-upload> is a script which automates the process of uploading
a file to CPAN using PAUSE, the Perl Authors Upload SErver.
Before using this script you must register with PAUSE,
to get a username and password.
If you are a regular uploader to PAUSE, you'll probably want to
create a C<.pause> configuration file. If not, you can probably
just use the command-line options, as described below.
If everything went OK, you'll get a mail message from the PAUSE monitor.
=head1 OPTIONS
=over 4
=item -user <string>
Your PAUSE username which was registered with PAUSE.
=item -password <string>
The password for your PAUSE username.
=item -ftp_firewall <host>
Specifies the name of the host which has your ftp firewall gateway,
if you're behind a firewall.
=item -http_proxy <URL>
Specifies the URL for a proxy to use when making HTTP requests.
=item -mailto <email>
Your email address, to include the HTTP request header.
This is also used as the password for the ftp upload to PAUSE.
=item -help
Displays a short help message.
=item -doc
Display the full documentation for B<orange>.
=item -verbose
Turns on verbose information as the script runs.
=item -debug
Turns on debugging information. Useful mainly for the developer,
it displays the HTTP request and response.
=item -version
Display the version number of the B<cpan-upload> script.
=back
=cut
use strict;
use vars qw($VERSION);
use HTTP::Request::Common qw(POST);
$VERSION = '1.3';
#-----------------------------------------------------------------------
# CONFIGURATION CONSTANTS
#-----------------------------------------------------------------------
my $PROGRAM = 'CPAN Upload Script';
my $SITE = 'pause.kbx.de';
my $UPLOAD_DIR = 'incoming';
my $agent;
my $config;
my $file;
#-----------------------------------------------------------------------
# MAIN BODY
#-----------------------------------------------------------------------
initialise();
ftp_upload_files(@ARGV);
print " Adding files via PAUSE web server...\n";
foreach $file (@ARGV)
{
pause_add_file($file);
}
exit 0;
sub initialise
{
my $config_file;
my $HOME;
#-------------------------------------------------------------------
# Turn off buffering on STDOUT
#-------------------------------------------------------------------
$| = 1;
#-------------------------------------------------------------------
# Create an App::Config object, and define our interface
#-------------------------------------------------------------------
$HOME = $ENV{'HOME'} || (getpwuid($<))[7];
$config_file = "$HOME/.pause";
if (-e $config_file && ((stat($config_file))[2] & 36) != 0)
{
die "$PROGRAM: your config file $config_file is readable by others!\n";
}
$config = new App::Config({ GLOBAL => { CMDARG => 1, ARGCOUNT => 1}});
$config->define('user');
$config->define('password', { EXPAND => 0 });
$config->define('mailto');
$config->define('ftp_firewall');
$config->define('http_proxy');
$config->define('help', { ARGCOUNT => 0 } );
$config->define('doc', { ARGCOUNT => 0 });
$config->define('debug', { ARGCOUNT => 0 } );
$config->define('verbose', { ARGCOUNT => 0 } );
$config->define('version', { ARGCOUNT => 0 } );
#-------------------------------------------------------------------
# Read the user's config file, if they have one,
# then parse the command-line.
#-------------------------------------------------------------------
if (-f $config_file)
{
$config->cfg_file($config_file) || exit 1;
}
$config->cmd_line(\@ARGV) || exit 1;
#-------------------------------------------------------------------
# Did they give one of the informational switches?
#-------------------------------------------------------------------
pod2usage(verbose => 2, exitval => 0) if $config->doc();
pod2usage(verbose => 1, exitval => 0) if $config->help();
show_version() if $config->version;
#-------------------------------------------------------------------
# Check we have the information we need
#-------------------------------------------------------------------
die "No email address (mailto) specified\n" unless $config->mailto;
die "No PAUSE user specified\n" unless $config->user;
die "No password specified\n" unless $config->password;
#-------------------------------------------------------------------
# Display banner at the start of the run
#-------------------------------------------------------------------
print "$PROGRAM v$VERSION\n";
}
#=======================================================================
# ftp_upload_files() - upload the one or more files to PAUSE ftp server
#=======================================================================
sub ftp_upload_files
{
my @files = @_;
my $ftp;
my $file;
print "\nFTP UPLOAD\n\n";
print " opening FTP connection to $SITE . . .";
if ($config->ftp_firewall)
{
verbose("\n\t(using firewall ".$config->ftp_firewall.")");
$ftp = Net::FTP->new($SITE, 'Firewall' => $config->ftp_firewall);
}
else
{
$ftp = Net::FTP->new($SITE);
}
if (not defined $ftp)
{
die "Failed to create FTP object: $!\n";
}
$ftp->login('ftp', $config->mailto);
print "\n";
verbose(" changing to \"$UPLOAD_DIR\" directory...");
$ftp->cwd($UPLOAD_DIR);
verbose(" setting binary mode.");
$ftp->binary() || do
{
$ftp->quit();
die " failed to change type to 'binary'\n";
};
foreach $file (@files)
{
print " uploading file \"$file\" . . .";
$ftp->put($file) || do
{
warn "\n failed to upload.\n";
next;
};
print "\n";
}
$ftp->quit;
print "\n";
}
#=======================================================================
# pause_add_file() - make an HTTP request to the add_uri form
#=======================================================================
sub pause_add_file
{
my $file = shift;
my $basename = basename($file);
my $request;
my $response;
my $formbody;
#-------------------------------------------------------------------
# Create the agent we'll use to make the web requests
#-------------------------------------------------------------------
if (not defined $agent)
{
$agent = eval { new LWP::UserAgent };
die "Failed to create UserAgent: $@\n" if ($@ || (not defined $agent));
$agent->agent("CpanUpload/$VERSION");
$agent->from($config->mailto) if $config->mailto;
if (defined $config->http_proxy)
{
$agent->proxy(['http'], $config->http_proxy);
}
}
#-------------------------------------------------------------------
# Create the request to add the file
#-------------------------------------------------------------------
$request = POST(PAUSE_ADD_URI, { HIDDENNAME => $config->user(),
upload => $basename });
$request->authorization_basic($config->user, $config->password);
if ($config->debug)
{
print "----- REQUEST BEGIN -----\n";
print $request->as_string();
print "----- REQUEST END -------\n";
}
#-------------------------------------------------------------------
# Make the request to the PAUSE web server
#-------------------------------------------------------------------
$response = $agent->request($request);
#-------------------------------------------------------------------
# So, how'd we do?
#-------------------------------------------------------------------
if (not defined $response)
{
die "Request completely failed - we got undef back: $!\n";
}
if ($response->is_error)
{
if ($response->code == RC_NOT_FOUND)
{
die "PAUSE's CGI for handling messages seems to have moved!\n",
"(HTTP response code of 404 from the PAUSE web server)\n",
"It used to be:\n\n\t", PAUSE_ADD_URI, "\n\n",
"Please inform the maintainer of this script\n";
}
else
{
die "request failed\n Error code: ", $response->code,
"\n Message: ", $response->message, "\n";
}
}
else
{
if ($config->debug)
{
print "\nLooks OK!\n";
print "----- RESPONSE BEGIN -----\n";
print $response->as_string();
print "----- RESPONSE END -------\n";
}
else
{
print "\tPAUSE add message sent ok [", $response->code, "]\n";
}
}
}
#=======================================================================
# show_version() - show the version of script (in response to -version)
#=======================================================================
sub show_version
{
print STDERR "$VERSION\n";
exit 0;
}
#=======================================================================
# verbose() - displays the message strings passed if in verbose mode
#=======================================================================
sub verbose
{
return unless $config->verbose;
print join("\n", @_), "\n";
}
exit 0;
#-----------------------------------------------------------------------
=head1 CONFIGURATION FILE
You can provide the configuration information needed
via a .pause file in your home directory.
If you upload files at all regularly you will want to
set up one of these.
=over 4
=item user <username>
This is used to specify your PAUSE username.
This just saves you from typing it every time you run the script.
=item password <password>
This is used to specify your PAUSE password.
=item ftp_firewall <HOST>
Specifies the hostname of your ftp gateway used to get through
a firewall. For example:
ftp_proxy = ftp-gw
=item http_proxy <URL>
The URL for the proxy to use when making HTTP requests to the PAUSE
web server. For example:
http_proxy = http://proxy/
=item mailto <EMAIL>
Specifies the email address which is passed in the header of
the HTTP request, and as the password for the anonymous ftp upload.
You must provide this.
=back
The following is a sample .pause file:
# example .pause for user neilb
# the user is your registered PAUSE username
user NEILB
password thisisnotmyrealpassword
mailto = neilb@cre.canon.co.uk
ftp_firewall = ftp-gw
Note that your .pause must not be readable by others,
since it can contain your PAUSE password. The b<cpan-upload> script
refuses to run if your config file can be read by others.
=cut
=head1 SEE ALSO
=over 4
Our web page about Canon Research Europe and Perl.
=item App::Config
Andy Wardley's module for unifying command-line switches and
cofiguration files into the notion of configuration variables.
B<orange> requires version 1.07 of the module,
which is available from CPAN:
=item Net::FTP
Graham Barr's studly module for doing that crazy ftp thang.
=item libwww-perl5
The LWP distribution which provides the modules used by this script
to talk to the PAUSE web server. You can get the latest version from:
=item Pod::Usage
Brad Appleton's module for extracting usage information out
of a file's pod. This is used for the B<-doc> and B<-help> switches.
Available from CPAN:
=back
=head1 VERSION
$Id: cpan-upload,v 1.4 1998/07/24 11:19:00 neilb Exp $
=head1 AUTHOR
Neil Bowers E<lt>neilb@cre.canon.co.ukE<gt>
=head1 COPYRIGHT
Copyright (c) 1998 Canon Research Centre Europe. All rights reserved.
This script is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut