#!/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 App::Config;
use Net::FTP;
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
use HTTP::Response;
use HTTP::Status;
use File::Basename;
use Pod::Usage;

$VERSION    = '1.3';

#-----------------------------------------------------------------------
#       CONFIGURATION CONSTANTS
#-----------------------------------------------------------------------
my $PROGRAM    = 'CPAN Upload Script';
my $SITE       = 'pause.kbx.de';
my $UPLOAD_DIR = 'incoming';

use constant PAUSE_ADD_URI => 'http://pause.kbx.de/perl/user/add_uri';

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
    http_proxy    = http://proxy.cre.canon.co.uk/

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

=item http://www.cre.canon.co.uk/perl/

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:

    http://www.perl.com/CPAN/modules/by-module/App/

=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:

    http://www.perl.com/CPAN/modules/by-module/LWP/

=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:

    http://www.perl.com/CPAN/modules/by-module/Pod/

=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