[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 package CPANPLUS::Internals::Fetch; 2 3 use strict; 4 5 use CPANPLUS::Error; 6 use CPANPLUS::Internals::Constants; 7 8 use File::Fetch; 9 use File::Spec; 10 use Cwd qw[cwd]; 11 use IPC::Cmd qw[run]; 12 use Params::Check qw[check]; 13 use Module::Load::Conditional qw[can_load]; 14 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext'; 15 16 $Params::Check::VERBOSE = 1; 17 18 =pod 19 20 =head1 NAME 21 22 CPANPLUS::Internals::Fetch 23 24 =head1 SYNOPSIS 25 26 my $output = $cb->_fetch( 27 module => $modobj, 28 fetchdir => '/path/to/save/to', 29 verbose => BOOL, 30 force => BOOL, 31 ); 32 33 $cb->_add_fail_host( host => 'foo.com' ); 34 $cb->_host_ok( host => 'foo.com' ); 35 36 37 =head1 DESCRIPTION 38 39 CPANPLUS::Internals::Fetch fetches files from either ftp, http, file 40 or rsync mirrors. 41 42 This is the rough flow: 43 44 $cb->_fetch 45 Delegate to File::Fetch; 46 47 48 =head1 METHODS 49 50 =cut 51 52 =head1 $path = _fetch( module => $modobj, [fetchdir => '/path/to/save/to', fetch_from => 'scheme://path/to/fetch/from', verbose => BOOL, force => BOOL, prefer_bin => BOOL] ) 53 54 C<_fetch> will fetch files based on the information in a module 55 object. You always need a module object. If you want a fake module 56 object for a one-off fetch, look at C<CPANPLUS::Module::Fake>. 57 58 C<fetchdir> is the place to save the file to. Usually this 59 information comes from your configuration, but you can override it 60 expressly if needed. 61 62 C<fetch_from> lets you specify an URI to get this file from. If you 63 do not specify one, your list of configured hosts will be probed to 64 download the file from. 65 66 C<force> forces a new download, even if the file already exists. 67 68 C<verbose> simply indicates whether or not to print extra messages. 69 70 C<prefer_bin> indicates whether you prefer the use of commandline 71 programs over perl modules. Defaults to your corresponding config 72 setting. 73 74 C<_fetch> figures out, based on the host list, what scheme to use and 75 from there, delegates to C<File::Fetch> do the actual fetching. 76 77 Returns the path of the output file on success, false on failure. 78 79 Note that you can set a C<blacklist> on certain methods in the config. 80 Simply add the identifying name of the method (ie, C<lwp>) to: 81 $conf->_set_fetch( blacklist => ['lwp'] ); 82 83 And the C<LWP> function will be skipped by C<File::Fetch>. 84 85 =cut 86 87 sub _fetch { 88 my $self = shift; 89 my $conf = $self->configure_object; 90 my %hash = @_; 91 92 local $Params::Check::NO_DUPLICATES = 0; 93 94 my ($modobj, $verbose, $force, $fetch_from); 95 my $tmpl = { 96 module => { required => 1, allow => IS_MODOBJ, store => \$modobj }, 97 fetchdir => { default => $conf->get_conf('fetchdir') }, 98 fetch_from => { default => '', store => \$fetch_from }, 99 force => { default => $conf->get_conf('force'), 100 store => \$force }, 101 verbose => { default => $conf->get_conf('verbose'), 102 store => \$verbose }, 103 prefer_bin => { default => $conf->get_conf('prefer_bin') }, 104 }; 105 106 107 my $args = check( $tmpl, \%hash ) or return; 108 109 ### check if we already downloaded the thing ### 110 if( (my $where = $modobj->status->fetch()) && !$force ) { 111 msg(loc("Already fetched '%1' to '%2', " . 112 "won't fetch again without force", 113 $modobj->module, $where ), $verbose ); 114 return $where; 115 } 116 117 my ($remote_file, $local_file, $local_path); 118 119 ### build the local path to downlaod to ### 120 { 121 $local_path = $args->{fetchdir} || 122 File::Spec->catdir( 123 $conf->get_conf('base'), 124 $modobj->path, 125 ); 126 127 ### create the path if it doesn't exist ### 128 unless( -d $local_path ) { 129 unless( $self->_mkdir( dir => $local_path ) ) { 130 msg( loc("Could not create path '%1'", $local_path), $verbose); 131 return; 132 } 133 } 134 135 $local_file = File::Spec->rel2abs( 136 File::Spec->catfile( 137 $local_path, 138 $modobj->package, 139 ) 140 ); 141 } 142 143 ### do we already have the file? ### 144 if( -e $local_file ) { 145 146 if( $args->{force} ) { 147 148 ### some fetches will fail if the files exist already, so let's 149 ### delete them first 150 unlink $local_file 151 or msg( loc("Could not delete %1, some methods may " . 152 "fail to force a download", $local_file), $verbose); 153 } else { 154 155 ### store where we fetched it ### 156 $modobj->status->fetch( $local_file ); 157 158 return $local_file; 159 } 160 } 161 162 163 ### we got a custom URI 164 if ( $fetch_from ) { 165 my $abs = $self->__file_fetch( from => $fetch_from, 166 to => $local_path, 167 verbose => $verbose ); 168 169 unless( $abs ) { 170 error(loc("Unable to download '%1'", $fetch_from)); 171 return; 172 } 173 174 ### store where we fetched it ### 175 $modobj->status->fetch( $abs ); 176 177 return $abs; 178 179 ### we will get it from one of our mirrors 180 } else { 181 ### build the remote path to download from ### 182 { $remote_file = File::Spec::Unix->catfile( 183 $modobj->path, 184 $modobj->package, 185 ); 186 unless( $remote_file ) { 187 error( loc('No remote file given for download') ); 188 return; 189 } 190 } 191 192 ### see if we even have a host or a method to use to download with ### 193 my $found_host; 194 my @maybe_bad_host; 195 196 HOST: { 197 ### F*CKING PIECE OF F*CKING p4 SHIT makes 198 ### '$File :: Fetch::SOME_VAR' 199 ### into a meta variable and starts substituting the file name... 200 ### GRAAAAAAAAAAAAAAAAAAAAAAH! 201 ### use ' to combat it! 202 203 ### set up some flags for File::Fetch ### 204 local $File'Fetch::BLACKLIST = $conf->_get_fetch('blacklist'); 205 local $File'Fetch::TIMEOUT = $conf->get_conf('timeout'); 206 local $File'Fetch::DEBUG = $conf->get_conf('debug'); 207 local $File'Fetch::FTP_PASSIVE = $conf->get_conf('passive'); 208 local $File'Fetch::FROM_EMAIL = $conf->get_conf('email'); 209 local $File'Fetch::PREFER_BIN = $conf->get_conf('prefer_bin'); 210 local $File'Fetch::WARN = $verbose; 211 212 213 ### loop over all hosts we have ### 214 for my $host ( @{$conf->get_conf('hosts')} ) { 215 $found_host++; 216 217 my $where; 218 219 ### file:// uris are special and need parsing 220 if( $host->{'scheme'} eq 'file' ) { 221 222 ### the full path in the native format of the OS 223 my $host_spec = 224 File::Spec->file_name_is_absolute( $host->{'path'} ) 225 ? $host->{'path'} 226 : File::Spec->rel2abs( $host->{'path'} ); 227 228 ### there might be volumes involved on vms/win32 229 if( ON_WIN32 or ON_VMS ) { 230 231 ### now extract the volume in order to be Win32 and 232 ### VMS friendly. 233 ### 'no_file' indicates that there's no file part 234 ### of this path, so we only get 2 bits returned. 235 my ($vol, $host_path) = File::Spec->splitpath( 236 $host_spec, 'no_file' 237 ); 238 239 ### and split up the directories 240 my @host_dirs = File::Spec->splitdir( $host_path ); 241 242 ### if we got a volume we pretend its a directory for 243 ### the sake of the file:// url 244 if( defined $vol and $vol ) { 245 246 ### D:\foo\bar needs to be encoded as D|\foo\bar 247 ### For details, see the following link: 248 ### http://en.wikipedia.org/wiki/File:// 249 ### The RFC doesnt seem to address Windows volume 250 ### descriptors but it does address VMS volume 251 ### descriptors, however wikipedia covers a bit of 252 ### history regarding win32 253 $vol =~ s/:$/|/ if ON_WIN32; 254 255 $vol =~ s/:// if ON_VMS; 256 257 ### XXX i'm not sure what cases this is addressing. 258 ### this comes straight from dmq's file:// patches 259 ### for win32. --kane 260 ### According to dmq, the best summary is: 261 ### "if file:// urls dont look right on VMS reuse 262 ### the win32 logic and see if that fixes things" 263 264 ### first element not empty? Might happen on VMS. 265 ### prepend the volume in that case. 266 if( $host_dirs[0] ) { 267 unshift @host_dirs, $vol; 268 269 ### element empty? reuse it to store the volume 270 ### encoded as a directory name. (Win32/VMS) 271 } else { 272 $host_dirs[0] = $vol; 273 } 274 } 275 276 ### now it's in UNIX format, which is the same format 277 ### as used for URIs 278 $host_spec = File::Spec::Unix->catdir( @host_dirs ); 279 } 280 281 ### now create the file:// uri from the components 282 $where = CREATE_FILE_URI->( 283 File::Spec::Unix->catfile( 284 $host->{'host'} || '', 285 $host_spec, 286 $remote_file, 287 ) 288 ); 289 290 ### its components will be in unix format, for a http://, 291 ### ftp:// or any other style of URI 292 } else { 293 my $mirror_path = File::Spec::Unix->catfile( 294 $host->{'path'}, $remote_file 295 ); 296 297 my %args = ( scheme => $host->{scheme}, 298 host => $host->{host}, 299 path => $mirror_path, 300 ); 301 302 $where = $self->_host_to_uri( %args ); 303 } 304 305 my $abs = $self->__file_fetch( from => $where, 306 to => $local_path, 307 verbose => $verbose ); 308 309 ### we got a path back? 310 if( $abs ) { 311 ### store where we fetched it ### 312 $modobj->status->fetch( $abs ); 313 314 ### this host is good, the previous ones are apparently 315 ### not, so mark them as such. 316 $self->_add_fail_host( host => $_ ) for @maybe_bad_host; 317 318 return $abs; 319 } 320 321 ### so we tried to get the file but didn't actually fetch it -- 322 ### there's a chance this host is bad. mark it as such and 323 ### actually flag it back if we manage to get the file 324 ### somewhere else 325 push @maybe_bad_host, $host; 326 } 327 } 328 329 $found_host 330 ? error(loc("Fetch failed: host list exhausted " . 331 "-- are you connected today?")) 332 : error(loc("No hosts found to download from " . 333 "-- check your config")); 334 } 335 336 return; 337 } 338 339 sub __file_fetch { 340 my $self = shift; 341 my $conf = $self->configure_object; 342 my %hash = @_; 343 344 my ($where, $local_path, $verbose); 345 my $tmpl = { 346 from => { required => 1, store => \$where }, 347 to => { required => 1, store => \$local_path }, 348 verbose => { default => $conf->get_conf('verbose'), 349 store => \$verbose }, 350 }; 351 352 check( $tmpl, \%hash ) or return; 353 354 msg(loc("Trying to get '%1'", $where ), $verbose ); 355 356 ### build the object ### 357 my $ff = File::Fetch->new( uri => $where ); 358 359 ### sanity check ### 360 error(loc("Bad uri '%1'",$where)), return unless $ff; 361 362 if( my $file = $ff->fetch( to => $local_path ) ) { 363 unless( -e $file && -s _ ) { 364 msg(loc("'%1' said it fetched '%2', but it was not created", 365 'File::Fetch', $file), $verbose); 366 367 } else { 368 my $abs = File::Spec->rel2abs( $file ); 369 return $abs; 370 } 371 372 } else { 373 error(loc("Fetching of '%1' failed: %2", $where, $ff->error)); 374 } 375 376 return; 377 } 378 379 =pod 380 381 =head2 _add_fail_host( host => $host_hashref ) 382 383 Mark a particular host as bad. This makes C<CPANPLUS::Internals::Fetch> 384 skip it in fetches until this cache is flushed. 385 386 =head2 _host_ok( host => $host_hashref ) 387 388 Query the cache to see if this host is ok, or if it has been flagged 389 as bad. 390 391 Returns true if the host is ok, false otherwise. 392 393 =cut 394 395 { ### caching functions ### 396 397 sub _add_fail_host { 398 my $self = shift; 399 my %hash = @_; 400 401 my $host; 402 my $tmpl = { 403 host => { required => 1, default => {}, 404 strict_type => 1, store => \$host }, 405 }; 406 407 check( $tmpl, \%hash ) or return; 408 409 return $self->_hosts->{$host} = 1; 410 } 411 412 sub _host_ok { 413 my $self = shift; 414 my %hash = @_; 415 416 my $host; 417 my $tmpl = { 418 host => { required => 1, store => \$host }, 419 }; 420 421 check( $tmpl, \%hash ) or return; 422 423 return $self->_hosts->{$host} ? 0 : 1; 424 } 425 } 426 427 428 1; 429 430 # Local variables: 431 # c-indentation-style: bsd 432 # c-basic-offset: 4 433 # indent-tabs-mode: nil 434 # End: 435 # vim: expandtab shiftwidth=4:
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |