1 package CPANPLUS::Internals::Fetch;
6 use CPANPLUS::Internals::Constants;
12 use Params::Check qw[check];
13 use Module::Load::Conditional qw[can_load];
14 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
16 $Params::Check::VERBOSE = 1;
22 CPANPLUS::Internals::Fetch
26 my $output = $cb->_fetch(
28 fetchdir => '/path/to/save/to',
33 $cb->_add_fail_host( host => 'foo.com' );
34 $cb->_host_ok( host => 'foo.com' );
39 CPANPLUS::Internals::Fetch fetches files from either ftp, http, file
42 This is the rough flow:
45 Delegate to File::Fetch;
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] )
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>.
58 C<fetchdir> is the place to save the file to. Usually this
59 information comes from your configuration, but you can override it
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.
66 C<force> forces a new download, even if the file already exists.
68 C<verbose> simply indicates whether or not to print extra messages.
70 C<prefer_bin> indicates whether you prefer the use of commandline
71 programs over perl modules. Defaults to your corresponding config
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.
77 Returns the path of the output file on success, false on failure.
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'] );
83 And the C<LWP> function will be skipped by C<File::Fetch>.
89 my $conf = $self->configure_object;
92 local $Params::Check::NO_DUPLICATES = 0;
94 my ($modobj, $verbose, $force, $fetch_from);
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'),
101 verbose => { default => $conf->get_conf('verbose'),
102 store => \$verbose },
103 prefer_bin => { default => $conf->get_conf('prefer_bin') },
107 my $args = check( $tmpl, \%hash ) or return;
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 );
117 my ($remote_file, $local_file, $local_path);
119 ### build the local path to downlaod to ###
121 $local_path = $args->{fetchdir} ||
123 $conf->get_conf('base'),
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);
135 $local_file = File::Spec->rel2abs(
143 ### do we already have the file? ###
144 if( -e $local_file ) {
146 if( $args->{force} ) {
148 ### some fetches will fail if the files exist already, so let's
149 ### delete them first
151 or msg( loc("Could not delete %1, some methods may " .
152 "fail to force a download", $local_file), $verbose);
155 ### store where we fetched it ###
156 $modobj->status->fetch( $local_file );
163 ### we got a custom URI
165 my $abs = $self->__file_fetch( from => $fetch_from,
167 verbose => $verbose );
170 error(loc("Unable to download '%1'", $fetch_from));
174 ### store where we fetched it ###
175 $modobj->status->fetch( $abs );
179 ### we will get it from one of our mirrors
181 ### build the remote path to download from ###
182 { $remote_file = File::Spec::Unix->catfile(
186 unless( $remote_file ) {
187 error( loc('No remote file given for download') );
192 ### see if we even have a host or a method to use to download with ###
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!
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;
213 ### loop over all hosts we have ###
214 for my $host ( @{$conf->get_conf('hosts')} ) {
219 ### file:// uris are special and need parsing
220 if( $host->{'scheme'} eq 'file' ) {
222 ### the full path in the native format of the OS
224 File::Spec->file_name_is_absolute( $host->{'path'} )
226 : File::Spec->rel2abs( $host->{'path'} );
228 ### there might be volumes involved on vms/win32
229 if( ON_WIN32 or ON_VMS ) {
231 ### now extract the volume in order to be Win32 and
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'
239 ### and split up the directories
240 my @host_dirs = File::Spec->splitdir( $host_path );
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 ) {
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;
255 $vol =~ s/:// if ON_VMS;
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"
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;
269 ### element empty? reuse it to store the volume
270 ### encoded as a directory name. (Win32/VMS)
272 $host_dirs[0] = $vol;
276 ### now it's in UNIX format, which is the same format
278 $host_spec = File::Spec::Unix->catdir( @host_dirs );
281 ### now create the file:// uri from the components
282 $where = CREATE_FILE_URI->(
283 File::Spec::Unix->catfile(
284 $host->{'host'} || '',
290 ### its components will be in unix format, for a http://,
291 ### ftp:// or any other style of URI
293 my $mirror_path = File::Spec::Unix->catfile(
294 $host->{'path'}, $remote_file
297 my %args = ( scheme => $host->{scheme},
298 host => $host->{host},
299 path => $mirror_path,
302 $where = $self->_host_to_uri( %args );
305 my $abs = $self->__file_fetch( from => $where,
307 verbose => $verbose );
309 ### we got a path back?
311 ### store where we fetched it ###
312 $modobj->status->fetch( $abs );
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;
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
325 push @maybe_bad_host, $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"));
341 my $conf = $self->configure_object;
344 my ($where, $local_path, $verbose);
346 from => { required => 1, store => \$where },
347 to => { required => 1, store => \$local_path },
348 verbose => { default => $conf->get_conf('verbose'),
349 store => \$verbose },
352 check( $tmpl, \%hash ) or return;
354 msg(loc("Trying to get '%1'", $where ), $verbose );
356 ### build the object ###
357 my $ff = File::Fetch->new( uri => $where );
360 error(loc("Bad uri '%1'",$where)), return unless $ff;
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);
368 my $abs = File::Spec->rel2abs( $file );
373 error(loc("Fetching of '%1' failed: %2", $where, $ff->error));
381 =head2 _add_fail_host( host => $host_hashref )
383 Mark a particular host as bad. This makes C<CPANPLUS::Internals::Fetch>
384 skip it in fetches until this cache is flushed.
386 =head2 _host_ok( host => $host_hashref )
388 Query the cache to see if this host is ok, or if it has been flagged
391 Returns true if the host is ok, false otherwise.
395 { ### caching functions ###
403 host => { required => 1, default => {},
404 strict_type => 1, store => \$host },
407 check( $tmpl, \%hash ) or return;
409 return $self->_hosts->{$host} = 1;
418 host => { required => 1, store => \$host },
421 check( $tmpl, \%hash ) or return;
423 return $self->_hosts->{$host} ? 0 : 1;
431 # c-indentation-style: bsd
433 # indent-tabs-mode: nil
435 # vim: expandtab shiftwidth=4: