File Coverage

blib/lib/CPAN/Uploader.pm
Criterion Covered Total %
statement 30 90 33.3
branch 0 38 0.0
condition 0 23 0.0
subroutine 10 18 55.5
pod 0 6 0.0
total 40 175 22.8


line stmt bran cond sub pod time code
1 1     1   329 use strict;
  1         2  
  1         26  
2 1     1   4 use warnings;
  1         1  
  1         53  
3             package CPAN::Uploader;
4             # ABSTRACT: upload things to the CPAN
5             $CPAN::Uploader::VERSION = '0.103007';
6             =head1 ORIGIN
7              
8             This code is mostly derived from C by Brad Fitzpatrick, which
9             in turn was based on C by Neil Bowers. I (I) didn't want to
10             have to use a C call to run either of those, so I refactored the code
11             into this module.
12              
13             =cut
14              
15 1     1   6 use Carp ();
  1         2  
  1         23  
16 1     1   8 use File::Basename ();
  1         2  
  1         11  
17 1     1   473 use File::HomeDir ();
  1         4427  
  1         26  
18 1     1   5 use File::Spec;
  1         1  
  1         14  
19 1     1   416 use HTTP::Tiny::UA;
  1         49287  
  1         25  
20 1     1   436 use HTTP::Tiny::Multipart;
  1         2068  
  1         24  
21 1     1   475 use URI;
  1         5345  
  1         42  
22              
23 1     1   9 use constant ALT => 'tinyua';
  1         2  
  1         931  
24              
25             my $UPLOAD_URI = $ENV{CPAN_UPLOADER_UPLOAD_URI}
26             || 'https://pause.perl.org/pause/authenquery';
27              
28             =method upload_file
29              
30             CPAN::Uploader->upload_file($file, \%arg);
31              
32             $uploader->upload_file($file);
33              
34             Valid arguments are:
35              
36             user - (required) your CPAN / PAUSE id
37             password - (required) your CPAN / PAUSE password
38             subdir - the directory (under your home directory) to upload to
39             http_proxy - uri of the http proxy to use
40             upload_uri - uri of the upload handler; usually the default (PAUSE) is right
41             debug - if set to true, spew lots more debugging output
42              
43             This method attempts to actually upload the named file to the CPAN. It will
44             raise an exception on error.
45              
46             =cut
47              
48             sub upload_file {
49 0     0 0   my ($self, $file, $arg) = @_;
50              
51 0 0 0       Carp::confess(q{don't supply %arg when calling upload_file on an object})
52             if $arg and ref $self;
53              
54             # class call with no args is no good
55 0 0 0       Carp::confess(q{need to supply %arg when calling upload_file from the class})
56             if not (ref $self) and not $arg;
57              
58 0 0         $self = $self->new($arg) if $arg;
59              
60 0 0         if ($arg->{dry_run}) {
61 0           require Data::Dumper;
62 0           $self->log("By request, cowardly refusing to do anything at all.");
63 0           $self->log(
64             "The following arguments would have been used to upload: \n"
65             . '$self: ' . Data::Dumper::Dumper($self)
66             . '$file: ' . Data::Dumper::Dumper($file)
67             );
68             } else {
69 0           $self->_upload($file);
70             }
71             }
72              
73             sub _ua_string {
74 0     0     my ($self) = @_;
75 0   0       my $class = ref $self || $self;
76 0 0         my $version = defined $class->VERSION ? $class->VERSION : 'dev';
77              
78 0           return "$class/$version";
79             }
80              
81 0 0   0 0   sub target { shift->{target} || 'PAUSE' }
82              
83             sub _upload {
84 0     0     my $self = shift;
85 0           my $file = shift;
86              
87 0           $self->log("registering upload with " . $self->target . " web server");
88              
89 0 0         my $agent = HTTP::Tiny::UA->new(
90             agent => $self->_ua_string,
91             ($self->{http_proxy} ? (http_proxy => $self->{http_proxy}) : ()),
92             );
93              
94 0   0       my $uri = URI->new($self->{upload_uri} || $UPLOAD_URI);
95 0           $uri->userinfo(join ':', $self->{user}, $self->{password});
96              
97             # Make the request to the PAUSE web server
98 0           $self->log("POSTing upload for $file to $uri");
99 0           my $response = $agent->post_multipart($uri, {
100             HIDDENNAME => $self->{user},
101             CAN_MULTIPART => 1,
102             pause99_add_uri_upload => File::Basename::basename($file),
103             SUBMIT_pause99_add_uri_httpupload => " Upload this file from my disk ",
104             pause99_add_uri_uri => "",
105             pause99_add_uri_httpupload => {
106             filename => $file,
107 0 0         content => do {open my $fh, '<', $file; binmode $fh; local $/ = <$fh>},
  0            
  0            
108             },
109             ($self->{subdir} ? (pause99_add_uri_subdirtext => $self->{subdir}) : ()),
110             });
111              
112             # So, how'd we do?
113 0 0         if (not defined $response) {
114 0           die "Request completely failed - we got undef back: $!";
115             }
116              
117 0 0         if (!$response->success) {
118 0 0         if ($response->status eq '404') {
119 0           die $self->target, "'s CGI for handling messages seems to have moved!\n",
120             "(HTTP response code of 404 from the ", $self->target, " web server)\n",
121             "It used to be: ", $uri, "\n",
122 0           "Please inform the maintainer of @{[__PACKAGE__]}.\n";
123             } else {
124 0           die "request failed with error code ", $response->status,
125             "\n Message: ", $response->reason, "\n";
126             }
127             } else {
128 0           $self->log_debug($_) for (
129             "Looks OK!",
130             "----- RESPONSE BEGIN -----\n" .
131             $response->content . "\n" .
132             "----- RESPONSE END -------\n"
133             );
134              
135 0           $self->log($self->target . " add message sent ok [" . $response->status . "]");
136             }
137             }
138              
139              
140             =method new
141              
142             my $uploader = CPAN::Uploader->new(\%arg);
143              
144             This method returns a new uploader. You probably don't need to worry about
145             this method.
146              
147             Valid arguments are the same as those to C.
148              
149             =cut
150              
151             sub new {
152 0     0 0   my ($class, $arg) = @_;
153              
154 0   0       $arg->{$_} or Carp::croak("missing $_ argument") for qw(user password);
155 0           bless $arg => $class;
156             }
157              
158             =method read_config_file
159              
160             my $config = CPAN::Uploader->read_config_file( $filename );
161              
162             This reads the config file and returns a hashref of its contents that can be
163             used as configuration for CPAN::Uploader.
164              
165             If no filename is given, it looks for F<.pause> in the user's home directory
166             (from the env var C, or the current directory if C isn't set).
167              
168             See L for the config format.
169              
170             =cut
171              
172             sub read_config_file {
173 0     0 0   my ($class, $filename) = @_;
174              
175 0 0         unless (defined $filename) {
176 0   0       my $home = File::HomeDir->my_home || '.';
177 0           $filename = File::Spec->catfile($home, '.pause');
178              
179 0 0 0       return {} unless -e $filename and -r _;
180             }
181              
182 0           my %conf;
183 0 0         if ( eval { require Config::Identity } ) {
  0            
184 0           %conf = Config::Identity->load($filename);
185 0 0         $conf{user} = delete $conf{username} unless $conf{user};
186             }
187             else { # Process .pause manually
188 0 0         open my $pauserc, '<', $filename
189             or die "can't open $filename for reading: $!";
190              
191 0           while (<$pauserc>) {
192 0           chomp;
193 0 0 0       next unless $_ and $_ !~ /^\s*#/;
194              
195 0           my ($k, $v) = /^\s*(\w+)\s+(.+)$/;
196 0 0         Carp::croak "multiple enties for $k" if $conf{$k};
197 0           $conf{$k} = $v;
198             }
199             }
200              
201 0           return \%conf;
202             }
203              
204             =method log
205              
206             $uploader->log($message);
207              
208             This method logs the given string. The default behavior is to print it to the
209             screen. The message should not end in a newline, as one will be added as
210             needed.
211              
212             =cut
213              
214             sub log {
215 0     0 0   shift;
216 0           print "$_[0]\n"
217             }
218              
219             =method log_debug
220              
221             This method behaves like C>, but only logs the message if the
222             CPAN::Uploader is in debug mode.
223              
224             =cut
225              
226             sub log_debug {
227 0     0 0   my $self = shift;
228 0 0         return unless $self->{debug};
229 0           $self->log($_[0]);
230             }
231              
232             1;