1/*	$NetBSD: pkg_io.c,v 1.4 2021/04/10 19:49:59 nia Exp $	*/
2/*-
3 * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in
14 *    the documentation and/or other materials provided with the
15 *    distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#if HAVE_CONFIG_H
32#include "config.h"
33#endif
34#include <nbcompat.h>
35#if HAVE_SYS_CDEFS_H
36#include <sys/cdefs.h>
37#endif
38
39__RCSID("$NetBSD: pkg_io.c,v 1.4 2021/04/10 19:49:59 nia Exp $");
40
41#include <archive.h>
42#include <archive_entry.h>
43#if HAVE_ERR_H
44#include <err.h>
45#endif
46#if HAVE_ERRNO_H
47#include <errno.h>
48#endif
49#include <stdlib.h>
50
51#ifdef BOOTSTRAP
52#undef IS_URL
53#define IS_URL(x) 0
54#else
55#include <fetch.h>
56#endif
57
58#include "lib.h"
59
60struct pkg_path {
61	TAILQ_ENTRY(pkg_path) pl_link;
62	char *pl_path;
63};
64
65static char *orig_cwd, *last_toplevel;
66static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path);
67
68#ifndef BOOTSTRAP
69struct fetch_archive {
70	struct url *url;
71	fetchIO *fetch;
72	char buffer[32768];
73	off_t size;
74	int restart;
75};
76
77static int
78fetch_archive_open(struct archive *a, void *client_data)
79{
80	struct fetch_archive *f = client_data;
81	struct url_stat us;
82
83	f->fetch = fetchXGet(f->url, &us, fetch_flags);
84	if (f->fetch == NULL)
85		return ENOENT;
86	f->size = us.size;
87	f->restart = 1;
88	f->url->offset = 0;
89	return 0;
90}
91
92static ssize_t
93fetch_archive_read(struct archive *a, void *client_data,
94    const void **buffer)
95{
96	struct fetch_archive *f = client_data;
97	struct url_stat us;
98	ssize_t rv;
99
100	*buffer = f->buffer;
101	rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
102	if (rv > 0) {
103		f->url->offset += rv;
104		return rv;
105	}
106	if (f->restart == 0)
107		return rv;
108	if (rv == 0) {
109		if (f->size == -1)
110			return 0;
111		if (f->url->offset == f->size)
112			return 0;
113	}
114	f->restart = 0;
115	if (1) {
116		char *url = fetchStringifyURL(f->url);
117		fprintf(stderr, "Trying to reconnect %s\n", url);
118		free(url);
119	}
120	fetchIO_close(f->fetch);
121	f->fetch = fetchXGet(f->url, &us, fetch_flags);
122	if (f->fetch == NULL)
123		return -1;
124	if (us.size != f->size)
125		return -1;
126	rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
127	if (rv > 0)
128		f->url->offset += rv;
129	return rv;
130}
131
132static int
133fetch_archive_close(struct archive *a, void *client_data)
134{
135	struct fetch_archive *f = client_data;
136
137	if (f->fetch != NULL)
138		fetchIO_close(f->fetch);
139	fetchFreeURL(f->url);
140	free(f);
141	return 0;
142}
143
144static struct archive *
145open_archive_by_url(struct url *url, char **archive_name)
146{
147	struct fetch_archive *f;
148	struct archive *a;
149
150	f = xmalloc(sizeof(*f));
151	f->url = fetchCopyURL(url);
152
153	*archive_name = fetchStringifyURL(url);
154
155	a = prepare_archive();
156	if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read,
157	    fetch_archive_close)) {
158		free(*archive_name);
159		*archive_name = NULL;
160		archive_read_free(a);
161		return NULL;
162	}
163
164	return a;
165}
166#endif /* !BOOTSTRAP */
167
168struct archive *
169prepare_archive(void)
170{
171	struct archive *a = archive_read_new();
172	if (a == NULL)
173		errx(EXIT_FAILURE, "memory allocation failed");
174	archive_read_support_filter_gzip(a);
175	archive_read_support_filter_bzip2(a);
176	archive_read_support_filter_xz(a);
177	archive_read_support_format_ar(a);
178	archive_read_support_format_tar(a);
179	archive_read_set_options(a, "hdrcharset=BINARY");
180	return a;
181}
182
183struct archive *
184open_archive(const char *url, char **archive_name)
185{
186	struct url *u;
187	struct archive *a;
188
189	*archive_name = NULL;
190
191	if (!IS_URL(url)) {
192		a = prepare_archive();
193		if (archive_read_open_filename(a, url, 1024)) {
194			archive_read_close(a);
195			return NULL;
196		}
197		*archive_name = xstrdup(url);
198		return a;
199	}
200
201#ifdef BOOTSTRAP
202	return NULL;
203#else
204	if ((u = fetchParseURL(url)) == NULL)
205		return NULL;
206
207	a = open_archive_by_url(u, archive_name);
208
209	fetchFreeURL(u);
210	return a;
211#endif
212}
213
214#ifndef BOOTSTRAP
215static int
216strip_suffix(char *filename)
217{
218	size_t len;
219
220	len = strlen(filename);
221	if (len <= 4)
222		return 0;
223	if (strcmp(filename + len - 4, ".tgz") == 0 ||
224	    strcmp(filename + len - 4, ".tbz") == 0) {
225		filename[len - 4] = '\0';
226		return 1;
227	} else
228		return 0;
229}
230
231static int
232find_best_package_int(struct url *url, const char *pattern,
233    struct url **best_url)
234{
235	char *cur_match, *url_pattern, *best_match = NULL;
236	struct url_list ue;
237	size_t i;
238
239	if (*best_url) {
240		if ((best_match = fetchUnquoteFilename(*best_url)) == NULL)
241			return -1;
242	} else
243		best_match = NULL;
244
245	if (best_match && strip_suffix(best_match) == 0) {
246		free(best_match);
247		return -1;
248	}
249
250	for (i = 0; pattern[i] != '\0'; ++i) {
251		if (!isalnum((unsigned char)(pattern[i])) &&
252		    (pattern[i]) != '-')
253			break;
254	}
255	url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern);
256
257	fetchInitURLList(&ue);
258	if (fetchList(&ue, url, url_pattern, fetch_flags)) {
259		char *base_url;
260		base_url = fetchStringifyURL(url);
261		warnx("Can't process %s/%s: %s", base_url, url_pattern,
262		    fetchLastErrString);
263		free(base_url);
264		free(url_pattern);
265		fetchFreeURLList(&ue);
266		return -1;
267	}
268	free(url_pattern);
269
270	for (i = 0; i < ue.length; ++i) {
271		cur_match = fetchUnquoteFilename(ue.urls + i);
272
273		if (cur_match == NULL) {
274			free(best_match);
275			fetchFreeURLList(&ue);
276			return -1;
277		}
278		if (strip_suffix(cur_match) == 0) {
279			free(cur_match);
280			continue;
281		}
282		if (pkg_order(pattern, cur_match, best_match) == 1) {
283			if (*best_url)
284				fetchFreeURL(*best_url);
285			*best_url = fetchCopyURL(ue.urls + i);
286			free(best_match);
287			best_match = cur_match;
288			cur_match = NULL;
289			if (*best_url == NULL) {
290				free(best_match);
291				return -1;
292			}
293		}
294		free(cur_match);
295	}
296	free(best_match);
297	fetchFreeURLList(&ue);
298	return 0;
299}
300
301void
302process_pkg_path(void)
303{
304	char cwd[PATH_MAX];
305	int relative_path;
306	struct pkg_path *pl;
307	const char *start, *next;
308	size_t len;
309
310	if (getcwd(cwd, sizeof(cwd)) == NULL)
311		errx(EXIT_FAILURE, "getcwd failed");
312
313	orig_cwd = xstrdup(cwd);
314
315	if (config_pkg_path == NULL)
316		return;
317
318	for (start = config_pkg_path; *start; start = next) {
319		len = strcspn(start, ";");
320		if (*(next = start + len) != '\0')
321			++next;
322
323		relative_path = !IS_FULLPATH(start) && !IS_URL(start);
324		pl = xmalloc(sizeof(*pl));
325		pl->pl_path = xasprintf("%s%s%*.*s",
326		    relative_path ? cwd : "", len && relative_path ? "/" : "",
327		    (int)len, (int)len, start);
328		TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link);
329	}
330}
331
332struct url *
333find_best_package(const char *toplevel, const char *pattern, int do_path)
334{
335	struct url *url, *best_match = NULL;
336	struct pkg_path *pl;
337
338	if (toplevel) {
339		url = fetchParseURL(last_toplevel);
340		if (url != NULL) {
341			find_best_package_int(url, pattern, &best_match);
342			/* XXX Check return value and complain */
343			fetchFreeURL(url);
344		}
345	}
346	if (!do_path)
347		return best_match;
348
349	TAILQ_FOREACH(pl, &pkg_path, pl_link) {
350		url = fetchParseURL(pl->pl_path);
351		if (url != NULL) {
352			find_best_package_int(url, pattern, &best_match);
353			/* XXX Check return value and complain */
354			fetchFreeURL(url);
355		}
356	}
357
358	return best_match;
359}
360#endif /* !BOOTSTRAP */
361
362struct archive *
363find_archive(const char *fname, int top_level, char **archive_name)
364{
365	struct archive *a;
366	struct url *best_match;
367	char *full_fname, *last_slash;
368	int search_path;
369
370	search_path = 0;
371	if (IS_FULLPATH(fname) || IS_URL(fname)) {
372		full_fname = xstrdup(fname);
373	} else {
374		if (strchr(fname, '/') == NULL)
375			search_path = 1;
376		full_fname = xasprintf("%s/%s", orig_cwd, fname);
377	}
378
379	last_slash = strrchr(full_fname, '/');
380	if (top_level) {
381		free(last_toplevel);
382		*last_slash = '\0';
383		last_toplevel = xstrdup(full_fname);
384		*last_slash = '/';
385	}
386
387	a = open_archive(full_fname, archive_name);
388	if (a != NULL) {
389		free(full_fname);
390		return a;
391	}
392#ifndef BOOTSTRAP
393	fname = last_slash + 1;
394	*last_slash = '\0';
395
396	best_match = find_best_package(full_fname, fname, 0);
397
398	if (search_path && best_match == NULL)
399		best_match = find_best_package(last_toplevel, fname, 1);
400
401	free(full_fname);
402
403	if (best_match == NULL)
404		return NULL;
405	a = open_archive_by_url(best_match, archive_name);
406	fetchFreeURL(best_match);
407#endif /* !BOOTSTRAP */
408	return a;
409}
410