1 /* $NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $ */
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason Thorpe and Luke Mewburn.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
39 #include <sys/cdefs.h>
41 __RCSID("$Id: fetch.c,v 1.4 1997/12/16 08:22:35 ache Exp $");
42 __RCSID_SOURCE("$NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $");
46 * FTP User Program -- Command line file retrieval
49 #include <sys/types.h>
50 #include <sys/param.h>
51 #include <sys/socket.h>
53 #include <netinet/in.h>
56 #include <arpa/inet.h>
70 static int url_get __P((const char *, const char *));
71 void aborthttp __P((int));
74 #define FTP_URL "ftp://" /* ftp URL prefix */
75 #define HTTP_URL "http://" /* http URL prefix */
76 #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
77 #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
80 #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
85 * Retrieve URL, via the proxy in $proxyvar if necessary.
86 * Modifies the string argument given.
87 * Returns -1 on failure, 0 on success
90 url_get(origline, proxyenv)
94 struct sockaddr_in sin;
99 char c, *cp, *ep, *portnum, *path, buf[4096];
100 const char *savefile;
101 char *line, *proxy, *host;
102 volatile sig_t oldintr;
109 #ifdef __GNUC__ /* XXX: to shut up gcc warnings */
114 line = strdup(origline);
116 errx(1, "Can't allocate memory to parse URL");
117 if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
118 host = line + sizeof(HTTP_URL) - 1;
119 else if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
120 host = line + sizeof(FTP_URL) - 1;
123 errx(1, "url_get: Invalid URL '%s'", line);
125 path = strchr(host, '/'); /* find path */
126 if (EMPTYSTRING(path)) {
129 warnx("Invalid URL (no `/' after host): %s", origline);
130 goto cleanup_url_get;
133 if (EMPTYSTRING(path)) {
136 warnx("Invalid URL (no file after host): %s", origline);
137 goto cleanup_url_get;
140 savefile = strrchr(path, '/'); /* find savefile */
141 if (savefile != NULL)
145 if (EMPTYSTRING(savefile)) {
148 warnx("Invalid URL (no file after directory): %s", origline);
149 goto cleanup_url_get;
152 if (proxyenv != NULL) { /* use proxy */
153 proxy = strdup(proxyenv);
155 errx(1, "Can't allocate memory for proxy URL.");
156 if (strncasecmp(proxy, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
157 host = proxy + sizeof(HTTP_URL) - 1;
158 else if (strncasecmp(proxy, FTP_URL, sizeof(FTP_URL) - 1) == 0)
159 host = proxy + sizeof(FTP_URL) - 1;
161 warnx("Malformed proxy URL: %s", proxyenv);
162 goto cleanup_url_get;
164 if (EMPTYSTRING(host)) {
165 warnx("Malformed proxy URL: %s", proxyenv);
166 goto cleanup_url_get;
168 *--path = '/'; /* add / back to real path */
169 path = strchr(host, '/'); /* remove trailing / on host */
170 if (! EMPTYSTRING(path))
175 portnum = strchr(host, ':'); /* find portnum */
180 printf("host %s, port %s, path %s, save as %s.\n",
181 host, portnum, path, savefile);
183 memset(&sin, 0, sizeof(sin));
184 sin.sin_family = AF_INET;
186 if (isdigit((unsigned char)host[0])) {
187 if (inet_aton(host, &sin.sin_addr) == 0) {
188 warnx("Invalid IP address: %s", host);
189 goto cleanup_url_get;
194 hp = gethostbyname(host);
196 warnx("%s: %s", host, hstrerror(h_errno));
197 goto cleanup_url_get;
199 if (hp->h_addrtype != AF_INET) {
200 warnx("%s: not an Internet address?", host);
201 goto cleanup_url_get;
203 memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
206 if (! EMPTYSTRING(portnum)) {
210 nport = strtol(portnum, &ep, 10);
211 if (nport < 1 || nport > 0xffff || *ep != '\0') {
212 warnx("Invalid port: %s", portnum);
213 goto cleanup_url_get;
220 s = socket(AF_INET, SOCK_STREAM, 0);
222 warn("Can't create socket");
223 goto cleanup_url_get;
226 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
227 warn("Can't connect to %s", host);
228 goto cleanup_url_get;
232 * Construct and send the request. We're expecting a return
233 * status of "200". Proxy requests don't want leading /.
236 printf("Requesting %s\n", origline);
238 printf("Requesting %s (via %s)\n", origline, proxyenv);
239 len = snprintf(buf, sizeof(buf), "GET %s%s HTTP/1.0\r\n\r\n",
240 proxy ? "" : "/", path);
241 if (write(s, buf, len) < len) {
242 warn("Writing HTTP request");
243 goto cleanup_url_get;
245 memset(buf, 0, sizeof(buf));
246 for (cp = buf; cp < buf + sizeof(buf); ) {
247 if (read(s, cp, 1) != 1)
255 buf[sizeof(buf) - 1] = '\0'; /* sanity */
256 cp = strchr(buf, ' ');
261 if (strncmp(cp, "200", 3)) {
262 warnx("Error retrieving file: %s", cp);
263 goto cleanup_url_get;
267 * Read the rest of the header.
269 memset(buf, 0, sizeof(buf));
271 for (cp = buf; cp < buf + sizeof(buf); ) {
272 if (read(s, cp, 1) != 1)
276 if (*cp == '\n' && c == '\n')
281 buf[sizeof(buf) - 1] = '\0'; /* sanity */
284 * Look for the "Content-length: " header.
286 #define CONTENTLEN "Content-Length: "
287 for (cp = buf; *cp != '\0'; cp++) {
288 if (tolower((unsigned char)*cp) == 'c' &&
289 strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0)
293 cp += sizeof(CONTENTLEN) - 1;
294 ep = strchr(cp, '\n');
299 filesize = strtol(cp, &ep, 10);
300 if (filesize < 1 || *ep != '\0')
305 /* Open the output file. */
306 out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
308 warn("Can't open %s", savefile);
309 goto cleanup_url_get;
314 if (setjmp(httpabort)) {
316 (void)signal(SIGINT, oldintr);
317 goto cleanup_url_get;
319 oldintr = signal(SIGINT, aborthttp);
325 /* Finally, suck down the file. */
327 while ((len = read(s, buf, sizeof(buf))) > 0) {
329 for (cp = buf; len > 0; len -= i, cp += i) {
330 if ((i = write(out, cp, len)) == -1) {
331 warn("Writing %s", savefile);
332 goto cleanup_url_get;
337 if (hash && !progress) {
338 while (bytes >= hashbytes) {
342 (void)fflush(stdout);
345 if (hash && !progress && bytes > 0) {
349 (void)fflush(stdout);
352 warn("Reading from socket");
353 goto cleanup_url_get;
357 puts("Successfully retrieved file.");
358 (void)signal(SIGINT, oldintr);
369 "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
370 goto cleanup_url_get;
373 warnx("Improper response from %s", host);
385 * Abort a http retrieval
393 puts("\nhttp fetch aborted.");
394 (void)fflush(stdout);
395 longjmp(httpabort, 1);
399 * Retrieve multiple files from the command line, transferring
400 * files of the form "host:path", "ftp://host/path" using the
401 * ftp protocol, and files of the form "http://host/path" using
403 * If path has a trailing "/", then return (-1);
404 * the path will be cd-ed into and the connection remains open,
405 * and the function will return -1 (to indicate the connection
407 * If an error occurs the return value will be the offset+1 in
408 * argv[] of the file that caused a problem (i.e, argv[x]
410 * Otherwise, 0 is returned if all files retrieved successfully.
413 auto_fetch(argc, argv)
417 static char lasthost[MAXHOSTNAMELEN];
419 char *cp, *line, *host, *dir, *file, *portnum;
421 char *ftpproxy, *httpproxy;
424 int dirhasglob, filehasglob;
425 char rempath[MAXPATHLEN];
429 if (setjmp(toplevel)) {
434 (void)signal(SIGINT, (sig_t)intr);
435 (void)signal(SIGPIPE, (sig_t)lostpeer);
437 ftpproxy = getenv(FTP_PROXY);
438 httpproxy = getenv(HTTP_PROXY);
441 * Loop through as long as there's files to fetch.
443 for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) {
444 if (strchr(argv[argpos], ':') == NULL)
446 host = dir = file = portnum = user = pass = NULL;
449 * We muck with the string, so we make a copy.
451 line = strdup(argv[argpos]);
453 errx(1, "Can't allocate memory for auto-fetch.");
456 * Try HTTP URL-style arguments first.
458 if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
459 if (url_get(line, httpproxy) == -1)
465 * Try FTP URL-style arguments next. If ftpproxy is
466 * set, use url_get() instead of standard ftp.
467 * Finally, try host:file.
470 if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
472 if (url_get(line, ftpproxy) == -1)
476 host += sizeof(FTP_URL) - 1;
477 dir = strchr(host, '/');
479 /* look for [user:pass@]host[:port] */
480 pass = strpbrk(host, ":@/");
481 if (pass == NULL || *pass == '/') {
485 if (pass == host || *pass == '@') {
487 warnx("Invalid URL: %s", argv[argpos]);
492 cp = strpbrk(pass, ":@/");
493 if (cp == NULL || *cp == '/') {
498 if (EMPTYSTRING(cp) || *cp == ':')
502 if (EMPTYSTRING(user))
505 portnum = strchr(host, ':');
508 } else { /* classic style `host:file' */
509 dir = strchr(host, ':');
512 if (EMPTYSTRING(host)) {
518 * If dir is NULL, the file wasn't specified
519 * (URL looked something like ftp://host)
525 * Extract the file and (if present) directory name.
527 if (! EMPTYSTRING(dir)) {
528 cp = strrchr(dir, '/');
538 printf("user %s:%s host %s port %s dir %s file %s\n",
539 user, pass, host, portnum, dir, file);
542 * Set up the connection if we don't have one.
544 if (strcmp(host, lasthost) != 0) {
547 (void)strcpy(lasthost, host);
550 xargv[0] = __progname;
554 if (! EMPTYSTRING(portnum)) {
559 oautologin = autologin;
562 setpeer(xargc, xargv);
563 autologin = oautologin;
565 || ((connected == 1) && !login(host, user, pass)) ) {
566 warnx("Can't connect or login to host `%s'",
572 /* Always use binary transfers. */
585 dirhasglob = filehasglob = 0;
587 if (! EMPTYSTRING(dir) &&
588 strpbrk(dir, "*?[]{}") != NULL)
590 if (! EMPTYSTRING(file) &&
591 strpbrk(file, "*?[]{}") != NULL)
595 /* Change directories, if necessary. */
596 if (! EMPTYSTRING(dir) && !dirhasglob) {
607 if (EMPTYSTRING(file)) {
613 printf("Retrieving %s/%s\n", dir ? dir : "", file);
616 snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
620 /* Fetch the file(s). */
624 if (dirhasglob || filehasglob) {
627 ointeractive = interactive;
631 interactive = ointeractive;
635 if ((code / 100) != COMPLETE)
638 if (connected && rval != -1)