1d39602bdSespie# ex:ts=8 sw=4: 2*57050d8cSespie# $OpenBSD: PackageRepository.pm,v 1.177 2023/11/25 10:29:23 espie Exp $ 3d39602bdSespie# 4eab3425cSphessler# Copyright (c) 2003-2010 Marc Espie <espie@openbsd.org> 5d39602bdSespie# 6d39602bdSespie# Permission to use, copy, modify, and distribute this software for any 7d39602bdSespie# purpose with or without fee is hereby granted, provided that the above 8d39602bdSespie# copyright notice and this permission notice appear in all copies. 9d39602bdSespie# 10d39602bdSespie# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11d39602bdSespie# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12d39602bdSespie# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13d39602bdSespie# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14d39602bdSespie# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15d39602bdSespie# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16d39602bdSespie# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17d39602bdSespie 18039cbdaaSespieuse v5.36; 19d39602bdSespie 20119c509dSespie# XXX load extra class, grab match from Base class, and tweak inheritance 21119c509dSespie# to get all methods. 22119c509dSespie 23119c509dSespieuse OpenBSD::PackageRepository::Installed; 2424f8b0e6Sespie$OpenBSD::PackageRepository::Installed::ISA = qw(OpenBSD::PackageRepository); 25119c509dSespie 26d39602bdSespiepackage OpenBSD::PackageRepository; 27119c509dSespieour @ISA=(qw(OpenBSD::PackageRepositoryBase)); 28119c509dSespie 29d39602bdSespieuse OpenBSD::PackageLocation; 3073c16894Sespieuse OpenBSD::Paths; 31caf99896Sespieuse OpenBSD::Error; 32018a7b40Sespieuse OpenBSD::Temp; 33018a7b40Sespie 34039cbdaaSespiesub make_error_file($self, $object) 35018a7b40Sespie{ 36018a7b40Sespie $object->{errors} = OpenBSD::Temp->file; 37018a7b40Sespie if (!defined $object->{errors}) { 38c7833d9aSespie $self->{state}->fatal(OpenBSD::Temp->last_error); 39018a7b40Sespie } 40018a7b40Sespie} 41d39602bdSespie 42039cbdaaSespiesub baseurl($self) 4318cd0704Sespie{ 4418cd0704Sespie return $self->{path}; 4518cd0704Sespie} 46d39602bdSespie 47039cbdaaSespiesub new($class, $baseurl, $state) 48d39602bdSespie{ 492f58182fSespie if (!defined $state) { 502f58182fSespie require Carp; 512f58182fSespie Carp::croak "fatal: old api call to $class: needs state"; 522f58182fSespie } 535732efc0Sespie my $o = $class->parse(\$baseurl, $state); 543eadd976Sespie if ($baseurl ne '') { 553eadd976Sespie return undef; 56229c7eb8Sespie } 57229c7eb8Sespie return $o; 58229c7eb8Sespie} 59229c7eb8Sespie 60039cbdaaSespiesub can_be_empty($self) 6126e75840Sespie{ 6226e75840Sespie $self->{empty_okay} = 1; 6326e75840Sespie return $self; 6426e75840Sespie} 6526e75840Sespie 66165c4191Sespiemy $cache = {}; 67165c4191Sespie 68039cbdaaSespiesub unique($class, $o) 69165c4191Sespie{ 70165c4191Sespie return $o unless defined $o; 71165c4191Sespie if (defined $cache->{$o->url}) { 72165c4191Sespie return $cache->{$o->url}; 73165c4191Sespie } 74165c4191Sespie $cache->{$o->url} = $o; 75165c4191Sespie return $o; 76165c4191Sespie} 7716c58e7cSespie 785a29d933SespieOpenBSD::Handler->atend( 79039cbdaaSespie sub($) { 80becd6ee6Sespie for my $repo (values %$cache) { 81becd6ee6Sespie $repo->cleanup; 82becd6ee6Sespie } 835a29d933Sespie }); 84becd6ee6Sespie 85039cbdaaSespiesub parse_fullurl($class, $r, $state) 865c3fa476Sespie{ 87229c7eb8Sespie $class->strip_urlscheme($r) or return undef; 885732efc0Sespie return $class->unique($class->parse_url($r, $state)); 89a0b7813cSespie} 90a0b7813cSespie 91039cbdaaSespiesub dont_cleanup($) 927b25c30aSespie{ 937b25c30aSespie} 947b25c30aSespie 95039cbdaaSespiesub ftp($) { 'OpenBSD::PackageRepository::FTP' } 96039cbdaaSespiesub http($) { 'OpenBSD::PackageRepository::HTTP' } 97039cbdaaSespiesub https($) { 'OpenBSD::PackageRepository::HTTPS' } 98039cbdaaSespiesub scp($) { 'OpenBSD::PackageRepository::SCP' } 99039cbdaaSespiesub file($) { 'OpenBSD::PackageRepository::Local' } 100039cbdaaSespiesub installed($) { 'OpenBSD::PackageRepository::Installed' } 1015732efc0Sespie 102039cbdaaSespiesub parse($class, $r, $state) 103a0b7813cSespie{ 1042d7516c0Sespie { 1052d7516c0Sespie no warnings qw(uninitialized); # in case installpath is empty 106b1b61206Sespie $$r =~ s/^installpath(\:|$)/$state->installpath.$1/e; 1072d7516c0Sespie } 108b1b61206Sespie 109b62674ebSespie my $u = $$r; 110b62674ebSespie return undef if $u eq ''; 11118cd0704Sespie 112b1b61206Sespie 113b1b61206Sespie 114b62674ebSespie if ($u =~ m/^ftp\:/io) { 1155732efc0Sespie return $class->ftp->parse_fullurl($r, $state); 116b62674ebSespie } elsif ($u =~ m/^http\:/io) { 117caf99896Sespie# require OpenBSD::PackageRepository::HTTP; 118caf99896Sespie 1195732efc0Sespie return $class->http->parse_fullurl($r, $state); 120b62674ebSespie } elsif ($u =~ m/^https\:/io) { 1215732efc0Sespie return $class->https->parse_fullurl($r, $state); 122b62674ebSespie } elsif ($u =~ m/^scp\:/io) { 12326dcf9dfSespie return undef if $state->defines("NO_SCP"); 12426dcf9dfSespie 12518cd0704Sespie require OpenBSD::PackageRepository::SCP; 12618cd0704Sespie 1275732efc0Sespie return $class->scp->parse_fullurl($r, $state); 128b62674ebSespie } elsif ($u =~ m/^file\:/io) { 1295732efc0Sespie return $class->file->parse_fullurl($r, $state); 130b62674ebSespie } elsif ($u =~ m/^inst\:$/io) { 1315732efc0Sespie return $class->installed->parse_fullurl($r, $state); 132a0b7813cSespie } else { 133e078b635Sespie if ($$r =~ m/^([a-z0-9][a-z0-9.]+\.[a-z0-9.]+)(\:|$)/ 134e078b635Sespie && !-d $1) { 135e078b635Sespie $$r =~ s//http:\/\/$1\/%m$2/; 136e078b635Sespie return $class->http->parse_fullurl($r, $state); 137e078b635Sespie } 1385732efc0Sespie return $class->file->parse_fullurl($r, $state); 139a0b7813cSespie } 140a0b7813cSespie} 141a0b7813cSespie 142039cbdaaSespiesub available($self) 143d39602bdSespie{ 144f04afe28Sespie return @{$self->list}; 145d39602bdSespie} 146d39602bdSespie 147039cbdaaSespiesub stemlist($self) 1480a4f8832Sespie{ 1490a4f8832Sespie if (!defined $self->{stemlist}) { 1500a4f8832Sespie require OpenBSD::PackageName; 15126e75840Sespie my @l = $self->available; 15226e75840Sespie if (@l == 0 && !$self->{empty_okay}) { 153d1e8d136Sespie $self->{state}->errsay("#1: #2", $self->url, 154d1e8d136Sespie $self->{no_such_dir} ? "no such dir" : "empty"); 15526e75840Sespie } 15626e75840Sespie $self->{stemlist} = OpenBSD::PackageName::avail2stems(@l); 1570a4f8832Sespie } 15873ef2950Sespie return $self->{stemlist}; 15973ef2950Sespie} 16073ef2950Sespie 161039cbdaaSespiesub wipe_info($self, $pkg) 162d39602bdSespie{ 163d39602bdSespie require File::Path; 164d39602bdSespie 165d39602bdSespie my $dir = $pkg->{dir}; 166d39602bdSespie if (defined $dir) { 167caf99896Sespie OpenBSD::Error->rmtree($dir); 168c088c583Sespie OpenBSD::Temp->reclaim($dir); 169d39602bdSespie delete $pkg->{dir}; 170d39602bdSespie } 171d39602bdSespie} 172d39602bdSespie 173d39602bdSespie# by default, all objects may exist 174039cbdaaSespie# $repo->may_exist($name) 175039cbdaaSespiesub may_exist($, $) 176d39602bdSespie{ 177d39602bdSespie return 1; 178d39602bdSespie} 179d39602bdSespie 180d39602bdSespie# by default, we don't track opened files for this key 181d39602bdSespie 182039cbdaaSespiesub opened($) 183d39602bdSespie{ 184d39602bdSespie undef; 185d39602bdSespie} 186d39602bdSespie 187d39602bdSespie# hint: 0 premature close, 1 real error. undef, normal ! 188d39602bdSespie 189039cbdaaSespiesub close($self, $object, $hint = undef) 190d39602bdSespie{ 191d39602bdSespie close($object->{fh}) if defined $object->{fh}; 192d715fc48Sespie if (defined $object->{pid2}) { 193d715fc48Sespie local $SIG{ALRM} = sub { 194d715fc48Sespie kill HUP => $object->{pid2}; 195d715fc48Sespie }; 196d715fc48Sespie alarm(30); 197d715fc48Sespie waitpid($object->{pid2}, 0); 198d715fc48Sespie alarm(0); 199d715fc48Sespie } 200bfe8fdcbSespie $self->parse_problems($object->{errors}, $hint, $object) 201d39602bdSespie if defined $object->{errors}; 202d39602bdSespie undef $object->{errors}; 203cc24e6f2Sespie $object->deref; 204d39602bdSespie} 205d39602bdSespie 206039cbdaaSespiesub make_room($self) 207d39602bdSespie{ 208d39602bdSespie # kill old files if too many 209f04afe28Sespie my $already = $self->opened; 210d39602bdSespie if (defined $already) { 211d39602bdSespie # gc old objects 212f04afe28Sespie if (@$already >= $self->maxcount) { 213d39602bdSespie @$already = grep { defined $_->{fh} } @$already; 214d39602bdSespie } 215f04afe28Sespie while (@$already >= $self->maxcount) { 216d39602bdSespie my $o = shift @$already; 217d39602bdSespie $self->close_now($o); 218d39602bdSespie } 219d39602bdSespie } 220d39602bdSespie return $already; 221d39602bdSespie} 222d39602bdSespie 223d39602bdSespie# open method that tracks opened files per-host. 224039cbdaaSespiesub open($self, $object) 225d39602bdSespie{ 226b9b2024bSespie return unless $self->may_exist($object->{name}); 227d39602bdSespie 228d39602bdSespie # kill old files if too many 229f04afe28Sespie my $already = $self->make_room; 230544be565Sespie local $SIG{'PIPE'} = 'DEFAULT'; 231d39602bdSespie my $fh = $self->open_pipe($object); 232d39602bdSespie if (!defined $fh) { 233b9b2024bSespie return; 234d39602bdSespie } 235d39602bdSespie $object->{fh} = $fh; 236d39602bdSespie if (defined $already) { 237d39602bdSespie push @$already, $object; 238d39602bdSespie } 239d39602bdSespie return $fh; 240d39602bdSespie} 241d39602bdSespie 242039cbdaaSespiesub find($repository, $name) 243d39602bdSespie{ 244db099e94Sespie my $self = $repository->new_location($name); 245d39602bdSespie 246e451b54dSespie if ($self->contents) { 247e451b54dSespie return $self; 248e451b54dSespie } 24984f70789Sespie return undef; 250d39602bdSespie} 251d39602bdSespie 252039cbdaaSespiesub grabPlist($repository, $name, @code) 253d39602bdSespie{ 254db099e94Sespie my $self = $repository->new_location($name); 255d39602bdSespie 25616b4afceSespie return $self->grabPlist(@code); 257d39602bdSespie} 258d39602bdSespie 259039cbdaaSespiesub parse_problems($self, $filename, $hint = 0, $object = undef) 260d39602bdSespie{ 261012b1aa4Sespie CORE::open(my $fh, '<', $filename) or return; 262012b1aa4Sespie my $baseurl = $self->url; 263ea4bc424Sespie my $objecturl = $baseurl; 264ea4bc424Sespie if (defined $object) { 265ea4bc424Sespie $objecturl = $object->url; 266ea4bc424Sespie $object->{error_reported} = 1; 267ea4bc424Sespie } 268012b1aa4Sespie my $notyet = 1; 269cde78867Sespie my $broken = 0; 270cde78867Sespie my $signify_error = 0; 2710650cc2aSespie $self->{last_error} = 0; 27234fd0ff1Sespie $self->{count}++; 273012b1aa4Sespie while(<$fh>) { 27464dc887cSespie if (m/^Redirected to (https?)\:\/\/([^\/]*)/) { 27564dc887cSespie my ($scheme, $newhost) = ($1, $2); 276380f0cc2Sespie $self->{state}->print("#1", $_); 27764dc887cSespie next if $scheme ne $self->urlscheme; 278e4539078Sespie # XXX try logging but syslog doesn't exist for Info 279e4539078Sespie eval { 280cdd531aaSespie $self->{state}->syslog("Redirected from #1 to #2", 281cdd531aaSespie $self->{host}, $newhost); 282e4539078Sespie }; 283cdd531aaSespie $self->{host} = $newhost; 28434fd0ff1Sespie $self->setup_session; 285960e5aacSespie $baseurl = $self->url; 286960e5aacSespie next; 287960e5aacSespie } 288012b1aa4Sespie next if m/^(?:200|220|221|226|229|230|227|250|331|500|150)[\s\-]/o; 289012b1aa4Sespie next if m/^EPSV command not understood/o; 290012b1aa4Sespie next if m/^Trying [\da-f\.\:]+\.\.\./o; 291ea4bc424Sespie # XXX make_room may call close_now on objects of the right 292ea4bc424Sespie # type, but from a different repository 293ea4bc424Sespie next if m/^Requesting (?:\Q$baseurl\E|\Q$objecturl\E)/; 294012b1aa4Sespie next if m/^Remote system type is\s+/o; 295012b1aa4Sespie next if m/^Connected to\s+/o; 296012b1aa4Sespie next if m/^remote\:\s+/o; 297012b1aa4Sespie next if m/^Using binary mode to transfer files/o; 298012b1aa4Sespie next if m/^Retrieving\s+/o; 299012b1aa4Sespie next if m/^Success?fully retrieved file/o; 300012b1aa4Sespie next if m/^\d+\s+bytes\s+received\s+in/o; 301012b1aa4Sespie next if m/^ftp: connect to address.*: No route to host/o; 302cde78867Sespie if (m/^ftp: Writing -: Broken pipe/o) { 303cde78867Sespie $broken = 1; 304cde78867Sespie next; 305cde78867Sespie } 30634fd0ff1Sespie if (m/^tls session resumed\: (\w+)/) { 3070a7c59edSespie next; # disable the detailed handling for now 30834fd0ff1Sespie my $s = $1; 30934fd0ff1Sespie if ($s eq 'yes') { 31034fd0ff1Sespie # everything okay for now 31134fd0ff1Sespie $self->{said_slow} = 0; 31234fd0ff1Sespie next; 31334fd0ff1Sespie } 31434fd0ff1Sespie next if $self->{count} < 2 || $self->{said_slow}; 31534fd0ff1Sespie $self->{said_slow} = 1; 316a73ba4b2Sjob $self->{state}->say("#1: no session resumption supported by ftp(1) on connection ##2", $self->{host}, $self->{count}); 317a73ba4b2Sjob $self->{state}->say("#1: https will be slow", $self->{host}); 31834fd0ff1Sespie next; 31934fd0ff1Sespie } 320d1e8d136Sespie # http error 32178895fa0Sderaadt if (m/^ftp: Error retrieving .*: 404/o) { 322e84fb231Sespie $self->{lasterror} = 404; 323d1e8d136Sespie if (!defined $object) { 324d1e8d136Sespie $self->{no_such_dir} = 1; 325d1e8d136Sespie next; 326d1e8d136Sespie } 3270650cc2aSespie # ignore errors for stable packages 3280650cc2aSespie next if $self->can_be_empty; 329d1e8d136Sespie } 330012b1aa4Sespie 331012b1aa4Sespie if (defined $hint && $hint == 0) { 332012b1aa4Sespie next if m/^ftp: -: short write/o; 333012b1aa4Sespie next if m/^ftp: local: -: Broken pipe/o; 334012b1aa4Sespie next if m/^421\s+/o; 335012b1aa4Sespie } 3360650cc2aSespie # not retrieving the file => always the same message 3370650cc2aSespie # so it's superfluous 3380650cc2aSespie next if m/^signify:/ && $self->{lasterror}; 339012b1aa4Sespie if ($notyet) { 340ea4bc424Sespie $self->{state}->errprint("#1: ", $objecturl); 341012b1aa4Sespie $notyet = 0; 342012b1aa4Sespie } 343c457619cSespie if (m/^signify:/) { 344c457619cSespie $signify_error = 1; 34584f70789Sespie s/.*unsigned .*archive.*/unsigned package/; 346c457619cSespie } 347012b1aa4Sespie if (m/^421\s+/o || 348012b1aa4Sespie m/^ftp: connect: Connection timed out/o || 349012b1aa4Sespie m/^ftp: Can't connect or login to host/o) { 350012b1aa4Sespie $self->{lasterror} = 421; 351012b1aa4Sespie } 352012b1aa4Sespie if (m/^550\s+/o) { 353012b1aa4Sespie $self->{lasterror} = 550; 354012b1aa4Sespie } 355012b1aa4Sespie $self->{state}->errprint("#1", $_); 356012b1aa4Sespie } 357cde78867Sespie if ($broken) { 358cde78867Sespie unless ($signify_error || defined $hint && $hint == 0) { 359cde78867Sespie $self->{state}->errprint('#1', "ftp: Broken pipe"); 360cde78867Sespie } 361cde78867Sespie } 362012b1aa4Sespie CORE::close($fh); 363c088c583Sespie OpenBSD::Temp->reclaim($filename); 364d39602bdSespie unlink $filename; 365d39602bdSespie} 366d39602bdSespie 367039cbdaaSespiesub cleanup($) 3680b82e7efSespie{ 3690b82e7efSespie # nothing to do 3700b82e7efSespie} 3710b82e7efSespie 372039cbdaaSespiesub relative_url($self, $name = undef) 37326bcda07Sespie{ 37426bcda07Sespie if (defined $name) { 37518cd0704Sespie return $self->baseurl.$name.".tgz"; 37626bcda07Sespie } else { 37718cd0704Sespie return $self->baseurl; 37826bcda07Sespie } 37926bcda07Sespie} 38026bcda07Sespie 381039cbdaaSespiesub add_to_list($self, $list, $filename) 382b0a198ceSespie{ 383b0a198ceSespie if ($filename =~ m/^(.*\-\d.*)\.tgz$/o) { 384b0a198ceSespie push(@$list, $1); 385b0a198ceSespie } 386b0a198ceSespie} 387b0a198ceSespie 388039cbdaaSespiesub did_it_fork($self, $pid) 389af238ddbSespie{ 390af238ddbSespie if (!defined $pid) { 391af238ddbSespie $self->{state}->fatal("Cannot fork: #1", $!); 392af238ddbSespie } 39361e101f9Sespie if ($pid == 0) { 3945108b370Sespie delete $SIG{'WINCH'}; 3955108b370Sespie delete $SIG{'CONT'}; 3965108b370Sespie delete $SIG{'INFO'}; 39761e101f9Sespie } 398af238ddbSespie} 399af238ddbSespie 400039cbdaaSespiesub uncompress($self, $object, @p) 401af238ddbSespie{ 402119c5febSespie require IO::Uncompress::Gunzip; 403039cbdaaSespie my $fh = IO::Uncompress::Gunzip->new(@p, MultiStream => 1); 404012b1aa4Sespie my $result = ""; 405012b1aa4Sespie if ($object->{is_signed}) { 406012b1aa4Sespie my $h = $fh->getHeaderInfo; 407012b1aa4Sespie if ($h) { 408012b1aa4Sespie for my $line (split /\n/, $h->{Comment}) { 409012b1aa4Sespie if ($line =~ m/^key=.*\/(.*)\.sec$/) { 41027828011Sespie $object->{signer} = $1; 411012b1aa4Sespie } elsif ($line =~ m/^date=(.*)$/) { 41227828011Sespie $object->{signdate} = $1; 413e2815186Sespie } 414012b1aa4Sespie } 415643a5bfcSespie } else { 416643a5bfcSespie $fh->close; 417643a5bfcSespie return undef; 418012b1aa4Sespie } 419012b1aa4Sespie } 420e2815186Sespie return $fh; 421119c5febSespie} 422119c5febSespie 423*57050d8cSespiesub keytype($self) 424*57050d8cSespie{ 425*57050d8cSespie if ($self->{state}->defines("FW_UPDATE")) { 426*57050d8cSespie return "fw"; 427*57050d8cSespie } else { 428*57050d8cSespie return "pkg"; 429*57050d8cSespie } 430*57050d8cSespie} 431*57050d8cSespie 432039cbdaaSespiesub signify_pipe($self, $object, @p) 433119c5febSespie{ 434012b1aa4Sespie CORE::open STDERR, ">>", $object->{errors}; 435119c5febSespie exec {OpenBSD::Paths->signify} 436119c5febSespie ("signify", 437119c5febSespie "-zV", 438*57050d8cSespie "-t", $self->keytype, 439039cbdaaSespie @p) 440d23ebfb1Sespie or $self->{state}->fatal("Can't run #1: #2", 441d23ebfb1Sespie OpenBSD::Paths->signify, $!); 442119c5febSespie} 443119c5febSespie 444039cbdaaSespiesub check_signed($self, $object) 445119c5febSespie{ 4461fdee857Sespie if ($object->{repository}{trusted}) { 4471fdee857Sespie return 0; 4481fdee857Sespie } 449c21f7911Sespie if ($self->{state}{signature_style} eq 'new') { 450dbd12574Sespie $object->{is_signed} = 1; 451dbd12574Sespie return 1; 452dbd12574Sespie } else { 453119c5febSespie return 0; 454dbd12574Sespie } 455af238ddbSespie} 456af238ddbSespie 457d39602bdSespiepackage OpenBSD::PackageRepository::Local; 458d39602bdSespieour @ISA=qw(OpenBSD::PackageRepository); 459105697f6Sbernduse OpenBSD::Error; 460d39602bdSespie 461039cbdaaSespiesub is_local_file($) 4625de8099bSespie{ 4635de8099bSespie return 1; 4645de8099bSespie} 4655de8099bSespie 466039cbdaaSespiesub urlscheme($) 467c932ac86Sespie{ 468c932ac86Sespie return 'file'; 469c932ac86Sespie} 470c932ac86Sespie 4713eadd976Sespiemy $pkg_db; 4723eadd976Sespie 473039cbdaaSespiesub pkg_db($) 4743eadd976Sespie{ 4753eadd976Sespie if (!defined $pkg_db) { 4763eadd976Sespie use OpenBSD::Paths; 4773eadd976Sespie $pkg_db = $ENV{"PKG_DBDIR"} || OpenBSD::Paths->pkgdb; 4783eadd976Sespie } 4793eadd976Sespie return $pkg_db; 4803eadd976Sespie} 4813eadd976Sespie 482039cbdaaSespiesub parse_fullurl($class, $r, $state) 48318cd0704Sespie{ 4843eadd976Sespie my $ok = $class->strip_urlscheme($r); 4855732efc0Sespie my $o = $class->parse_url($r, $state); 4865732efc0Sespie if (!$ok && $o->{path} eq $class->pkg_db."/") { 4875732efc0Sespie return $class->installed->new(0, $state); 4883eadd976Sespie } else { 489b1b61206Sespie if ($o->{path} eq './') { 490b1b61206Sespie $o->can_be_empty; 491b1b61206Sespie } 492165c4191Sespie return $class->unique($o); 4933eadd976Sespie } 49418cd0704Sespie} 49518cd0704Sespie 496fcf92ed2Sespie# wrapper around copy, that sometimes does not copy 497039cbdaaSespiesub may_copy($self, $object, $destdir) 498fcf92ed2Sespie{ 499fcf92ed2Sespie my $src = $self->relative_url($object->{name}); 500fcf92ed2Sespie require File::Spec; 501fcf92ed2Sespie my (undef, undef, $base) = File::Spec->splitpath($src); 502fcf92ed2Sespie my $dest = File::Spec->catfile($destdir, $base); 503fcf92ed2Sespie if (File::Spec->canonpath($dest) eq File::Spec->canonpath($src)) { 504fcf92ed2Sespie return; 505fcf92ed2Sespie } 506fcf92ed2Sespie if (-f $dest) { 507fcf92ed2Sespie my ($ddev, $dino) = (stat $dest)[0,1]; 508fcf92ed2Sespie my ($sdev, $sino) = (stat $src)[0, 1]; 509fcf92ed2Sespie if ($ddev == $sdev and $sino == $dino) { 510fcf92ed2Sespie return; 511fcf92ed2Sespie } 512fcf92ed2Sespie } 51361e101f9Sespie $self->{state}->copy_file($src, $destdir); 514fcf92ed2Sespie} 515fcf92ed2Sespie 516039cbdaaSespiesub open_pipe($self, $object) 517d39602bdSespie{ 518d71f6f90Sespie if (defined $self->{state}->cache_directory) { 519d71f6f90Sespie $self->may_copy($object, $self->{state}->cache_directory); 520105697f6Sbernd } 521e2815186Sespie my $name = $self->relative_url($object->{name}); 522e2815186Sespie if ($self->check_signed($object)) { 523012b1aa4Sespie $self->make_error_file($object); 524119c5febSespie my $pid = open(my $fh, "-|"); 525119c5febSespie $self->did_it_fork($pid); 526119c5febSespie if ($pid) { 527119c5febSespie $object->{pid} = $pid; 528e2815186Sespie return $self->uncompress($object, $fh); 529119c5febSespie } else { 530012b1aa4Sespie $self->signify_pipe($object, "-x", $name); 531119c5febSespie } 532119c5febSespie } else { 533e2815186Sespie return $self->uncompress($object, $name); 534d39602bdSespie } 535119c5febSespie} 536d39602bdSespie 537039cbdaaSespiesub may_exist($self, $name) 538d39602bdSespie{ 53926bcda07Sespie return -r $self->relative_url($name); 540d39602bdSespie} 541d39602bdSespie 542d1963656Sespiemy $local = []; 543d1963656Sespie 544039cbdaaSespiesub opened($) 545d1963656Sespie{ 546d1963656Sespie return $local; 547d1963656Sespie} 548d1963656Sespie 549039cbdaaSespiesub maxcount($) 550d1963656Sespie{ 551d1963656Sespie return 3; 552d1963656Sespie} 553d1963656Sespie 554039cbdaaSespiesub list($self) 555d39602bdSespie{ 556d39602bdSespie my $l = []; 55718cd0704Sespie my $dname = $self->baseurl; 558d39602bdSespie opendir(my $dir, $dname) or return $l; 559d39602bdSespie while (my $e = readdir $dir) { 560d39602bdSespie next unless -f "$dname/$e"; 561b0a198ceSespie $self->add_to_list($l, $e); 562d39602bdSespie } 563d39602bdSespie close($dir); 564d39602bdSespie return $l; 565d39602bdSespie} 566d39602bdSespie 567d39602bdSespiepackage OpenBSD::PackageRepository::Distant; 568d39602bdSespieour @ISA=qw(OpenBSD::PackageRepository); 569d39602bdSespie 570039cbdaaSespiesub baseurl($self) 5715c3fa476Sespie{ 572bd6d27e7Ssthen return "//$self->{host}$self->{path}"; 5735c3fa476Sespie} 5745c3fa476Sespie 575039cbdaaSespiesub setup_session($) 57634fd0ff1Sespie{ 57734fd0ff1Sespie # nothing to do except for https 57834fd0ff1Sespie} 57934fd0ff1Sespie 580039cbdaaSespiesub parse_url($class, $r, $state) 5815c3fa476Sespie{ 5825c3fa476Sespie # same heuristics as ftp(1): 5835c3fa476Sespie # find host part, rest is parsed as a local url 5843eadd976Sespie if (my ($host, $path) = $$r =~ m/^\/\/(.*?)(\/.*)$/) { 5853eadd976Sespie 5863eadd976Sespie $$r = $path; 5875732efc0Sespie my $o = $class->SUPER::parse_url($r, $state); 5883eadd976Sespie $o->{host} = $host; 5896d41fd6eSespie if (defined $o->{release}) { 5906d41fd6eSespie $o->can_be_empty; 5916d41fd6eSespie $$r = $class->urlscheme."://$o->{host}$o->{release}:$$r"; 5926d41fd6eSespie } 59334fd0ff1Sespie $o->setup_session; 5943eadd976Sespie return $o; 5955c3fa476Sespie } else { 5965c3fa476Sespie return undef; 5975c3fa476Sespie } 5985c3fa476Sespie} 5995c3fa476Sespie 600d39602bdSespiemy $buffsize = 2 * 1024 * 1024; 601d39602bdSespie 602039cbdaaSespiesub pkg_copy($self, $in, $object) 603d39602bdSespie{ 604f87ffdeeSespie my $name = $object->{name}; 605f87ffdeeSespie my $dir = $object->{cache_dir}; 606d39602bdSespie 607c7833d9aSespie my ($copy, $filename) = OpenBSD::Temp::permanent_file($dir, $name) or 608c7833d9aSespie $self->{state}->fatal(OpenBSD::Temp->last_error); 609efc8ef94Sespie chmod((0666 & ~umask), $filename); 6109afcdde6Sbernd $object->{tempname} = $filename; 611d39602bdSespie my $handler = sub { 612d39602bdSespie my ($sig) = @_; 613d39602bdSespie unlink $filename; 614822cc53aSespie close($in); 615d39602bdSespie $SIG{$sig} = 'DEFAULT'; 616d39602bdSespie kill $sig, $$; 617d39602bdSespie }; 618d39602bdSespie 619d39602bdSespie my $nonempty = 0; 620e2e85796Sespie my $error = 0; 621d39602bdSespie { 622d39602bdSespie 623d39602bdSespie local $SIG{'PIPE'} = $handler; 624d39602bdSespie local $SIG{'INT'} = $handler; 625d39602bdSespie local $SIG{'HUP'} = $handler; 626d39602bdSespie local $SIG{'QUIT'} = $handler; 627d39602bdSespie local $SIG{'KILL'} = $handler; 628d39602bdSespie local $SIG{'TERM'} = $handler; 629d39602bdSespie 630d39602bdSespie my ($buffer, $n); 631d39602bdSespie # copy stuff over 632d39602bdSespie do { 633d39602bdSespie $n = sysread($in, $buffer, $buffsize); 634d39602bdSespie if (!defined $n) { 635af238ddbSespie $self->{state}->fatal("Error reading: #1", $!); 636d39602bdSespie } 637d39602bdSespie if ($n > 0) { 638d39602bdSespie $nonempty = 1; 639d39602bdSespie } 640e2e85796Sespie if (!$error) { 641e2e85796Sespie my $r = syswrite $copy, $buffer; 642e2e85796Sespie if (!defined $r || $r < $n) { 643e2e85796Sespie $error = 1; 644e2e85796Sespie } 645e2e85796Sespie } 646d39602bdSespie syswrite STDOUT, $buffer; 647d39602bdSespie } while ($n != 0); 648d39602bdSespie close($copy); 649d39602bdSespie } 650d39602bdSespie 651e2e85796Sespie if ($nonempty && !$error) { 652a02898f7Sespie rename $filename, "$dir/$name.tgz"; 653d39602bdSespie } else { 654d39602bdSespie unlink $filename; 655d39602bdSespie } 656822cc53aSespie close($in); 657d39602bdSespie} 658d39602bdSespie 659039cbdaaSespiesub open_pipe($self, $object) 660012b1aa4Sespie{ 661012b1aa4Sespie $self->make_error_file($object); 662d71f6f90Sespie my $d = $self->{state}->cache_directory; 6633417e276Sespie if (defined $d) { 6643417e276Sespie $object->{cache_dir} = $d; 6653417e276Sespie if (! -d -w $d) { 6663417e276Sespie $self->{state}->fatal("bad PKG_CACHE directory #1", $d); 6673417e276Sespie } 6683417e276Sespie $object->{cache_dir} = $d; 6693417e276Sespie } 670f87ffdeeSespie $object->{parent} = $$; 671822cc53aSespie 672119c5febSespie my ($rdfh, $wrfh); 673119c5febSespie 674119c5febSespie pipe($rdfh, $wrfh); 675119c5febSespie my $pid2 = fork(); 676af238ddbSespie $self->did_it_fork($pid2); 677822cc53aSespie if ($pid2) { 678822cc53aSespie $object->{pid2} = $pid2; 679119c5febSespie close($wrfh); 680d39602bdSespie } else { 681012b1aa4Sespie open STDERR, '>>', $object->{errors}; 682119c5febSespie open(STDOUT, '>&', $wrfh); 683119c5febSespie close($rdfh); 684119c5febSespie close($wrfh); 6853417e276Sespie if (defined $d) { 686d39602bdSespie my $pid3 = open(my $in, "-|"); 687af238ddbSespie $self->did_it_fork($pid3); 688d39602bdSespie if ($pid3) { 6897b25c30aSespie $self->dont_cleanup; 690f87ffdeeSespie $self->pkg_copy($in, $object); 691822cc53aSespie } else { 692822cc53aSespie $self->grab_object($object); 693822cc53aSespie } 694822cc53aSespie } else { 695822cc53aSespie $self->grab_object($object); 696822cc53aSespie } 697d39602bdSespie exit(0); 698d39602bdSespie } 699119c5febSespie 700e2815186Sespie if ($self->check_signed($object)) { 701119c5febSespie my $pid = open(my $fh, "-|"); 702119c5febSespie $self->did_it_fork($pid); 703119c5febSespie if ($pid) { 704119c5febSespie $object->{pid} = $pid; 705119c5febSespie close($rdfh); 706119c5febSespie } else { 707119c5febSespie open(STDIN, '<&', $rdfh) or 708119c5febSespie $self->{state}->fatal("Bad dup: #1", $!); 709119c5febSespie close($rdfh); 710012b1aa4Sespie $self->signify_pipe($object); 711119c5febSespie } 712119c5febSespie 713e2815186Sespie return $self->uncompress($object, $fh); 714119c5febSespie } else { 715e2815186Sespie return $self->uncompress($object, $rdfh); 716d39602bdSespie } 717119c5febSespie} 718d39602bdSespie 719039cbdaaSespiesub finish_and_close($self, $object) 720d39602bdSespie{ 721d39602bdSespie if (defined $object->{cache_dir}) { 7227b75b2c6Sespie while (defined $object->next) { 723d39602bdSespie } 724d39602bdSespie } 725d39602bdSespie $self->SUPER::finish_and_close($object); 726d39602bdSespie} 727d39602bdSespie 728d39602bdSespiepackage OpenBSD::PackageRepository::HTTPorFTP; 729d39602bdSespieour @ISA=qw(OpenBSD::PackageRepository::Distant); 730d39602bdSespie 731d39602bdSespieour %distant = (); 732d39602bdSespie 7338d7a4daeSespiemy ($fetch_uid, $fetch_gid, $fetch_user); 7348d7a4daeSespie 735039cbdaaSespiesub fill_up_fetch_data($self) 7368d7a4daeSespie{ 7378d7a4daeSespie if ($< == 0) { 7388d7a4daeSespie $fetch_user = '_pkgfetch'; 7398d7a4daeSespie unless ((undef, undef, $fetch_uid, $fetch_gid) = 7408d7a4daeSespie getpwnam($fetch_user)) { 7418d7a4daeSespie $self->{state}->fatal( 7428d7a4daeSespie "Couldn't change identity: can't find #1 user", 7438d7a4daeSespie $fetch_user); 7448d7a4daeSespie } 7458d7a4daeSespie } else { 7468d7a4daeSespie ($fetch_user) = getpwuid($<); 7478d7a4daeSespie } 7488d7a4daeSespie} 7498d7a4daeSespie 750039cbdaaSespiesub fetch_id($self) 7518d7a4daeSespie{ 7528d7a4daeSespie if (!defined $fetch_user) { 7538d7a4daeSespie $self->fill_up_fetch_data; 7548d7a4daeSespie } 7558d7a4daeSespie return ($fetch_uid, $fetch_gid, $fetch_user); 7568d7a4daeSespie} 7578d7a4daeSespie 758039cbdaaSespiesub ftp_cmd($self) 7594023c398Sespie{ 7604023c398Sespie return OpenBSD::Paths->ftp; 7614023c398Sespie} 7624023c398Sespie 763039cbdaaSespiesub drop_privileges_and_setup_env($self) 764ee274177Sespie{ 7658d7a4daeSespie my ($uid, $gid, $user) = $self->fetch_id; 7668d7a4daeSespie if (defined $uid) { 7678d7a4daeSespie # we happen right before exec, so change id permanently 768ee274177Sespie $( = $gid; 769ee274177Sespie $) = "$gid $gid"; 770ee274177Sespie $< = $uid; 771ee274177Sespie $> = $uid; 772933326beSespie } 773b133ba27Sespie # create sanitized env for ftp 774b133ba27Sespie my %newenv = ( 775b133ba27Sespie HOME => '/var/empty', 7767ad2148eSespie USER => $user, 7777ad2148eSespie LOGNAME => $user, 778b133ba27Sespie SHELL => '/bin/sh', 779b133ba27Sespie LC_ALL => 'C', # especially, laundry error messages 780b133ba27Sespie PATH => '/bin:/usr/bin' 781b133ba27Sespie ); 782b133ba27Sespie 783b133ba27Sespie # copy selected stuff; 784b133ba27Sespie for my $k (qw( 785b133ba27Sespie TERM 786b133ba27Sespie FTPMODE 787b133ba27Sespie FTPSERVER 788b133ba27Sespie FTPSERVERPORT 789b133ba27Sespie ftp_proxy 790b133ba27Sespie http_proxy 791b133ba27Sespie http_cookies 792b133ba27Sespie ALL_PROXY 793b133ba27Sespie FTP_PROXY 794b133ba27Sespie HTTPS_PROXY 795b133ba27Sespie HTTP_PROXY 796b133ba27Sespie NO_PROXY)) { 797b133ba27Sespie if (exists $ENV{$k}) { 798b133ba27Sespie $newenv{$k} = $ENV{$k}; 799b133ba27Sespie } 800b133ba27Sespie } 801b133ba27Sespie # don't forget to swap! 802b133ba27Sespie %ENV = %newenv; 803ee274177Sespie} 804ee274177Sespie 805d39602bdSespie 806039cbdaaSespiesub grab_object($self, $object) 807d39602bdSespie{ 8084023c398Sespie my ($ftp, @extra) = split(/\s+/, $self->ftp_cmd); 809ba846347Sespie $self->drop_privileges_and_setup_env; 8106b30713fSbernd exec {$ftp} 8116b30713fSbernd $ftp, 81297c2d361Sespie @extra, 813d39602bdSespie "-o", 81426bcda07Sespie "-", $self->url($object->{name}) 8154023c398Sespie or $self->{state}->fatal("Can't run #1: #2", $self->ftp_cmd, $!); 816d39602bdSespie} 817d39602bdSespie 818039cbdaaSespiesub open_read_ftp($self, $cmd, $errors = undef) 819ee274177Sespie{ 820ee274177Sespie my $child_pid = open(my $fh, '-|'); 821ee274177Sespie if ($child_pid) { 822ee274177Sespie $self->{pipe_pid} = $child_pid; 823ee274177Sespie return $fh; 824ee274177Sespie } else { 825012b1aa4Sespie open STDERR, '>>', $errors if defined $errors; 826ee274177Sespie 827ba846347Sespie $self->drop_privileges_and_setup_env; 828ba846347Sespie exec($cmd) 829d23ebfb1Sespie or $self->{state}->fatal("Can't run #1: #2", $cmd, $!); 830ee274177Sespie } 831ee274177Sespie} 832ee274177Sespie 833039cbdaaSespiesub close_read_ftp($self, $fh) 834ee274177Sespie{ 835ee274177Sespie close($fh); 836ee274177Sespie waitpid $self->{pipe_pid}, 0; 837ee274177Sespie} 838ee274177Sespie 839039cbdaaSespiesub maxcount($) 840d39602bdSespie{ 841d39602bdSespie return 1; 842d39602bdSespie} 843d39602bdSespie 844039cbdaaSespiesub opened($self) 845d39602bdSespie{ 84618cd0704Sespie my $k = $self->{host}; 847d39602bdSespie if (!defined $distant{$k}) { 848d39602bdSespie $distant{$k} = []; 849d39602bdSespie } 850d39602bdSespie return $distant{$k}; 851d39602bdSespie} 852d39602bdSespie 853039cbdaaSespiesub should_have($self, $pkgname) 8542606b85fSespie{ 8552606b85fSespie if (defined $self->{lasterror} && $self->{lasterror} == 421) { 8562606b85fSespie return (defined $self->{list}) && 8572606b85fSespie grep { $_ eq $pkgname } @{$self->{list}}; 8582606b85fSespie } else { 8592606b85fSespie return 0; 8602606b85fSespie } 8612606b85fSespie} 8622606b85fSespie 863039cbdaaSespiesub try_until_success($self, $pkgname, $code) 8642606b85fSespie{ 8651d87b35aSckuethe for (my $retry = 5; $retry <= 160; $retry *= 2) { 8662606b85fSespie undef $self->{lasterror}; 867039cbdaaSespie my $o = &$code(); 8682606b85fSespie if (defined $o) { 8692606b85fSespie return $o; 8702606b85fSespie } 87106966842Sespie if (defined $self->{lasterror} && 87206966842Sespie ($self->{lasterror} == 550 || $self->{lasterror} == 404)) { 873e04c1e66Sespie last; 874e04c1e66Sespie } 8752606b85fSespie if ($self->should_have($pkgname)) { 876af238ddbSespie $self->errsay("Temporary error, sleeping #1 seconds", 877af238ddbSespie $retry); 8782606b85fSespie sleep($retry); 8792606b85fSespie } 8802606b85fSespie } 8812606b85fSespie return undef; 8822606b85fSespie} 8832606b85fSespie 884039cbdaaSespiesub find($self, $pkgname, @extra) 8852606b85fSespie{ 8862606b85fSespie return $self->try_until_success($pkgname, 887039cbdaaSespie sub() { 8882606b85fSespie return $self->SUPER::find($pkgname, @extra); }); 8892606b85fSespie 8902606b85fSespie} 8912606b85fSespie 892039cbdaaSespiesub grabPlist($self, $pkgname, @extra) 8932606b85fSespie{ 8942606b85fSespie return $self->try_until_success($pkgname, 895039cbdaaSespie sub() { 8962606b85fSespie return $self->SUPER::grabPlist($pkgname, @extra); }); 8972606b85fSespie} 8982606b85fSespie 899039cbdaaSespiesub list($self) 900d39602bdSespie{ 901d39602bdSespie if (!defined $self->{list}) { 902f04afe28Sespie $self->make_room; 903e451b54dSespie my $error = OpenBSD::Temp->file; 9046f76be05Sespie if (!defined $error) { 90517ddcb71Sespie $self->{state}->fatal(OpenBSD::Temp->last_error); 9066f76be05Sespie } 907a856695bSespie $self->{list} = $self->obtain_list($error); 908a856695bSespie $self->parse_problems($error); 909a856695bSespie } 910a856695bSespie return $self->{list}; 911a856695bSespie} 912a856695bSespie 913039cbdaaSespiesub get_http_list($self, $error) 914a856695bSespie{ 91526bcda07Sespie my $fullname = $self->url; 916a856695bSespie my $l = []; 9174023c398Sespie my $fh = $self->open_read_ftp($self->ftp_cmd." -o - $fullname", 918ee274177Sespie $error) or return; 919d39602bdSespie while(<$fh>) { 920d39602bdSespie chomp; 921d63625b9Ssthen for my $pkg (m/\<A[^>]*\s+HREF=\"(.*?\.tgz)\"/gio) { 9223ee72fdfSsturm $pkg = $1 if $pkg =~ m|^.*/(.*)$|; 9234261c94cSsthen # decode uri-encoding; from URI::Escape 9244261c94cSsthen $pkg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; 925b0a198ceSespie $self->add_to_list($l, $pkg); 926d39602bdSespie } 927d39602bdSespie } 928ba846347Sespie $self->close_read_ftp($fh); 929a856695bSespie return $l; 930d39602bdSespie} 931a856695bSespie 932a856695bSespiepackage OpenBSD::PackageRepository::HTTP; 933a856695bSespieour @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); 934a856695bSespie 935039cbdaaSespiesub urlscheme($) 936a856695bSespie{ 937a856695bSespie return 'http'; 938a856695bSespie} 939a856695bSespie 940039cbdaaSespiesub obtain_list($self, $error) 941a856695bSespie{ 942a856695bSespie return $self->get_http_list($error); 943d39602bdSespie} 944d39602bdSespie 9454c90429fSespiepackage OpenBSD::PackageRepository::HTTPS; 9464c90429fSespieour @ISA=qw(OpenBSD::PackageRepository::HTTP); 9474c90429fSespie 948039cbdaaSespiesub urlscheme($) 9494c90429fSespie{ 9504c90429fSespie return 'https'; 9514c90429fSespie} 9524c90429fSespie 953039cbdaaSespiesub setup_session($self) 95434fd0ff1Sespie{ 95534fd0ff1Sespie require OpenBSD::Temp; 95634fd0ff1Sespie $self->{count} = 0; 957cf08ccf9Sespie local $>; 9584023c398Sespie my ($uid, $gid, $user) = $self->fetch_id; 9594023c398Sespie if (defined $uid) { 9604023c398Sespie $> = $uid; 9614023c398Sespie } 96234fd0ff1Sespie my ($fh, undef) = OpenBSD::Temp::fh_file("session", 963039cbdaaSespie sub($name) { unlink($name); }); 96434fd0ff1Sespie if (!defined $fh) { 96517ddcb71Sespie $self->{state}->fatal(OpenBSD::Temp->last_error); 96634fd0ff1Sespie } 96734fd0ff1Sespie $self->{fh} = $fh; # XXX store the full fh and not the fileno 96834fd0ff1Sespie} 96934fd0ff1Sespie 970039cbdaaSespiesub ftp_cmd($self) 97134fd0ff1Sespie{ 97234fd0ff1Sespie return $self->SUPER::ftp_cmd." -S session=/dev/fd/".fileno($self->{fh}); 97334fd0ff1Sespie} 97434fd0ff1Sespie 975039cbdaaSespiesub drop_privileges_and_setup_env($self) 97634fd0ff1Sespie{ 97734fd0ff1Sespie $self->SUPER::drop_privileges_and_setup_env; 97834fd0ff1Sespie # reset the CLOEXEC flag on that one 97934fd0ff1Sespie use Fcntl; 98034fd0ff1Sespie fcntl($self->{fh}, F_SETFD, 0); 98134fd0ff1Sespie} 9824023c398Sespie 983d39602bdSespiepackage OpenBSD::PackageRepository::FTP; 984d39602bdSespieour @ISA=qw(OpenBSD::PackageRepository::HTTPorFTP); 985d39602bdSespie 986039cbdaaSespiesub urlscheme($) 987c932ac86Sespie{ 988c932ac86Sespie return 'ftp'; 989c932ac86Sespie} 990d39602bdSespie 991039cbdaaSespiesub _list($self, $cmd, $error) 992d39602bdSespie{ 993a856695bSespie my $l =[]; 994ba846347Sespie my $fh = $self->open_read_ftp($cmd, $error) or return; 995a856695bSespie while(<$fh>) { 996a856695bSespie chomp; 99796393fa9Sespie next if m/^\d\d\d\s+\S/; 9983376cb88Sespie if (m/No such file or directory|Failed to change directory/i) { 9993376cb88Sespie $self->{no_such_dir} = 1; 10003376cb88Sespie } 1001b0a198ceSespie next unless m/^(?:\.\/)?(\S+\.tgz)\s*$/; 1002b0a198ceSespie $self->add_to_list($l, $1); 1003d39602bdSespie } 1004ba846347Sespie $self->close_read_ftp($fh); 1005a856695bSespie return $l; 1006a856695bSespie} 1007a856695bSespie 1008039cbdaaSespiesub get_ftp_list($self, $error) 1009a856695bSespie{ 1010a856695bSespie my $fullname = $self->url; 10114023c398Sespie return $self->_list("echo 'nlist'| ".$self->ftp_cmd." $fullname", 10124023c398Sespie $error); 1013a856695bSespie} 1014a856695bSespie 1015039cbdaaSespiesub obtain_list($self, $error) 1016a856695bSespie{ 1017dfa874a3Sespie if (defined $ENV{'ftp_proxy'} && $ENV{'ftp_proxy'} ne '') { 1018a856695bSespie return $self->get_http_list($error); 1019a856695bSespie } else { 1020a856695bSespie return $self->get_ftp_list($error); 1021a856695bSespie } 1022d39602bdSespie} 1023d39602bdSespie 1024d39602bdSespie1; 1025