1# ex:ts=8 sw=4:
2# $OpenBSD: PackageLocation.pm,v 1.61 2023/06/13 09:07:17 espie Exp $
3#
4# Copyright (c) 2003-2007 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use v5.36;
19
20package OpenBSD::PackageLocation;
21
22use OpenBSD::PackageInfo;
23use OpenBSD::Temp;
24use OpenBSD::Error;
25use OpenBSD::Paths;
26
27sub new($class, $repository, $name)
28{
29	return bless {
30	    repository => $repository,
31	    name => $repository->canonicalize($name)
32	    }, $class;
33
34}
35
36sub decorate($self, $plist)
37{
38	$self->{repository}->decorate($plist, $self);
39}
40
41sub url($self)
42{
43	return $self->{repository}->url($self->name);
44}
45
46sub name($self)
47{
48	return $self->{name};
49}
50
51OpenBSD::Auto::cache(pkgname,
52    sub($self) {
53	return OpenBSD::PackageName->from_string($self->name);
54    });
55
56OpenBSD::Auto::cache(update_info,
57    sub($self) {
58	my $name = $self->name;
59	if ($name =~ /^quirks\-/) {
60		return $self->plist;
61	}
62	my $state = $self->{repository}{state};
63	my $info = $self->{repository}->get_cached_info($name);
64	if (defined $info &&
65	    !defined $state->defines("CACHING_RECHECK")) {
66		return $info;
67	}
68	my $result = $self->plist(\&OpenBSD::PackingList::UpdateInfoOnly);
69	if (defined $info) {
70		my $s1 = OpenBSD::Signature->from_plist($info);
71		my $s2 = OpenBSD::Signature->from_plist($result);
72		my $r = $s1->compare($s2, $state);
73		if (defined $r && $r == 0) {
74			$state->say("Cache comparison for #1 is okay", $name)
75			    if $state->defines("TEST_CACHING_VERBOSE");
76			return $result;
77		} else {
78			$state->fatal("Signatures differ cache=#1, regular=#2",
79			    $s1->string, $s2->string);
80		}
81	}
82	return $result;
83    });
84
85# make sure self is opened and move to the right location if need be.
86sub _opened($self)
87{
88	if (defined $self->{fh}) {
89		return $self;
90	}
91	my $fh = $self->{repository}->open($self);
92	if (!defined $fh) {
93		$self->{repository}->parse_problems($self->{errors}, undef,
94		    $self) if defined $self->{errors};
95		undef $self->{errors};
96		return;
97	}
98	require OpenBSD::Ustar;
99	my $archive = OpenBSD::Ustar->new($fh, $self->{repository}{state});
100	$archive->set_description($self->{repository}->url($self->{name}));
101	$self->{_archive} = $archive;
102	$self->_set_callback;
103
104	if (defined $self->{_current_name}) {
105		while (my $e = $self->{_archive}->next) {
106			if ($e->{name} eq $self->{_current_name}) {
107				$self->{_current} = $e;
108				return $self;
109			}
110		}
111	}
112	return $self;
113}
114
115sub _set_callback($self)
116{
117	if (defined $self->{callback} && defined $self->{_archive}) {
118		$self->{_archive}->set_callback($self->{callback});
119	}
120}
121
122sub find_contents($self)
123{
124	while (my $e = $self->next) {
125		if ($e->isFile && is_info_name($e->{name})) {
126			if ($e->{name} eq CONTENTS ) {
127				my $v = $e->contents;
128				return $v;
129			}
130		} else {
131			$self->unput;
132			last;
133		}
134	}
135}
136
137sub contents($self)
138{
139	if (!defined $self->{contents}) {
140		if (!$self->_opened) {
141			return;
142		}
143		$self->{contents} = $self->find_contents;
144	}
145
146	return $self->{contents};
147}
148
149sub grab_info($self)
150{
151	my $dir = $self->{dir} = OpenBSD::Temp->dir;
152	if (!defined $dir) {
153		$self->{repository}{state}->fatal(OpenBSD::Temp->last_error);
154	}
155
156	my $c = $self->contents;
157	if (!defined $c) {
158		return 0;
159	}
160
161	if (! -f $dir.CONTENTS) {
162		open my $fh, '>', $dir.CONTENTS or
163		    die "write to ",$dir.CONTENTS, ": ", $!;
164		print $fh $self->contents;
165		close $fh;
166	}
167
168	while (my $e = $self->next) {
169		if ($e->isFile && is_info_name($e->{name})) {
170			$e->{name} = $dir.$e->{name};
171			undef $e->{mtime};
172			undef $e->{atime};
173			eval { $e->create; };
174			if ($@) {
175				unlink($e->{name});
176				$@ =~ s/\s+at.*//o;
177				$self->{repository}{state}->errprint('#1', $@);
178				return 0;
179			}
180		} else {
181			$self->unput;
182			last;
183		}
184	}
185	return 1;
186}
187
188sub grabPlist($self, $code = \&OpenBSD::PackingList::defaultCode)
189{
190	my $plist = $self->plist($code);
191	if (defined $plist) {
192		$self->wipe_info;
193		$self->close_now;
194		return $plist;
195	} else {
196		return;
197	}
198}
199
200sub forget($self)
201{
202	$self->wipe_info;
203	$self->close_now;
204}
205
206sub wipe_info($self)
207{
208	$self->{repository}->wipe_info($self);
209	$self->{repository}->close_now($self);
210	delete $self->{contents};
211	$self->deref;
212	delete $self->{_current_name};
213	delete $self->{update_info};
214	delete $self->{_unput};
215}
216
217sub info($self)
218{
219	if (!defined $self->{dir}) {
220		$self->grab_info;
221	}
222	return $self->{dir};
223}
224
225sub plist($self, $code = \&OpenBSD::PackingList::defaultCode)
226{
227	require OpenBSD::PackingList;
228
229	if (defined $self->{dir} && -f $self->{dir}.CONTENTS) {
230		my $plist =
231		    OpenBSD::PackingList->fromfile($self->{dir}.CONTENTS,
232		    $code);
233		$plist->set_infodir($self->{dir});
234		return $plist;
235	}
236	if (my $value = $self->contents) {
237		return OpenBSD::PackingList->fromfile(\$value, $code);
238	}
239	# hopeless
240	$self->close_with_client_error;
241
242	return;
243}
244
245sub close($self, $hint = 0)
246{
247	$self->{repository}->close($self, $hint);
248}
249
250sub finish_and_close($self)
251{
252	$self->{repository}->finish_and_close($self);
253}
254
255sub close_now($self)
256{
257	$self->{repository}->close_now($self);
258}
259
260sub close_after_error($self)
261{
262	$self->{repository}->close_after_error($self);
263}
264
265sub close_with_client_error($self)
266{
267	$self->{repository}->close_with_client_error($self);
268}
269
270sub deref($self)
271{
272	delete $self->{fh};
273	delete $self->{pid2};
274	delete $self->{_archive};
275	delete $self->{_current};
276}
277
278# proxy for archive operations
279sub next($self)
280{
281	if (!$self->_opened) {
282		return;
283	}
284	if (!$self->{_unput}) {
285		$self->{_current} = $self->getNext;
286		if (defined $self->{_current}) {
287			$self->{_current_name} = $self->{_current}{name};
288		} else {
289			delete $self->{_current_name};
290		}
291	} else {
292		$self->{_unput} = 0;
293	}
294	return $self->{_current};
295}
296
297sub unput($self)
298{
299	$self->{_unput} = 1;
300}
301
302sub getNext($self)
303{
304	return $self->{_archive}->next;
305}
306
307sub skip($self)
308{
309	return $self->{_archive}->skip;
310}
311
312sub set_callback($self, $code)
313{
314	$self->{callback} = $code;
315	$self->_set_callback;
316}
317
318package OpenBSD::PackageLocation::Installed;
319our @ISA = qw(OpenBSD::PackageLocation);
320
321
322sub info($self)
323{
324	require OpenBSD::PackageInfo;
325	$self->{dir} = OpenBSD::PackageInfo::installed_info($self->name);
326}
327
328sub plist($self, $code = \&OpenBSD::PackingList::defaultCode)
329{
330	require OpenBSD::PackingList;
331	return OpenBSD::PackingList->from_installation($self->name, $code);
332}
333
3341;
335