1/*
2 * FreeBSD install - a package for the installation and maintenance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * Jordan K. Hubbard
15 * 18 July 1993
16 *
17 * URL file access utilities.
18 *
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD$");
23
24#include "lib.h"
25#include <err.h>
26#include <fetch.h>
27#include <libgen.h>
28#include <sys/wait.h>
29#include <stdio.h>
30
31/*
32 * Try and fetch a file by URL, returning the directory name for where
33 * it's unpacked, if successful.
34 */
35const char *
36fileGetURL(const char *base, const char *spec, int keep_package)
37{
38    const char *rp;
39    char *cp, *tmp;
40    char fname[FILENAME_MAX];
41    char pen[FILENAME_MAX];
42    char pkg[FILENAME_MAX];
43    char buf[8192];
44    FILE *ftp;
45    pid_t tpid;
46    int pfd[2], pstat, r, w = 0;
47    char *hint;
48    int fd, pkgfd = 0;
49
50    rp = NULL;
51    /* Special tip that sysinstall left for us */
52    hint = getenv("PKG_ADD_BASE");
53    if (!isURL(spec)) {
54	if (!base && !hint)
55	    return NULL;
56	/*
57	 * We've been given an existing URL (that's known-good) and now we need
58	 * to construct a composite one out of that and the basename we were
59	 * handed as a dependency.
60	 */
61	if (base) {
62	    strcpy(fname, base);
63	    /*
64	     * Advance back two slashes to get to the root of the package
65	     * hierarchy
66	     */
67	    cp = strrchr(fname, '/');
68	    if (cp) {
69		*cp = '\0';	/* chop name */
70		cp = strrchr(fname, '/');
71	    }
72	    if (cp) {
73		*(cp + 1) = '\0';
74		strcat(cp, "All/");
75		strcat(cp, spec);
76		if (getenv("PACKAGESUFFIX"))
77		   strcat(cp, getenv("PACKAGESUFFIX"));
78                else
79		   strcat(cp, ".tbz");
80	    }
81	    else
82		return NULL;
83	}
84	else {
85	    /*
86	     * Otherwise, we've been given an environment variable hinting
87	     * at the right location from sysinstall
88	     */
89	    strcpy(fname, hint);
90	    strcat(fname, spec);
91	    if (getenv("PACKAGESUFFIX"))
92	       strcat(fname, getenv("PACKAGESUFFIX"));
93            else
94	       strcat(fname, ".tbz");
95	}
96    }
97    else
98	strcpy(fname, spec);
99
100    if (keep_package) {
101	tmp = getenv("PKGDIR");
102	strlcpy(pkg, tmp ? tmp : ".", sizeof(pkg));
103	tmp = basename(fname);
104	strlcat(pkg, "/", sizeof(pkg));
105	strlcat(pkg, tmp, sizeof(pkg));
106	if ((pkgfd = open(pkg, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
107	    printf("Error: Unable to open %s\n", pkg);
108	    perror("open");
109	    return NULL;
110	}
111    }
112
113    fetchDebug = (Verbose > 0);
114    if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
115	printf("Error: Unable to get %s: %s\n",
116	       fname, fetchLastErrString);
117	/* If the fetch fails, yank the package. */
118	if (keep_package && unlink(pkg) < 0 && Verbose) {
119	    warnx("failed to remove partially fetched package: %s", pkg);
120	}
121	return NULL;
122    }
123
124    if (isatty(0) || Verbose)
125	printf("Fetching %s...", fname), fflush(stdout);
126    pen[0] = '\0';
127    if ((rp = make_playpen(pen, 0)) == NULL) {
128	printf("Error: Unable to construct a new playpen for FTP!\n");
129	fclose(ftp);
130	return NULL;
131    }
132    if (pipe(pfd) == -1) {
133	warn("pipe()");
134	cleanup(0);
135	exit(2);
136    }
137    if ((tpid = fork()) == -1) {
138	warn("pipe()");
139	cleanup(0);
140	exit(2);
141    }
142    if (!tpid) {
143	dup2(pfd[0], 0);
144	for (fd = getdtablesize() - 1; fd >= 3; --fd)
145	    close(fd);
146	execl("/usr/bin/tar", "tar",
147	    Verbose ? "-xpjvf" : "-xpjf",
148	    "-", (char *)0);
149	_exit(2);
150    }
151    close(pfd[0]);
152    for (;;) {
153	if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
154	    break;
155	if ((w = write(pfd[1], buf, r)) != r)
156	    break;
157	if (keep_package) {
158	    if ((w = write(pkgfd, buf, r)) != r)
159		break;
160	}
161    }
162    if (ferror(ftp))
163	warn("warning: error reading from server");
164    fclose(ftp);
165    if (keep_package) {
166	close(pkgfd);
167    }
168    close(pfd[1]);
169    if (w == -1)
170	warn("warning: error writing to tar");
171    tpid = waitpid(tpid, &pstat, 0);
172    if (Verbose)
173	printf("tar command returns %d status\n", WEXITSTATUS(pstat));
174    if (rp && (isatty(0) || Verbose))
175	printf(" Done.\n");
176    return rp;
177}
178