File Coverage

lib/File/Fetch.pm
Criterion Covered Total %
statement 286 522 54.7
branch 69 244 28.2
condition 21 72 29.1
subroutine 39 47 82.9
pod 4 4 100.0
total 419 889 47.1


line stmt bran cond sub pod time code
1             package File::Fetch;
2              
3 2     2   1561 use strict;
  2         5  
  2         75  
4 2     2   934 use FileHandle;
  2         20150  
  2         11  
5 2     2   2832 use File::Temp;
  2         19409  
  2         148  
6 2     2   1002 use File::Copy;
  2         4666  
  2         115  
7 2     2   13 use File::Spec;
  2         4  
  2         37  
8 2     2   9 use File::Spec::Unix;
  2         3  
  2         50  
9 2     2   10 use File::Basename qw[dirname];
  2         3  
  2         120  
10              
11 2     2   12 use Cwd qw[cwd];
  2         4  
  2         97  
12 2     2   12 use Carp qw[carp];
  2         3  
  2         97  
13 2     2   2067 use IPC::Cmd qw[can_run run QUOTE];
  2         108165  
  2         157  
14 2     2   17 use File::Path qw[mkpath];
  2         5  
  2         115  
15 2     2   12 use File::Temp qw[tempdir];
  2         5  
  2         130  
16 2     2   11 use Params::Check qw[check];
  2         21  
  2         81  
17 2     2   11 use Module::Load::Conditional qw[can_load];
  2         5  
  2         73  
18 2     2   9 use Locale::Maketext::Simple Style => 'gettext';
  2         4  
  2         7  
19              
20 2         530 use vars qw[ $VERBOSE $PREFER_BIN $FROM_EMAIL $USER_AGENT
21             $BLACKLIST $METHOD_FAIL $VERSION $METHODS
22             $FTP_PASSIVE $TIMEOUT $DEBUG $WARN $FORCEIPV4
23 2     2   504 ];
  2         26  
24              
25             $VERSION = '1.04';
26             $VERSION = eval $VERSION; # avoid warnings with development releases
27             $PREFER_BIN = 0; # XXX TODO implement
28             $FROM_EMAIL = 'File-Fetch@example.com';
29             $USER_AGENT = "File::Fetch/$VERSION";
30             $BLACKLIST = [qw|ftp|];
31             push @$BLACKLIST, qw|lftp| if $^O eq 'dragonfly' || $^O eq 'hpux';
32             $METHOD_FAIL = { };
33             $FTP_PASSIVE = 1;
34             $TIMEOUT = 0;
35             $DEBUG = 0;
36             $WARN = 1;
37             $FORCEIPV4 = 0;
38              
39             ### methods available to fetch the file depending on the scheme
40             $METHODS = {
41             http => [ qw|lwp httptiny wget curl lftp fetch httplite lynx iosock| ],
42             https => [ qw|lwp wget curl| ],
43             ftp => [ qw|lwp netftp wget curl lftp fetch ncftp ftp| ],
44             file => [ qw|lwp lftp file| ],
45             rsync => [ qw|rsync| ],
46             git => [ qw|git| ],
47             };
48              
49             ### silly warnings ###
50             local $Params::Check::VERBOSE = 1;
51             local $Params::Check::VERBOSE = 1;
52             local $Module::Load::Conditional::VERBOSE = 0;
53             local $Module::Load::Conditional::VERBOSE = 0;
54              
55             ### see what OS we are on, important for file:// uris ###
56 2     2   15 use constant ON_WIN => ($^O eq 'MSWin32');
  2         3  
  2         140  
57 2     2   12 use constant ON_VMS => ($^O eq 'VMS');
  2         4  
  2         131  
58 2     2   13 use constant ON_UNIX => (!ON_WIN);
  2         4  
  2         120  
59 2     2   23 use constant HAS_VOL => (ON_WIN);
  2         4  
  2         102  
60 2     2   10 use constant HAS_SHARE => (ON_WIN);
  2         4  
  2         217  
61 2     2   13 use constant HAS_FETCH => ( $^O =~ m!^(freebsd|netbsd|dragonfly)$! );
  2         4  
  2         301  
62              
63             =pod
64              
65             =head1 NAME
66              
67             File::Fetch - A generic file fetching mechanism
68              
69             =head1 SYNOPSIS
70              
71             use File::Fetch;
72              
73             ### build a File::Fetch object ###
74             my $ff = File::Fetch->new(uri => 'http://some.where.com/dir/a.txt');
75              
76             ### fetch the uri to cwd() ###
77             my $where = $ff->fetch() or die $ff->error;
78              
79             ### fetch the uri to /tmp ###
80             my $where = $ff->fetch( to => '/tmp' );
81              
82             ### parsed bits from the uri ###
83             $ff->uri;
84             $ff->scheme;
85             $ff->host;
86             $ff->path;
87             $ff->file;
88              
89             =head1 DESCRIPTION
90              
91             File::Fetch is a generic file fetching mechanism.
92              
93             It allows you to fetch any file pointed to by a C, C,
94             C, C or C uri by a number of different means.
95              
96             See the C section further down for details.
97              
98             =head1 ACCESSORS
99              
100             A C object has the following accessors
101              
102             =over 4
103              
104             =item $ff->uri
105              
106             The uri you passed to the constructor
107              
108             =item $ff->scheme
109              
110             The scheme from the uri (like 'file', 'http', etc)
111              
112             =item $ff->host
113              
114             The hostname in the uri. Will be empty if host was originally
115             'localhost' for a 'file://' url.
116              
117             =item $ff->vol
118              
119             On operating systems with the concept of a volume the second element
120             of a file:// is considered to the be volume specification for the file.
121             Thus on Win32 this routine returns the volume, on other operating
122             systems this returns nothing.
123              
124             On Windows this value may be empty if the uri is to a network share, in
125             which case the 'share' property will be defined. Additionally, volume
126             specifications that use '|' as ':' will be converted on read to use ':'.
127              
128             On VMS, which has a volume concept, this field will be empty because VMS
129             file specifications are converted to absolute UNIX format and the volume
130             information is transparently included.
131              
132             =item $ff->share
133              
134             On systems with the concept of a network share (currently only Windows) returns
135             the sharename from a file://// url. On other operating systems returns empty.
136              
137             =item $ff->path
138              
139             The path from the uri, will be at least a single '/'.
140              
141             =item $ff->file
142              
143             The name of the remote file. For the local file name, the
144             result of $ff->output_file will be used.
145              
146             =item $ff->file_default
147              
148             The name of the default local file, that $ff->output_file falls back to if
149             it would otherwise return no filename. For example when fetching a URI like
150             http://www.abc.net.au/ the contents retrieved may be from a remote file called
151             'index.html'. The default value of this attribute is literally 'file_default'.
152              
153             =cut
154              
155              
156             ##########################
157             ### Object & Accessors ###
158             ##########################
159              
160             {
161             ### template for autogenerated accessors ###
162             my $Tmpl = {
163             scheme => { default => 'http' },
164             host => { default => 'localhost' },
165             path => { default => '/' },
166             file => { required => 1 },
167             uri => { required => 1 },
168             userinfo => { default => '' },
169             vol => { default => '' }, # windows for file:// uris
170             share => { default => '' }, # windows for file:// uris
171             file_default => { default => 'file_default' },
172             tempdir_root => { required => 1 }, # Should be lazy-set at ->new()
173             _error_msg => { no_override => 1 },
174             _error_msg_long => { no_override => 1 },
175             };
176              
177             for my $method ( keys %$Tmpl ) {
178 2     2   15 no strict 'refs';
  2         3  
  2         12157  
179             *$method = sub {
180 418     418   24407 my $self = shift;
181 418 50       1023 $self->{$method} = $_[0] if @_;
182 418         4202 return $self->{$method};
183             }
184             }
185              
186             sub _create {
187 44     44   426 my $class = shift;
188 44         1051 my %hash = @_;
189              
190 44 50       1212 my $args = check( $Tmpl, \%hash ) or return;
191              
192 44         18404 bless $args, $class;
193              
194 44 50 66     407 if( lc($args->scheme) ne 'file' and not $args->host ) {
195 0         0 return $class->_error(loc(
196             "Hostname required when fetching from '%1'",$args->scheme));
197             }
198              
199 44         137 for (qw[path]) {
200 44 50       351 unless( $args->$_() ) { # 5.5.x needs the ()
201 0         0 return $class->_error(loc("No '%1' specified",$_));
202             }
203             }
204              
205 44         248 return $args;
206             }
207             }
208              
209             =item $ff->output_file
210              
211             The name of the output file. This is the same as $ff->file,
212             but any query parameters are stripped off. For example:
213              
214             http://example.com/index.html?x=y
215              
216             would make the output file be C rather than
217             C.
218              
219             =back
220              
221             =cut
222              
223             sub output_file {
224 92     92 1 45269 my $self = shift;
225 92         988 my $file = $self->file;
226              
227 92         1603 $file =~ s/\?.*$//g;
228              
229 92   33     345 $file ||= $self->file_default;
230              
231 92         1998 return $file;
232             }
233              
234             ### XXX do this or just point to URI::Escape?
235             # =head2 $esc_uri = $ff->escaped_uri
236             #
237             # =cut
238             #
239             # ### most of this is stolen straight from URI::escape
240             # { ### Build a char->hex map
241             # my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
242             #
243             # sub escaped_uri {
244             # my $self = shift;
245             # my $uri = $self->uri;
246             #
247             # ### Default unsafe characters. RFC 2732 ^(uric - reserved)
248             # $uri =~ s/([^A-Za-z0-9\-_.!~*'()])/
249             # $escapes{$1} || $self->_fail_hi($1)/ge;
250             #
251             # return $uri;
252             # }
253             #
254             # sub _fail_hi {
255             # my $self = shift;
256             # my $char = shift;
257             #
258             # $self->_error(loc(
259             # "Can't escape '%1', try using the '%2' module instead",
260             # sprintf("\\x{%04X}", ord($char)), 'URI::Escape'
261             # ));
262             # }
263             #
264             # sub output_file {
265             #
266             # }
267             #
268             #
269             # }
270              
271             =head1 METHODS
272              
273             =head2 $ff = File::Fetch->new( uri => 'http://some.where.com/dir/file.txt' );
274              
275             Parses the uri and creates a corresponding File::Fetch::Item object,
276             that is ready to be Ced and returns it.
277              
278             Returns false on failure.
279              
280             =cut
281              
282             sub new {
283 44     44 1 440659 my $class = shift;
284 44         246 my %hash = @_;
285              
286 44         110 my ($uri, $file_default, $tempdir_root);
287 44         458 my $tmpl = {
288             uri => { required => 1, store => \$uri },
289             file_default => { required => 0, store => \$file_default },
290             tempdir_root => { required => 0, store => \$tempdir_root },
291             };
292              
293 44 50       273 check( $tmpl, \%hash ) or return;
294              
295             ### parse the uri to usable parts ###
296 44 50       5632 my $href = $class->_parse_uri( $uri ) or return;
297              
298 44 50       159 $href->{file_default} = $file_default if $file_default;
299 44 50       101 $href->{tempdir_root} = File::Spec->rel2abs( $tempdir_root ) if $tempdir_root;
300 44 50       173596 $href->{tempdir_root} = File::Spec->rel2abs( Cwd::cwd ) if not $href->{tempdir_root};
301              
302             ### make it into a FFI object ###
303 44 50       2074 my $ff = $class->_create( %$href ) or return;
304              
305              
306             ### return the object ###
307 44         960 return $ff;
308             }
309              
310             ### parses an uri to a hash structure:
311             ###
312             ### $class->_parse_uri( 'ftp://ftp.cpan.org/pub/mirror/index.txt' )
313             ###
314             ### becomes:
315             ###
316             ### $href = {
317             ### scheme => 'ftp',
318             ### host => 'ftp.cpan.org',
319             ### path => '/pub/mirror',
320             ### file => 'index.html'
321             ### };
322             ###
323             ### In the case of file:// urls there maybe be additional fields
324             ###
325             ### For systems with volume specifications such as Win32 there will be
326             ### a volume specifier provided in the 'vol' field.
327             ###
328             ### 'vol' => 'volumename'
329             ###
330             ### For windows file shares there may be a 'share' key specified
331             ###
332             ### 'share' => 'sharename'
333             ###
334             ### Note that the rules of what a file:// url means vary by the operating system
335             ### of the host being addressed. Thus file:///d|/foo/bar.txt means the obvious
336             ### 'D:\foo\bar.txt' on windows, but on unix it means '/d|/foo/bar.txt' and
337             ### not '/foo/bar.txt'
338             ###
339             ### Similarly if the host interpreting the url is VMS then
340             ### file:///disk$user/my/notes/note12345.txt' means
341             ### 'DISK$USER:[MY.NOTES]NOTE123456.TXT' but will be returned the same as
342             ### if it is unix where it means /disk$user/my/notes/note12345.txt'.
343             ### Except for some cases in the File::Spec methods, Perl on VMS will generally
344             ### handle UNIX format file specifications.
345             ###
346             ### This means it is impossible to serve certain file:// urls on certain systems.
347             ###
348             ### Thus are the problems with a protocol-less specification. :-(
349             ###
350              
351             sub _parse_uri {
352 51     51   3018119 my $self = shift;
353 51 50       351 my $uri = shift or return;
354              
355 51         197 my $href = { uri => $uri };
356              
357             ### find the scheme ###
358 51         761 $uri =~ s|^(\w+)://||;
359 51         395 $href->{scheme} = $1;
360              
361             ### See rfc 1738 section 3.10
362             ### https://datatracker.ietf.org/doc/html/rfc1738#section-3.10
363             ### And wikipedia for more on windows file:// urls
364             ### http://en.wikipedia.org/wiki/File://
365 51 100       203 if( $href->{scheme} eq 'file' ) {
366              
367 9         79 my @parts = split '/',$uri;
368              
369             ### file://hostname/...
370             ### file://hostname/...
371             ### normalize file://localhost with file:///
372 9   100     62 $href->{host} = $parts[0] || '';
373              
374             ### index in @parts where the path components begin;
375 9         19 my $index = 1;
376              
377             ### file:////hostname/sharename/blah.txt
378 9         11 if ( HAS_SHARE and not length $parts[0] and not length $parts[1] ) {
379              
380             $href->{host} = $parts[2] || ''; # avoid warnings
381             $href->{share} = $parts[3] || ''; # avoid warnings
382              
383             $index = 4 # index after the share
384              
385             ### file:///D|/blah.txt
386             ### file:///D:/blah.txt
387 0         0 } elsif (HAS_VOL) {
388              
389             ### this code comes from dmq's patch, but:
390             ### XXX if volume is empty, wouldn't that be an error? --kane
391             ### if so, our file://localhost test needs to be fixed as wel
392             $href->{vol} = $parts[1] || '';
393              
394             ### correct D| style colume descriptors
395             $href->{vol} =~ s/\A([A-Z])\|\z/$1:/i if ON_WIN;
396              
397             $index = 2; # index after the volume
398             }
399              
400             ### rebuild the path from the leftover parts;
401 9         51 $href->{path} = join '/', '', splice( @parts, $index, $#parts );
402              
403             } else {
404             ### using anything but qw() in hash slices may produce warnings
405             ### in older perls :-(
406 42         435 @{$href}{ qw(userinfo host path) } = $uri =~ m|(?:([^\@:]*:[^\:\@]*)@)?([^/]*)(/.*)$|s;
  42         215  
407             }
408              
409             ### split the path into file + dir ###
410 51         110 { my @parts = File::Spec::Unix->splitpath( delete $href->{path} );
  51         1802  
411 51         219 $href->{path} = $parts[1];
412 51         150 $href->{file} = $parts[2];
413             }
414              
415             ### host will be empty if the target was 'localhost' and the
416             ### scheme was 'file'
417             $href->{host} = '' if ($href->{host} eq 'localhost') and
418 51 100 100     230 ($href->{scheme} eq 'file');
419              
420 51         280 return $href;
421             }
422              
423             =head2 $where = $ff->fetch( [to => /my/output/dir/ | \$scalar] )
424              
425             Fetches the file you requested and returns the full path to the file.
426              
427             By default it writes to C, but you can override that by specifying
428             the C argument:
429              
430             ### file fetch to /tmp, full path to the file in $where
431             $where = $ff->fetch( to => '/tmp' );
432              
433             ### file slurped into $scalar, full path to the file in $where
434             ### file is downloaded to a temp directory and cleaned up at exit time
435             $where = $ff->fetch( to => \$scalar );
436              
437             Returns the full path to the downloaded file on success, and false
438             on failure.
439              
440             =cut
441              
442             sub fetch {
443 70 50   70 1 8005 my $self = shift or return;
444 70         545 my %hash = @_;
445              
446 70         159 my $target;
447 70         280696 my $tmpl = {
448             to => { default => cwd(), store => \$target },
449             };
450              
451 70 50       2689 check( $tmpl, \%hash ) or return;
452              
453 70         12929 my ($to, $fh);
454             ### you want us to slurp the contents
455 70 100 66     1305 if( ref $target and UNIVERSAL::isa( $target, 'SCALAR' ) ) {
456 35         619 $to = tempdir( 'FileFetch.XXXXXX', DIR => $self->tempdir_root, CLEANUP => 1 );
457              
458             ### plain old fetch
459             } else {
460 35         188 $to = $target;
461              
462             ### On VMS force to VMS format so File::Spec will work.
463 35         74 $to = VMS::Filespec::vmspath($to) if ON_VMS;
464              
465             ### create the path if it doesn't exist yet ###
466 35 100       1110 unless( -d $to ) {
467 1         7 eval { mkpath( $to ) };
  1         294  
468              
469 1 50       7 return $self->_error(loc("Could not create path '%1'",$to)) if $@;
470             }
471             }
472              
473             ### set passive ftp if required ###
474 70         27932 local $ENV{FTP_PASSIVE} = $FTP_PASSIVE;
475              
476             ### we dont use catfile on win32 because if we are using a cygwin tool
477             ### under cmd.exe they wont understand windows style separators.
478 70         1335 my $out_to = ON_WIN ? $to.'/'.$self->output_file
479             : File::Spec->catfile( $to, $self->output_file );
480              
481 70         266 for my $method ( @{ $METHODS->{$self->scheme} } ) {
  70         600  
482 68         600 my $sub = '_'.$method.'_fetch';
483              
484 68 50       1152 unless( __PACKAGE__->can($sub) ) {
485 0         0 $self->_error(loc("Cannot call method for '%1' -- WEIRD!",
486             $method));
487 0         0 next;
488             }
489              
490             ### method is blacklisted ###
491 68 50       243 next if grep { lc $_ eq $method } @$BLACKLIST;
  68         779  
492              
493             ### method is known to fail ###
494 68 100       423 next if $METHOD_FAIL->{$method};
495              
496             ### there's serious issues with IPC::Run and quoting of command
497             ### line arguments. using quotes in the wrong place breaks things,
498             ### and in the case of say,
499             ### C:\cygwin\bin\wget.EXE --quiet --passive-ftp --output-document
500             ### "index.html" "http://www.cpan.org/index.html?q=1&y=2"
501             ### it doesn't matter how you quote, it always fails.
502 28         231 local $IPC::Cmd::USE_IPC_RUN = 0;
503              
504 28 100       252 if( my $file = $self->$sub(
505             to => $out_to
506             )){
507              
508 22 50 33     1277 unless( -e $file && -s _ ) {
509 0         0 $self->_error(loc("'%1' said it fetched '%2', ".
510             "but it was not created",$method,$file));
511              
512             ### mark the failure ###
513 0         0 $METHOD_FAIL->{$method} = 1;
514              
515 0         0 next;
516              
517             } else {
518              
519             ### slurp mode?
520 22 100 66     508 if( ref $target and UNIVERSAL::isa( $target, 'SCALAR' ) ) {
521              
522             ### open the file
523 11 50       975 open my $fh, "<$file" or do {
524 0         0 $self->_error(
525             loc("Could not open '%1': %2", $file, $!));
526 0         0 return;
527             };
528              
529             ### slurp
530 11         74 $$target = do { local $/; <$fh> };
  11         130  
  11         644  
531              
532             }
533              
534 22         2031 my $abs = File::Spec->rel2abs( $file );
535 22         988 return $abs;
536              
537             }
538             }
539             }
540              
541              
542             ### if we got here, we looped over all methods, but we weren't able
543             ### to fetch it.
544 48         816 return;
545             }
546              
547             ########################
548             ### _*_fetch methods ###
549             ########################
550              
551             ### LWP fetching ###
552             sub _lwp_fetch {
553 1     1   5 my $self = shift;
554 1         10 my %hash = @_;
555              
556 1         3 my ($to);
557 1         12 my $tmpl = {
558             to => { required => 1, store => \$to }
559             };
560 1 50       6 check( $tmpl, \%hash ) or return;
561              
562             ### modules required to download with lwp ###
563 1         96 my $use_list = {
564             LWP => '0.0',
565             'LWP::UserAgent' => '0.0',
566             'HTTP::Request' => '0.0',
567             'HTTP::Status' => '0.0',
568             URI => '0.0',
569              
570             };
571              
572 1 50       4 if ($self->scheme eq 'https') {
573 0         0 $use_list->{'LWP::Protocol::https'} = '0';
574             }
575              
576             ### Fix CVE-2016-1238 ###
577 1         9 local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
578 1 50       18 unless( can_load( modules => $use_list ) ) {
579 1         814 $METHOD_FAIL->{'lwp'} = 1;
580 1         10 return;
581             }
582              
583             ### setup the uri object
584 0         0 my $uri = URI->new( File::Spec::Unix->catfile(
585             $self->path, $self->file
586             ) );
587              
588             ### special rules apply for file:// uris ###
589 0         0 $uri->scheme( $self->scheme );
590 0 0       0 $uri->host( $self->scheme eq 'file' ? '' : $self->host );
591              
592 0 0       0 if ($self->userinfo) {
    0          
593 0         0 $uri->userinfo($self->userinfo);
594             } elsif ($self->scheme ne 'file') {
595 0         0 $uri->userinfo("anonymous:$FROM_EMAIL");
596             }
597              
598             ### set up the useragent object
599 0         0 my $ua = LWP::UserAgent->new();
600 0 0       0 $ua->timeout( $TIMEOUT ) if $TIMEOUT;
601 0         0 $ua->agent( $USER_AGENT );
602 0         0 $ua->from( $FROM_EMAIL );
603 0         0 $ua->env_proxy;
604              
605 0 0       0 my $res = $ua->mirror($uri, $to) or return;
606              
607             ### uptodate or fetched ok ###
608 0 0 0     0 if ( $res->code == 304 or $res->code == 200 ) {
609 0         0 return $to;
610              
611             } else {
612 0         0 return $self->_error(loc("Fetch failed! HTTP response: %1 %2 [%3]",
613             $res->code, HTTP::Status::status_message($res->code),
614             $res->status_line));
615             }
616              
617             }
618              
619             ### HTTP::Tiny fetching ###
620             sub _httptiny_fetch {
621 6     6   35 my $self = shift;
622 6         62 my %hash = @_;
623              
624 6         37 my ($to);
625 6         58 my $tmpl = {
626             to => { required => 1, store => \$to }
627             };
628 6 50       41 check( $tmpl, \%hash ) or return;
629              
630 6         527 my $use_list = {
631             'HTTP::Tiny' => '0.008',
632              
633             };
634              
635             ### Fix CVE-2016-1238 ###
636 6         64 local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
637 6 50       121 unless( can_load(modules => $use_list) ) {
638 0         0 $METHOD_FAIL->{'httptiny'} = 1;
639 0         0 return;
640             }
641              
642 6         55397 my $uri = $self->uri;
643              
644 6 50       153 my $http = HTTP::Tiny->new( ( $TIMEOUT ? ( timeout => $TIMEOUT ) : () ) );
645              
646 6         1089 my $rc = $http->mirror( $uri, $to );
647              
648 6 50       1192801 unless ( $rc->{success} ) {
649              
650             return $self->_error(loc( "Fetch failed! HTTP response: %1 [%2]",
651 0         0 $rc->{status}, $rc->{reason} ) );
652              
653             }
654              
655 6         1025 return $to;
656              
657             }
658              
659             ### HTTP::Lite fetching ###
660             sub _httplite_fetch {
661 1     1   13 my $self = shift;
662 1         14 my %hash = @_;
663              
664 1         6 my ($to);
665 1         14 my $tmpl = {
666             to => { required => 1, store => \$to }
667             };
668 1 50       13 check( $tmpl, \%hash ) or return;
669              
670             ### modules required to download with lwp ###
671 1         106 my $use_list = {
672             'HTTP::Lite' => '2.2',
673             'MIME::Base64' => '0',
674             };
675              
676             ### Fix CVE-2016-1238 ###
677 1         17 local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
678 1 50       26 unless( can_load(modules => $use_list) ) {
679 1         6445 $METHOD_FAIL->{'httplite'} = 1;
680 1         17 return;
681             }
682              
683 0         0 my $uri = $self->uri;
684 0         0 my $retries = 0;
685              
686 0         0 RETRIES: while ( $retries++ < 5 ) {
687              
688 0         0 my $http = HTTP::Lite->new();
689             # Naughty naughty but there isn't any accessor/setter
690 0 0       0 $http->{timeout} = $TIMEOUT if $TIMEOUT;
691 0         0 $http->http11_mode(1);
692              
693 0 0       0 if ($self->userinfo) {
694 0         0 my $encoded = MIME::Base64::encode($self->userinfo, '');
695 0         0 $http->add_req_header("Authorization", "Basic $encoded");
696             }
697              
698 0         0 my $fh = FileHandle->new;
699              
700 0 0       0 unless ( $fh->open($to,'>') ) {
701 0         0 return $self->_error(loc(
702             "Could not open '%1' for writing: %2",$to,$!));
703             }
704              
705 0         0 $fh->autoflush(1);
706              
707 0         0 binmode $fh;
708              
709 0     0   0 my $rc = $http->request( $uri, sub { my ($self,$dref,$cbargs) = @_; local $\; print {$cbargs} $$dref }, $fh );
  0         0  
  0         0  
  0         0  
  0         0  
710              
711 0         0 close $fh;
712              
713 0 0 0     0 if ( $rc == 301 || $rc == 302 ) {
    0          
714 0         0 my $loc;
715 0         0 HEADERS: for ($http->headers_array) {
716 0 0       0 /Location: (\S+)/ and $loc = $1, last HEADERS;
717             }
718             #$loc or last; # Think we should squeal here.
719 0 0       0 if ($loc =~ m!^/!) {
720 0         0 $uri =~ s{^(\w+?://[^/]+)/.*$}{$1};
721 0         0 $uri .= $loc;
722             }
723             else {
724 0         0 $uri = $loc;
725             }
726 0         0 next RETRIES;
727             }
728             elsif ( $rc == 200 ) {
729 0         0 return $to;
730             }
731             else {
732 0         0 return $self->_error(loc("Fetch failed! HTTP response: %1 [%2]",
733             $rc, $http->status_message));
734             }
735              
736             } # Loop for 5 retries.
737              
738 0         0 return $self->_error("Fetch failed! Gave up after 5 tries");
739              
740             }
741              
742             ### Simple IO::Socket::INET fetching ###
743             sub _iosock_fetch {
744 6     6   39 my $self = shift;
745 6         69 my %hash = @_;
746              
747 6         38 my ($to);
748 6         52 my $tmpl = {
749             to => { required => 1, store => \$to }
750             };
751 6 50       68 check( $tmpl, \%hash ) or return;
752              
753 6         647 my $use_list = {
754             'IO::Socket::INET' => '0.0',
755             'IO::Select' => '0.0',
756             };
757              
758             ### Fix CVE-2016-1238 ###
759 6         82 local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
760 6 50       112 unless( can_load(modules => $use_list) ) {
761 0         0 $METHOD_FAIL->{'iosock'} = 1;
762 0         0 return;
763             }
764              
765 6 50       8885 my $sock = IO::Socket::INET->new(
766             PeerHost => $self->host,
767             ( $self->host =~ /:/ ? () : ( PeerPort => 80 ) ),
768             );
769              
770 6 50       601924 unless ( $sock ) {
771 0         0 return $self->_error(loc("Could not open socket to '%1', '%2'",$self->host,$!));
772             }
773              
774 6         199 my $fh = FileHandle->new;
775              
776             # Check open()
777              
778 6 50       615 unless ( $fh->open($to,'>') ) {
779 0         0 return $self->_error(loc(
780             "Could not open '%1' for writing: %2",$to,$!));
781             }
782              
783 6         1140 $fh->autoflush(1);
784 6         421 binmode $fh;
785              
786 6         66 my $path = File::Spec::Unix->catfile( $self->path, $self->file );
787 6         56 my $req = "GET $path HTTP/1.0\x0d\x0aHost: " . $self->host . "\x0d\x0a\x0d\x0a";
788 6         124 $sock->send( $req );
789              
790 6         1247 my $select = IO::Select->new( $sock );
791              
792 6         818 my $resp = '';
793 6         17 my $normal = 0;
794 6   50     131 while ( $select->can_read( $TIMEOUT || 60 ) ) {
795 12         512783 my $ret = $sock->sysread( $resp, 4096, length($resp) );
796 12 100 66     596 if ( !defined $ret or $ret == 0 ) {
797 6         53 $select->remove( $sock );
798 6         474 $normal++;
799             }
800             }
801 6         610 close $sock;
802              
803 6 50       38 unless ( $normal ) {
804 0   0     0 return $self->_error(loc("Socket timed out after '%1' seconds", ( $TIMEOUT || 60 )));
805             }
806              
807             # Check the "response"
808             # Strip preceding blank lines apparently they are allowed (RFC 2616 4.1)
809 6         90 $resp =~ s/^(\x0d?\x0a)+//;
810             # Check it is an HTTP response
811 6 50       63 unless ( $resp =~ m!^HTTP/(\d+)\.(\d+)!i ) {
812 0         0 return $self->_error(loc("Did not get a HTTP response from '%1'",$self->host));
813             }
814              
815             # Check for OK
816 6         52 my ($code) = $resp =~ m!^HTTP/\d+\.\d+\s+(\d+)!i;
817 6 50       27 unless ( $code eq '200' ) {
818 0         0 return $self->_error(loc("Got a '%1' from '%2' expected '200'",$code,$self->host));
819             }
820              
821             {
822 6         12 local $\;
  6         82  
823 6         496 print $fh +($resp =~ m/\x0d\x0a\x0d\x0a(.*)$/s )[0];
824             }
825 6         101 close $fh;
826 6         214 return $to;
827             }
828              
829             ### Net::FTP fetching
830             sub _netftp_fetch {
831 0     0   0 my $self = shift;
832 0         0 my %hash = @_;
833              
834 0         0 my ($to);
835 0         0 my $tmpl = {
836             to => { required => 1, store => \$to }
837             };
838 0 0       0 check( $tmpl, \%hash ) or return;
839              
840             ### required modules ###
841 0         0 my $use_list = { 'Net::FTP' => 0 };
842              
843             ### Fix CVE-2016-1238 ###
844 0         0 local $Module::Load::Conditional::FORCE_SAFE_INC = 1;
845 0 0       0 unless( can_load( modules => $use_list ) ) {
846 0         0 $METHOD_FAIL->{'netftp'} = 1;
847 0         0 return;
848             }
849              
850             ### make connection ###
851 0         0 my $ftp;
852 0         0 my @options = ($self->host);
853 0 0       0 push(@options, Timeout => $TIMEOUT) if $TIMEOUT;
854 0 0       0 unless( $ftp = Net::FTP->new( @options ) ) {
855 0         0 return $self->_error(loc("Ftp creation failed: %1",$@));
856             }
857              
858             ### login ###
859 0 0       0 unless( $ftp->login( anonymous => $FROM_EMAIL ) ) {
860 0         0 return $self->_error(loc("Could not login to '%1'",$self->host));
861             }
862              
863             ### set binary mode, just in case ###
864 0         0 $ftp->binary;
865              
866             ### create the remote path
867             ### remember remote paths are unix paths! [#11483]
868 0         0 my $remote = File::Spec::Unix->catfile( $self->path, $self->file );
869              
870             ### fetch the file ###
871 0         0 my $target;
872 0 0       0 unless( $target = $ftp->get( $remote, $to ) ) {
873 0         0 return $self->_error(loc("Could not fetch '%1' from '%2'",
874             $remote, $self->host));
875             }
876              
877             ### log out ###
878 0         0 $ftp->quit;
879              
880 0         0 return $target;
881              
882             }
883              
884             ### /bin/wget fetch ###
885             sub _wget_fetch {
886 8     8   58 my $self = shift;
887 8         86 my %hash = @_;
888              
889 8         42 my ($to);
890 8         91 my $tmpl = {
891             to => { required => 1, store => \$to }
892             };
893 8 50       59 check( $tmpl, \%hash ) or return;
894              
895 8         837 my $wget;
896             ### see if we have a wget binary ###
897 8 50       168 unless( $wget = can_run('wget') ) {
898 0         0 $METHOD_FAIL->{'wget'} = 1;
899 0         0 return;
900             }
901              
902             ### no verboseness, thanks ###
903 8         4903 my $cmd = [ $wget, '--quiet' ];
904              
905             ### if a timeout is set, add it ###
906 8 50       45 push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT;
907              
908             ### run passive if specified ###
909 8 50 66     29 push @$cmd, '--passive-ftp' if $self->scheme eq 'ftp' && $FTP_PASSIVE;
910              
911             ### set the output document, add the uri ###
912 8         49 push @$cmd, '--output-document', $to, $self->uri;
913              
914             ### with IPC::Cmd > 0.41, this is fixed in teh library,
915             ### and there's no need for special casing any more.
916             ### DO NOT quote things for IPC::Run, it breaks stuff.
917             # $IPC::Cmd::USE_IPC_RUN
918             # ? ($to, $self->uri)
919             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
920              
921             ### shell out ###
922 8         25 my $captured;
923 8 50       183 unless(run( command => $cmd,
924             buffer => \$captured,
925             verbose => $DEBUG
926             )) {
927             ### wget creates the output document always, even if the fetch
928             ### fails.. so unlink it in that case
929 0         0 1 while unlink $to;
930              
931 0   0     0 return $self->_error(loc( "Command failed: %1", $captured || '' ));
932             }
933              
934 8         2238437 return $to;
935             }
936              
937             ### /bin/lftp fetch ###
938             sub _lftp_fetch {
939 1     1   12 my $self = shift;
940 1         12 my %hash = @_;
941              
942 1         6 my ($to);
943 1         10 my $tmpl = {
944             to => { required => 1, store => \$to }
945             };
946 1 50       46 check( $tmpl, \%hash ) or return;
947              
948             ### see if we have a lftp binary ###
949 1         85 my $lftp;
950 1 50       17 unless( $lftp = can_run('lftp') ) {
951 1         113850 $METHOD_FAIL->{'lftp'} = 1;
952 1         12 return;
953             }
954              
955             ### no verboseness, thanks ###
956 0         0 my $cmd = [ $lftp, '-f' ];
957              
958 0         0 my $fh = File::Temp->new;
959              
960 0         0 my $str;
961              
962             ### if a timeout is set, add it ###
963 0 0       0 $str .= "set net:timeout $TIMEOUT;\n" if $TIMEOUT;
964              
965             ### run passive if specified ###
966 0 0       0 $str .= "set ftp:passive-mode 1;\n" if $FTP_PASSIVE;
967              
968             ### set the output document, add the uri ###
969             ### quote the URI, because lftp supports certain shell
970             ### expansions, most notably & for backgrounding.
971             ### ' quote does nto work, must be "
972 0         0 $str .= q[get ']. $self->uri .q[' -o ]. $to . $/;
973              
974 0 0       0 if( $DEBUG ) {
975 0         0 my $pp_str = join ' ', split $/, $str;
976 0         0 print "# lftp command: $pp_str\n";
977             }
978              
979             ### write straight to the file.
980 0         0 $fh->autoflush(1);
981 0         0 print $fh $str;
982              
983             ### the command needs to be 1 string to be executed
984 0         0 push @$cmd, $fh->filename;
985              
986             ### with IPC::Cmd > 0.41, this is fixed in teh library,
987             ### and there's no need for special casing any more.
988             ### DO NOT quote things for IPC::Run, it breaks stuff.
989             # $IPC::Cmd::USE_IPC_RUN
990             # ? ($to, $self->uri)
991             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
992              
993              
994             ### shell out ###
995 0         0 my $captured;
996 0 0       0 unless(run( command => $cmd,
997             buffer => \$captured,
998             verbose => $DEBUG
999             )) {
1000             ### wget creates the output document always, even if the fetch
1001             ### fails.. so unlink it in that case
1002 0         0 1 while unlink $to;
1003              
1004 0   0     0 return $self->_error(loc( "Command failed: %1", $captured || '' ));
1005             }
1006              
1007 0         0 return $to;
1008             }
1009              
1010              
1011              
1012             ### /bin/ftp fetch ###
1013             sub _ftp_fetch {
1014 0     0   0 my $self = shift;
1015 0         0 my %hash = @_;
1016              
1017 0         0 my ($to);
1018 0         0 my $tmpl = {
1019             to => { required => 1, store => \$to }
1020             };
1021 0 0       0 check( $tmpl, \%hash ) or return;
1022              
1023             ### see if we have a ftp binary ###
1024 0         0 my $ftp;
1025 0 0       0 unless( $ftp = can_run('ftp') ) {
1026 0         0 $METHOD_FAIL->{'ftp'} = 1;
1027 0         0 return;
1028             }
1029              
1030 0         0 my $fh = FileHandle->new;
1031              
1032 0         0 local $SIG{CHLD} = 'IGNORE';
1033              
1034 0 0       0 unless ($fh->open("$ftp -n", '|-')) {
1035 0         0 return $self->_error(loc("%1 creation failed: %2", $ftp, $!));
1036             }
1037              
1038 0         0 my @dialog = (
1039             "lcd " . dirname($to),
1040             "open " . $self->host,
1041             "user anonymous $FROM_EMAIL",
1042             "cd /",
1043             "cd " . $self->path,
1044             "binary",
1045             "get " . $self->file . " " . $self->output_file,
1046             "quit",
1047             );
1048              
1049 0         0 foreach (@dialog) { $fh->print($_, "\n") }
  0         0  
1050 0 0       0 $fh->close or return;
1051              
1052 0         0 return $to;
1053             }
1054              
1055             ### lynx is stupid - it decompresses any .gz file it finds to be text
1056             ### use /bin/lynx to fetch files
1057             sub _lynx_fetch {
1058 1     1   15 my $self = shift;
1059 1         13 my %hash = @_;
1060              
1061 1         10 my ($to);
1062 1         13 my $tmpl = {
1063             to => { required => 1, store => \$to }
1064             };
1065 1 50       15 check( $tmpl, \%hash ) or return;
1066              
1067             ### see if we have a lynx binary ###
1068 1         86 my $lynx;
1069 1 50       25 unless ( $lynx = can_run('lynx') ){
1070 1         666 $METHOD_FAIL->{'lynx'} = 1;
1071 1         13 return;
1072             }
1073              
1074 0 0       0 unless( IPC::Cmd->can_capture_buffer ) {
1075 0         0 $METHOD_FAIL->{'lynx'} = 1;
1076              
1077 0         0 return $self->_error(loc(
1078             "Can not capture buffers. Can not use '%1' to fetch files",
1079             'lynx' ));
1080             }
1081              
1082             ### check if the HTTP resource exists ###
1083 0 0       0 if ($self->uri =~ /^https?:\/\//i) {
1084 0         0 my $cmd = [
1085             $lynx,
1086             '-head',
1087             '-source',
1088             "-auth=anonymous:$FROM_EMAIL",
1089             ];
1090              
1091 0 0       0 push @$cmd, "-connect_timeout=$TIMEOUT" if $TIMEOUT;
1092              
1093 0         0 push @$cmd, $self->uri;
1094              
1095             ### shell out ###
1096 0         0 my $head;
1097 0 0       0 unless(run( command => $cmd,
1098             buffer => \$head,
1099             verbose => $DEBUG )
1100             ) {
1101 0   0     0 return $self->_error(loc("Command failed: %1", $head || ''));
1102             }
1103              
1104 0 0       0 unless($head =~ /^HTTP\/\d+\.\d+ 200\b/) {
1105 0   0     0 return $self->_error(loc("Command failed: %1", $head || ''));
1106             }
1107             }
1108              
1109             ### write to the output file ourselves, since lynx ass_u_mes to much
1110 0 0       0 my $local = FileHandle->new( $to, 'w' )
1111             or return $self->_error(loc(
1112             "Could not open '%1' for writing: %2",$to,$!));
1113              
1114             ### dump to stdout ###
1115 0         0 my $cmd = [
1116             $lynx,
1117             '-source',
1118             "-auth=anonymous:$FROM_EMAIL",
1119             ];
1120              
1121 0 0       0 push @$cmd, "-connect_timeout=$TIMEOUT" if $TIMEOUT;
1122              
1123             ### DO NOT quote things for IPC::Run, it breaks stuff.
1124 0         0 push @$cmd, $self->uri;
1125              
1126             ### with IPC::Cmd > 0.41, this is fixed in teh library,
1127             ### and there's no need for special casing any more.
1128             ### DO NOT quote things for IPC::Run, it breaks stuff.
1129             # $IPC::Cmd::USE_IPC_RUN
1130             # ? $self->uri
1131             # : QUOTE. $self->uri .QUOTE;
1132              
1133              
1134             ### shell out ###
1135 0         0 my $captured;
1136 0 0       0 unless(run( command => $cmd,
1137             buffer => \$captured,
1138             verbose => $DEBUG )
1139             ) {
1140 0   0     0 return $self->_error(loc("Command failed: %1", $captured || ''));
1141             }
1142              
1143             ### print to local file ###
1144             ### XXX on a 404 with a special error page, $captured will actually
1145             ### hold the contents of that page, and make it *appear* like the
1146             ### request was a success, when really it wasn't :(
1147             ### there doesn't seem to be an option for lynx to change the exit
1148             ### code based on a 4XX status or so.
1149             ### the closest we can come is using --error_file and parsing that,
1150             ### which is very unreliable ;(
1151 0         0 $local->print( $captured );
1152 0 0       0 $local->close or return;
1153              
1154 0         0 return $to;
1155             }
1156              
1157             ### use /bin/ncftp to fetch files
1158             sub _ncftp_fetch {
1159 0     0   0 my $self = shift;
1160 0         0 my %hash = @_;
1161              
1162 0         0 my ($to);
1163 0         0 my $tmpl = {
1164             to => { required => 1, store => \$to }
1165             };
1166 0 0       0 check( $tmpl, \%hash ) or return;
1167              
1168             ### we can only set passive mode in interactive sessions, so bail out
1169             ### if $FTP_PASSIVE is set
1170 0 0       0 return if $FTP_PASSIVE;
1171              
1172             ### see if we have a ncftp binary ###
1173 0         0 my $ncftp;
1174 0 0       0 unless( $ncftp = can_run('ncftp') ) {
1175 0         0 $METHOD_FAIL->{'ncftp'} = 1;
1176 0         0 return;
1177             }
1178              
1179 0 0       0 my $cmd = [
1180             $ncftp,
1181             '-V', # do not be verbose
1182             '-p', $FROM_EMAIL, # email as password
1183             $self->host, # hostname
1184             dirname($to), # local dir for the file
1185             # remote path to the file
1186             ### DO NOT quote things for IPC::Run, it breaks stuff.
1187             $IPC::Cmd::USE_IPC_RUN
1188             ? File::Spec::Unix->catdir( $self->path, $self->file )
1189             : QUOTE. File::Spec::Unix->catdir(
1190             $self->path, $self->file ) .QUOTE
1191              
1192             ];
1193              
1194             ### shell out ###
1195 0         0 my $captured;
1196 0 0       0 unless(run( command => $cmd,
1197             buffer => \$captured,
1198             verbose => $DEBUG )
1199             ) {
1200 0   0     0 return $self->_error(loc("Command failed: %1", $captured || ''));
1201             }
1202              
1203 0         0 return $to;
1204              
1205             }
1206              
1207             ### use /bin/curl to fetch files
1208             sub _curl_fetch {
1209 1     1   14 my $self = shift;
1210 1         19 my %hash = @_;
1211              
1212 1         6 my ($to);
1213 1         18 my $tmpl = {
1214             to => { required => 1, store => \$to }
1215             };
1216 1 50       16 check( $tmpl, \%hash ) or return;
1217 1         104 my $curl;
1218 1 50       24 unless ( $curl = can_run('curl') ) {
1219 1         677 $METHOD_FAIL->{'curl'} = 1;
1220 1         13 return;
1221             }
1222              
1223             ### these long opts are self explanatory - I like that -jmb
1224 0         0 my $cmd = [ $curl, '-q' ];
1225              
1226 0 0 0     0 push(@$cmd, '-4') if $^O eq 'netbsd' && $FORCEIPV4; # only seen this on NetBSD so far
1227              
1228 0 0       0 push(@$cmd, '--connect-timeout', $TIMEOUT) if $TIMEOUT;
1229              
1230 0 0       0 push(@$cmd, '--silent') unless $DEBUG;
1231              
1232             ### curl does the right thing with passive, regardless ###
1233 0 0       0 if ($self->scheme eq 'ftp') {
1234 0         0 push(@$cmd, '--user', "anonymous:$FROM_EMAIL");
1235             }
1236              
1237             ### curl doesn't follow 302 (temporarily moved) etc automatically
1238             ### so we add --location to enable that.
1239 0         0 push @$cmd, '--fail', '--location', '--output', $to, $self->uri;
1240              
1241             ### with IPC::Cmd > 0.41, this is fixed in teh library,
1242             ### and there's no need for special casing any more.
1243             ### DO NOT quote things for IPC::Run, it breaks stuff.
1244             # $IPC::Cmd::USE_IPC_RUN
1245             # ? ($to, $self->uri)
1246             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
1247              
1248              
1249 0         0 my $captured;
1250 0 0       0 unless(run( command => $cmd,
1251             buffer => \$captured,
1252             verbose => $DEBUG )
1253             ) {
1254              
1255 0   0     0 return $self->_error(loc("Command failed: %1", $captured || ''));
1256             }
1257              
1258 0         0 return $to;
1259              
1260             }
1261              
1262             ### /usr/bin/fetch fetch! ###
1263             sub _fetch_fetch {
1264 1     1   14 my $self = shift;
1265 1         14 my %hash = @_;
1266              
1267 1         7 my ($to);
1268 1         10 my $tmpl = {
1269             to => { required => 1, store => \$to }
1270             };
1271 1 50       14 check( $tmpl, \%hash ) or return;
1272              
1273             ### see if we have a fetch binary ###
1274 1         98 my $fetch;
1275 1 50 33     70 unless( HAS_FETCH and $fetch = can_run('fetch') ) {
1276 1         14 $METHOD_FAIL->{'fetch'} = 1;
1277 1         8 return;
1278             }
1279              
1280             ### no verboseness, thanks ###
1281 0         0 my $cmd = [ $fetch, '-q' ];
1282              
1283             ### if a timeout is set, add it ###
1284 0 0       0 push(@$cmd, '-T', $TIMEOUT) if $TIMEOUT;
1285              
1286             ### run passive if specified ###
1287             #push @$cmd, '-p' if $FTP_PASSIVE;
1288 0 0       0 local $ENV{'FTP_PASSIVE_MODE'} = 1 if $FTP_PASSIVE;
1289              
1290             ### set the output document, add the uri ###
1291 0         0 push @$cmd, '-o', $to, $self->uri;
1292              
1293             ### with IPC::Cmd > 0.41, this is fixed in teh library,
1294             ### and there's no need for special casing any more.
1295             ### DO NOT quote things for IPC::Run, it breaks stuff.
1296             # $IPC::Cmd::USE_IPC_RUN
1297             # ? ($to, $self->uri)
1298             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
1299              
1300             ### shell out ###
1301 0         0 my $captured;
1302 0 0       0 unless(run( command => $cmd,
1303             buffer => \$captured,
1304             verbose => $DEBUG
1305             )) {
1306             ### wget creates the output document always, even if the fetch
1307             ### fails.. so unlink it in that case
1308 0         0 1 while unlink $to;
1309              
1310 0   0     0 return $self->_error(loc( "Command failed: %1", $captured || '' ));
1311             }
1312              
1313 0         0 return $to;
1314             }
1315              
1316             ### use File::Copy for fetching file:// urls ###
1317             ###
1318             ### See section 3.10 of RFC 1738 (https://datatracker.ietf.org/doc/html/rfc1738#section-3.10)
1319             ### Also see wikipedia on file:// (http://en.wikipedia.org/wiki/File://)
1320             ###
1321              
1322             sub _file_fetch {
1323 2     2   17 my $self = shift;
1324 2         18 my %hash = @_;
1325              
1326 2         9 my ($to);
1327 2         15 my $tmpl = {
1328             to => { required => 1, store => \$to }
1329             };
1330 2 50       22 check( $tmpl, \%hash ) or return;
1331              
1332              
1333              
1334             ### prefix a / on unix systems with a file uri, since it would
1335             ### look somewhat like this:
1336             ### file:///home/kane/file
1337             ### whereas windows file uris for 'c:\some\dir\file' might look like:
1338             ### file:///C:/some/dir/file
1339             ### file:///C|/some/dir/file
1340             ### or for a network share '\\host\share\some\dir\file':
1341             ### file:////host/share/some/dir/file
1342             ###
1343             ### VMS file uri's for 'DISK$USER:[MY.NOTES]NOTE123456.TXT' might look like:
1344             ### file://vms.host.edu/disk$user/my/notes/note12345.txt
1345             ###
1346              
1347 2         193 my $path = $self->path;
1348 2         21 my $vol = $self->vol;
1349 2         13 my $share = $self->share;
1350              
1351 2         3 my $remote;
1352 2 50 33     50 if (!$share and $self->host) {
1353 0         0 return $self->_error(loc(
1354             "Currently %1 cannot handle hosts in %2 urls",
1355             'File::Fetch', 'file://'
1356             ));
1357             }
1358              
1359 2 50       45 if( $vol ) {
    50          
1360 0         0 $path = File::Spec->catdir( split /\//, $path );
1361 0         0 $remote = File::Spec->catpath( $vol, $path, $self->file);
1362              
1363             } elsif( $share ) {
1364             ### win32 specific, and a share name, so we wont bother with File::Spec
1365 0         0 $path =~ s|/+|\\|g;
1366 0         0 $remote = "\\\\".$self->host."\\$share\\$path";
1367              
1368             } else {
1369             ### File::Spec on VMS can not currently handle UNIX syntax.
1370 2         15 my $file_class = ON_VMS
1371             ? 'File::Spec::Unix'
1372             : 'File::Spec';
1373              
1374 2         28 $remote = $file_class->catfile( $path, $self->file );
1375             }
1376              
1377             ### File::Copy is littered with 'die' statements :( ###
1378 2         10 my $rv = eval { File::Copy::copy( $remote, $to ) };
  2         34  
1379              
1380             ### something went wrong ###
1381 2 50 33     1139 if( !$rv or $@ ) {
1382 0         0 return $self->_error(loc("Could not copy '%1' to '%2': %3 %4",
1383             $remote, $to, $!, $@));
1384             }
1385              
1386 2         16 return $to;
1387             }
1388              
1389             ### use /usr/bin/rsync to fetch files
1390             sub _rsync_fetch {
1391 0     0     my $self = shift;
1392 0           my %hash = @_;
1393              
1394 0           my ($to);
1395 0           my $tmpl = {
1396             to => { required => 1, store => \$to }
1397             };
1398 0 0         check( $tmpl, \%hash ) or return;
1399 0           my $rsync;
1400 0 0         unless ( $rsync = can_run('rsync') ) {
1401 0           $METHOD_FAIL->{'rsync'} = 1;
1402 0           return;
1403             }
1404              
1405 0           my $cmd = [ $rsync ];
1406              
1407             ### XXX: rsync has no I/O timeouts at all, by default
1408 0 0         push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT;
1409              
1410 0 0         push(@$cmd, '--quiet') unless $DEBUG;
1411              
1412             ### DO NOT quote things for IPC::Run, it breaks stuff.
1413 0           push @$cmd, $self->uri, $to;
1414              
1415             ### with IPC::Cmd > 0.41, this is fixed in teh library,
1416             ### and there's no need for special casing any more.
1417             ### DO NOT quote things for IPC::Run, it breaks stuff.
1418             # $IPC::Cmd::USE_IPC_RUN
1419             # ? ($to, $self->uri)
1420             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
1421              
1422 0           my $captured;
1423 0 0         unless(run( command => $cmd,
1424             buffer => \$captured,
1425             verbose => $DEBUG )
1426             ) {
1427              
1428 0   0       return $self->_error(loc("Command %1 failed: %2",
      0        
1429             "@$cmd" || '', $captured || ''));
1430             }
1431              
1432 0           return $to;
1433              
1434             }
1435              
1436             ### use git to fetch files
1437             sub _git_fetch {
1438 0     0     my $self = shift;
1439 0           my %hash = @_;
1440              
1441 0           my ($to);
1442 0           my $tmpl = {
1443             to => { required => 1, store => \$to }
1444             };
1445 0 0         check( $tmpl, \%hash ) or return;
1446 0           my $git;
1447 0 0         unless ( $git = can_run('git') ) {
1448 0           $METHOD_FAIL->{'git'} = 1;
1449 0           return;
1450             }
1451              
1452 0           my $cmd = [ $git, 'clone' ];
1453              
1454             #push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT;
1455              
1456 0 0         push(@$cmd, '--quiet') unless $DEBUG;
1457              
1458             ### DO NOT quote things for IPC::Run, it breaks stuff.
1459 0           push @$cmd, $self->uri, $to;
1460              
1461             ### with IPC::Cmd > 0.41, this is fixed in teh library,
1462             ### and there's no need for special casing any more.
1463             ### DO NOT quote things for IPC::Run, it breaks stuff.
1464             # $IPC::Cmd::USE_IPC_RUN
1465             # ? ($to, $self->uri)
1466             # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE);
1467              
1468 0           my $captured;
1469 0 0         unless(run( command => $cmd,
1470             buffer => \$captured,
1471             verbose => $DEBUG )
1472             ) {
1473              
1474 0   0       return $self->_error(loc("Command %1 failed: %2",
      0        
1475             "@$cmd" || '', $captured || ''));
1476             }
1477              
1478 0           return $to;
1479              
1480             }
1481              
1482             #################################
1483             #
1484             # Error code
1485             #
1486             #################################
1487              
1488             =pod
1489              
1490             =head2 $ff->error([BOOL])
1491              
1492             Returns the last encountered error as string.
1493             Pass it a true value to get the C output instead.
1494              
1495             =cut
1496              
1497             ### error handling the way Archive::Extract does it
1498             sub _error {
1499 0     0     my $self = shift;
1500 0           my $error = shift;
1501              
1502 0           $self->_error_msg( $error );
1503 0           $self->_error_msg_long( Carp::longmess($error) );
1504              
1505 0 0         if( $WARN ) {
1506 0 0         carp $DEBUG ? $self->_error_msg_long : $self->_error_msg;
1507             }
1508              
1509 0           return;
1510             }
1511              
1512             sub error {
1513 0     0 1   my $self = shift;
1514 0 0         return shift() ? $self->_error_msg_long : $self->_error_msg;
1515             }
1516              
1517              
1518             1;
1519              
1520             =pod
1521              
1522             =head1 HOW IT WORKS
1523              
1524             File::Fetch is able to fetch a variety of uris, by using several
1525             external programs and modules.
1526              
1527             Below is a mapping of what utilities will be used in what order
1528             for what schemes, if available:
1529              
1530             file => LWP, lftp, file
1531             http => LWP, HTTP::Tiny, wget, curl, lftp, fetch, HTTP::Lite, lynx, iosock
1532             ftp => LWP, Net::FTP, wget, curl, lftp, fetch, ncftp, ftp
1533             rsync => rsync
1534             git => git
1535              
1536             If you'd like to disable the use of one or more of these utilities
1537             and/or modules, see the C<$BLACKLIST> variable further down.
1538              
1539             If a utility or module isn't available, it will be marked in a cache
1540             (see the C<$METHOD_FAIL> variable further down), so it will not be
1541             tried again. The C method will only fail when all options are
1542             exhausted, and it was not able to retrieve the file.
1543              
1544             The C utility is available on FreeBSD. NetBSD and Dragonfly BSD
1545             may also have it from C. We only check for C on those
1546             three platforms.
1547              
1548             C is a very limited L based mechanism for
1549             retrieving C schemed urls. It doesn't follow redirects for instance.
1550              
1551             C only supports C style urls.
1552              
1553             A special note about fetching files from an ftp uri:
1554              
1555             By default, all ftp connections are done in passive mode. To change
1556             that, see the C<$FTP_PASSIVE> variable further down.
1557              
1558             Furthermore, ftp uris only support anonymous connections, so no
1559             named user/password pair can be passed along.
1560              
1561             C is blacklisted by default; see the C<$BLACKLIST> variable
1562             further down.
1563              
1564             =head1 GLOBAL VARIABLES
1565              
1566             The behaviour of File::Fetch can be altered by changing the following
1567             global variables:
1568              
1569             =head2 $File::Fetch::FROM_EMAIL
1570              
1571             This is the email address that will be sent as your anonymous ftp
1572             password.
1573              
1574             Default is C.
1575              
1576             =head2 $File::Fetch::USER_AGENT
1577              
1578             This is the useragent as C will report it.
1579              
1580             Default is C.
1581              
1582             =head2 $File::Fetch::FTP_PASSIVE
1583              
1584             This variable controls whether the environment variable C
1585             and any passive switches to commandline tools will be set to true.
1586              
1587             Default value is 1.
1588              
1589             Note: When $FTP_PASSIVE is true, C will not be used to fetch
1590             files, since passive mode can only be set interactively for this binary
1591              
1592             =head2 $File::Fetch::TIMEOUT
1593              
1594             When set, controls the network timeout (counted in seconds).
1595              
1596             Default value is 0.
1597              
1598             =head2 $File::Fetch::WARN
1599              
1600             This variable controls whether errors encountered internally by
1601             C should be C'd or not.
1602              
1603             Set to false to silence warnings. Inspect the output of the C
1604             method manually to see what went wrong.
1605              
1606             Defaults to C.
1607              
1608             =head2 $File::Fetch::DEBUG
1609              
1610             This enables debugging output when calling commandline utilities to
1611             fetch files.
1612             This also enables C errors, instead of the regular
1613             C errors.
1614              
1615             Good for tracking down why things don't work with your particular
1616             setup.
1617              
1618             Default is 0.
1619              
1620             =head2 $File::Fetch::BLACKLIST
1621              
1622             This is an array ref holding blacklisted modules/utilities for fetching
1623             files with.
1624              
1625             To disallow the use of, for example, C and C, you could
1626             set $File::Fetch::BLACKLIST to:
1627              
1628             $File::Fetch::BLACKLIST = [qw|lwp netftp|]
1629              
1630             The default blacklist is [qw|ftp|], as C is rather unreliable.
1631              
1632             See the note on C below.
1633              
1634             =head2 $File::Fetch::METHOD_FAIL
1635              
1636             This is a hashref registering what modules/utilities were known to fail
1637             for fetching files (mostly because they weren't installed).
1638              
1639             You can reset this cache by assigning an empty hashref to it, or
1640             individually remove keys.
1641              
1642             See the note on C below.
1643              
1644             =head1 MAPPING
1645              
1646              
1647             Here's a quick mapping for the utilities/modules, and their names for
1648             the $BLACKLIST, $METHOD_FAIL and other internal functions.
1649              
1650             LWP => lwp
1651             HTTP::Lite => httplite
1652             HTTP::Tiny => httptiny
1653             Net::FTP => netftp
1654             wget => wget
1655             lynx => lynx
1656             ncftp => ncftp
1657             ftp => ftp
1658             curl => curl
1659             rsync => rsync
1660             lftp => lftp
1661             fetch => fetch
1662             IO::Socket => iosock
1663              
1664             =head1 FREQUENTLY ASKED QUESTIONS
1665              
1666             =head2 So how do I use a proxy with File::Fetch?
1667              
1668             C currently only supports proxies with LWP::UserAgent.
1669             You will need to set your environment variables accordingly. For
1670             example, to use an ftp proxy:
1671              
1672             $ENV{ftp_proxy} = 'foo.com';
1673              
1674             Refer to the LWP::UserAgent manpage for more details.
1675              
1676             =head2 I used 'lynx' to fetch a file, but its contents is all wrong!
1677              
1678             C can only fetch remote files by dumping its contents to C,
1679             which we in turn capture. If that content is a 'custom' error file
1680             (like, say, a C<404 handler>), you will get that contents instead.
1681              
1682             Sadly, C doesn't support any options to return a different exit
1683             code on non-C<200 OK> status, giving us no way to tell the difference
1684             between a 'successful' fetch and a custom error page.
1685              
1686             Therefor, we recommend to only use C as a last resort. This is
1687             why it is at the back of our list of methods to try as well.
1688              
1689             =head2 Files I'm trying to fetch have reserved characters or non-ASCII characters in them. What do I do?
1690              
1691             C is relatively smart about things. When trying to write
1692             a file to disk, it removes the C (see the
1693             C method for details) from the file name before creating
1694             it. In most cases this suffices.
1695              
1696             If you have any other characters you need to escape, please install
1697             the C module from CPAN, and pre-encode your URI before
1698             passing it to C. You can read about the details of URIs
1699             and URI encoding here:
1700              
1701             L
1702              
1703             =head1 TODO
1704              
1705             =over 4
1706              
1707             =item Implement $PREFER_BIN
1708              
1709             To indicate to rather use commandline tools than modules
1710              
1711             =back
1712              
1713             =head1 BUG REPORTS
1714              
1715             Please report bugs or other issues to Ebug-file-fetch@rt.cpan.org.
1716              
1717             =head1 AUTHOR
1718              
1719             This module by Jos Boumans Ekane@cpan.orgE.
1720              
1721             =head1 COPYRIGHT
1722              
1723             This library is free software; you may redistribute and/or modify it
1724             under the same terms as Perl itself.
1725              
1726              
1727             =cut
1728              
1729             # Local variables:
1730             # c-indentation-style: bsd
1731             # c-basic-offset: 4
1732             # indent-tabs-mode: nil
1733             # End:
1734             # vim: expandtab shiftwidth=4:
1735              
1736              
1737              
1738