xref: /openbsd-src/usr.sbin/pkg_add/OpenBSD/PackageRepository.pm (revision 57050d8c82febe21c61f0e78336f54f95465c332)
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