Annotation of src/usr.bin/ftp/fetch.c, Revision 1.30
1.30 ! lukem 1: /* $NetBSD: fetch.c,v 1.29 1998/08/04 03:35:24 lukem Exp $ */
1.1 lukem 2:
3: /*-
1.25 lukem 4: * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
1.1 lukem 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to The NetBSD Foundation
8: * by Jason Thorpe and Luke Mewburn.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
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.
25: *
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
1.14 lukem 29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 lukem 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.
37: */
38:
1.12 lukem 39: #include <sys/cdefs.h>
1.1 lukem 40: #ifndef lint
1.30 ! lukem 41: __RCSID("$NetBSD: fetch.c,v 1.29 1998/08/04 03:35:24 lukem Exp $");
1.1 lukem 42: #endif /* not lint */
43:
44: /*
45: * FTP User Program -- Command line file retrieval
46: */
47:
48: #include <sys/types.h>
49: #include <sys/param.h>
50: #include <sys/socket.h>
1.30 ! lukem 51: #include <sys/stat.h>
1.1 lukem 52:
53: #include <netinet/in.h>
54:
1.2 lukem 55: #include <arpa/ftp.h>
1.1 lukem 56: #include <arpa/inet.h>
57:
58: #include <ctype.h>
59: #include <err.h>
1.22 lukem 60: #include <errno.h>
1.1 lukem 61: #include <netdb.h>
62: #include <fcntl.h>
1.3 lukem 63: #include <signal.h>
1.1 lukem 64: #include <stdio.h>
65: #include <stdlib.h>
66: #include <string.h>
67: #include <unistd.h>
1.24 lukem 68: #include <util.h>
1.1 lukem 69:
70: #include "ftp_var.h"
71:
1.27 lukem 72: typedef enum {
73: UNKNOWN_URL_T=-1,
74: HTTP_URL_T,
75: FTP_URL_T,
76: FILE_URL_T
77: } url_t;
78:
79: static int parse_url __P((const char *, const char *, url_t *, char **,
80: char **, char **, in_port_t *, char **));
1.22 lukem 81: static int url_get __P((const char *, const char *, const char *));
1.12 lukem 82: void aborthttp __P((int));
83:
84:
1.25 lukem 85: #define ABOUT_URL "about:" /* propaganda */
86: #define FILE_URL "file://" /* file URL prefix */
1.1 lukem 87: #define FTP_URL "ftp://" /* ftp URL prefix */
88: #define HTTP_URL "http://" /* http URL prefix */
1.5 lukem 89: #define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
1.1 lukem 90: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
1.27 lukem 91: #define NO_PROXY "no_proxy" /* env var with list of non-proxied
92: * hosts, comma or space separated */
1.1 lukem 93:
94:
95: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
1.27 lukem 96: #define FREEPTR(x) if ((x) != NULL) { free(x); (x) = NULL; }
97:
98: /*
99: * Parse URL of form:
100: * <type>://[<user>[:<password>@]]<host>[:<port>]/<url-path>
101: * Returns -1 if a parse error occurred, otherwise 0.
102: * Sets type to url_t, each of the given char ** pointers to a
103: * malloc(3)ed strings of the relevant section, and port to
104: * 0 if not given, or the number given.
105: */
106: static int
107: parse_url(url, desc, type, user, pass, host, port, path)
108: const char *url;
109: const char *desc;
110: url_t *type;
111: char **user;
112: char **pass;
113: char **host;
114: in_port_t *port;
115: char **path;
116: {
117: char *cp, *ep, *thost;
118:
119: if (url == NULL || desc == NULL || type == NULL || user == NULL
120: || pass == NULL || host == NULL || port == NULL || path == NULL)
121: errx(1, "parse_url: invoked with NULL argument!");
122:
123: *type = UNKNOWN_URL_T;
124: *user = *pass = *host = *path = NULL;
125: *port = 0;
126:
127: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
128: url += sizeof(HTTP_URL) - 1;
129: *type = HTTP_URL_T;
130: } else if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
131: url += sizeof(FTP_URL) - 1;
132: *type = FTP_URL_T;
133: } else if (strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
134: url += sizeof(FILE_URL) - 1;
135: *type = FILE_URL_T;
136: } else {
137: warnx("Invalid %s `%s'", desc, url);
138: cleanup_parse_url:
139: FREEPTR(*user);
140: FREEPTR(*pass);
141: FREEPTR(*host);
142: FREEPTR(*path);
143: return (-1);
144: }
145:
146: if (*url == '\0')
147: return (0);
148:
149: /* find [user[:pass]@]host[:port] */
150: ep = strchr(url, '/');
151: if (ep == NULL)
152: thost = xstrdup(url);
153: else {
154: size_t len = ep - url;
155: thost = (char *)xmalloc(len + 1);
156: strncpy(thost, url, len);
157: thost[len] = '\0';
158: *path = xstrdup(ep);
159: }
160:
161: cp = strchr(thost, '@');
162: if (cp != NULL) {
163: *user = thost;
164: *cp = '\0';
165: *host = xstrdup(cp + 1);
166: cp = strchr(*user, ':');
167: if (cp != NULL) {
168: *cp = '\0';
169: *pass = xstrdup(cp + 1);
170: }
171: } else
172: *host = thost;
173:
174: /* look for [:port] */
175: cp = strrchr(*host, ':');
176: if (cp != NULL) {
177: long nport;
178:
179: *cp = '\0';
180: nport = strtol(cp + 1, &ep, 10);
181: if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') {
182: warnx("Invalid port `%s' in %s `%s'", cp, desc, line);
183: goto cleanup_parse_url;
184: }
185: *port = htons((in_port_t)nport);
186: }
187:
188: if (debug)
189: fprintf(ttyout,
190: "parse_url: user `%s', pass `%s', host %s:%d, path `%s'\n",
191: *user ? *user : "", *pass ? *pass : "", *host ? *host : "",
192: ntohs(*port), *path ? *path : "");
193:
194: return (0);
195: }
196:
1.1 lukem 197:
1.2 lukem 198: jmp_buf httpabort;
199:
1.1 lukem 200: /*
1.5 lukem 201: * Retrieve URL, via the proxy in $proxyvar if necessary.
1.1 lukem 202: * Modifies the string argument given.
203: * Returns -1 on failure, 0 on success
204: */
1.12 lukem 205: static int
1.27 lukem 206: url_get(url, proxyenv, outfile)
207: const char *url;
1.9 lukem 208: const char *proxyenv;
1.22 lukem 209: const char *outfile;
1.1 lukem 210: {
211: struct sockaddr_in sin;
1.27 lukem 212: int isredirected, isproxy;
1.12 lukem 213: volatile int s;
1.13 lukem 214: size_t len;
1.27 lukem 215: char *cp, *ep;
1.29 lukem 216: char *buf, *savefile;
1.22 lukem 217: volatile sig_t oldintr, oldintp;
1.1 lukem 218: off_t hashbytes;
1.22 lukem 219: struct hostent *hp = NULL;
220: int (*closefunc) __P((FILE *));
1.24 lukem 221: FILE *fin, *fout;
222: int retval;
1.25 lukem 223: time_t mtime;
1.27 lukem 224: url_t urltype;
225: char *user, *pass, *host;
226: in_port_t port;
227: char *path;
1.1 lukem 228:
1.22 lukem 229: closefunc = NULL;
1.24 lukem 230: fin = fout = NULL;
1.1 lukem 231: s = -1;
1.29 lukem 232: buf = savefile = NULL;
1.27 lukem 233: isredirected = isproxy = 0;
1.24 lukem 234: retval = -1;
1.1 lukem 235:
1.27 lukem 236: #ifdef __GNUC__ /* shut up gcc warnings */
1.22 lukem 237: (void)&closefunc;
1.24 lukem 238: (void)&fin;
1.22 lukem 239: (void)&fout;
1.24 lukem 240: (void)&buf;
1.12 lukem 241: (void)&savefile;
1.24 lukem 242: (void)&retval;
1.28 fvdl 243: (void)&isproxy;
1.12 lukem 244: #endif
245:
1.27 lukem 246: if (parse_url(url, "URL", &urltype, &user, &pass, &host, &port, &path)
247: == -1)
248: goto cleanup_url_get;
249: if (port == 0)
250: port = httpport;
1.5 lukem 251:
1.27 lukem 252: if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
253: && strcasecmp(host, "localhost") != 0) {
254: warnx("No support for non local file URL `%s'", url);
1.5 lukem 255: goto cleanup_url_get;
1.9 lukem 256: }
1.27 lukem 257:
1.9 lukem 258: if (EMPTYSTRING(path)) {
1.25 lukem 259: if (urltype == FTP_URL_T)
1.11 lukem 260: goto noftpautologin;
1.27 lukem 261: if (urltype != HTTP_URL_T || outfile == NULL) {
262: warnx("Invalid URL (no file after host) `%s'", url);
263: goto cleanup_url_get;
264: }
1.9 lukem 265: }
1.1 lukem 266:
1.22 lukem 267: if (outfile)
1.29 lukem 268: savefile = xstrdup(outfile);
1.22 lukem 269: else {
1.29 lukem 270: cp = strrchr(path, '/'); /* find savefile */
271: if (cp != NULL)
272: savefile = xstrdup(cp + 1);
1.22 lukem 273: else
1.29 lukem 274: savefile = xstrdup(path);
1.22 lukem 275: }
1.9 lukem 276: if (EMPTYSTRING(savefile)) {
1.25 lukem 277: if (urltype == FTP_URL_T)
1.11 lukem 278: goto noftpautologin;
1.27 lukem 279: warnx("Invalid URL (no file after directory) `%s'", url);
1.5 lukem 280: goto cleanup_url_get;
1.9 lukem 281: }
1.1 lukem 282:
1.25 lukem 283: filesize = -1;
284: mtime = -1;
285: if (urltype == FILE_URL_T) { /* file:// URLs */
286: struct stat sb;
287:
288: direction = "copied";
289: fin = fopen(path, "r");
290: if (fin == NULL) {
1.27 lukem 291: warn("Cannot open file `%s'", path);
1.5 lukem 292: goto cleanup_url_get;
293: }
1.25 lukem 294: if (fstat(fileno(fin), &sb) == 0) {
295: mtime = sb.st_mtime;
296: filesize = sb.st_size;
297: }
298: fprintf(ttyout, "Copying %s\n", path);
299: } else { /* ftp:// or http:// URLs */
300: direction = "retrieved";
301: if (proxyenv != NULL) { /* use proxy */
1.27 lukem 302: url_t purltype;
303: char *puser, *ppass, *phost;
304: in_port_t pport;
305: char *ppath;
306: char *no_proxy;
307:
308: isproxy = 1;
309:
310: /* check URL against list of no_proxied sites */
311: no_proxy = getenv(NO_PROXY);
312: if (no_proxy != NULL) {
313: char *np, *np_copy;
314: long np_port;
315: size_t hlen, plen;
316:
317: np_copy = xstrdup(no_proxy);
318: hlen = strlen(host);
319: while ((cp = strsep(&np_copy, " ,")) != NULL) {
320: if (*cp == '\0')
321: continue;
322: if ((np = strchr(cp, ':')) != NULL) {
323: *np = '\0';
324: np_port =
325: strtol(np + 1, &ep, 10);
326: if (*ep != '\0')
327: continue;
328: if (port !=
329: htons((in_port_t)np_port))
330: continue;
331: }
332: plen = strlen(cp);
333: if (strncasecmp(host + hlen - plen,
334: cp, plen) == 0) {
335: isproxy = 0;
336: break;
337: }
338: }
339: FREEPTR(np_copy);
1.25 lukem 340: }
1.1 lukem 341:
1.27 lukem 342: if (isproxy) {
343: if (parse_url(proxyenv, "proxy URL", &purltype,
344: &puser, &ppass, &phost, &pport, &ppath)
345: == -1)
346: goto cleanup_url_get;
347:
348: if ((purltype != HTTP_URL_T
349: && purltype != FTP_URL_T) ||
350: EMPTYSTRING(phost) ||
351: (! EMPTYSTRING(ppath)
352: && strcmp(ppath, "/") != 0)) {
353: warnx("Malformed proxy URL `%s'",
354: proxyenv);
355: FREEPTR(puser);
356: FREEPTR(ppass);
357: FREEPTR(phost);
358: FREEPTR(ppath);
359: goto cleanup_url_get;
360: }
1.12 lukem 361:
1.27 lukem 362: FREEPTR(user);
363: user = puser;
364: FREEPTR(pass);
365: pass = ppass;
366: FREEPTR(host);
367: host = phost;
368: if (pport == 0)
369: port = httpport;
370: else
371: port = pport;
372: FREEPTR(path);
373: FREEPTR(ppath);
374: path = xstrdup(url);
375: }
376: } /* proxyenv != NULL */
1.25 lukem 377:
378: memset(&sin, 0, sizeof(sin));
379: sin.sin_family = AF_INET;
380:
381: if (isdigit((unsigned char)host[0])) {
382: if (inet_aton(host, &sin.sin_addr) == 0) {
1.27 lukem 383: warnx("Invalid IP address `%s'", host);
1.25 lukem 384: goto cleanup_url_get;
385: }
386: } else {
387: hp = gethostbyname(host);
388: if (hp == NULL) {
389: warnx("%s: %s", host, hstrerror(h_errno));
390: goto cleanup_url_get;
391: }
392: if (hp->h_addrtype != AF_INET) {
1.27 lukem 393: warnx("`%s': not an Internet address?", host);
1.25 lukem 394: goto cleanup_url_get;
395: }
396: memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
1.1 lukem 397: }
398:
1.27 lukem 399: if (port == 0)
1.25 lukem 400: port = httpport;
401: sin.sin_port = port;
402:
403: s = socket(AF_INET, SOCK_STREAM, 0);
404: if (s == -1) {
405: warn("Can't create socket");
406: goto cleanup_url_get;
1.22 lukem 407: }
1.1 lukem 408:
1.25 lukem 409: while (xconnect(s, (struct sockaddr *)&sin,
410: sizeof(sin)) == -1) {
411: if (errno == EINTR)
412: continue;
413: if (hp && hp->h_addr_list[1]) {
414: int oerrno = errno;
415: char *ia;
416:
417: ia = inet_ntoa(sin.sin_addr);
418: errno = oerrno;
1.27 lukem 419: warn("Connect to address `%s'", ia);
1.25 lukem 420: hp->h_addr_list++;
421: memcpy(&sin.sin_addr, hp->h_addr_list[0],
422: (size_t)hp->h_length);
423: fprintf(ttyout, "Trying %s...\n",
424: inet_ntoa(sin.sin_addr));
425: (void)close(s);
426: s = socket(AF_INET, SOCK_STREAM, 0);
427: if (s < 0) {
428: warn("Can't create socket");
429: goto cleanup_url_get;
430: }
431: continue;
432: }
1.27 lukem 433: warn("Can't connect to `%s'", host);
1.25 lukem 434: goto cleanup_url_get;
435: }
1.24 lukem 436:
1.25 lukem 437: fin = fdopen(s, "r+");
438: /*
1.27 lukem 439: * Construct and send the request.
440: * Proxy requests don't want leading /.
1.25 lukem 441: */
1.27 lukem 442: if (isproxy) {
443: fprintf(ttyout, "Requesting %s\n (via %s)\n",
444: url, proxyenv);
445: fprintf(fin, "GET %s HTTP/1.0\r\n\r\n", path);
446: } else {
447: fprintf(ttyout, "Requesting %s\n", url);
448: fprintf(fin, "GET %s HTTP/1.1\r\n", path);
1.25 lukem 449: fprintf(fin, "Host: %s\r\n", host);
450: fprintf(fin, "Connection: close\r\n\r\n");
451: }
452: if (fflush(fin) == EOF) {
453: warn("Writing HTTP request");
454: goto cleanup_url_get;
455: }
1.1 lukem 456:
1.25 lukem 457: /* Read the response */
1.24 lukem 458: if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) {
459: warn("Receiving HTTP reply");
460: goto cleanup_url_get;
461: }
462: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
463: buf[--len] = '\0';
464: if (debug)
1.27 lukem 465: fprintf(ttyout, "received `%s'\n", buf);
1.1 lukem 466:
1.25 lukem 467: cp = strchr(buf, ' ');
468: if (cp == NULL)
469: goto improper;
470: else
471: cp++;
472: if (strncmp(cp, "301", 3) == 0 || strncmp(cp, "302", 3) == 0) {
473: isredirected++;
474: } else if (strncmp(cp, "200", 3)) {
1.27 lukem 475: warnx("Error retrieving file `%s'", cp);
1.25 lukem 476: goto cleanup_url_get;
477: }
478:
479: /* Read the rest of the header. */
1.27 lukem 480: FREEPTR(buf);
1.25 lukem 481: while (1) {
482: if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0))
483: == NULL) {
484: warn("Receiving HTTP reply");
485: goto cleanup_url_get;
486: }
487: while (len > 0 &&
488: (buf[len-1] == '\r' || buf[len-1] == '\n'))
489: buf[--len] = '\0';
490: if (len == 0)
491: break;
492: if (debug)
1.27 lukem 493: fprintf(ttyout, "received `%s'\n", buf);
1.25 lukem 494:
495: /* Look for some headers */
496: cp = buf;
1.1 lukem 497: #define CONTENTLEN "Content-Length: "
1.25 lukem 498: if (strncasecmp(cp, CONTENTLEN,
499: sizeof(CONTENTLEN) - 1) == 0) {
500: cp += sizeof(CONTENTLEN) - 1;
501: filesize = strtol(cp, &ep, 10);
502: if (filesize < 1 || *ep != '\0')
503: goto improper;
504: if (debug)
505: fprintf(ttyout,
506: #ifndef NO_QUAD
507: "parsed length as: %qd\n",
508: (long long)filesize);
509: #else
510: "parsed length as: %ld\n",
511: (long)filesize);
512: #endif
513: #define LASTMOD "Last-Modified: "
514: } else if (strncasecmp(cp, LASTMOD,
515: sizeof(LASTMOD) - 1) == 0) {
516: struct tm parsed;
517: char *t;
518:
519: cp += sizeof(LASTMOD) - 1;
520: /* RFC 1123 */
521: if ((t = strptime(cp,
522: "%a, %d %b %Y %H:%M:%S GMT",
523: &parsed))
524: /* RFC 850 */
525: || (t = strptime(cp,
526: "%a, %d-%b-%y %H:%M:%S GMT",
527: &parsed))
528: /* asctime */
529: || (t = strptime(cp,
530: "%a, %b %d %H:%M:%S %Y",
531: &parsed))) {
532: if (*t == '\0')
533: mtime = mktime(&parsed);
534: if (debug && mtime != -1)
535: fprintf(ttyout,
536: "parsed date as: %s",
537: ctime(&mtime));
538: }
1.24 lukem 539: #define LOCATION "Location: "
1.25 lukem 540: } else if (isredirected &&
541: strncasecmp(cp, LOCATION,
542: sizeof(LOCATION) - 1) == 0) {
543: cp += sizeof(LOCATION) - 1;
544: if (debug)
545: fprintf(ttyout,
546: "parsed location as: %s\n", cp);
547: if (verbose)
548: fprintf(ttyout,
549: "Redirected to %s\n", cp);
550: retval = url_get(cp, proxyenv, outfile);
1.27 lukem 551: goto cleanup_url_get;
1.25 lukem 552: }
1.24 lukem 553: }
1.27 lukem 554: FREEPTR(buf);
1.1 lukem 555: }
556:
1.22 lukem 557: oldintr = oldintp = NULL;
558:
559: /* Open the output file. */
560: if (strcmp(savefile, "-") == 0) {
561: fout = stdout;
562: } else if (*savefile == '|') {
563: oldintp = signal(SIGPIPE, SIG_IGN);
564: fout = popen(savefile + 1, "w");
565: if (fout == NULL) {
1.27 lukem 566: warn("Can't run `%s'", savefile + 1);
1.22 lukem 567: goto cleanup_url_get;
568: }
569: closefunc = pclose;
570: } else {
571: fout = fopen(savefile, "w");
572: if (fout == NULL) {
1.27 lukem 573: warn("Can't open `%s'", savefile);
1.22 lukem 574: goto cleanup_url_get;
575: }
576: closefunc = fclose;
1.1 lukem 577: }
578:
1.22 lukem 579: /* Trap signals */
1.2 lukem 580: if (setjmp(httpabort)) {
581: if (oldintr)
1.3 lukem 582: (void)signal(SIGINT, oldintr);
1.22 lukem 583: if (oldintp)
584: (void)signal(SIGPIPE, oldintp);
1.5 lukem 585: goto cleanup_url_get;
1.2 lukem 586: }
587: oldintr = signal(SIGINT, aborthttp);
588:
1.1 lukem 589: bytes = 0;
590: hashbytes = mark;
591: progressmeter(-1);
1.2 lukem 592:
1.24 lukem 593: /* Finally, suck down the file. */
1.27 lukem 594: buf = xmalloc(BUFSIZ);
1.24 lukem 595: while ((len = fread(buf, sizeof(char), BUFSIZ, fin)) > 0) {
1.1 lukem 596: bytes += len;
1.25 lukem 597: if (fwrite(buf, sizeof(char), len, fout) != len) {
1.27 lukem 598: warn("Writing `%s'", savefile);
1.25 lukem 599: goto cleanup_url_get;
1.1 lukem 600: }
601: if (hash && !progress) {
602: while (bytes >= hashbytes) {
1.22 lukem 603: (void)putc('#', ttyout);
1.1 lukem 604: hashbytes += mark;
605: }
1.22 lukem 606: (void)fflush(ttyout);
1.1 lukem 607: }
608: }
609: if (hash && !progress && bytes > 0) {
610: if (bytes < mark)
1.22 lukem 611: (void)putc('#', ttyout);
612: (void)putc('\n', ttyout);
613: (void)fflush(ttyout);
1.1 lukem 614: }
1.25 lukem 615: if (ferror(fin)) {
616: warn("Reading file");
1.5 lukem 617: goto cleanup_url_get;
1.1 lukem 618: }
1.2 lukem 619: progressmeter(1);
1.25 lukem 620: (void)fflush(fout);
1.3 lukem 621: (void)signal(SIGINT, oldintr);
1.22 lukem 622: if (oldintp)
623: (void)signal(SIGPIPE, oldintp);
1.25 lukem 624: if (closefunc == fclose && mtime != -1) {
625: struct timeval tval[2];
626:
627: (void)gettimeofday(&tval[0], NULL);
628: tval[1].tv_sec = mtime;
629: tval[1].tv_usec = 0;
630: if (futimes(fileno(fout), tval) == -1) {
631: fprintf(ttyout,
632: "Can't change modification time to %s",
633: asctime(localtime(&mtime)));
634: }
635: }
636: if (bytes > 0)
637: ptransfer(0);
1.1 lukem 638:
1.24 lukem 639: retval = 0;
640: goto cleanup_url_get;
1.1 lukem 641:
1.11 lukem 642: noftpautologin:
643: warnx(
644: "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
645: goto cleanup_url_get;
646:
1.1 lukem 647: improper:
1.27 lukem 648: warnx("Improper response from `%s'", host);
1.11 lukem 649:
1.5 lukem 650: cleanup_url_get:
1.23 thorpej 651: resetsockbufsize();
1.24 lukem 652: if (fin != NULL)
653: fclose(fin);
654: else if (s != -1)
1.1 lukem 655: close(s);
1.22 lukem 656: if (closefunc != NULL && fout != NULL)
657: (*closefunc)(fout);
1.29 lukem 658: FREEPTR(savefile);
1.27 lukem 659: FREEPTR(user);
660: FREEPTR(pass);
661: FREEPTR(host);
662: FREEPTR(path);
663: FREEPTR(buf);
1.24 lukem 664: return (retval);
1.1 lukem 665: }
666:
667: /*
1.2 lukem 668: * Abort a http retrieval
669: */
670: void
1.3 lukem 671: aborthttp(notused)
672: int notused;
1.2 lukem 673: {
674:
675: alarmtimer(0);
1.24 lukem 676: fputs("\nHTTP fetch aborted.\n", ttyout);
1.22 lukem 677: (void)fflush(ttyout);
1.2 lukem 678: longjmp(httpabort, 1);
679: }
680:
681: /*
1.1 lukem 682: * Retrieve multiple files from the command line, transferring
1.25 lukem 683: * URLs of the form "host:path", "ftp://host/path" using the
684: * ftp protocol, URLs of the form "http://host/path" using the
685: * http protocol, and URLs of the form "file:///" by simple
686: * copying.
1.3 lukem 687: * If path has a trailing "/", then return (-1);
1.1 lukem 688: * the path will be cd-ed into and the connection remains open,
689: * and the function will return -1 (to indicate the connection
690: * is alive).
691: * If an error occurs the return value will be the offset+1 in
692: * argv[] of the file that caused a problem (i.e, argv[x]
693: * returns x+1)
694: * Otherwise, 0 is returned if all files retrieved successfully.
695: */
696: int
1.22 lukem 697: auto_fetch(argc, argv, outfile)
1.1 lukem 698: int argc;
699: char *argv[];
1.22 lukem 700: char *outfile;
1.1 lukem 701: {
702: static char lasthost[MAXHOSTNAMELEN];
1.27 lukem 703: char portnum[6]; /* large enough for "65535\0" */
1.1 lukem 704: char *xargv[5];
1.27 lukem 705: const char *line;
706: char *cp, *host, *path, *dir, *file;
1.6 lukem 707: char *user, *pass;
1.27 lukem 708: in_port_t port;
1.5 lukem 709: char *ftpproxy, *httpproxy;
1.12 lukem 710: int rval, xargc;
711: volatile int argpos;
1.3 lukem 712: int dirhasglob, filehasglob;
713: char rempath[MAXPATHLEN];
1.1 lukem 714:
1.22 lukem 715: #ifdef __GNUC__ /* to shut up gcc warnings */
716: (void)&outfile;
717: #endif
718:
1.2 lukem 719: argpos = 0;
1.1 lukem 720:
1.2 lukem 721: if (setjmp(toplevel)) {
722: if (connected)
723: disconnect(0, NULL);
1.3 lukem 724: return (argpos + 1);
1.2 lukem 725: }
1.3 lukem 726: (void)signal(SIGINT, (sig_t)intr);
727: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 lukem 728:
1.5 lukem 729: ftpproxy = getenv(FTP_PROXY);
730: httpproxy = getenv(HTTP_PROXY);
1.27 lukem 731: host = path = dir = file = user = pass = NULL;
1.5 lukem 732:
1.1 lukem 733: /*
734: * Loop through as long as there's files to fetch.
735: */
1.27 lukem 736: for (rval = 0; (rval == 0) && (argpos < argc); argpos++) {
1.1 lukem 737: if (strchr(argv[argpos], ':') == NULL)
738: break;
1.27 lukem 739: host = path = dir = file = user = pass = NULL;
740: line = argv[argpos];
1.1 lukem 741:
1.25 lukem 742: #ifndef SMALL
743: /*
744: * Check for about:*
745: */
746: if (strncasecmp(line, ABOUT_URL, sizeof(ABOUT_URL) - 1) == 0) {
1.27 lukem 747: line += sizeof(ABOUT_URL) -1;
748: if (strcasecmp(line, "ftp") == 0) {
1.25 lukem 749: fprintf(ttyout, "%s\n%s\n",
1.26 lukem 750: "This version of ftp has been enhanced by Luke Mewburn <lukem@netbsd.org>.",
1.25 lukem 751: "Execute 'man ftp' for more details");
1.27 lukem 752: } else if (strcasecmp(line, "netbsd") == 0) {
1.25 lukem 753: fprintf(ttyout, "%s\n%s\n",
754: "NetBSD is a freely available and redistributable UNIX-like operating system.",
755: "For more information, see http://www.netbsd.org/index.html");
756: } else {
757: fprintf(ttyout,
1.27 lukem 758: "`%s' is an interesting topic.\n", line);
1.25 lukem 759: }
760: continue;
761: }
762: #endif /* SMALL */
763:
1.1 lukem 764: /*
1.25 lukem 765: * Check for file:// and http:// URLs.
1.1 lukem 766: */
1.25 lukem 767: if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
768: strncasecmp(line, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
1.22 lukem 769: if (url_get(line, httpproxy, outfile) == -1)
1.1 lukem 770: rval = argpos + 1;
771: continue;
772: }
773:
774: /*
1.5 lukem 775: * Try FTP URL-style arguments next. If ftpproxy is
776: * set, use url_get() instead of standard ftp.
777: * Finally, try host:file.
1.1 lukem 778: */
1.6 lukem 779: if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1.27 lukem 780: int urltype;
781:
1.5 lukem 782: if (ftpproxy) {
1.22 lukem 783: if (url_get(line, ftpproxy, outfile) == -1)
1.5 lukem 784: rval = argpos + 1;
785: continue;
786: }
1.27 lukem 787: if ((parse_url(line, "URL", &urltype, &user, &pass,
788: &host, &port, &path) == -1) ||
789: (user != NULL && *user == '\0') ||
790: (pass != NULL && *pass == '\0') ||
791: EMPTYSTRING(host)) {
792: warnx("Invalid URL `%s'", argv[argpos]);
1.6 lukem 793: rval = argpos + 1;
1.27 lukem 794: break;
1.6 lukem 795: }
796: } else { /* classic style `host:file' */
1.27 lukem 797: host = xstrdup(line);
798: cp = strchr(host, ':');
799: if (cp != NULL) {
800: *cp = '\0';
801: path = xstrdup(cp + 1);
802: }
1.6 lukem 803: }
1.1 lukem 804: if (EMPTYSTRING(host)) {
805: rval = argpos + 1;
1.27 lukem 806: break;
1.1 lukem 807: }
808:
809: /*
1.15 lukem 810: * If dir is NULL, the file wasn't specified
1.1 lukem 811: * (URL looked something like ftp://host)
812: */
1.27 lukem 813: dir = path;
1.6 lukem 814: if (dir != NULL)
815: *dir++ = '\0';
1.1 lukem 816:
817: /*
818: * Extract the file and (if present) directory name.
819: */
820: if (! EMPTYSTRING(dir)) {
1.6 lukem 821: cp = strrchr(dir, '/');
1.1 lukem 822: if (cp != NULL) {
823: *cp++ = '\0';
824: file = cp;
825: } else {
826: file = dir;
827: dir = NULL;
828: }
829: }
830: if (debug)
1.22 lukem 831: fprintf(ttyout,
1.27 lukem 832: "auto_fetch: user `%s', pass `%s', host %s:%d, path, `%s', dir `%s', file `%s'\n",
833: user ? user : "", pass ? pass : "",
834: host ? host : "", ntohs(port), path ? path : "",
835: dir ? dir : "", file ? file : "");
1.1 lukem 836:
837: /*
838: * Set up the connection if we don't have one.
839: */
1.27 lukem 840: if (strcasecmp(host, lasthost) != 0) {
1.6 lukem 841: int oautologin;
842:
1.4 lukem 843: (void)strcpy(lasthost, host);
1.1 lukem 844: if (connected)
845: disconnect(0, NULL);
846: xargv[0] = __progname;
847: xargv[1] = host;
848: xargv[2] = NULL;
849: xargc = 2;
1.27 lukem 850: if (port) {
851: snprintf(portnum, sizeof(portnum),
852: "%d", (int)port);
1.1 lukem 853: xargv[2] = portnum;
854: xargv[3] = NULL;
855: xargc = 3;
856: }
1.6 lukem 857: oautologin = autologin;
858: if (user != NULL)
859: autologin = 0;
1.1 lukem 860: setpeer(xargc, xargv);
1.6 lukem 861: autologin = oautologin;
862: if ((connected == 0)
1.24 lukem 863: || ((connected == 1) &&
864: !ftp_login(host, user, pass)) ) {
1.6 lukem 865: warnx("Can't connect or login to host `%s'",
866: host);
1.1 lukem 867: rval = argpos + 1;
1.27 lukem 868: break;
1.1 lukem 869: }
870:
871: /* Always use binary transfers. */
872: setbinary(0, NULL);
873: }
1.27 lukem 874: /* cd back to `/' */
1.6 lukem 875: xargv[0] = "cd";
876: xargv[1] = "/";
877: xargv[2] = NULL;
878: cd(2, xargv);
879: if (! dirchange) {
880: rval = argpos + 1;
1.27 lukem 881: break;
1.1 lukem 882: }
883:
1.3 lukem 884: dirhasglob = filehasglob = 0;
885: if (doglob) {
886: if (! EMPTYSTRING(dir) &&
887: strpbrk(dir, "*?[]{}") != NULL)
888: dirhasglob = 1;
889: if (! EMPTYSTRING(file) &&
890: strpbrk(file, "*?[]{}") != NULL)
891: filehasglob = 1;
892: }
893:
1.27 lukem 894: /* Change directories, if necessary. */
1.3 lukem 895: if (! EMPTYSTRING(dir) && !dirhasglob) {
1.1 lukem 896: xargv[0] = "cd";
897: xargv[1] = dir;
898: xargv[2] = NULL;
899: cd(2, xargv);
900: if (! dirchange) {
901: rval = argpos + 1;
1.27 lukem 902: break;
1.1 lukem 903: }
904: }
905:
906: if (EMPTYSTRING(file)) {
907: rval = -1;
1.27 lukem 908: break;
1.1 lukem 909: }
910:
1.2 lukem 911: if (!verbose)
1.22 lukem 912: fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "",
913: file);
1.2 lukem 914:
1.3 lukem 915: if (dirhasglob) {
916: snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
917: file = rempath;
918: }
919:
1.27 lukem 920: /* Fetch the file(s). */
1.22 lukem 921: xargc = 2;
1.1 lukem 922: xargv[0] = "get";
923: xargv[1] = file;
924: xargv[2] = NULL;
1.3 lukem 925: if (dirhasglob || filehasglob) {
926: int ointeractive;
927:
928: ointeractive = interactive;
929: interactive = 0;
930: xargv[0] = "mget";
1.22 lukem 931: mget(xargc, xargv);
1.4 lukem 932: interactive = ointeractive;
1.22 lukem 933: } else {
934: if (outfile != NULL) {
935: xargv[2] = outfile;
936: xargv[3] = NULL;
937: xargc++;
938: }
939: get(xargc, xargv);
940: if (outfile != NULL && strcmp(outfile, "-") != 0
941: && outfile[0] != '|')
942: outfile = NULL;
943: }
1.2 lukem 944:
1.3 lukem 945: if ((code / 100) != COMPLETE)
1.2 lukem 946: rval = argpos + 1;
1.1 lukem 947: }
948: if (connected && rval != -1)
949: disconnect(0, NULL);
1.27 lukem 950: FREEPTR(host);
951: FREEPTR(path);
952: FREEPTR(user);
953: FREEPTR(pass);
1.1 lukem 954: return (rval);
955: }
CVSweb <webmaster@jp.NetBSD.org>