Annotation of src/usr.bin/ftp/fetch.c, Revision 1.89
1.89 ! lukem 1: /* $NetBSD: fetch.c,v 1.88 1999/10/09 03:00:55 lukem Exp $ */
1.1 lukem 2:
3: /*-
1.84 lukem 4: * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
1.1 lukem 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to The NetBSD Foundation
1.54 lukem 8: * by Luke Mewburn.
1.1 lukem 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:
1.84 lukem 20: * This product includes software developed by the NetBSD
21: * Foundation, Inc. and its contributors.
1.1 lukem 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.89 ! lukem 41: __RCSID("$NetBSD: fetch.c,v 1.88 1999/10/09 03:00:55 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.32 lukem 52: #include <sys/time.h>
1.43 lukem 53: #include <sys/utsname.h>
1.1 lukem 54:
55: #include <netinet/in.h>
56:
1.2 lukem 57: #include <arpa/ftp.h>
1.1 lukem 58: #include <arpa/inet.h>
59:
60: #include <ctype.h>
61: #include <err.h>
1.22 lukem 62: #include <errno.h>
1.1 lukem 63: #include <netdb.h>
64: #include <fcntl.h>
65: #include <stdio.h>
66: #include <stdlib.h>
67: #include <string.h>
68: #include <unistd.h>
1.57 christos 69: #include <time.h>
1.24 lukem 70: #include <util.h>
1.1 lukem 71:
72: #include "ftp_var.h"
73:
1.27 lukem 74: typedef enum {
75: UNKNOWN_URL_T=-1,
76: HTTP_URL_T,
77: FTP_URL_T,
1.53 lukem 78: FILE_URL_T,
79: CLASSIC_URL_T
1.27 lukem 80: } url_t;
81:
1.50 lukem 82: void aborthttp __P((int));
1.54 lukem 83: static int auth_url __P((const char *, char **, const char *,
84: const char *));
1.50 lukem 85: static void base64_encode __P((const char *, size_t, char *));
1.52 lukem 86: static int go_fetch __P((const char *));
87: static int fetch_ftp __P((const char *));
88: static int fetch_url __P((const char *, const char *, char *, char *));
1.27 lukem 89: static int parse_url __P((const char *, const char *, url_t *, char **,
1.82 lukem 90: char **, char **, char **, in_port_t *,
91: char **));
1.50 lukem 92: static void url_decode __P((char *));
1.12 lukem 93:
1.42 lukem 94: static int redirect_loop;
95:
1.12 lukem 96:
1.25 lukem 97: #define ABOUT_URL "about:" /* propaganda */
98: #define FILE_URL "file://" /* file URL prefix */
1.1 lukem 99: #define FTP_URL "ftp://" /* ftp URL prefix */
100: #define HTTP_URL "http://" /* http URL prefix */
101:
102:
103: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
1.27 lukem 104: #define FREEPTR(x) if ((x) != NULL) { free(x); (x) = NULL; }
105:
106: /*
1.44 lukem 107: * Generate authorization response based on given authentication challenge.
108: * Returns -1 if an error occurred, otherwise 0.
109: * Sets response to a malloc(3)ed string; caller should free.
110: */
111: static int
1.54 lukem 112: auth_url(challenge, response, guser, gpass)
1.44 lukem 113: const char *challenge;
114: char **response;
1.54 lukem 115: const char *guser;
116: const char *gpass;
1.44 lukem 117: {
118: char *cp, *ep, *clear, *line, *realm, *scheme;
1.82 lukem 119: char user[BUFSIZ], *pass;
120: int rval;
121: size_t len, clen, rlen;
1.44 lukem 122:
123: *response = NULL;
124: clear = realm = scheme = NULL;
125: rval = -1;
126: line = xstrdup(challenge);
127: cp = line;
128:
129: if (debug)
130: fprintf(ttyout, "auth_url: challenge `%s'\n", challenge);
131:
132: scheme = strsep(&cp, " ");
133: #define SCHEME_BASIC "Basic"
134: if (strncasecmp(scheme, SCHEME_BASIC, sizeof(SCHEME_BASIC) - 1) != 0) {
135: warnx("Unsupported WWW Authentication challenge - `%s'",
136: challenge);
137: goto cleanup_auth_url;
138: }
139: cp += strspn(cp, " ");
140:
141: #define REALM "realm=\""
142: if (strncasecmp(cp, REALM, sizeof(REALM) - 1) == 0)
143: cp += sizeof(REALM) - 1;
144: else {
145: warnx("Unsupported WWW Authentication challenge - `%s'",
146: challenge);
147: goto cleanup_auth_url;
148: }
149: if ((ep = strchr(cp, '\"')) != NULL) {
150: size_t len = ep - cp;
151:
152: realm = (char *)xmalloc(len + 1);
1.78 lukem 153: (void)strlcpy(realm, cp, len + 1);
1.44 lukem 154: } else {
155: warnx("Unsupported WWW Authentication challenge - `%s'",
156: challenge);
157: goto cleanup_auth_url;
158: }
159:
1.78 lukem 160: if (guser != NULL)
161: (void)strlcpy(user, guser, sizeof(user));
162: else {
1.54 lukem 163: fprintf(ttyout, "Username for `%s': ", realm);
164: (void)fflush(ttyout);
165: if (fgets(user, sizeof(user) - 1, stdin) == NULL)
166: goto cleanup_auth_url;
167: user[strlen(user) - 1] = '\0';
168: }
169: if (gpass != NULL)
170: pass = (char *)gpass;
171: else
172: pass = getpass("Password: ");
1.44 lukem 173:
1.72 lukem 174: clen = strlen(user) + strlen(pass) + 2; /* user + ":" + pass + "\0" */
175: clear = (char *)xmalloc(clen);
1.78 lukem 176: (void)strlcpy(clear, user, clen);
177: (void)strlcat(clear, ":", clen);
178: (void)strlcat(clear, pass, clen);
1.54 lukem 179: if (gpass == NULL)
1.80 lukem 180: memset(pass, 0, strlen(pass));
1.44 lukem 181:
1.64 lukem 182: /* scheme + " " + enc + "\0" */
1.74 lukem 183: rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
1.72 lukem 184: *response = (char *)xmalloc(rlen);
1.78 lukem 185: (void)strlcpy(*response, scheme, rlen);
1.72 lukem 186: len = strlcat(*response, " ", rlen);
187: base64_encode(clear, clen, *response + len);
1.80 lukem 188: memset(clear, 0, clen);
1.44 lukem 189: rval = 0;
190:
191: cleanup_auth_url:
192: FREEPTR(clear);
193: FREEPTR(line);
194: FREEPTR(realm);
195: return (rval);
196: }
197:
198: /*
199: * Encode len bytes starting at clear using base64 encoding into encoded,
200: * which should be at least ((len + 2) * 4 / 3 + 1) in size.
201: */
202: void
1.50 lukem 203: base64_encode(clear, len, encoded)
1.44 lukem 204: const char *clear;
205: size_t len;
206: char *encoded;
207: {
208: static const char enc[] =
209: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.82 lukem 210: char *cp;
211: int i;
1.44 lukem 212:
213: cp = encoded;
214: for (i = 0; i < len; i += 3) {
215: *(cp++) = enc[((clear[i + 0] >> 2))];
216: *(cp++) = enc[((clear[i + 0] << 4) & 0x30)
217: | ((clear[i + 1] >> 4) & 0x0f)];
218: *(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
219: | ((clear[i + 2] >> 6) & 0x03)];
220: *(cp++) = enc[((clear[i + 2] ) & 0x3f)];
221: }
222: *cp = '\0';
223: while (i-- > len)
224: *(--cp) = '=';
225: }
226:
1.50 lukem 227: /*
228: * Decode %xx escapes in given string, `in-place'.
229: */
230: static void
231: url_decode(url)
232: char *url;
233: {
234: unsigned char *p, *q;
235:
236: if (EMPTYSTRING(url))
237: return;
1.75 lukem 238: p = q = (unsigned char *)url;
1.50 lukem 239:
240: #define HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10))
241: while (*p) {
242: if (p[0] == '%'
243: && p[1] && isxdigit((unsigned char)p[1])
244: && p[2] && isxdigit((unsigned char)p[2])) {
245: *q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]);
246: p+=3;
247: } else
248: *q++ = *p++;
249: }
250: *q = '\0';
251: }
252:
1.44 lukem 253:
254: /*
1.27 lukem 255: * Parse URL of form:
1.63 lukem 256: * <type>://[<user>[:<password>@]]<host>[:<port>][/<path>]
1.27 lukem 257: * Returns -1 if a parse error occurred, otherwise 0.
1.50 lukem 258: * It's the caller's responsibility to url_decode() the returned
259: * user, pass and path.
1.63 lukem 260: *
1.27 lukem 261: * Sets type to url_t, each of the given char ** pointers to a
262: * malloc(3)ed strings of the relevant section, and port to
1.41 lukem 263: * the number given, or ftpport if ftp://, or httpport if http://.
1.52 lukem 264: *
1.63 lukem 265: * If <host> is surrounded by `[' and ']', it's parsed as an
266: * IPv6 address (as per draft-ietf-ipngwg-url-literal-01.txt).
267: *
268: * XXX: this is not totally RFC 1738 compliant; <path> will have the
1.53 lukem 269: * leading `/' unless it's an ftp:// URL, as this makes things easier
270: * for file:// and http:// URLs. ftp:// URLs have the `/' between the
271: * host and the url-path removed, but any additional leading slashes
272: * in the url-path are retained (because they imply that we should
273: * later do "CWD" with a null argument).
274: *
275: * Examples:
276: * input url output path
277: * --------- -----------
1.63 lukem 278: * "ftp://host" NULL
279: * "http://host/" NULL
280: * "file://host/dir/file" "dir/file"
281: * "ftp://host/" ""
282: * "ftp://host//" NULL
1.53 lukem 283: * "ftp://host//dir/file" "/dir/file"
1.27 lukem 284: */
285: static int
1.82 lukem 286: parse_url(url, desc, type, user, pass, host, port, portnum, path)
1.27 lukem 287: const char *url;
288: const char *desc;
289: url_t *type;
290: char **user;
291: char **pass;
292: char **host;
1.60 itojun 293: char **port;
1.82 lukem 294: in_port_t *portnum;
1.27 lukem 295: char **path;
296: {
1.82 lukem 297: const char *origurl;
298: char *cp, *ep, *thost, *tport;
299: size_t len;
1.27 lukem 300:
301: if (url == NULL || desc == NULL || type == NULL || user == NULL
1.82 lukem 302: || pass == NULL || host == NULL || port == NULL || portnum == NULL
303: || path == NULL)
1.27 lukem 304: errx(1, "parse_url: invoked with NULL argument!");
305:
1.82 lukem 306: origurl = url;
1.27 lukem 307: *type = UNKNOWN_URL_T;
1.63 lukem 308: *user = *pass = *host = *port = *path = NULL;
1.82 lukem 309: *portnum = 0;
1.63 lukem 310: tport = NULL;
1.27 lukem 311:
312: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
313: url += sizeof(HTTP_URL) - 1;
314: *type = HTTP_URL_T;
1.82 lukem 315: *portnum = HTTP_PORT;
1.63 lukem 316: tport = httpport;
1.27 lukem 317: } else if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
318: url += sizeof(FTP_URL) - 1;
319: *type = FTP_URL_T;
1.82 lukem 320: *portnum = FTP_PORT;
1.63 lukem 321: tport = ftpport;
1.27 lukem 322: } else if (strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) {
323: url += sizeof(FILE_URL) - 1;
324: *type = FILE_URL_T;
325: } else {
326: warnx("Invalid %s `%s'", desc, url);
327: cleanup_parse_url:
328: FREEPTR(*user);
329: FREEPTR(*pass);
330: FREEPTR(*host);
1.60 itojun 331: FREEPTR(*port);
1.27 lukem 332: FREEPTR(*path);
333: return (-1);
334: }
335:
336: if (*url == '\0')
337: return (0);
338:
339: /* find [user[:pass]@]host[:port] */
340: ep = strchr(url, '/');
341: if (ep == NULL)
342: thost = xstrdup(url);
343: else {
1.50 lukem 344: len = ep - url;
1.27 lukem 345: thost = (char *)xmalloc(len + 1);
1.78 lukem 346: (void)strlcpy(thost, url, len + 1);
1.53 lukem 347: if (*type == FTP_URL_T) /* skip first / for ftp URLs */
348: ep++;
1.27 lukem 349: *path = xstrdup(ep);
350: }
351:
1.63 lukem 352: cp = strchr(thost, '@'); /* look for user[:pass]@ in URLs */
1.54 lukem 353: if (cp != NULL) {
354: if (*type == FTP_URL_T)
355: anonftp = 0; /* disable anonftp */
1.27 lukem 356: *user = thost;
357: *cp = '\0';
1.63 lukem 358: thost = xstrdup(cp + 1);
1.27 lukem 359: cp = strchr(*user, ':');
360: if (cp != NULL) {
361: *cp = '\0';
362: *pass = xstrdup(cp + 1);
363: }
1.63 lukem 364: }
365:
366: #ifdef INET6
367: /*
368: * Check if thost is an encoded IPv6 address, as per
369: * draft-ietf-ipngwg-url-literal-01.txt:
370: * `[' ipv6-address ']'
371: */
372: if (*thost == '[') {
373: cp = thost + 1;
374: if ((ep = strchr(cp, ']')) == NULL ||
375: (ep[1] != '\0' && ep[1] != '\0')) {
376: warnx("Invalid address `%s' in %s `%s'",
1.82 lukem 377: thost, desc, origurl);
1.63 lukem 378: goto cleanup_parse_url;
379: }
380: len = ep - cp; /* change `[xxx]' -> `xxx' */
381: memmove(thost, thost + 1, len);
382: thost[len] = '\0';
383: if (! isipv6addr(thost)) {
384: warnx("Invalid IPv6 address `%s' in %s `%s'",
1.82 lukem 385: thost, desc, origurl);
1.63 lukem 386: goto cleanup_parse_url;
387: }
388: cp = ep + 1;
389: if (*cp == ':')
390: cp++;
391: else
392: cp = NULL;
1.27 lukem 393: } else
1.63 lukem 394: #endif /* INET6 */
395: if ((cp = strchr(thost, ':')) != NULL)
396: *cp++ = '\0';
397: *host = thost;
1.60 itojun 398:
1.27 lukem 399: /* look for [:port] */
400: if (cp != NULL) {
1.82 lukem 401: long nport;
1.27 lukem 402:
1.63 lukem 403: nport = strtol(cp, &ep, 10);
1.82 lukem 404: if (*ep != '\0' && ep == cp) {
405: struct servent *svp;
406:
407: svp = getservbyname(cp, "tcp");
408: if (svp == NULL) {
409: warnx("Unknown port `%s' in %s `%s'",
410: cp, desc, origurl);
411: goto cleanup_parse_url;
412: } else
413: nport = ntohs(svp->s_port);
414: } else if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') {
415: warnx("Invalid port `%s' in %s `%s'", cp, desc,
416: origurl);
1.27 lukem 417: goto cleanup_parse_url;
418: }
1.82 lukem 419: *portnum = nport;
1.63 lukem 420: tport = cp;
1.27 lukem 421: }
1.82 lukem 422:
1.63 lukem 423: if (tport != NULL);
424: *port = xstrdup(tport);
1.81 lukem 425: if (*path == NULL)
426: *path = xstrdup("");
1.27 lukem 427:
428: if (debug)
429: fprintf(ttyout,
1.82 lukem 430: "parse_url: user `%s' pass `%s' host %s:%s(%d) path `%s'\n",
1.53 lukem 431: *user ? *user : "<null>", *pass ? *pass : "<null>",
1.60 itojun 432: *host ? *host : "<null>", *port ? *port : "<null>",
1.82 lukem 433: *portnum ? *portnum : -1, *path ? *path : "<null>");
1.27 lukem 434:
435: return (0);
436: }
437:
1.89 ! lukem 438: sigjmp_buf httpabort;
1.2 lukem 439:
1.1 lukem 440: /*
1.50 lukem 441: * Retrieve URL, via a proxy if necessary, using HTTP.
442: * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
443: * http_proxy as appropriate.
444: * Supports HTTP redirects.
1.41 lukem 445: * Returns -1 on failure, 0 on completed xfer, 1 if ftp connection
446: * is still open (e.g, ftp xfer with trailing /)
1.1 lukem 447: */
1.12 lukem 448: static int
1.52 lukem 449: fetch_url(url, proxyenv, proxyauth, wwwauth)
1.44 lukem 450: const char *url;
451: const char *proxyenv;
452: char *proxyauth;
453: char *wwwauth;
1.1 lukem 454: {
1.66 christos 455: #ifdef NI_NUMERICHOST
456: struct addrinfo hints, *res = NULL;
457: int error;
458: #else
459: struct sockaddr_in sin;
460: struct hostent *hp = NULL;
461: #endif
1.42 lukem 462: volatile sig_t oldintr, oldintp;
463: volatile int s;
1.45 lukem 464: int ischunked, isproxy, rval, hcode;
1.42 lukem 465: size_t len;
1.73 lukem 466: static size_t bufsize;
467: static char *xferbuf;
1.44 lukem 468: char *cp, *ep, *buf, *savefile;
469: char *auth, *location, *message;
1.63 lukem 470: char *user, *pass, *host, *port, *path, *decodedpath;
1.54 lukem 471: char *puser, *ppass;
1.42 lukem 472: off_t hashbytes;
473: int (*closefunc) __P((FILE *));
474: FILE *fin, *fout;
475: time_t mtime;
476: url_t urltype;
1.60 itojun 477: in_port_t portnum;
1.1 lukem 478:
1.86 lukem 479: oldintr = oldintp = NULL;
1.22 lukem 480: closefunc = NULL;
1.24 lukem 481: fin = fout = NULL;
1.1 lukem 482: s = -1;
1.44 lukem 483: buf = savefile = NULL;
484: auth = location = message = NULL;
1.52 lukem 485: ischunked = isproxy = hcode = 0;
1.42 lukem 486: rval = 1;
1.54 lukem 487: user = pass = host = path = decodedpath = puser = ppass = NULL;
1.1 lukem 488:
1.27 lukem 489: #ifdef __GNUC__ /* shut up gcc warnings */
1.22 lukem 490: (void)&closefunc;
1.24 lukem 491: (void)&fin;
1.22 lukem 492: (void)&fout;
1.24 lukem 493: (void)&buf;
1.12 lukem 494: (void)&savefile;
1.42 lukem 495: (void)&rval;
1.28 fvdl 496: (void)&isproxy;
1.51 christos 497: (void)&hcode;
498: (void)&ischunked;
499: (void)&message;
500: (void)&location;
501: (void)&auth;
502: (void)&decodedpath;
1.12 lukem 503: #endif
504:
1.82 lukem 505: if (parse_url(url, "URL", &urltype, &user, &pass, &host, &port,
506: &portnum, &path) == -1)
1.42 lukem 507: goto cleanup_fetch_url;
1.5 lukem 508:
1.27 lukem 509: if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
510: && strcasecmp(host, "localhost") != 0) {
511: warnx("No support for non local file URL `%s'", url);
1.42 lukem 512: goto cleanup_fetch_url;
1.9 lukem 513: }
1.27 lukem 514:
1.9 lukem 515: if (EMPTYSTRING(path)) {
1.42 lukem 516: if (urltype == FTP_URL_T) {
1.52 lukem 517: rval = fetch_ftp(url);
1.42 lukem 518: goto cleanup_fetch_url;
519: }
1.27 lukem 520: if (urltype != HTTP_URL_T || outfile == NULL) {
521: warnx("Invalid URL (no file after host) `%s'", url);
1.42 lukem 522: goto cleanup_fetch_url;
1.27 lukem 523: }
1.9 lukem 524: }
1.1 lukem 525:
1.50 lukem 526: decodedpath = xstrdup(path);
527: url_decode(decodedpath);
528:
1.22 lukem 529: if (outfile)
1.29 lukem 530: savefile = xstrdup(outfile);
1.22 lukem 531: else {
1.50 lukem 532: cp = strrchr(decodedpath, '/'); /* find savefile */
1.29 lukem 533: if (cp != NULL)
534: savefile = xstrdup(cp + 1);
1.22 lukem 535: else
1.50 lukem 536: savefile = xstrdup(decodedpath);
1.22 lukem 537: }
1.9 lukem 538: if (EMPTYSTRING(savefile)) {
1.42 lukem 539: if (urltype == FTP_URL_T) {
1.52 lukem 540: rval = fetch_ftp(url);
1.42 lukem 541: goto cleanup_fetch_url;
542: }
1.27 lukem 543: warnx("Invalid URL (no file after directory) `%s'", url);
1.42 lukem 544: goto cleanup_fetch_url;
1.52 lukem 545: } else {
546: if (debug)
547: fprintf(ttyout, "got savefile as `%s'\n", savefile);
1.9 lukem 548: }
1.1 lukem 549:
1.25 lukem 550: filesize = -1;
551: mtime = -1;
552: if (urltype == FILE_URL_T) { /* file:// URLs */
553: struct stat sb;
554:
555: direction = "copied";
1.50 lukem 556: fin = fopen(decodedpath, "r");
1.25 lukem 557: if (fin == NULL) {
1.50 lukem 558: warn("Cannot open file `%s'", decodedpath);
1.42 lukem 559: goto cleanup_fetch_url;
1.5 lukem 560: }
1.25 lukem 561: if (fstat(fileno(fin), &sb) == 0) {
562: mtime = sb.st_mtime;
563: filesize = sb.st_size;
564: }
1.49 lukem 565: if (verbose)
1.50 lukem 566: fprintf(ttyout, "Copying %s\n", decodedpath);
1.25 lukem 567: } else { /* ftp:// or http:// URLs */
1.65 lukem 568: char *leading;
569: int hasleading;
570:
1.42 lukem 571: if (proxyenv == NULL) {
572: if (urltype == HTTP_URL_T)
573: proxyenv = httpproxy;
574: else if (urltype == FTP_URL_T)
575: proxyenv = ftpproxy;
576: }
1.25 lukem 577: direction = "retrieved";
578: if (proxyenv != NULL) { /* use proxy */
1.27 lukem 579: url_t purltype;
1.54 lukem 580: char *phost, *ppath;
1.60 itojun 581: char *pport;
1.27 lukem 582:
583: isproxy = 1;
584:
585: /* check URL against list of no_proxied sites */
586: if (no_proxy != NULL) {
587: char *np, *np_copy;
588: long np_port;
589: size_t hlen, plen;
590:
591: np_copy = xstrdup(no_proxy);
592: hlen = strlen(host);
593: while ((cp = strsep(&np_copy, " ,")) != NULL) {
594: if (*cp == '\0')
595: continue;
1.60 itojun 596: if ((np = strrchr(cp, ':')) != NULL) {
1.27 lukem 597: *np = '\0';
598: np_port =
599: strtol(np + 1, &ep, 10);
600: if (*ep != '\0')
601: continue;
1.82 lukem 602: if (np_port != portnum)
1.27 lukem 603: continue;
604: }
605: plen = strlen(cp);
606: if (strncasecmp(host + hlen - plen,
607: cp, plen) == 0) {
608: isproxy = 0;
609: break;
610: }
611: }
612: FREEPTR(np_copy);
1.25 lukem 613: }
1.1 lukem 614:
1.27 lukem 615: if (isproxy) {
616: if (parse_url(proxyenv, "proxy URL", &purltype,
1.82 lukem 617: &puser, &ppass, &phost, &pport, &portnum,
618: &ppath) == -1)
1.42 lukem 619: goto cleanup_fetch_url;
1.27 lukem 620:
621: if ((purltype != HTTP_URL_T
622: && purltype != FTP_URL_T) ||
623: EMPTYSTRING(phost) ||
624: (! EMPTYSTRING(ppath)
625: && strcmp(ppath, "/") != 0)) {
626: warnx("Malformed proxy URL `%s'",
627: proxyenv);
628: FREEPTR(phost);
1.60 itojun 629: FREEPTR(pport);
1.27 lukem 630: FREEPTR(ppath);
1.42 lukem 631: goto cleanup_fetch_url;
1.27 lukem 632: }
1.12 lukem 633:
1.27 lukem 634: FREEPTR(host);
635: host = phost;
1.62 tron 636: FREEPTR(port);
637: port = pport;
1.27 lukem 638: FREEPTR(path);
1.54 lukem 639: path = xstrdup(url);
1.27 lukem 640: FREEPTR(ppath);
641: }
642: } /* proxyenv != NULL */
1.25 lukem 643:
1.66 christos 644: #ifndef NI_NUMERICHOST
1.25 lukem 645: memset(&sin, 0, sizeof(sin));
646: sin.sin_family = AF_INET;
647:
648: if (isdigit((unsigned char)host[0])) {
649: if (inet_aton(host, &sin.sin_addr) == 0) {
1.27 lukem 650: warnx("Invalid IP address `%s'", host);
1.42 lukem 651: goto cleanup_fetch_url;
1.25 lukem 652: }
653: } else {
654: hp = gethostbyname(host);
655: if (hp == NULL) {
656: warnx("%s: %s", host, hstrerror(h_errno));
1.42 lukem 657: goto cleanup_fetch_url;
1.25 lukem 658: }
659: if (hp->h_addrtype != AF_INET) {
1.27 lukem 660: warnx("`%s': not an Internet address?", host);
1.42 lukem 661: goto cleanup_fetch_url;
1.25 lukem 662: }
1.66 christos 663: if (hp->h_length > sizeof(sin.sin_addr))
664: hp->h_length = sizeof(sin.sin_addr);
1.25 lukem 665: memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
1.1 lukem 666: }
1.82 lukem 667: sin.sin_port = htons(portnum);
1.25 lukem 668:
669: s = socket(AF_INET, SOCK_STREAM, 0);
670: if (s == -1) {
671: warn("Can't create socket");
1.42 lukem 672: goto cleanup_fetch_url;
1.22 lukem 673: }
1.1 lukem 674:
1.25 lukem 675: while (xconnect(s, (struct sockaddr *)&sin,
676: sizeof(sin)) == -1) {
677: if (errno == EINTR)
678: continue;
679: if (hp && hp->h_addr_list[1]) {
680: int oerrno = errno;
681: char *ia;
682:
683: ia = inet_ntoa(sin.sin_addr);
684: errno = oerrno;
1.27 lukem 685: warn("Connect to address `%s'", ia);
1.25 lukem 686: hp->h_addr_list++;
687: memcpy(&sin.sin_addr, hp->h_addr_list[0],
688: (size_t)hp->h_length);
1.49 lukem 689: if (verbose)
690: fprintf(ttyout, "Trying %s...\n",
691: inet_ntoa(sin.sin_addr));
1.25 lukem 692: (void)close(s);
693: s = socket(AF_INET, SOCK_STREAM, 0);
694: if (s < 0) {
695: warn("Can't create socket");
1.42 lukem 696: goto cleanup_fetch_url;
1.25 lukem 697: }
698: continue;
699: }
1.27 lukem 700: warn("Can't connect to `%s'", host);
1.42 lukem 701: goto cleanup_fetch_url;
1.25 lukem 702: }
1.60 itojun 703: #else
704: memset(&hints, 0, sizeof(hints));
1.65 lukem 705: hints.ai_flags = 0;
1.60 itojun 706: hints.ai_family = AF_UNSPEC;
707: hints.ai_socktype = SOCK_STREAM;
708: hints.ai_protocol = 0;
709: error = getaddrinfo(host, port, &hints, &res);
710: if (error) {
1.69 mycroft 711: warnx(gai_strerror(error));
1.60 itojun 712: goto cleanup_fetch_url;
713: }
714:
715: while (1) {
716: s = socket(res->ai_family,
717: res->ai_socktype, res->ai_protocol);
718: if (s < 0) {
719: warn("Can't create socket");
720: goto cleanup_fetch_url;
721: }
722:
723: if (xconnect(s, res->ai_addr, res->ai_addrlen) < 0) {
724: char hbuf[MAXHOSTNAMELEN];
725: getnameinfo(res->ai_addr, res->ai_addrlen,
726: hbuf, sizeof(hbuf), NULL, 0,
727: NI_NUMERICHOST);
728: warn("Connect to address `%s'", hbuf);
729: close(s);
730: res = res->ai_next;
731: if (res) {
1.63 lukem 732: getnameinfo(res->ai_addr,
733: res->ai_addrlen, hbuf, sizeof(hbuf),
734: NULL, 0, NI_NUMERICHOST);
1.60 itojun 735: if (verbose)
1.63 lukem 736: fprintf(ttyout,
737: "Trying %s...\n", hbuf);
1.60 itojun 738: continue;
739: }
740: warn("Can't connect to %s", host);
741: goto cleanup_fetch_url;
742: }
743:
744: break;
745: }
746: #endif
747:
1.24 lukem 748:
1.25 lukem 749: fin = fdopen(s, "r+");
750: /*
1.27 lukem 751: * Construct and send the request.
1.25 lukem 752: */
1.65 lukem 753: if (verbose)
754: fprintf(ttyout, "Requesting %s\n", url);
755: leading = " (";
756: hasleading = 0;
1.27 lukem 757: if (isproxy) {
1.65 lukem 758: if (verbose) {
759: fprintf(ttyout, "%svia %s:%s", leading,
760: host, port);
761: leading = ", ";
762: hasleading++;
763: }
1.44 lukem 764: fprintf(fin, "GET %s HTTP/1.0\r\n", path);
1.47 lukem 765: if (flushcache)
766: fprintf(fin, "Pragma: no-cache\r\n");
1.27 lukem 767: } else {
1.43 lukem 768: struct utsname unam;
769:
1.27 lukem 770: fprintf(fin, "GET %s HTTP/1.1\r\n", path);
1.60 itojun 771: fprintf(fin, "Host: %s:%s\r\n", host, port);
1.40 explorer 772: fprintf(fin, "Accept: */*\r\n");
1.43 lukem 773: if (uname(&unam) != -1) {
774: fprintf(fin, "User-Agent: %s-%s/ftp\r\n",
775: unam.sysname, unam.release);
776: }
1.42 lukem 777: fprintf(fin, "Connection: close\r\n");
1.47 lukem 778: if (flushcache)
779: fprintf(fin, "Cache-Control: no-cache\r\n");
1.25 lukem 780: }
1.44 lukem 781: if (wwwauth) {
1.65 lukem 782: if (verbose) {
783: fprintf(ttyout, "%swith authorization",
784: leading);
785: leading = ", ";
786: hasleading++;
787: }
1.44 lukem 788: fprintf(fin, "Authorization: %s\r\n", wwwauth);
789: }
1.46 lukem 790: if (proxyauth) {
1.65 lukem 791: if (verbose) {
1.49 lukem 792: fprintf(ttyout,
1.65 lukem 793: "%swith proxy authorization", leading);
794: leading = ", ";
795: hasleading++;
796: }
1.44 lukem 797: fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
1.46 lukem 798: }
1.65 lukem 799: if (verbose && hasleading)
800: fputs(")\n", ttyout);
1.44 lukem 801: fprintf(fin, "\r\n");
1.25 lukem 802: if (fflush(fin) == EOF) {
803: warn("Writing HTTP request");
1.42 lukem 804: goto cleanup_fetch_url;
1.25 lukem 805: }
1.1 lukem 806:
1.25 lukem 807: /* Read the response */
1.24 lukem 808: if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0)) == NULL) {
809: warn("Receiving HTTP reply");
1.42 lukem 810: goto cleanup_fetch_url;
1.24 lukem 811: }
812: while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n'))
813: buf[--len] = '\0';
814: if (debug)
1.27 lukem 815: fprintf(ttyout, "received `%s'\n", buf);
1.1 lukem 816:
1.42 lukem 817: /* Determine HTTP response code */
1.25 lukem 818: cp = strchr(buf, ' ');
819: if (cp == NULL)
820: goto improper;
821: else
822: cp++;
1.42 lukem 823: hcode = strtol(cp, &ep, 10);
824: if (*ep != '\0' && !isspace((unsigned char)*ep))
825: goto improper;
826: message = xstrdup(cp);
1.25 lukem 827:
828: /* Read the rest of the header. */
1.27 lukem 829: FREEPTR(buf);
1.25 lukem 830: while (1) {
831: if ((buf = fparseln(fin, &len, NULL, "\0\0\0", 0))
832: == NULL) {
833: warn("Receiving HTTP reply");
1.42 lukem 834: goto cleanup_fetch_url;
1.25 lukem 835: }
836: while (len > 0 &&
837: (buf[len-1] == '\r' || buf[len-1] == '\n'))
838: buf[--len] = '\0';
839: if (len == 0)
840: break;
841: if (debug)
1.27 lukem 842: fprintf(ttyout, "received `%s'\n", buf);
1.25 lukem 843:
844: /* Look for some headers */
845: cp = buf;
1.42 lukem 846:
1.1 lukem 847: #define CONTENTLEN "Content-Length: "
1.25 lukem 848: if (strncasecmp(cp, CONTENTLEN,
1.44 lukem 849: sizeof(CONTENTLEN) - 1) == 0) {
1.25 lukem 850: cp += sizeof(CONTENTLEN) - 1;
851: filesize = strtol(cp, &ep, 10);
852: if (filesize < 1 || *ep != '\0')
853: goto improper;
854: if (debug)
855: fprintf(ttyout,
856: #ifndef NO_QUAD
1.75 lukem 857: "parsed length as: %lld\n",
1.25 lukem 858: (long long)filesize);
859: #else
860: "parsed length as: %ld\n",
861: (long)filesize);
862: #endif
1.42 lukem 863:
1.25 lukem 864: #define LASTMOD "Last-Modified: "
865: } else if (strncasecmp(cp, LASTMOD,
1.44 lukem 866: sizeof(LASTMOD) - 1) == 0) {
1.25 lukem 867: struct tm parsed;
868: char *t;
869:
870: cp += sizeof(LASTMOD) - 1;
871: /* RFC 1123 */
872: if ((t = strptime(cp,
873: "%a, %d %b %Y %H:%M:%S GMT",
874: &parsed))
875: /* RFC 850 */
876: || (t = strptime(cp,
877: "%a, %d-%b-%y %H:%M:%S GMT",
878: &parsed))
879: /* asctime */
880: || (t = strptime(cp,
881: "%a, %b %d %H:%M:%S %Y",
882: &parsed))) {
1.38 lukem 883: parsed.tm_isdst = -1;
1.25 lukem 884: if (*t == '\0')
1.76 lukem 885: mtime = timegm(&parsed);
1.38 lukem 886: if (debug && mtime != -1) {
1.25 lukem 887: fprintf(ttyout,
888: "parsed date as: %s",
889: ctime(&mtime));
1.38 lukem 890: }
1.25 lukem 891: }
1.42 lukem 892:
1.24 lukem 893: #define LOCATION "Location: "
1.42 lukem 894: } else if (strncasecmp(cp, LOCATION,
1.44 lukem 895: sizeof(LOCATION) - 1) == 0) {
1.25 lukem 896: cp += sizeof(LOCATION) - 1;
1.42 lukem 897: location = xstrdup(cp);
1.25 lukem 898: if (debug)
899: fprintf(ttyout,
900: "parsed location as: %s\n", cp);
1.44 lukem 901:
1.45 lukem 902: #define TRANSENC "Transfer-Encoding: "
903: } else if (strncasecmp(cp, TRANSENC,
904: sizeof(TRANSENC) - 1) == 0) {
905: cp += sizeof(TRANSENC) - 1;
906: if (strcasecmp(cp, "chunked") != 0) {
907: warnx(
908: "Unsupported transfer encoding - `%s'",
909: cp);
910: goto cleanup_fetch_url;
911: }
912: ischunked++;
913: if (debug)
914: fprintf(ttyout,
915: "using chunked encoding\n");
916:
1.44 lukem 917: #define PROXYAUTH "Proxy-Authenticate: "
918: } else if (strncasecmp(cp, PROXYAUTH,
919: sizeof(PROXYAUTH) - 1) == 0) {
920: cp += sizeof(PROXYAUTH) - 1;
921: FREEPTR(auth);
922: auth = xstrdup(cp);
923: if (debug)
924: fprintf(ttyout,
925: "parsed proxy-auth as: %s\n", cp);
926:
927: #define WWWAUTH "WWW-Authenticate: "
928: } else if (strncasecmp(cp, WWWAUTH,
929: sizeof(WWWAUTH) - 1) == 0) {
930: cp += sizeof(WWWAUTH) - 1;
931: FREEPTR(auth);
932: auth = xstrdup(cp);
933: if (debug)
934: fprintf(ttyout,
935: "parsed www-auth as: %s\n", cp);
936:
1.25 lukem 937: }
1.44 lukem 938:
1.24 lukem 939: }
1.54 lukem 940: /* finished parsing header */
1.27 lukem 941: FREEPTR(buf);
1.1 lukem 942:
1.52 lukem 943: switch (hcode) {
944: case 200:
945: break;
946: case 300:
947: case 301:
948: case 302:
949: case 303:
950: case 305:
951: if (EMPTYSTRING(location)) {
952: warnx(
953: "No redirection Location provided by server");
954: goto cleanup_fetch_url;
955: }
956: if (redirect_loop++ > 5) {
957: warnx("Too many redirections requested");
958: goto cleanup_fetch_url;
959: }
960: if (hcode == 305) {
961: if (verbose)
962: fprintf(ttyout, "Redirected via %s\n",
963: location);
964: rval = fetch_url(url, location,
965: proxyauth, wwwauth);
966: } else {
967: if (verbose)
968: fprintf(ttyout, "Redirected to %s\n",
969: location);
970: rval = go_fetch(location);
971: }
1.42 lukem 972: goto cleanup_fetch_url;
1.52 lukem 973: case 401:
974: case 407:
975: {
976: char **authp;
1.54 lukem 977: char *auser, *apass;
1.52 lukem 978:
979: fprintf(ttyout, "%s\n", message);
980: if (EMPTYSTRING(auth)) {
981: warnx(
982: "No authentication challenge provided by server");
983: goto cleanup_fetch_url;
984: }
1.54 lukem 985: if (hcode == 401) {
986: authp = &wwwauth;
987: auser = user;
988: apass = pass;
989: } else {
990: authp = &proxyauth;
991: auser = puser;
992: apass = ppass;
993: }
1.52 lukem 994: if (*authp != NULL) {
995: char reply[10];
996:
997: fprintf(ttyout,
998: "Authorization failed. Retry (y/n)? ");
999: if (fgets(reply, sizeof(reply), stdin) != NULL
1000: && tolower(reply[0]) != 'y')
1001: goto cleanup_fetch_url;
1.54 lukem 1002: auser = NULL;
1003: apass = NULL;
1.52 lukem 1004: }
1.54 lukem 1005: if (auth_url(auth, authp, auser, apass) == 0) {
1.52 lukem 1006: rval = fetch_url(url, proxyenv,
1007: proxyauth, wwwauth);
1.80 lukem 1008: memset(*authp, 0, strlen(*authp));
1.52 lukem 1009: FREEPTR(*authp);
1010: }
1.42 lukem 1011: goto cleanup_fetch_url;
1.52 lukem 1012: }
1013: default:
1014: if (message)
1015: warnx("Error retrieving file - `%s'", message);
1016: else
1017: warnx("Unknown error retrieving file");
1.44 lukem 1018: goto cleanup_fetch_url;
1019: }
1.52 lukem 1020: } /* end of ftp:// or http:// specific setup */
1.42 lukem 1021:
1.22 lukem 1022: /* Open the output file. */
1023: if (strcmp(savefile, "-") == 0) {
1024: fout = stdout;
1025: } else if (*savefile == '|') {
1.77 lukem 1026: oldintp = xsignal(SIGPIPE, SIG_IGN);
1.22 lukem 1027: fout = popen(savefile + 1, "w");
1028: if (fout == NULL) {
1.27 lukem 1029: warn("Can't run `%s'", savefile + 1);
1.42 lukem 1030: goto cleanup_fetch_url;
1.22 lukem 1031: }
1032: closefunc = pclose;
1033: } else {
1034: fout = fopen(savefile, "w");
1035: if (fout == NULL) {
1.27 lukem 1036: warn("Can't open `%s'", savefile);
1.42 lukem 1037: goto cleanup_fetch_url;
1.22 lukem 1038: }
1039: closefunc = fclose;
1.1 lukem 1040: }
1041:
1.22 lukem 1042: /* Trap signals */
1.88 lukem 1043: if (sigsetjmp(httpabort, 1))
1.42 lukem 1044: goto cleanup_fetch_url;
1.86 lukem 1045: (void)xsignal(SIGQUIT, psummary);
1.77 lukem 1046: oldintr = xsignal(SIGINT, aborthttp);
1.2 lukem 1047:
1.73 lukem 1048: if (rcvbuf_size > bufsize) {
1049: if (xferbuf)
1050: (void)free(xferbuf);
1051: bufsize = rcvbuf_size;
1052: xferbuf = xmalloc(bufsize);
1053: }
1054:
1.1 lukem 1055: bytes = 0;
1056: hashbytes = mark;
1057: progressmeter(-1);
1.2 lukem 1058:
1.24 lukem 1059: /* Finally, suck down the file. */
1.45 lukem 1060: do {
1.77 lukem 1061: long chunksize;
1.45 lukem 1062:
1063: chunksize = 0;
1064: /* read chunksize */
1065: if (ischunked) {
1.73 lukem 1066: if (fgets(xferbuf, bufsize, fin) == NULL) {
1.45 lukem 1067: warnx("Unexpected EOF reading chunksize");
1068: goto cleanup_fetch_url;
1069: }
1.73 lukem 1070: chunksize = strtol(xferbuf, &ep, 16);
1.45 lukem 1071: if (strcmp(ep, "\r\n") != 0) {
1072: warnx("Unexpected data following chunksize");
1073: goto cleanup_fetch_url;
1074: }
1075: if (debug)
1.59 lukem 1076: fprintf(ttyout,
1077: #ifndef NO_QUAD
1.75 lukem 1078: "got chunksize of %lld\n",
1.45 lukem 1079: (long long)chunksize);
1.59 lukem 1080: #else
1081: "got chunksize of %ld\n",
1082: (long)chunksize);
1083: #endif
1.45 lukem 1084: if (chunksize == 0)
1085: break;
1086: }
1.59 lukem 1087: /* transfer file or chunk */
1088: while (1) {
1089: struct timeval then, now, td;
1090: off_t bufrem;
1091:
1.71 lukem 1092: if (rate_get)
1093: (void)gettimeofday(&then, NULL);
1.73 lukem 1094: bufrem = rate_get ? rate_get : bufsize;
1.59 lukem 1095: while (bufrem > 0) {
1.73 lukem 1096: len = fread(xferbuf, sizeof(char),
1097: ischunked ? MIN(chunksize, bufrem)
1098: : bufsize, fin);
1.59 lukem 1099: if (len <= 0)
1100: goto chunkdone;
1101: bytes += len;
1102: bufrem -= len;
1.73 lukem 1103: if (fwrite(xferbuf, sizeof(char), len, fout)
1.59 lukem 1104: != len) {
1105: warn("Writing `%s'", savefile);
1106: goto cleanup_fetch_url;
1107: }
1.45 lukem 1108: }
1109: if (hash && !progress) {
1110: while (bytes >= hashbytes) {
1111: (void)putc('#', ttyout);
1112: hashbytes += mark;
1113: }
1114: (void)fflush(ttyout);
1115: }
1.59 lukem 1116: if (ischunked) {
1.45 lukem 1117: chunksize -= len;
1.59 lukem 1118: if (chunksize <= 0)
1119: goto chunkdone;
1120: }
1121: if (rate_get) {
1122: while (1) {
1123: (void)gettimeofday(&now, NULL);
1124: timersub(&now, &then, &td);
1125: if (td.tv_sec > 0)
1126: break;
1127: usleep(1000000 - td.tv_usec);
1128: }
1129: }
1.1 lukem 1130: }
1.45 lukem 1131: /* read CRLF after chunk*/
1.59 lukem 1132: chunkdone:
1.45 lukem 1133: if (ischunked) {
1.73 lukem 1134: if (fgets(xferbuf, bufsize, fin) == NULL)
1.45 lukem 1135: break;
1.73 lukem 1136: if (strcmp(xferbuf, "\r\n") != 0) {
1.45 lukem 1137: warnx("Unexpected data following chunk");
1138: goto cleanup_fetch_url;
1.1 lukem 1139: }
1140: }
1.45 lukem 1141: } while (ischunked);
1.1 lukem 1142: if (hash && !progress && bytes > 0) {
1143: if (bytes < mark)
1.22 lukem 1144: (void)putc('#', ttyout);
1145: (void)putc('\n', ttyout);
1.1 lukem 1146: }
1.25 lukem 1147: if (ferror(fin)) {
1148: warn("Reading file");
1.42 lukem 1149: goto cleanup_fetch_url;
1.1 lukem 1150: }
1.2 lukem 1151: progressmeter(1);
1.85 lukem 1152: bytes = 0;
1.25 lukem 1153: (void)fflush(fout);
1154: if (closefunc == fclose && mtime != -1) {
1155: struct timeval tval[2];
1156:
1157: (void)gettimeofday(&tval[0], NULL);
1158: tval[1].tv_sec = mtime;
1159: tval[1].tv_usec = 0;
1.31 lukem 1160: (*closefunc)(fout);
1161: fout = NULL;
1162:
1163: if (utimes(savefile, tval) == -1) {
1.25 lukem 1164: fprintf(ttyout,
1165: "Can't change modification time to %s",
1166: asctime(localtime(&mtime)));
1167: }
1168: }
1169: if (bytes > 0)
1170: ptransfer(0);
1.1 lukem 1171:
1.42 lukem 1172: rval = 0;
1173: goto cleanup_fetch_url;
1.11 lukem 1174:
1.1 lukem 1175: improper:
1.27 lukem 1176: warnx("Improper response from `%s'", host);
1.11 lukem 1177:
1.42 lukem 1178: cleanup_fetch_url:
1.86 lukem 1179: if (oldintr)
1180: (void)xsignal(SIGINT, oldintr);
1181: if (oldintp)
1182: (void)xsignal(SIGPIPE, oldintp);
1.24 lukem 1183: if (fin != NULL)
1184: fclose(fin);
1185: else if (s != -1)
1.1 lukem 1186: close(s);
1.22 lukem 1187: if (closefunc != NULL && fout != NULL)
1188: (*closefunc)(fout);
1.66 christos 1189: #ifdef NI_NUMERICHOST
1.65 lukem 1190: if (res != NULL)
1191: freeaddrinfo(res);
1.66 christos 1192: #endif
1.29 lukem 1193: FREEPTR(savefile);
1.27 lukem 1194: FREEPTR(user);
1195: FREEPTR(pass);
1196: FREEPTR(host);
1.60 itojun 1197: FREEPTR(port);
1.27 lukem 1198: FREEPTR(path);
1.50 lukem 1199: FREEPTR(decodedpath);
1.54 lukem 1200: FREEPTR(puser);
1201: FREEPTR(ppass);
1.27 lukem 1202: FREEPTR(buf);
1.44 lukem 1203: FREEPTR(auth);
1.42 lukem 1204: FREEPTR(location);
1205: FREEPTR(message);
1206: return (rval);
1.1 lukem 1207: }
1208:
1209: /*
1.50 lukem 1210: * Abort a HTTP retrieval
1.2 lukem 1211: */
1212: void
1.3 lukem 1213: aborthttp(notused)
1214: int notused;
1.2 lukem 1215: {
1.88 lukem 1216: char msgbuf[100];
1217: int len;
1.2 lukem 1218:
1219: alarmtimer(0);
1.88 lukem 1220: len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf));
1221: write(fileno(ttyout), msgbuf, len);
1222: siglongjmp(httpabort, 1);
1.2 lukem 1223: }
1224:
1225: /*
1.50 lukem 1226: * Retrieve ftp URL or classic ftp argument using FTP.
1.63 lukem 1227: * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1.42 lukem 1228: * is still open (e.g, ftp xfer with trailing /)
1229: */
1230: static int
1.52 lukem 1231: fetch_ftp(url)
1.42 lukem 1232: const char *url;
1233: {
1234: char *cp, *xargv[5], rempath[MAXPATHLEN];
1235: char *host, *path, *dir, *file, *user, *pass;
1.60 itojun 1236: char *port;
1.82 lukem 1237: int dirhasglob, filehasglob, oautologin, rval, type, xargc;
1238: in_port_t portnum;
1239: url_t urltype;
1.42 lukem 1240:
1241: host = path = dir = file = user = pass = NULL;
1.60 itojun 1242: port = NULL;
1.42 lukem 1243: rval = 1;
1.53 lukem 1244: type = TYPE_I;
1.42 lukem 1245:
1246: if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
1247: if ((parse_url(url, "URL", &urltype, &user, &pass,
1.82 lukem 1248: &host, &port, &portnum, &path) == -1) ||
1.42 lukem 1249: (user != NULL && *user == '\0') ||
1250: (pass != NULL && *pass == '\0') ||
1251: EMPTYSTRING(host)) {
1252: warnx("Invalid URL `%s'", url);
1253: goto cleanup_fetch_ftp;
1254: }
1.50 lukem 1255: url_decode(user);
1256: url_decode(pass);
1.53 lukem 1257: /*
1258: * Note: Don't url_decode(path) here. We need to keep the
1259: * distinction between "/" and "%2F" until later.
1260: */
1261:
1262: /* check for trailing ';type=[aid]' */
1.63 lukem 1263: if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) {
1.53 lukem 1264: if (strcasecmp(cp, ";type=a") == 0)
1265: type = TYPE_A;
1266: else if (strcasecmp(cp, ";type=i") == 0)
1267: type = TYPE_I;
1268: else if (strcasecmp(cp, ";type=d") == 0) {
1269: warnx(
1270: "Directory listing via a URL is not supported");
1271: goto cleanup_fetch_ftp;
1272: } else {
1273: warnx("Invalid suffix `%s' in URL `%s'", cp,
1274: url);
1275: goto cleanup_fetch_ftp;
1276: }
1277: *cp = 0;
1278: }
1.42 lukem 1279: } else { /* classic style `host:file' */
1.53 lukem 1280: urltype = CLASSIC_URL_T;
1.42 lukem 1281: host = xstrdup(url);
1282: cp = strchr(host, ':');
1283: if (cp != NULL) {
1284: *cp = '\0';
1285: path = xstrdup(cp + 1);
1286: }
1287: }
1288: if (EMPTYSTRING(host))
1289: goto cleanup_fetch_ftp;
1290:
1.50 lukem 1291: /* Extract the file and (if present) directory name. */
1.42 lukem 1292: dir = path;
1293: if (! EMPTYSTRING(dir)) {
1.53 lukem 1294: /*
1295: * If we are dealing with classic `host:path' syntax,
1296: * then a path of the form `/file' (resulting from
1297: * input of the form `host:/file') means that we should
1.63 lukem 1298: * do "CWD /" before retrieving the file. So we set
1.53 lukem 1299: * dir="/" and file="file".
1300: *
1301: * But if we are dealing with URLs like
1302: * `ftp://host/path' then a path of the form `/file'
1303: * (resulting from a URL of the form `ftp://host//file')
1304: * means that we should do `CWD ' (with an empty
1305: * argument) before retrieving the file. So we set
1306: * dir="" and file="file".
1307: *
1308: * If the path does not contain / at all, we set
1309: * dir=NULL. (We get a path without any slashes if
1310: * we are dealing with classic `host:file' or URL
1311: * `ftp://host/file'.)
1312: *
1313: * In all other cases, we set dir to a string that does
1314: * not include the final '/' that separates the dir part
1315: * from the file part of the path. (This will be the
1316: * empty string if and only if we are dealing with a
1317: * path of the form `/file' resulting from an URL of the
1318: * form `ftp://host//file'.)
1319: */
1.42 lukem 1320: cp = strrchr(dir, '/');
1.53 lukem 1321: if (cp == dir && urltype == CLASSIC_URL_T) {
1.50 lukem 1322: file = cp + 1;
1323: dir = "/";
1324: } else if (cp != NULL) {
1.42 lukem 1325: *cp++ = '\0';
1326: file = cp;
1327: } else {
1328: file = dir;
1329: dir = NULL;
1330: }
1.63 lukem 1331: } else
1332: dir = NULL;
1.53 lukem 1333: if (urltype == FTP_URL_T && file != NULL) {
1334: url_decode(file);
1335: /* but still don't url_decode(dir) */
1336: }
1.42 lukem 1337: if (debug)
1338: fprintf(ttyout,
1.60 itojun 1339: "fetch_ftp: user `%s' pass `%s' host %s:%s path `%s' dir `%s' file `%s'\n",
1.53 lukem 1340: user ? user : "<null>", pass ? pass : "<null>",
1.60 itojun 1341: host ? host : "<null>", port ? port : "<null>",
1342: path ? path : "<null>",
1.53 lukem 1343: dir ? dir : "<null>", file ? file : "<null>");
1.42 lukem 1344:
1345: dirhasglob = filehasglob = 0;
1.53 lukem 1346: if (doglob && urltype == CLASSIC_URL_T) {
1.42 lukem 1347: if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
1348: dirhasglob = 1;
1349: if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
1350: filehasglob = 1;
1351: }
1352:
1.50 lukem 1353: /* Set up the connection */
1354: if (connected)
1355: disconnect(0, NULL);
1356: xargv[0] = __progname;
1357: xargv[1] = host;
1358: xargv[2] = NULL;
1359: xargc = 2;
1360: if (port) {
1.60 itojun 1361: xargv[2] = port;
1.50 lukem 1362: xargv[3] = NULL;
1363: xargc = 3;
1364: }
1365: oautologin = autologin;
1366: if (user != NULL)
1367: autologin = 0;
1368: setpeer(xargc, xargv);
1369: autologin = oautologin;
1370: if ((connected == 0) || ((connected == 1)
1371: && !ftp_login(host, user, pass))) {
1372: warnx("Can't connect or login to host `%s'", host);
1373: goto cleanup_fetch_ftp;
1374: }
1.42 lukem 1375:
1.53 lukem 1376: switch (type) {
1377: case TYPE_A:
1378: setascii(0, NULL);
1379: break;
1380: case TYPE_I:
1381: setbinary(0, NULL);
1382: break;
1383: default:
1384: errx(1, "fetch_ftp: unknown transfer type %d\n", type);
1385: }
1386:
1.63 lukem 1387: /*
1388: * Change directories, if necessary.
1389: *
1390: * Note: don't use EMPTYSTRING(dir) below, because
1391: * dir=="" means something different from dir==NULL.
1392: */
1.53 lukem 1393: if (dir != NULL && !dirhasglob) {
1394: char *nextpart;
1.42 lukem 1395:
1.53 lukem 1396: /*
1397: * If we are dealing with a classic `host:path' (urltype
1398: * is CLASSIC_URL_T) then we have a raw directory
1399: * name (not encoded in any way) and we can change
1400: * directories in one step.
1401: *
1402: * If we are dealing with an `ftp://host/path' URL
1403: * (urltype is FTP_URL_T), then RFC 1738 says we need to
1404: * send a separate CWD command for each unescaped "/"
1405: * in the path, and we have to interpret %hex escaping
1406: * *after* we find the slashes. It's possible to get
1407: * empty components here, (from multiple adjacent
1408: * slashes in the path) and RFC 1738 says that we should
1409: * still do `CWD ' (with a null argument) in such cases.
1410: *
1.63 lukem 1411: * Many ftp servers don't support `CWD ', so if there's an
1412: * error performing that command, bail out with a descriptive
1413: * message.
1.53 lukem 1414: *
1415: * Examples:
1416: *
1.63 lukem 1417: * host: dir="", urltype=CLASSIC_URL_T
1418: * logged in (to default directory)
1.53 lukem 1419: * host:file dir=NULL, urltype=CLASSIC_URL_T
1420: * "RETR file"
1.63 lukem 1421: * host:dir/ dir="dir", urltype=CLASSIC_URL_T
1422: * "CWD dir", logged in
1423: * ftp://host/ dir="", urltype=FTP_URL_T
1424: * logged in (to default directory)
1425: * ftp://host/dir/ dir="dir", urltype=FTP_URL_T
1426: * "CWD dir", logged in
1.53 lukem 1427: * ftp://host/file dir=NULL, urltype=FTP_URL_T
1428: * "RETR file"
1429: * ftp://host//file dir="", urltype=FTP_URL_T
1.63 lukem 1430: * "CWD ", "RETR file"
1.53 lukem 1431: * host:/file dir="/", urltype=CLASSIC_URL_T
1432: * "CWD /", "RETR file"
1433: * ftp://host///file dir="/", urltype=FTP_URL_T
1.63 lukem 1434: * "CWD ", "CWD ", "RETR file"
1.53 lukem 1435: * ftp://host/%2F/file dir="%2F", urltype=FTP_URL_T
1436: * "CWD /", "RETR file"
1437: * ftp://host/foo/file dir="foo", urltype=FTP_URL_T
1438: * "CWD foo", "RETR file"
1439: * ftp://host/foo/bar/file dir="foo/bar"
1440: * "CWD foo", "CWD bar", "RETR file"
1441: * ftp://host//foo/bar/file dir="/foo/bar"
1.63 lukem 1442: * "CWD ", "CWD foo", "CWD bar", "RETR file"
1.53 lukem 1443: * ftp://host/foo//bar/file dir="foo//bar"
1.63 lukem 1444: * "CWD foo", "CWD ", "CWD bar", "RETR file"
1.53 lukem 1445: * ftp://host/%2F/foo/bar/file dir="%2F/foo/bar"
1446: * "CWD /", "CWD foo", "CWD bar", "RETR file"
1447: * ftp://host/%2Ffoo/bar/file dir="%2Ffoo/bar"
1448: * "CWD /foo", "CWD bar", "RETR file"
1449: * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar"
1450: * "CWD /foo/bar", "RETR file"
1451: * ftp://host/%2Ffoo%2Fbar%2Ffile dir=NULL
1452: * "RETR /foo/bar/file"
1453: *
1454: * Note that we don't need `dir' after this point.
1455: */
1456: do {
1457: if (urltype == FTP_URL_T) {
1458: nextpart = strchr(dir, '/');
1459: if (nextpart) {
1460: *nextpart = '\0';
1461: nextpart++;
1462: }
1463: url_decode(dir);
1464: } else
1465: nextpart = NULL;
1466: if (debug)
1467: fprintf(ttyout, "dir `%s', nextpart `%s'\n",
1468: dir ? dir : "<null>",
1469: nextpart ? nextpart : "<null>");
1.63 lukem 1470: if (urltype == FTP_URL_T || *dir != '\0') {
1.53 lukem 1471: xargv[0] = "cd";
1472: xargv[1] = dir;
1473: xargv[2] = NULL;
1474: dirchange = 0;
1475: cd(2, xargv);
1.63 lukem 1476: if (! dirchange) {
1477: if (*dir == '\0' && code == 500)
1478: fprintf(stderr,
1479: "\n"
1480: "ftp: The `CWD ' command (without a directory), which is required by\n"
1481: " RFC 1738 to support the empty directory in the URL pathname (`//'),\n"
1482: " conflicts with the server's conformance to RFC 959.\n"
1483: " Try the same URL without the `//' in the URL pathname.\n"
1484: "\n");
1.53 lukem 1485: goto cleanup_fetch_ftp;
1.63 lukem 1486: }
1.53 lukem 1487: }
1488: dir = nextpart;
1489: } while (dir != NULL);
1.42 lukem 1490: }
1491:
1492: if (EMPTYSTRING(file)) {
1493: rval = -1;
1494: goto cleanup_fetch_ftp;
1495: }
1496:
1497: if (dirhasglob) {
1.78 lukem 1498: (void)strlcpy(rempath, dir, sizeof(rempath));
1499: (void)strlcat(rempath, "/", sizeof(rempath));
1500: (void)strlcat(rempath, file, sizeof(rempath));
1.42 lukem 1501: file = rempath;
1502: }
1503:
1504: /* Fetch the file(s). */
1505: xargc = 2;
1506: xargv[0] = "get";
1507: xargv[1] = file;
1508: xargv[2] = NULL;
1509: if (dirhasglob || filehasglob) {
1510: int ointeractive;
1511:
1512: ointeractive = interactive;
1513: interactive = 0;
1514: xargv[0] = "mget";
1515: mget(xargc, xargv);
1516: interactive = ointeractive;
1517: } else {
1.53 lukem 1518: if (outfile == NULL) {
1519: cp = strrchr(file, '/'); /* find savefile */
1520: if (cp != NULL)
1521: outfile = cp + 1;
1522: else
1523: outfile = file;
1.42 lukem 1524: }
1.53 lukem 1525: xargv[2] = (char *)outfile;
1526: xargv[3] = NULL;
1527: xargc++;
1.52 lukem 1528: if (restartautofetch)
1529: reget(xargc, xargv);
1530: else
1531: get(xargc, xargv);
1.42 lukem 1532: }
1533:
1534: if ((code / 100) == COMPLETE)
1535: rval = 0;
1536:
1537: cleanup_fetch_ftp:
1538: FREEPTR(host);
1539: FREEPTR(path);
1540: FREEPTR(user);
1541: FREEPTR(pass);
1542: return (rval);
1543: }
1544:
1545: /*
1546: * Retrieve the given file to outfile.
1547: * Supports arguments of the form:
1548: * "host:path", "ftp://host/path" if $ftpproxy, call fetch_url() else
1549: * call fetch_ftp()
1.50 lukem 1550: * "http://host/path" call fetch_url() to use HTTP
1.42 lukem 1551: * "file:///path" call fetch_url() to copy
1552: * "about:..." print a message
1553: *
1554: * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1555: * is still open (e.g, ftp xfer with trailing /)
1556: */
1557: static int
1.52 lukem 1558: go_fetch(url)
1.42 lukem 1559: const char *url;
1560: {
1561:
1562: /*
1563: * Check for about:*
1564: */
1565: if (strncasecmp(url, ABOUT_URL, sizeof(ABOUT_URL) - 1) == 0) {
1566: url += sizeof(ABOUT_URL) -1;
1567: if (strcasecmp(url, "ftp") == 0) {
1.87 lukem 1568: fputs(
1569: "This version of ftp has been enhanced by Luke Mewburn <lukem@netbsd.org>\n"
1570: "for the NetBSD project. Execute `man ftp' for more details.\n", ttyout);
1571: } else if (strcasecmp(url, "lukem") == 0) {
1572: fputs(
1573: "Luke Mewburn is the author of most of the enhancements in this ftp client.\n"
1574: "Please email feedback to <lukem@netbsd.org>.\n", ttyout);
1.42 lukem 1575: } else if (strcasecmp(url, "netbsd") == 0) {
1.87 lukem 1576: fputs(
1577: "NetBSD is a freely available and redistributable UNIX-like operating system.\n"
1578: "For more information, see http://www.netbsd.org/index.html\n", ttyout);
1.42 lukem 1579: } else {
1580: fprintf(ttyout, "`%s' is an interesting topic.\n", url);
1581: }
1582: return (0);
1583: }
1584:
1585: /*
1586: * Check for file:// and http:// URLs.
1587: */
1588: if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 ||
1589: strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0)
1.52 lukem 1590: return (fetch_url(url, NULL, NULL, NULL));
1.42 lukem 1591:
1592: /*
1593: * Try FTP URL-style and host:file arguments next.
1594: * If ftpproxy is set with an FTP URL, use fetch_url()
1595: * Othewise, use fetch_ftp().
1596: */
1597: if (ftpproxy && strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0)
1.52 lukem 1598: return (fetch_url(url, NULL, NULL, NULL));
1.42 lukem 1599:
1.52 lukem 1600: return (fetch_ftp(url));
1.42 lukem 1601: }
1602:
1603: /*
1604: * Retrieve multiple files from the command line,
1605: * calling go_fetch() for each file.
1606: *
1607: * If an ftp path has a trailing "/", the path will be cd-ed into and
1.41 lukem 1608: * the connection remains open, and the function will return -1
1609: * (to indicate the connection is alive).
1.1 lukem 1610: * If an error occurs the return value will be the offset+1 in
1611: * argv[] of the file that caused a problem (i.e, argv[x]
1612: * returns x+1)
1613: * Otherwise, 0 is returned if all files retrieved successfully.
1614: */
1615: int
1.52 lukem 1616: auto_fetch(argc, argv)
1.1 lukem 1617: int argc;
1618: char *argv[];
1619: {
1.42 lukem 1620: volatile int argpos;
1621: int rval;
1.22 lukem 1622:
1.2 lukem 1623: argpos = 0;
1.1 lukem 1624:
1.88 lukem 1625: if (sigsetjmp(toplevel, 1)) {
1.2 lukem 1626: if (connected)
1627: disconnect(0, NULL);
1.3 lukem 1628: return (argpos + 1);
1.2 lukem 1629: }
1.88 lukem 1630: (void)xsignal(SIGINT, intr);
1.77 lukem 1631: (void)xsignal(SIGPIPE, (sig_t)lostpeer);
1.1 lukem 1632:
1633: /*
1634: * Loop through as long as there's files to fetch.
1635: */
1.27 lukem 1636: for (rval = 0; (rval == 0) && (argpos < argc); argpos++) {
1.1 lukem 1637: if (strchr(argv[argpos], ':') == NULL)
1638: break;
1.42 lukem 1639: redirect_loop = 0;
1.50 lukem 1640: anonftp = 1; /* Handle "automatic" transfers. */
1.52 lukem 1641: rval = go_fetch(argv[argpos]);
1642: if (outfile != NULL && strcmp(outfile, "-") != 0
1643: && outfile[0] != '|')
1644: outfile = NULL;
1.42 lukem 1645: if (rval > 0)
1.1 lukem 1646: rval = argpos + 1;
1.42 lukem 1647: }
1.3 lukem 1648:
1.1 lukem 1649: if (connected && rval != -1)
1650: disconnect(0, NULL);
1651: return (rval);
1652: }
CVSweb <webmaster@jp.NetBSD.org>