Annotation of src/usr.bin/ftp/fetch.c, Revision 1.190
1.190 ! christos 1: /* $NetBSD: fetch.c,v 1.189 2009/08/13 17:55:18 drochner Exp $ */
1.1 lukem 2:
3: /*-
1.187 lukem 4: * Copyright (c) 1997-2009 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: *
1.114 lukem 10: * This code is derived from software contributed to The NetBSD Foundation
11: * by Scott Aaron Bamford.
12: *
1.1 lukem 13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.14 lukem 25: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1.1 lukem 27: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32: * POSSIBILITY OF SUCH DAMAGE.
33: */
34:
1.12 lukem 35: #include <sys/cdefs.h>
1.1 lukem 36: #ifndef lint
1.190 ! christos 37: __RCSID("$NetBSD: fetch.c,v 1.189 2009/08/13 17:55:18 drochner Exp $");
1.1 lukem 38: #endif /* not lint */
39:
40: /*
41: * FTP User Program -- Command line file retrieval
42: */
43:
44: #include <sys/types.h>
45: #include <sys/param.h>
46: #include <sys/socket.h>
1.30 lukem 47: #include <sys/stat.h>
1.32 lukem 48: #include <sys/time.h>
1.1 lukem 49:
50: #include <netinet/in.h>
51:
1.2 lukem 52: #include <arpa/ftp.h>
1.1 lukem 53: #include <arpa/inet.h>
54:
55: #include <ctype.h>
56: #include <err.h>
1.22 lukem 57: #include <errno.h>
1.1 lukem 58: #include <netdb.h>
59: #include <fcntl.h>
60: #include <stdio.h>
61: #include <stdlib.h>
62: #include <string.h>
63: #include <unistd.h>
1.57 christos 64: #include <time.h>
1.1 lukem 65:
66: #include "ftp_var.h"
1.100 lukem 67: #include "version.h"
1.1 lukem 68:
1.27 lukem 69: typedef enum {
70: UNKNOWN_URL_T=-1,
71: HTTP_URL_T,
72: FTP_URL_T,
1.53 lukem 73: FILE_URL_T,
74: CLASSIC_URL_T
1.27 lukem 75: } url_t;
76:
1.111 lukem 77: void aborthttp(int);
1.147 christos 78: #ifndef NO_AUTH
1.111 lukem 79: static int auth_url(const char *, char **, const char *, const char *);
1.154 lukem 80: static void base64_encode(const unsigned char *, size_t, unsigned char *);
1.147 christos 81: #endif
1.111 lukem 82: static int go_fetch(const char *);
83: static int fetch_ftp(const char *);
84: static int fetch_url(const char *, const char *, char *, char *);
1.154 lukem 85: static const char *match_token(const char **, const char *);
1.111 lukem 86: static int parse_url(const char *, const char *, url_t *, char **,
87: char **, char **, char **, in_port_t *, char **);
88: static void url_decode(char *);
1.12 lukem 89:
1.42 lukem 90: static int redirect_loop;
91:
1.12 lukem 92:
1.150 lukem 93: #define STRNEQUAL(a,b) (strncasecmp((a), (b), sizeof((b))-1) == 0)
94: #define ISLWS(x) ((x)=='\r' || (x)=='\n' || (x)==' ' || (x)=='\t')
95: #define SKIPLWS(x) do { while (ISLWS((*x))) x++; } while (0)
96:
97:
1.25 lukem 98: #define ABOUT_URL "about:" /* propaganda */
99: #define FILE_URL "file://" /* file URL prefix */
1.1 lukem 100: #define FTP_URL "ftp://" /* ftp URL prefix */
101: #define HTTP_URL "http://" /* http URL prefix */
102:
103:
1.154 lukem 104: /*
105: * Determine if token is the next word in buf (case insensitive).
106: * If so, advance buf past the token and any trailing LWS, and
107: * return a pointer to the token (in buf). Otherwise, return NULL.
1.172 christos 108: * token may be preceded by LWS.
1.154 lukem 109: * token must be followed by LWS or NUL. (I.e, don't partial match).
110: */
111: static const char *
112: match_token(const char **buf, const char *token)
113: {
114: const char *p, *orig;
115: size_t tlen;
116:
117: tlen = strlen(token);
118: p = *buf;
119: SKIPLWS(p);
120: orig = p;
121: if (strncasecmp(p, token, tlen) != 0)
122: return NULL;
123: p += tlen;
124: if (*p != '\0' && !ISLWS(*p))
125: return NULL;
126: SKIPLWS(p);
127: orig = *buf;
128: *buf = p;
129: return orig;
130: }
131:
1.147 christos 132: #ifndef NO_AUTH
1.27 lukem 133: /*
1.44 lukem 134: * Generate authorization response based on given authentication challenge.
135: * Returns -1 if an error occurred, otherwise 0.
136: * Sets response to a malloc(3)ed string; caller should free.
137: */
138: static int
1.111 lukem 139: auth_url(const char *challenge, char **response, const char *guser,
140: const char *gpass)
1.44 lukem 141: {
1.162 lukem 142: const char *cp, *scheme, *errormsg;
1.154 lukem 143: char *ep, *clear, *realm;
1.187 lukem 144: char uuser[BUFSIZ], *gotpass;
145: const char *upass;
1.82 lukem 146: int rval;
147: size_t len, clen, rlen;
1.44 lukem 148:
149: *response = NULL;
1.154 lukem 150: clear = realm = NULL;
1.44 lukem 151: rval = -1;
1.154 lukem 152: cp = challenge;
153: scheme = "Basic"; /* only support Basic authentication */
1.187 lukem 154: gotpass = NULL;
1.44 lukem 155:
1.163 christos 156: DPRINTF("auth_url: challenge `%s'\n", challenge);
1.44 lukem 157:
1.154 lukem 158: if (! match_token(&cp, scheme)) {
1.175 lukem 159: warnx("Unsupported authentication challenge `%s'",
1.44 lukem 160: challenge);
161: goto cleanup_auth_url;
162: }
163:
1.91 lukem 164: #define REALM "realm=\""
1.150 lukem 165: if (STRNEQUAL(cp, REALM))
1.44 lukem 166: cp += sizeof(REALM) - 1;
167: else {
1.175 lukem 168: warnx("Unsupported authentication challenge `%s'",
1.44 lukem 169: challenge);
170: goto cleanup_auth_url;
171: }
1.154 lukem 172: /* XXX: need to improve quoted-string parsing to support \ quoting, etc. */
1.44 lukem 173: if ((ep = strchr(cp, '\"')) != NULL) {
1.186 lukem 174: len = ep - cp;
1.166 christos 175: realm = (char *)ftp_malloc(len + 1);
1.78 lukem 176: (void)strlcpy(realm, cp, len + 1);
1.44 lukem 177: } else {
1.175 lukem 178: warnx("Unsupported authentication challenge `%s'",
1.44 lukem 179: challenge);
180: goto cleanup_auth_url;
181: }
182:
1.154 lukem 183: fprintf(ttyout, "Username for `%s': ", realm);
184: if (guser != NULL) {
1.186 lukem 185: (void)strlcpy(uuser, guser, sizeof(uuser));
186: fprintf(ttyout, "%s\n", uuser);
1.154 lukem 187: } else {
1.54 lukem 188: (void)fflush(ttyout);
1.188 roy 189: if (get_line(stdin, uuser, sizeof(uuser), &errormsg) < 0) {
1.162 lukem 190: warnx("%s; can't authenticate", errormsg);
1.54 lukem 191: goto cleanup_auth_url;
1.90 lukem 192: }
1.54 lukem 193: }
194: if (gpass != NULL)
1.187 lukem 195: upass = gpass;
1.174 lukem 196: else {
1.187 lukem 197: gotpass = getpass("Password: ");
198: if (gotpass == NULL) {
1.174 lukem 199: warnx("Can't read password");
200: goto cleanup_auth_url;
201: }
1.187 lukem 202: upass = gotpass;
1.174 lukem 203: }
1.44 lukem 204:
1.187 lukem 205: clen = strlen(uuser) + strlen(upass) + 2; /* user + ":" + pass + "\0" */
1.166 christos 206: clear = (char *)ftp_malloc(clen);
1.186 lukem 207: (void)strlcpy(clear, uuser, clen);
1.78 lukem 208: (void)strlcat(clear, ":", clen);
1.187 lukem 209: (void)strlcat(clear, upass, clen);
210: if (gotpass)
211: memset(gotpass, 0, strlen(gotpass));
1.44 lukem 212:
1.64 lukem 213: /* scheme + " " + enc + "\0" */
1.74 lukem 214: rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1;
1.166 christos 215: *response = (char *)ftp_malloc(rlen);
1.78 lukem 216: (void)strlcpy(*response, scheme, rlen);
1.72 lukem 217: len = strlcat(*response, " ", rlen);
1.152 lukem 218: /* use `clen - 1' to not encode the trailing NUL */
1.158 lukem 219: base64_encode((unsigned char *)clear, clen - 1,
220: (unsigned char *)*response + len);
1.80 lukem 221: memset(clear, 0, clen);
1.44 lukem 222: rval = 0;
223:
1.118 lukem 224: cleanup_auth_url:
1.44 lukem 225: FREEPTR(clear);
226: FREEPTR(realm);
227: return (rval);
228: }
229:
230: /*
231: * Encode len bytes starting at clear using base64 encoding into encoded,
232: * which should be at least ((len + 2) * 4 / 3 + 1) in size.
233: */
1.124 lukem 234: static void
1.154 lukem 235: base64_encode(const unsigned char *clear, size_t len, unsigned char *encoded)
1.44 lukem 236: {
1.154 lukem 237: static const unsigned char enc[] =
1.44 lukem 238: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1.154 lukem 239: unsigned char *cp;
1.187 lukem 240: size_t i;
1.44 lukem 241:
242: cp = encoded;
243: for (i = 0; i < len; i += 3) {
244: *(cp++) = enc[((clear[i + 0] >> 2))];
245: *(cp++) = enc[((clear[i + 0] << 4) & 0x30)
246: | ((clear[i + 1] >> 4) & 0x0f)];
247: *(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
248: | ((clear[i + 2] >> 6) & 0x03)];
249: *(cp++) = enc[((clear[i + 2] ) & 0x3f)];
250: }
251: *cp = '\0';
252: while (i-- > len)
253: *(--cp) = '=';
254: }
1.147 christos 255: #endif
1.44 lukem 256:
1.50 lukem 257: /*
258: * Decode %xx escapes in given string, `in-place'.
259: */
260: static void
1.111 lukem 261: url_decode(char *url)
1.50 lukem 262: {
263: unsigned char *p, *q;
264:
265: if (EMPTYSTRING(url))
266: return;
1.75 lukem 267: p = q = (unsigned char *)url;
1.50 lukem 268:
1.91 lukem 269: #define HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10))
1.50 lukem 270: while (*p) {
271: if (p[0] == '%'
272: && p[1] && isxdigit((unsigned char)p[1])
273: && p[2] && isxdigit((unsigned char)p[2])) {
274: *q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]);
275: p+=3;
276: } else
277: *q++ = *p++;
278: }
279: *q = '\0';
280: }
281:
1.44 lukem 282:
283: /*
1.177 lukem 284: * Parse URL of form (per RFC3986):
1.145 lukem 285: * <type>://[<user>[:<password>]@]<host>[:<port>][/<path>]
1.27 lukem 286: * Returns -1 if a parse error occurred, otherwise 0.
1.50 lukem 287: * It's the caller's responsibility to url_decode() the returned
288: * user, pass and path.
1.63 lukem 289: *
1.27 lukem 290: * Sets type to url_t, each of the given char ** pointers to a
291: * malloc(3)ed strings of the relevant section, and port to
1.41 lukem 292: * the number given, or ftpport if ftp://, or httpport if http://.
1.52 lukem 293: *
1.177 lukem 294: * XXX: this is not totally RFC3986 compliant; <path> will have the
1.53 lukem 295: * leading `/' unless it's an ftp:// URL, as this makes things easier
1.177 lukem 296: * for file:// and http:// URLs. ftp:// URLs have the `/' between the
1.141 wiz 297: * host and the URL-path removed, but any additional leading slashes
298: * in the URL-path are retained (because they imply that we should
1.53 lukem 299: * later do "CWD" with a null argument).
300: *
301: * Examples:
1.141 wiz 302: * input URL output path
1.53 lukem 303: * --------- -----------
1.177 lukem 304: * "http://host" "/"
305: * "http://host/" "/"
306: * "http://host/path" "/path"
1.63 lukem 307: * "file://host/dir/file" "dir/file"
1.177 lukem 308: * "ftp://host" ""
1.63 lukem 309: * "ftp://host/" ""
1.177 lukem 310: * "ftp://host//" "/"
311: * "ftp://host/dir/file" "dir/file"
1.53 lukem 312: * "ftp://host//dir/file" "/dir/file"
1.27 lukem 313: */
314: static int
1.186 lukem 315: parse_url(const char *url, const char *desc, url_t *utype,
316: char **uuser, char **pass, char **host, char **port,
1.111 lukem 317: in_port_t *portnum, char **path)
1.27 lukem 318: {
1.187 lukem 319: const char *origurl, *tport;
320: char *cp, *ep, *thost;
1.82 lukem 321: size_t len;
1.27 lukem 322:
1.186 lukem 323: if (url == NULL || desc == NULL || utype == NULL || uuser == NULL
1.82 lukem 324: || pass == NULL || host == NULL || port == NULL || portnum == NULL
325: || path == NULL)
1.27 lukem 326: errx(1, "parse_url: invoked with NULL argument!");
1.183 lukem 327: DPRINTF("parse_url: %s `%s'\n", desc, url);
1.27 lukem 328:
1.82 lukem 329: origurl = url;
1.186 lukem 330: *utype = UNKNOWN_URL_T;
331: *uuser = *pass = *host = *port = *path = NULL;
1.82 lukem 332: *portnum = 0;
1.63 lukem 333: tport = NULL;
1.27 lukem 334:
1.150 lukem 335: if (STRNEQUAL(url, HTTP_URL)) {
1.27 lukem 336: url += sizeof(HTTP_URL) - 1;
1.186 lukem 337: *utype = HTTP_URL_T;
1.82 lukem 338: *portnum = HTTP_PORT;
1.63 lukem 339: tport = httpport;
1.150 lukem 340: } else if (STRNEQUAL(url, FTP_URL)) {
1.27 lukem 341: url += sizeof(FTP_URL) - 1;
1.186 lukem 342: *utype = FTP_URL_T;
1.82 lukem 343: *portnum = FTP_PORT;
1.63 lukem 344: tport = ftpport;
1.150 lukem 345: } else if (STRNEQUAL(url, FILE_URL)) {
1.27 lukem 346: url += sizeof(FILE_URL) - 1;
1.186 lukem 347: *utype = FILE_URL_T;
1.27 lukem 348: } else {
349: warnx("Invalid %s `%s'", desc, url);
1.118 lukem 350: cleanup_parse_url:
1.186 lukem 351: FREEPTR(*uuser);
1.162 lukem 352: if (*pass != NULL)
353: memset(*pass, 0, strlen(*pass));
1.27 lukem 354: FREEPTR(*pass);
355: FREEPTR(*host);
1.60 itojun 356: FREEPTR(*port);
1.27 lukem 357: FREEPTR(*path);
358: return (-1);
359: }
360:
361: if (*url == '\0')
362: return (0);
363:
364: /* find [user[:pass]@]host[:port] */
365: ep = strchr(url, '/');
366: if (ep == NULL)
1.166 christos 367: thost = ftp_strdup(url);
1.27 lukem 368: else {
1.50 lukem 369: len = ep - url;
1.166 christos 370: thost = (char *)ftp_malloc(len + 1);
1.78 lukem 371: (void)strlcpy(thost, url, len + 1);
1.186 lukem 372: if (*utype == FTP_URL_T) /* skip first / for ftp URLs */
1.53 lukem 373: ep++;
1.166 christos 374: *path = ftp_strdup(ep);
1.27 lukem 375: }
376:
1.190 ! christos 377: cp = strrchr(thost, '@'); /* look for user[:pass]@ in URLs */
1.54 lukem 378: if (cp != NULL) {
1.186 lukem 379: if (*utype == FTP_URL_T)
1.54 lukem 380: anonftp = 0; /* disable anonftp */
1.186 lukem 381: *uuser = thost;
1.27 lukem 382: *cp = '\0';
1.166 christos 383: thost = ftp_strdup(cp + 1);
1.186 lukem 384: cp = strchr(*uuser, ':');
1.27 lukem 385: if (cp != NULL) {
386: *cp = '\0';
1.166 christos 387: *pass = ftp_strdup(cp + 1);
1.27 lukem 388: }
1.186 lukem 389: url_decode(*uuser);
1.154 lukem 390: if (*pass)
391: url_decode(*pass);
1.63 lukem 392: }
393:
394: #ifdef INET6
395: /*
396: * Check if thost is an encoded IPv6 address, as per
1.177 lukem 397: * RFC3986:
1.63 lukem 398: * `[' ipv6-address ']'
399: */
400: if (*thost == '[') {
401: cp = thost + 1;
402: if ((ep = strchr(cp, ']')) == NULL ||
1.106 itojun 403: (ep[1] != '\0' && ep[1] != ':')) {
1.63 lukem 404: warnx("Invalid address `%s' in %s `%s'",
1.82 lukem 405: thost, desc, origurl);
1.63 lukem 406: goto cleanup_parse_url;
407: }
1.103 lukem 408: len = ep - cp; /* change `[xyz]' -> `xyz' */
1.63 lukem 409: memmove(thost, thost + 1, len);
410: thost[len] = '\0';
411: if (! isipv6addr(thost)) {
412: warnx("Invalid IPv6 address `%s' in %s `%s'",
1.82 lukem 413: thost, desc, origurl);
1.63 lukem 414: goto cleanup_parse_url;
415: }
416: cp = ep + 1;
417: if (*cp == ':')
418: cp++;
419: else
420: cp = NULL;
1.27 lukem 421: } else
1.63 lukem 422: #endif /* INET6 */
1.177 lukem 423: if ((cp = strchr(thost, ':')) != NULL)
1.184 lukem 424: *cp++ = '\0';
1.63 lukem 425: *host = thost;
1.60 itojun 426:
1.27 lukem 427: /* look for [:port] */
428: if (cp != NULL) {
1.184 lukem 429: unsigned long nport;
1.27 lukem 430:
1.184 lukem 431: nport = strtoul(cp, &ep, 10);
432: if (*cp == '\0' || *ep != '\0' ||
433: nport < 1 || nport > MAX_IN_PORT_T) {
1.122 lukem 434: warnx("Unknown port `%s' in %s `%s'",
435: cp, desc, origurl);
1.27 lukem 436: goto cleanup_parse_url;
437: }
1.82 lukem 438: *portnum = nport;
1.63 lukem 439: tport = cp;
1.27 lukem 440: }
1.82 lukem 441:
1.95 lukem 442: if (tport != NULL)
1.166 christos 443: *port = ftp_strdup(tport);
1.177 lukem 444: if (*path == NULL) {
445: const char *emptypath = "/";
1.186 lukem 446: if (*utype == FTP_URL_T) /* skip first / for ftp URLs */
1.177 lukem 447: emptypath++;
448: *path = ftp_strdup(emptypath);
449: }
1.27 lukem 450:
1.163 christos 451: DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) "
452: "path `%s'\n",
1.186 lukem 453: STRorNULL(*uuser), STRorNULL(*pass),
1.183 lukem 454: STRorNULL(*host), STRorNULL(*port),
455: *portnum ? *portnum : -1, STRorNULL(*path));
1.27 lukem 456:
457: return (0);
458: }
459:
1.89 lukem 460: sigjmp_buf httpabort;
1.2 lukem 461:
1.1 lukem 462: /*
1.50 lukem 463: * Retrieve URL, via a proxy if necessary, using HTTP.
464: * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or
465: * http_proxy as appropriate.
466: * Supports HTTP redirects.
1.127 tacha 467: * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1.41 lukem 468: * is still open (e.g, ftp xfer with trailing /)
1.1 lukem 469: */
1.12 lukem 470: static int
1.111 lukem 471: fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth)
1.1 lukem 472: {
1.98 itojun 473: struct addrinfo hints, *res, *res0 = NULL;
1.66 christos 474: int error;
1.173 christos 475: sigfunc volatile oldintr;
476: sigfunc volatile oldintp;
477: int volatile s;
1.95 lukem 478: struct stat sb;
1.173 christos 479: int volatile ischunked;
480: int volatile isproxy;
481: int volatile rval;
482: int volatile hcode;
1.187 lukem 483: int len;
484: size_t flen;
1.73 lukem 485: static size_t bufsize;
486: static char *xferbuf;
1.154 lukem 487: const char *cp, *token;
1.173 christos 488: char *ep;
1.176 lukem 489: char buf[FTPBUFLEN];
490: const char *errormsg;
1.173 christos 491: char *volatile savefile;
492: char *volatile auth;
493: char *volatile location;
494: char *volatile message;
1.186 lukem 495: char *uuser, *pass, *host, *port, *path;
1.173 christos 496: char *volatile decodedpath;
1.144 lukem 497: char *puser, *ppass, *useragent;
1.95 lukem 498: off_t hashbytes, rangestart, rangeend, entitylen;
1.173 christos 499: int (*volatile closefunc)(FILE *);
500: FILE *volatile fin;
501: FILE *volatile fout;
1.42 lukem 502: time_t mtime;
503: url_t urltype;
1.60 itojun 504: in_port_t portnum;
1.1 lukem 505:
1.183 lukem 506: DPRINTF("fetch_url: `%s' proxyenv `%s'\n", url, STRorNULL(proxyenv));
507:
1.86 lukem 508: oldintr = oldintp = NULL;
1.22 lukem 509: closefunc = NULL;
1.24 lukem 510: fin = fout = NULL;
1.1 lukem 511: s = -1;
1.176 lukem 512: savefile = NULL;
1.44 lukem 513: auth = location = message = NULL;
1.52 lukem 514: ischunked = isproxy = hcode = 0;
1.42 lukem 515: rval = 1;
1.186 lukem 516: uuser = pass = host = path = decodedpath = puser = ppass = NULL;
1.1 lukem 517:
1.186 lukem 518: if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port,
1.82 lukem 519: &portnum, &path) == -1)
1.42 lukem 520: goto cleanup_fetch_url;
1.5 lukem 521:
1.27 lukem 522: if (urltype == FILE_URL_T && ! EMPTYSTRING(host)
523: && strcasecmp(host, "localhost") != 0) {
524: warnx("No support for non local file URL `%s'", url);
1.42 lukem 525: goto cleanup_fetch_url;
1.9 lukem 526: }
1.27 lukem 527:
1.9 lukem 528: if (EMPTYSTRING(path)) {
1.42 lukem 529: if (urltype == FTP_URL_T) {
1.52 lukem 530: rval = fetch_ftp(url);
1.42 lukem 531: goto cleanup_fetch_url;
532: }
1.27 lukem 533: if (urltype != HTTP_URL_T || outfile == NULL) {
534: warnx("Invalid URL (no file after host) `%s'", url);
1.42 lukem 535: goto cleanup_fetch_url;
1.27 lukem 536: }
1.9 lukem 537: }
1.1 lukem 538:
1.166 christos 539: decodedpath = ftp_strdup(path);
1.50 lukem 540: url_decode(decodedpath);
541:
1.22 lukem 542: if (outfile)
1.166 christos 543: savefile = ftp_strdup(outfile);
1.22 lukem 544: else {
1.50 lukem 545: cp = strrchr(decodedpath, '/'); /* find savefile */
1.29 lukem 546: if (cp != NULL)
1.166 christos 547: savefile = ftp_strdup(cp + 1);
1.22 lukem 548: else
1.166 christos 549: savefile = ftp_strdup(decodedpath);
1.22 lukem 550: }
1.183 lukem 551: DPRINTF("fetch_url: savefile `%s'\n", savefile);
1.9 lukem 552: if (EMPTYSTRING(savefile)) {
1.42 lukem 553: if (urltype == FTP_URL_T) {
1.52 lukem 554: rval = fetch_ftp(url);
1.42 lukem 555: goto cleanup_fetch_url;
556: }
1.175 lukem 557: warnx("No file after directory (you must specify an "
1.140 grant 558: "output file) `%s'", url);
1.42 lukem 559: goto cleanup_fetch_url;
1.9 lukem 560: }
1.1 lukem 561:
1.95 lukem 562: restart_point = 0;
1.25 lukem 563: filesize = -1;
1.95 lukem 564: rangestart = rangeend = entitylen = -1;
1.25 lukem 565: mtime = -1;
1.95 lukem 566: if (restartautofetch) {
567: if (strcmp(savefile, "-") != 0 && *savefile != '|' &&
568: stat(savefile, &sb) == 0)
569: restart_point = sb.st_size;
570: }
1.25 lukem 571: if (urltype == FILE_URL_T) { /* file:// URLs */
572: direction = "copied";
1.50 lukem 573: fin = fopen(decodedpath, "r");
1.25 lukem 574: if (fin == NULL) {
1.175 lukem 575: warn("Can't open `%s'", decodedpath);
1.42 lukem 576: goto cleanup_fetch_url;
1.5 lukem 577: }
1.25 lukem 578: if (fstat(fileno(fin), &sb) == 0) {
579: mtime = sb.st_mtime;
580: filesize = sb.st_size;
581: }
1.95 lukem 582: if (restart_point) {
583: if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
1.175 lukem 584: warn("Can't seek to restart `%s'",
1.95 lukem 585: decodedpath);
586: goto cleanup_fetch_url;
587: }
588: }
589: if (verbose) {
590: fprintf(ttyout, "Copying %s", decodedpath);
591: if (restart_point)
1.121 lukem 592: fprintf(ttyout, " (restarting at " LLF ")",
593: (LLT)restart_point);
1.95 lukem 594: fputs("\n", ttyout);
595: }
1.25 lukem 596: } else { /* ftp:// or http:// URLs */
1.187 lukem 597: const char *leading;
1.65 lukem 598: int hasleading;
599:
1.42 lukem 600: if (proxyenv == NULL) {
601: if (urltype == HTTP_URL_T)
1.91 lukem 602: proxyenv = getoptionvalue("http_proxy");
1.42 lukem 603: else if (urltype == FTP_URL_T)
1.91 lukem 604: proxyenv = getoptionvalue("ftp_proxy");
1.42 lukem 605: }
1.25 lukem 606: direction = "retrieved";
1.91 lukem 607: if (! EMPTYSTRING(proxyenv)) { /* use proxy */
1.27 lukem 608: url_t purltype;
1.54 lukem 609: char *phost, *ppath;
1.91 lukem 610: char *pport, *no_proxy;
1.184 lukem 611: in_port_t pportnum;
1.27 lukem 612:
613: isproxy = 1;
614:
615: /* check URL against list of no_proxied sites */
1.91 lukem 616: no_proxy = getoptionvalue("no_proxy");
617: if (! EMPTYSTRING(no_proxy)) {
1.171 elad 618: char *np, *np_copy, *np_iter;
1.184 lukem 619: unsigned long np_port;
1.27 lukem 620: size_t hlen, plen;
621:
1.171 elad 622: np_iter = np_copy = ftp_strdup(no_proxy);
1.27 lukem 623: hlen = strlen(host);
1.171 elad 624: while ((cp = strsep(&np_iter, " ,")) != NULL) {
1.27 lukem 625: if (*cp == '\0')
626: continue;
1.60 itojun 627: if ((np = strrchr(cp, ':')) != NULL) {
1.184 lukem 628: *np++ = '\0';
629: np_port = strtoul(np, &ep, 10);
630: if (*np == '\0' || *ep != '\0')
1.27 lukem 631: continue;
1.82 lukem 632: if (np_port != portnum)
1.27 lukem 633: continue;
634: }
635: plen = strlen(cp);
1.109 lukem 636: if (hlen < plen)
637: continue;
1.27 lukem 638: if (strncasecmp(host + hlen - plen,
639: cp, plen) == 0) {
640: isproxy = 0;
641: break;
642: }
643: }
644: FREEPTR(np_copy);
1.127 tacha 645: if (isproxy == 0 && urltype == FTP_URL_T) {
646: rval = fetch_ftp(url);
647: goto cleanup_fetch_url;
648: }
1.25 lukem 649: }
1.1 lukem 650:
1.27 lukem 651: if (isproxy) {
1.180 lukem 652: if (restart_point) {
653: warnx("Can't restart via proxy URL `%s'",
654: proxyenv);
655: goto cleanup_fetch_url;
656: }
1.27 lukem 657: if (parse_url(proxyenv, "proxy URL", &purltype,
1.184 lukem 658: &puser, &ppass, &phost, &pport, &pportnum,
1.82 lukem 659: &ppath) == -1)
1.42 lukem 660: goto cleanup_fetch_url;
1.27 lukem 661:
662: if ((purltype != HTTP_URL_T
663: && purltype != FTP_URL_T) ||
664: EMPTYSTRING(phost) ||
665: (! EMPTYSTRING(ppath)
666: && strcmp(ppath, "/") != 0)) {
667: warnx("Malformed proxy URL `%s'",
668: proxyenv);
669: FREEPTR(phost);
1.60 itojun 670: FREEPTR(pport);
1.27 lukem 671: FREEPTR(ppath);
1.42 lukem 672: goto cleanup_fetch_url;
1.27 lukem 673: }
1.112 itojun 674: if (isipv6addr(host) &&
675: strchr(host, '%') != NULL) {
676: warnx(
677: "Scoped address notation `%s' disallowed via web proxy",
678: host);
679: FREEPTR(phost);
680: FREEPTR(pport);
681: FREEPTR(ppath);
682: goto cleanup_fetch_url;
683: }
1.12 lukem 684:
1.27 lukem 685: FREEPTR(host);
686: host = phost;
1.62 tron 687: FREEPTR(port);
688: port = pport;
1.27 lukem 689: FREEPTR(path);
1.166 christos 690: path = ftp_strdup(url);
1.27 lukem 691: FREEPTR(ppath);
692: }
1.91 lukem 693: } /* ! EMPTYSTRING(proxyenv) */
1.25 lukem 694:
1.60 itojun 695: memset(&hints, 0, sizeof(hints));
1.65 lukem 696: hints.ai_flags = 0;
1.131 lukem 697: hints.ai_family = family;
1.60 itojun 698: hints.ai_socktype = SOCK_STREAM;
699: hints.ai_protocol = 0;
1.184 lukem 700: error = getaddrinfo(host, port, &hints, &res0);
1.60 itojun 701: if (error) {
1.184 lukem 702: warnx("Can't lookup `%s:%s': %s", host, port,
1.175 lukem 703: (error == EAI_SYSTEM) ? strerror(errno)
704: : gai_strerror(error));
1.60 itojun 705: goto cleanup_fetch_url;
706: }
1.98 itojun 707: if (res0->ai_canonname)
708: host = res0->ai_canonname;
1.60 itojun 709:
1.98 itojun 710: s = -1;
711: for (res = res0; res; res = res->ai_next) {
1.184 lukem 712: char hname[NI_MAXHOST], sname[NI_MAXSERV];
713:
1.113 itojun 714: ai_unmapped(res);
1.98 itojun 715: if (getnameinfo(res->ai_addr, res->ai_addrlen,
1.184 lukem 716: hname, sizeof(hname), sname, sizeof(sname),
717: NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
718: strlcpy(hname, "?", sizeof(hname));
719: strlcpy(sname, "?", sizeof(sname));
720: }
1.98 itojun 721:
1.161 lukem 722: if (verbose && res0->ai_next) {
1.184 lukem 723: fprintf(ttyout, "Trying %s:%s ...\n",
724: hname, sname);
1.161 lukem 725: }
1.98 itojun 726:
1.125 lukem 727: s = socket(res->ai_family, SOCK_STREAM,
728: res->ai_protocol);
1.60 itojun 729: if (s < 0) {
1.175 lukem 730: warn(
1.184 lukem 731: "Can't create socket for connection to "
732: "`%s:%s'", hname, sname);
1.98 itojun 733: continue;
1.60 itojun 734: }
735:
1.166 christos 736: if (ftp_connect(s, res->ai_addr, res->ai_addrlen) < 0) {
1.60 itojun 737: close(s);
1.98 itojun 738: s = -1;
739: continue;
1.60 itojun 740: }
1.98 itojun 741:
742: /* success */
1.60 itojun 743: break;
744: }
1.98 itojun 745:
746: if (s < 0) {
1.184 lukem 747: warnx("Can't connect to `%s:%s'", host, port);
1.98 itojun 748: goto cleanup_fetch_url;
749: }
1.24 lukem 750:
1.25 lukem 751: fin = fdopen(s, "r+");
752: /*
1.27 lukem 753: * Construct and send the request.
1.25 lukem 754: */
1.65 lukem 755: if (verbose)
756: fprintf(ttyout, "Requesting %s\n", url);
757: leading = " (";
758: hasleading = 0;
1.27 lukem 759: if (isproxy) {
1.65 lukem 760: if (verbose) {
761: fprintf(ttyout, "%svia %s:%s", leading,
762: host, port);
763: leading = ", ";
764: hasleading++;
765: }
1.44 lukem 766: fprintf(fin, "GET %s HTTP/1.0\r\n", path);
1.47 lukem 767: if (flushcache)
768: fprintf(fin, "Pragma: no-cache\r\n");
1.27 lukem 769: } else {
770: fprintf(fin, "GET %s HTTP/1.1\r\n", path);
1.106 itojun 771: if (strchr(host, ':')) {
1.112 itojun 772: char *h, *p;
773:
774: /*
775: * strip off IPv6 scope identifier, since it is
776: * local to the node
777: */
1.166 christos 778: h = ftp_strdup(host);
1.112 itojun 779: if (isipv6addr(h) &&
780: (p = strchr(h, '%')) != NULL) {
781: *p = '\0';
782: }
1.134 lukem 783: fprintf(fin, "Host: [%s]", h);
1.112 itojun 784: free(h);
1.106 itojun 785: } else
1.134 lukem 786: fprintf(fin, "Host: %s", host);
787: if (portnum != HTTP_PORT)
788: fprintf(fin, ":%u", portnum);
789: fprintf(fin, "\r\n");
1.40 explorer 790: fprintf(fin, "Accept: */*\r\n");
1.42 lukem 791: fprintf(fin, "Connection: close\r\n");
1.95 lukem 792: if (restart_point) {
793: fputs(leading, ttyout);
1.121 lukem 794: fprintf(fin, "Range: bytes=" LLF "-\r\n",
795: (LLT)restart_point);
796: fprintf(ttyout, "restarting at " LLF,
797: (LLT)restart_point);
1.95 lukem 798: leading = ", ";
799: hasleading++;
800: }
1.47 lukem 801: if (flushcache)
802: fprintf(fin, "Cache-Control: no-cache\r\n");
1.25 lukem 803: }
1.144 lukem 804: if ((useragent=getenv("FTPUSERAGENT")) != NULL) {
805: fprintf(fin, "User-Agent: %s\r\n", useragent);
806: } else {
807: fprintf(fin, "User-Agent: %s/%s\r\n",
808: FTP_PRODUCT, FTP_VERSION);
809: }
1.44 lukem 810: if (wwwauth) {
1.65 lukem 811: if (verbose) {
812: fprintf(ttyout, "%swith authorization",
813: leading);
814: leading = ", ";
815: hasleading++;
816: }
1.44 lukem 817: fprintf(fin, "Authorization: %s\r\n", wwwauth);
818: }
1.46 lukem 819: if (proxyauth) {
1.65 lukem 820: if (verbose) {
1.49 lukem 821: fprintf(ttyout,
1.65 lukem 822: "%swith proxy authorization", leading);
823: leading = ", ";
824: hasleading++;
825: }
1.44 lukem 826: fprintf(fin, "Proxy-Authorization: %s\r\n", proxyauth);
1.46 lukem 827: }
1.65 lukem 828: if (verbose && hasleading)
829: fputs(")\n", ttyout);
1.44 lukem 830: fprintf(fin, "\r\n");
1.25 lukem 831: if (fflush(fin) == EOF) {
832: warn("Writing HTTP request");
1.42 lukem 833: goto cleanup_fetch_url;
1.25 lukem 834: }
1.1 lukem 835:
1.25 lukem 836: /* Read the response */
1.188 roy 837: len = get_line(fin, buf, sizeof(buf), &errormsg);
1.176 lukem 838: if (len < 0) {
839: if (*errormsg == '\n')
840: errormsg++;
841: warnx("Receiving HTTP reply: %s", errormsg);
1.42 lukem 842: goto cleanup_fetch_url;
1.24 lukem 843: }
1.150 lukem 844: while (len > 0 && (ISLWS(buf[len-1])))
1.24 lukem 845: buf[--len] = '\0';
1.183 lukem 846: DPRINTF("fetch_url: received `%s'\n", buf);
1.1 lukem 847:
1.42 lukem 848: /* Determine HTTP response code */
1.25 lukem 849: cp = strchr(buf, ' ');
850: if (cp == NULL)
851: goto improper;
852: else
853: cp++;
1.42 lukem 854: hcode = strtol(cp, &ep, 10);
855: if (*ep != '\0' && !isspace((unsigned char)*ep))
856: goto improper;
1.166 christos 857: message = ftp_strdup(cp);
1.25 lukem 858:
859: /* Read the rest of the header. */
860: while (1) {
1.188 roy 861: len = get_line(fin, buf, sizeof(buf), &errormsg);
1.176 lukem 862: if (len < 0) {
863: if (*errormsg == '\n')
864: errormsg++;
865: warnx("Receiving HTTP reply: %s", errormsg);
1.42 lukem 866: goto cleanup_fetch_url;
1.25 lukem 867: }
1.150 lukem 868: while (len > 0 && (ISLWS(buf[len-1])))
1.25 lukem 869: buf[--len] = '\0';
870: if (len == 0)
871: break;
1.183 lukem 872: DPRINTF("fetch_url: received `%s'\n", buf);
1.25 lukem 873:
1.154 lukem 874: /*
875: * Look for some headers
876: */
877:
1.25 lukem 878: cp = buf;
1.42 lukem 879:
1.154 lukem 880: if (match_token(&cp, "Content-Length:")) {
1.119 lukem 881: filesize = STRTOLL(cp, &ep, 10);
1.95 lukem 882: if (filesize < 0 || *ep != '\0')
1.25 lukem 883: goto improper;
1.183 lukem 884: DPRINTF("fetch_url: parsed len as: " LLF "\n",
1.163 christos 885: (LLT)filesize);
1.42 lukem 886:
1.154 lukem 887: } else if (match_token(&cp, "Content-Range:")) {
888: if (! match_token(&cp, "bytes"))
1.150 lukem 889: goto improper;
1.154 lukem 890:
891: if (*cp == '*')
892: cp++;
1.128 yamt 893: else {
894: rangestart = STRTOLL(cp, &ep, 10);
895: if (rangestart < 0 || *ep != '-')
896: goto improper;
897: cp = ep + 1;
898: rangeend = STRTOLL(cp, &ep, 10);
899: if (rangeend < 0 || rangeend < rangestart)
900: goto improper;
1.154 lukem 901: cp = ep;
1.128 yamt 902: }
1.154 lukem 903: if (*cp != '/')
1.95 lukem 904: goto improper;
1.154 lukem 905: cp++;
906: if (*cp == '*')
907: cp++;
1.128 yamt 908: else {
909: entitylen = STRTOLL(cp, &ep, 10);
910: if (entitylen < 0)
911: goto improper;
1.154 lukem 912: cp = ep;
1.128 yamt 913: }
1.154 lukem 914: if (*cp != '\0')
1.95 lukem 915: goto improper;
916:
1.163 christos 917: #ifndef NO_DEBUG
1.167 christos 918: if (ftp_debug) {
1.128 yamt 919: fprintf(ttyout, "parsed range as: ");
920: if (rangestart == -1)
921: fprintf(ttyout, "*");
922: else
923: fprintf(ttyout, LLF "-" LLF,
924: (LLT)rangestart,
925: (LLT)rangeend);
926: fprintf(ttyout, "/" LLF "\n", (LLT)entitylen);
927: }
1.163 christos 928: #endif
1.95 lukem 929: if (! restart_point) {
930: warnx(
931: "Received unexpected Content-Range header");
932: goto cleanup_fetch_url;
933: }
934:
1.154 lukem 935: } else if (match_token(&cp, "Last-Modified:")) {
1.25 lukem 936: struct tm parsed;
937: char *t;
938:
1.178 lukem 939: memset(&parsed, 0, sizeof(parsed));
1.177 lukem 940: /* RFC1123 */
1.25 lukem 941: if ((t = strptime(cp,
942: "%a, %d %b %Y %H:%M:%S GMT",
943: &parsed))
1.177 lukem 944: /* RFC0850 */
1.25 lukem 945: || (t = strptime(cp,
946: "%a, %d-%b-%y %H:%M:%S GMT",
947: &parsed))
948: /* asctime */
949: || (t = strptime(cp,
950: "%a, %b %d %H:%M:%S %Y",
951: &parsed))) {
1.38 lukem 952: parsed.tm_isdst = -1;
1.25 lukem 953: if (*t == '\0')
1.76 lukem 954: mtime = timegm(&parsed);
1.163 christos 955: #ifndef NO_DEBUG
1.167 christos 956: if (ftp_debug && mtime != -1) {
1.25 lukem 957: fprintf(ttyout,
958: "parsed date as: %s",
1.179 lukem 959: rfc2822time(localtime(&mtime)));
1.38 lukem 960: }
1.163 christos 961: #endif
1.25 lukem 962: }
1.42 lukem 963:
1.154 lukem 964: } else if (match_token(&cp, "Location:")) {
1.166 christos 965: location = ftp_strdup(cp);
1.183 lukem 966: DPRINTF("fetch_url: parsed location as `%s'\n",
967: cp);
1.44 lukem 968:
1.154 lukem 969: } else if (match_token(&cp, "Transfer-Encoding:")) {
970: if (match_token(&cp, "binary")) {
1.109 lukem 971: warnx(
1.175 lukem 972: "Bogus transfer encoding `binary' (fetching anyway)");
1.109 lukem 973: continue;
974: }
1.154 lukem 975: if (! (token = match_token(&cp, "chunked"))) {
1.45 lukem 976: warnx(
1.175 lukem 977: "Unsupported transfer encoding `%s'",
1.154 lukem 978: token);
1.45 lukem 979: goto cleanup_fetch_url;
980: }
981: ischunked++;
1.183 lukem 982: DPRINTF("fetch_url: using chunked encoding\n");
1.45 lukem 983:
1.154 lukem 984: } else if (match_token(&cp, "Proxy-Authenticate:")
985: || match_token(&cp, "WWW-Authenticate:")) {
986: if (! (token = match_token(&cp, "Basic"))) {
1.163 christos 987: DPRINTF(
1.183 lukem 988: "fetch_url: skipping unknown auth scheme `%s'\n",
1.154 lukem 989: token);
990: continue;
991: }
1.44 lukem 992: FREEPTR(auth);
1.166 christos 993: auth = ftp_strdup(token);
1.183 lukem 994: DPRINTF("fetch_url: parsed auth as `%s'\n", cp);
1.25 lukem 995: }
1.44 lukem 996:
1.24 lukem 997: }
1.54 lukem 998: /* finished parsing header */
1.1 lukem 999:
1.52 lukem 1000: switch (hcode) {
1001: case 200:
1002: break;
1.95 lukem 1003: case 206:
1004: if (! restart_point) {
1005: warnx("Not expecting partial content header");
1006: goto cleanup_fetch_url;
1007: }
1008: break;
1.52 lukem 1009: case 300:
1010: case 301:
1011: case 302:
1012: case 303:
1013: case 305:
1.165 christos 1014: case 307:
1.52 lukem 1015: if (EMPTYSTRING(location)) {
1016: warnx(
1017: "No redirection Location provided by server");
1018: goto cleanup_fetch_url;
1019: }
1020: if (redirect_loop++ > 5) {
1021: warnx("Too many redirections requested");
1022: goto cleanup_fetch_url;
1023: }
1024: if (hcode == 305) {
1025: if (verbose)
1026: fprintf(ttyout, "Redirected via %s\n",
1027: location);
1028: rval = fetch_url(url, location,
1029: proxyauth, wwwauth);
1030: } else {
1031: if (verbose)
1032: fprintf(ttyout, "Redirected to %s\n",
1033: location);
1034: rval = go_fetch(location);
1035: }
1.42 lukem 1036: goto cleanup_fetch_url;
1.147 christos 1037: #ifndef NO_AUTH
1.52 lukem 1038: case 401:
1039: case 407:
1040: {
1041: char **authp;
1.54 lukem 1042: char *auser, *apass;
1.52 lukem 1043:
1.54 lukem 1044: if (hcode == 401) {
1045: authp = &wwwauth;
1.186 lukem 1046: auser = uuser;
1.54 lukem 1047: apass = pass;
1048: } else {
1049: authp = &proxyauth;
1050: auser = puser;
1051: apass = ppass;
1052: }
1.149 lukem 1053: if (verbose || *authp == NULL ||
1054: auser == NULL || apass == NULL)
1055: fprintf(ttyout, "%s\n", message);
1056: if (EMPTYSTRING(auth)) {
1057: warnx(
1058: "No authentication challenge provided by server");
1059: goto cleanup_fetch_url;
1060: }
1.52 lukem 1061: if (*authp != NULL) {
1062: char reply[10];
1063:
1064: fprintf(ttyout,
1065: "Authorization failed. Retry (y/n)? ");
1.188 roy 1066: if (get_line(stdin, reply, sizeof(reply), NULL)
1.162 lukem 1067: < 0) {
1.52 lukem 1068: goto cleanup_fetch_url;
1.90 lukem 1069: }
1.153 dsl 1070: if (tolower((unsigned char)reply[0]) != 'y')
1071: goto cleanup_fetch_url;
1.54 lukem 1072: auser = NULL;
1073: apass = NULL;
1.52 lukem 1074: }
1.54 lukem 1075: if (auth_url(auth, authp, auser, apass) == 0) {
1.52 lukem 1076: rval = fetch_url(url, proxyenv,
1077: proxyauth, wwwauth);
1.80 lukem 1078: memset(*authp, 0, strlen(*authp));
1.52 lukem 1079: FREEPTR(*authp);
1080: }
1.42 lukem 1081: goto cleanup_fetch_url;
1.52 lukem 1082: }
1.147 christos 1083: #endif
1.52 lukem 1084: default:
1085: if (message)
1.175 lukem 1086: warnx("Error retrieving file `%s'", message);
1.52 lukem 1087: else
1088: warnx("Unknown error retrieving file");
1.44 lukem 1089: goto cleanup_fetch_url;
1090: }
1.52 lukem 1091: } /* end of ftp:// or http:// specific setup */
1.42 lukem 1092:
1.22 lukem 1093: /* Open the output file. */
1094: if (strcmp(savefile, "-") == 0) {
1095: fout = stdout;
1096: } else if (*savefile == '|') {
1.77 lukem 1097: oldintp = xsignal(SIGPIPE, SIG_IGN);
1.22 lukem 1098: fout = popen(savefile + 1, "w");
1099: if (fout == NULL) {
1.175 lukem 1100: warn("Can't execute `%s'", savefile + 1);
1.42 lukem 1101: goto cleanup_fetch_url;
1.22 lukem 1102: }
1103: closefunc = pclose;
1104: } else {
1.129 yamt 1105: if ((rangeend != -1 && rangeend <= restart_point) ||
1106: (rangestart == -1 && filesize != -1 && filesize <= restart_point)) {
1107: /* already done */
1108: if (verbose)
1109: fprintf(ttyout, "already done\n");
1110: rval = 0;
1111: goto cleanup_fetch_url;
1112: }
1113: if (restart_point && rangestart != -1) {
1.95 lukem 1114: if (entitylen != -1)
1115: filesize = entitylen;
1.129 yamt 1116: if (rangestart != restart_point) {
1.95 lukem 1117: warnx(
1118: "Size of `%s' differs from save file `%s'",
1119: url, savefile);
1120: goto cleanup_fetch_url;
1121: }
1122: fout = fopen(savefile, "a");
1123: } else
1124: fout = fopen(savefile, "w");
1.22 lukem 1125: if (fout == NULL) {
1.27 lukem 1126: warn("Can't open `%s'", savefile);
1.42 lukem 1127: goto cleanup_fetch_url;
1.22 lukem 1128: }
1129: closefunc = fclose;
1.1 lukem 1130: }
1131:
1.22 lukem 1132: /* Trap signals */
1.88 lukem 1133: if (sigsetjmp(httpabort, 1))
1.42 lukem 1134: goto cleanup_fetch_url;
1.86 lukem 1135: (void)xsignal(SIGQUIT, psummary);
1.77 lukem 1136: oldintr = xsignal(SIGINT, aborthttp);
1.2 lukem 1137:
1.187 lukem 1138: if ((size_t)rcvbuf_size > bufsize) {
1.73 lukem 1139: if (xferbuf)
1140: (void)free(xferbuf);
1141: bufsize = rcvbuf_size;
1.166 christos 1142: xferbuf = ftp_malloc(bufsize);
1.73 lukem 1143: }
1144:
1.1 lukem 1145: bytes = 0;
1146: hashbytes = mark;
1147: progressmeter(-1);
1.2 lukem 1148:
1.24 lukem 1149: /* Finally, suck down the file. */
1.45 lukem 1150: do {
1.77 lukem 1151: long chunksize;
1.181 lukem 1152: short lastchunk;
1.45 lukem 1153:
1154: chunksize = 0;
1.181 lukem 1155: lastchunk = 0;
1156: /* read chunk-size */
1.45 lukem 1157: if (ischunked) {
1.73 lukem 1158: if (fgets(xferbuf, bufsize, fin) == NULL) {
1.181 lukem 1159: warnx("Unexpected EOF reading chunk-size");
1.45 lukem 1160: goto cleanup_fetch_url;
1161: }
1.181 lukem 1162: errno = 0;
1.73 lukem 1163: chunksize = strtol(xferbuf, &ep, 16);
1.181 lukem 1164: if (ep == xferbuf) {
1165: warnx("Invalid chunk-size");
1166: goto cleanup_fetch_url;
1167: }
1168: if (errno == ERANGE || chunksize < 0) {
1169: errno = ERANGE;
1170: warn("Chunk-size `%.*s'",
1.182 lukem 1171: (int)(ep-xferbuf), xferbuf);
1.181 lukem 1172: goto cleanup_fetch_url;
1173: }
1.103 lukem 1174:
1175: /*
1.123 lukem 1176: * XXX: Work around bug in Apache 1.3.9 and
1177: * 1.3.11, which incorrectly put trailing
1.181 lukem 1178: * space after the chunk-size.
1.103 lukem 1179: */
1.123 lukem 1180: while (*ep == ' ')
1.103 lukem 1181: ep++;
1182:
1.181 lukem 1183: /* skip [ chunk-ext ] */
1184: if (*ep == ';') {
1185: while (*ep && *ep != '\r')
1186: ep++;
1187: }
1188:
1.45 lukem 1189: if (strcmp(ep, "\r\n") != 0) {
1.181 lukem 1190: warnx("Unexpected data following chunk-size");
1.45 lukem 1191: goto cleanup_fetch_url;
1192: }
1.183 lukem 1193: DPRINTF("fetch_url: got chunk-size of " LLF "\n",
1194: (LLT)chunksize);
1.181 lukem 1195: if (chunksize == 0) {
1196: lastchunk = 1;
1197: goto chunkdone;
1198: }
1.45 lukem 1199: }
1.59 lukem 1200: /* transfer file or chunk */
1201: while (1) {
1202: struct timeval then, now, td;
1203: off_t bufrem;
1204:
1.71 lukem 1205: if (rate_get)
1206: (void)gettimeofday(&then, NULL);
1.187 lukem 1207: bufrem = rate_get ? rate_get : (off_t)bufsize;
1.101 lukem 1208: if (ischunked)
1209: bufrem = MIN(chunksize, bufrem);
1.59 lukem 1210: while (bufrem > 0) {
1.187 lukem 1211: flen = fread(xferbuf, sizeof(char),
1212: MIN((off_t)bufsize, bufrem), fin);
1213: if (flen <= 0)
1.59 lukem 1214: goto chunkdone;
1.187 lukem 1215: bytes += flen;
1216: bufrem -= flen;
1217: if (fwrite(xferbuf, sizeof(char), flen, fout)
1218: != flen) {
1.59 lukem 1219: warn("Writing `%s'", savefile);
1220: goto cleanup_fetch_url;
1221: }
1.101 lukem 1222: if (hash && !progress) {
1223: while (bytes >= hashbytes) {
1224: (void)putc('#', ttyout);
1225: hashbytes += mark;
1226: }
1227: (void)fflush(ttyout);
1228: }
1229: if (ischunked) {
1.187 lukem 1230: chunksize -= flen;
1.101 lukem 1231: if (chunksize <= 0)
1232: break;
1.45 lukem 1233: }
1.59 lukem 1234: }
1235: if (rate_get) {
1236: while (1) {
1237: (void)gettimeofday(&now, NULL);
1238: timersub(&now, &then, &td);
1239: if (td.tv_sec > 0)
1240: break;
1241: usleep(1000000 - td.tv_usec);
1242: }
1243: }
1.101 lukem 1244: if (ischunked && chunksize <= 0)
1245: break;
1.1 lukem 1246: }
1.45 lukem 1247: /* read CRLF after chunk*/
1.59 lukem 1248: chunkdone:
1.45 lukem 1249: if (ischunked) {
1.181 lukem 1250: if (fgets(xferbuf, bufsize, fin) == NULL) {
1251: warnx("Unexpected EOF reading chunk CRLF");
1252: goto cleanup_fetch_url;
1253: }
1.73 lukem 1254: if (strcmp(xferbuf, "\r\n") != 0) {
1.45 lukem 1255: warnx("Unexpected data following chunk");
1256: goto cleanup_fetch_url;
1.1 lukem 1257: }
1.181 lukem 1258: if (lastchunk)
1259: break;
1.1 lukem 1260: }
1.45 lukem 1261: } while (ischunked);
1.181 lukem 1262:
1263: /* XXX: deal with optional trailer & CRLF here? */
1264:
1.1 lukem 1265: if (hash && !progress && bytes > 0) {
1266: if (bytes < mark)
1.22 lukem 1267: (void)putc('#', ttyout);
1268: (void)putc('\n', ttyout);
1.1 lukem 1269: }
1.25 lukem 1270: if (ferror(fin)) {
1271: warn("Reading file");
1.42 lukem 1272: goto cleanup_fetch_url;
1.1 lukem 1273: }
1.2 lukem 1274: progressmeter(1);
1.25 lukem 1275: (void)fflush(fout);
1276: if (closefunc == fclose && mtime != -1) {
1277: struct timeval tval[2];
1278:
1279: (void)gettimeofday(&tval[0], NULL);
1280: tval[1].tv_sec = mtime;
1281: tval[1].tv_usec = 0;
1.31 lukem 1282: (*closefunc)(fout);
1283: fout = NULL;
1284:
1285: if (utimes(savefile, tval) == -1) {
1.25 lukem 1286: fprintf(ttyout,
1287: "Can't change modification time to %s",
1.179 lukem 1288: rfc2822time(localtime(&mtime)));
1.25 lukem 1289: }
1290: }
1291: if (bytes > 0)
1292: ptransfer(0);
1.136 lukem 1293: bytes = 0;
1.1 lukem 1294:
1.42 lukem 1295: rval = 0;
1296: goto cleanup_fetch_url;
1.11 lukem 1297:
1.118 lukem 1298: improper:
1.184 lukem 1299: warnx("Improper response from `%s:%s'", host, port);
1.11 lukem 1300:
1.118 lukem 1301: cleanup_fetch_url:
1.86 lukem 1302: if (oldintr)
1303: (void)xsignal(SIGINT, oldintr);
1304: if (oldintp)
1305: (void)xsignal(SIGPIPE, oldintp);
1.24 lukem 1306: if (fin != NULL)
1307: fclose(fin);
1308: else if (s != -1)
1.1 lukem 1309: close(s);
1.22 lukem 1310: if (closefunc != NULL && fout != NULL)
1311: (*closefunc)(fout);
1.156 lukem 1312: if (res0)
1313: freeaddrinfo(res0);
1.29 lukem 1314: FREEPTR(savefile);
1.186 lukem 1315: FREEPTR(uuser);
1.162 lukem 1316: if (pass != NULL)
1317: memset(pass, 0, strlen(pass));
1.27 lukem 1318: FREEPTR(pass);
1319: FREEPTR(host);
1.60 itojun 1320: FREEPTR(port);
1.27 lukem 1321: FREEPTR(path);
1.50 lukem 1322: FREEPTR(decodedpath);
1.54 lukem 1323: FREEPTR(puser);
1.162 lukem 1324: if (ppass != NULL)
1325: memset(ppass, 0, strlen(ppass));
1.54 lukem 1326: FREEPTR(ppass);
1.44 lukem 1327: FREEPTR(auth);
1.42 lukem 1328: FREEPTR(location);
1329: FREEPTR(message);
1330: return (rval);
1.1 lukem 1331: }
1332:
1333: /*
1.50 lukem 1334: * Abort a HTTP retrieval
1.2 lukem 1335: */
1336: void
1.111 lukem 1337: aborthttp(int notused)
1.2 lukem 1338: {
1.88 lukem 1339: char msgbuf[100];
1.159 lukem 1340: size_t len;
1.2 lukem 1341:
1.148 lukem 1342: sigint_raised = 1;
1.2 lukem 1343: alarmtimer(0);
1.88 lukem 1344: len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf));
1345: write(fileno(ttyout), msgbuf, len);
1346: siglongjmp(httpabort, 1);
1.2 lukem 1347: }
1348:
1349: /*
1.50 lukem 1350: * Retrieve ftp URL or classic ftp argument using FTP.
1.63 lukem 1351: * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1.42 lukem 1352: * is still open (e.g, ftp xfer with trailing /)
1353: */
1354: static int
1.111 lukem 1355: fetch_ftp(const char *url)
1.42 lukem 1356: {
1357: char *cp, *xargv[5], rempath[MAXPATHLEN];
1.186 lukem 1358: char *host, *path, *dir, *file, *uuser, *pass;
1.60 itojun 1359: char *port;
1.187 lukem 1360: char cmdbuf[MAXPATHLEN];
1361: char dirbuf[4];
1.186 lukem 1362: int dirhasglob, filehasglob, rval, transtype, xargc;
1.160 lukem 1363: int oanonftp, oautologin;
1.82 lukem 1364: in_port_t portnum;
1365: url_t urltype;
1.42 lukem 1366:
1.183 lukem 1367: DPRINTF("fetch_ftp: `%s'\n", url);
1.186 lukem 1368: host = path = dir = file = uuser = pass = NULL;
1.60 itojun 1369: port = NULL;
1.42 lukem 1370: rval = 1;
1.186 lukem 1371: transtype = TYPE_I;
1.42 lukem 1372:
1.150 lukem 1373: if (STRNEQUAL(url, FTP_URL)) {
1.186 lukem 1374: if ((parse_url(url, "URL", &urltype, &uuser, &pass,
1.82 lukem 1375: &host, &port, &portnum, &path) == -1) ||
1.186 lukem 1376: (uuser != NULL && *uuser == '\0') ||
1.42 lukem 1377: EMPTYSTRING(host)) {
1378: warnx("Invalid URL `%s'", url);
1379: goto cleanup_fetch_ftp;
1380: }
1.53 lukem 1381: /*
1382: * Note: Don't url_decode(path) here. We need to keep the
1383: * distinction between "/" and "%2F" until later.
1384: */
1385:
1386: /* check for trailing ';type=[aid]' */
1.63 lukem 1387: if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) {
1.53 lukem 1388: if (strcasecmp(cp, ";type=a") == 0)
1.186 lukem 1389: transtype = TYPE_A;
1.53 lukem 1390: else if (strcasecmp(cp, ";type=i") == 0)
1.186 lukem 1391: transtype = TYPE_I;
1.53 lukem 1392: else if (strcasecmp(cp, ";type=d") == 0) {
1393: warnx(
1394: "Directory listing via a URL is not supported");
1395: goto cleanup_fetch_ftp;
1396: } else {
1397: warnx("Invalid suffix `%s' in URL `%s'", cp,
1398: url);
1399: goto cleanup_fetch_ftp;
1400: }
1401: *cp = 0;
1402: }
1.97 lukem 1403: } else { /* classic style `[user@]host:[file]' */
1.53 lukem 1404: urltype = CLASSIC_URL_T;
1.166 christos 1405: host = ftp_strdup(url);
1.97 lukem 1406: cp = strchr(host, '@');
1407: if (cp != NULL) {
1408: *cp = '\0';
1.186 lukem 1409: uuser = host;
1.97 lukem 1410: anonftp = 0; /* disable anonftp */
1.166 christos 1411: host = ftp_strdup(cp + 1);
1.97 lukem 1412: }
1.42 lukem 1413: cp = strchr(host, ':');
1414: if (cp != NULL) {
1415: *cp = '\0';
1.166 christos 1416: path = ftp_strdup(cp + 1);
1.42 lukem 1417: }
1418: }
1419: if (EMPTYSTRING(host))
1420: goto cleanup_fetch_ftp;
1421:
1.50 lukem 1422: /* Extract the file and (if present) directory name. */
1.42 lukem 1423: dir = path;
1424: if (! EMPTYSTRING(dir)) {
1.53 lukem 1425: /*
1.97 lukem 1426: * If we are dealing with classic `[user@]host:[path]' syntax,
1427: * then a path of the form `/file' (resulting from input of the
1428: * form `host:/file') means that we should do "CWD /" before
1429: * retrieving the file. So we set dir="/" and file="file".
1.53 lukem 1430: *
1.97 lukem 1431: * But if we are dealing with URLs like `ftp://host/path' then
1432: * a path of the form `/file' (resulting from a URL of the form
1433: * `ftp://host//file') means that we should do `CWD ' (with an
1434: * empty argument) before retrieving the file. So we set
1.53 lukem 1435: * dir="" and file="file".
1436: *
1.97 lukem 1437: * If the path does not contain / at all, we set dir=NULL.
1438: * (We get a path without any slashes if we are dealing with
1439: * classic `[user@]host:[file]' or URL `ftp://host/file'.)
1.53 lukem 1440: *
1.97 lukem 1441: * In all other cases, we set dir to a string that does not
1442: * include the final '/' that separates the dir part from the
1443: * file part of the path. (This will be the empty string if
1444: * and only if we are dealing with a path of the form `/file'
1445: * resulting from an URL of the form `ftp://host//file'.)
1.53 lukem 1446: */
1.42 lukem 1447: cp = strrchr(dir, '/');
1.53 lukem 1448: if (cp == dir && urltype == CLASSIC_URL_T) {
1.50 lukem 1449: file = cp + 1;
1.187 lukem 1450: (void)strlcpy(dirbuf, "/", sizeof(dirbuf));
1451: dir = dirbuf;
1.50 lukem 1452: } else if (cp != NULL) {
1.42 lukem 1453: *cp++ = '\0';
1454: file = cp;
1455: } else {
1456: file = dir;
1457: dir = NULL;
1458: }
1.63 lukem 1459: } else
1460: dir = NULL;
1.53 lukem 1461: if (urltype == FTP_URL_T && file != NULL) {
1.157 lukem 1462: url_decode(file);
1.53 lukem 1463: /* but still don't url_decode(dir) */
1464: }
1.163 christos 1465: DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s "
1466: "path `%s' dir `%s' file `%s'\n",
1.186 lukem 1467: STRorNULL(uuser), STRorNULL(pass),
1.183 lukem 1468: STRorNULL(host), STRorNULL(port),
1469: STRorNULL(path), STRorNULL(dir), STRorNULL(file));
1.42 lukem 1470:
1471: dirhasglob = filehasglob = 0;
1.53 lukem 1472: if (doglob && urltype == CLASSIC_URL_T) {
1.42 lukem 1473: if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL)
1474: dirhasglob = 1;
1475: if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL)
1476: filehasglob = 1;
1477: }
1478:
1.50 lukem 1479: /* Set up the connection */
1.160 lukem 1480: oanonftp = anonftp;
1.50 lukem 1481: if (connected)
1482: disconnect(0, NULL);
1.160 lukem 1483: anonftp = oanonftp;
1.187 lukem 1484: (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf));
1485: xargv[0] = cmdbuf;
1.50 lukem 1486: xargv[1] = host;
1487: xargv[2] = NULL;
1488: xargc = 2;
1489: if (port) {
1.60 itojun 1490: xargv[2] = port;
1.50 lukem 1491: xargv[3] = NULL;
1492: xargc = 3;
1493: }
1494: oautologin = autologin;
1.116 lukem 1495: /* don't autologin in setpeer(), use ftp_login() below */
1496: autologin = 0;
1.50 lukem 1497: setpeer(xargc, xargv);
1498: autologin = oautologin;
1.116 lukem 1499: if ((connected == 0) ||
1.186 lukem 1500: (connected == 1 && !ftp_login(host, uuser, pass))) {
1.189 drochner 1501: warnx("Can't connect or login to host `%s:%s'",
1502: host, port ? port : "?");
1.50 lukem 1503: goto cleanup_fetch_ftp;
1504: }
1.42 lukem 1505:
1.186 lukem 1506: switch (transtype) {
1.53 lukem 1507: case TYPE_A:
1.118 lukem 1508: setascii(1, xargv);
1.53 lukem 1509: break;
1510: case TYPE_I:
1.118 lukem 1511: setbinary(1, xargv);
1.53 lukem 1512: break;
1513: default:
1.186 lukem 1514: errx(1, "fetch_ftp: unknown transfer type %d", transtype);
1.53 lukem 1515: }
1516:
1.63 lukem 1517: /*
1518: * Change directories, if necessary.
1519: *
1520: * Note: don't use EMPTYSTRING(dir) below, because
1521: * dir=="" means something different from dir==NULL.
1522: */
1.53 lukem 1523: if (dir != NULL && !dirhasglob) {
1524: char *nextpart;
1.42 lukem 1525:
1.53 lukem 1526: /*
1.97 lukem 1527: * If we are dealing with a classic `[user@]host:[path]'
1528: * (urltype is CLASSIC_URL_T) then we have a raw directory
1.53 lukem 1529: * name (not encoded in any way) and we can change
1530: * directories in one step.
1531: *
1532: * If we are dealing with an `ftp://host/path' URL
1.177 lukem 1533: * (urltype is FTP_URL_T), then RFC3986 says we need to
1.53 lukem 1534: * send a separate CWD command for each unescaped "/"
1535: * in the path, and we have to interpret %hex escaping
1536: * *after* we find the slashes. It's possible to get
1537: * empty components here, (from multiple adjacent
1.177 lukem 1538: * slashes in the path) and RFC3986 says that we should
1.53 lukem 1539: * still do `CWD ' (with a null argument) in such cases.
1540: *
1.63 lukem 1541: * Many ftp servers don't support `CWD ', so if there's an
1542: * error performing that command, bail out with a descriptive
1543: * message.
1.53 lukem 1544: *
1545: * Examples:
1546: *
1.63 lukem 1547: * host: dir="", urltype=CLASSIC_URL_T
1548: * logged in (to default directory)
1.53 lukem 1549: * host:file dir=NULL, urltype=CLASSIC_URL_T
1550: * "RETR file"
1.63 lukem 1551: * host:dir/ dir="dir", urltype=CLASSIC_URL_T
1552: * "CWD dir", logged in
1553: * ftp://host/ dir="", urltype=FTP_URL_T
1554: * logged in (to default directory)
1555: * ftp://host/dir/ dir="dir", urltype=FTP_URL_T
1556: * "CWD dir", logged in
1.53 lukem 1557: * ftp://host/file dir=NULL, urltype=FTP_URL_T
1558: * "RETR file"
1559: * ftp://host//file dir="", urltype=FTP_URL_T
1.63 lukem 1560: * "CWD ", "RETR file"
1.53 lukem 1561: * host:/file dir="/", urltype=CLASSIC_URL_T
1562: * "CWD /", "RETR file"
1563: * ftp://host///file dir="/", urltype=FTP_URL_T
1.63 lukem 1564: * "CWD ", "CWD ", "RETR file"
1.53 lukem 1565: * ftp://host/%2F/file dir="%2F", urltype=FTP_URL_T
1566: * "CWD /", "RETR file"
1567: * ftp://host/foo/file dir="foo", urltype=FTP_URL_T
1568: * "CWD foo", "RETR file"
1569: * ftp://host/foo/bar/file dir="foo/bar"
1570: * "CWD foo", "CWD bar", "RETR file"
1571: * ftp://host//foo/bar/file dir="/foo/bar"
1.63 lukem 1572: * "CWD ", "CWD foo", "CWD bar", "RETR file"
1.53 lukem 1573: * ftp://host/foo//bar/file dir="foo//bar"
1.63 lukem 1574: * "CWD foo", "CWD ", "CWD bar", "RETR file"
1.53 lukem 1575: * ftp://host/%2F/foo/bar/file dir="%2F/foo/bar"
1576: * "CWD /", "CWD foo", "CWD bar", "RETR file"
1577: * ftp://host/%2Ffoo/bar/file dir="%2Ffoo/bar"
1578: * "CWD /foo", "CWD bar", "RETR file"
1579: * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar"
1580: * "CWD /foo/bar", "RETR file"
1581: * ftp://host/%2Ffoo%2Fbar%2Ffile dir=NULL
1582: * "RETR /foo/bar/file"
1583: *
1584: * Note that we don't need `dir' after this point.
1585: */
1586: do {
1587: if (urltype == FTP_URL_T) {
1588: nextpart = strchr(dir, '/');
1589: if (nextpart) {
1590: *nextpart = '\0';
1591: nextpart++;
1592: }
1593: url_decode(dir);
1594: } else
1595: nextpart = NULL;
1.183 lukem 1596: DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n",
1597: STRorNULL(dir), STRorNULL(nextpart));
1.63 lukem 1598: if (urltype == FTP_URL_T || *dir != '\0') {
1.187 lukem 1599: (void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf));
1600: xargv[0] = cmdbuf;
1.53 lukem 1601: xargv[1] = dir;
1602: xargv[2] = NULL;
1603: dirchange = 0;
1604: cd(2, xargv);
1.63 lukem 1605: if (! dirchange) {
1606: if (*dir == '\0' && code == 500)
1607: fprintf(stderr,
1608: "\n"
1609: "ftp: The `CWD ' command (without a directory), which is required by\n"
1.177 lukem 1610: " RFC3986 to support the empty directory in the URL pathname (`//'),\n"
1611: " conflicts with the server's conformance to RFC0959.\n"
1.63 lukem 1612: " Try the same URL without the `//' in the URL pathname.\n"
1613: "\n");
1.53 lukem 1614: goto cleanup_fetch_ftp;
1.63 lukem 1615: }
1.53 lukem 1616: }
1617: dir = nextpart;
1618: } while (dir != NULL);
1.42 lukem 1619: }
1620:
1621: if (EMPTYSTRING(file)) {
1622: rval = -1;
1623: goto cleanup_fetch_ftp;
1624: }
1625:
1626: if (dirhasglob) {
1.78 lukem 1627: (void)strlcpy(rempath, dir, sizeof(rempath));
1628: (void)strlcat(rempath, "/", sizeof(rempath));
1629: (void)strlcat(rempath, file, sizeof(rempath));
1.42 lukem 1630: file = rempath;
1631: }
1632:
1633: /* Fetch the file(s). */
1634: xargc = 2;
1.187 lukem 1635: (void)strlcpy(cmdbuf, "get", sizeof(cmdbuf));
1636: xargv[0] = cmdbuf;
1.42 lukem 1637: xargv[1] = file;
1638: xargv[2] = NULL;
1639: if (dirhasglob || filehasglob) {
1640: int ointeractive;
1641:
1642: ointeractive = interactive;
1643: interactive = 0;
1.155 lukem 1644: if (restartautofetch)
1.187 lukem 1645: (void)strlcpy(cmdbuf, "mreget", sizeof(cmdbuf));
1.155 lukem 1646: else
1.187 lukem 1647: (void)strlcpy(cmdbuf, "mget", sizeof(cmdbuf));
1648: xargv[0] = cmdbuf;
1.42 lukem 1649: mget(xargc, xargv);
1650: interactive = ointeractive;
1651: } else {
1.53 lukem 1652: if (outfile == NULL) {
1653: cp = strrchr(file, '/'); /* find savefile */
1654: if (cp != NULL)
1655: outfile = cp + 1;
1656: else
1657: outfile = file;
1.42 lukem 1658: }
1.53 lukem 1659: xargv[2] = (char *)outfile;
1660: xargv[3] = NULL;
1661: xargc++;
1.52 lukem 1662: if (restartautofetch)
1663: reget(xargc, xargv);
1664: else
1665: get(xargc, xargv);
1.42 lukem 1666: }
1667:
1668: if ((code / 100) == COMPLETE)
1669: rval = 0;
1670:
1.118 lukem 1671: cleanup_fetch_ftp:
1.169 christos 1672: FREEPTR(port);
1.42 lukem 1673: FREEPTR(host);
1674: FREEPTR(path);
1.186 lukem 1675: FREEPTR(uuser);
1.162 lukem 1676: if (pass)
1677: memset(pass, 0, strlen(pass));
1.42 lukem 1678: FREEPTR(pass);
1679: return (rval);
1680: }
1681:
1682: /*
1683: * Retrieve the given file to outfile.
1684: * Supports arguments of the form:
1685: * "host:path", "ftp://host/path" if $ftpproxy, call fetch_url() else
1686: * call fetch_ftp()
1.50 lukem 1687: * "http://host/path" call fetch_url() to use HTTP
1.42 lukem 1688: * "file:///path" call fetch_url() to copy
1689: * "about:..." print a message
1690: *
1691: * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection
1692: * is still open (e.g, ftp xfer with trailing /)
1693: */
1694: static int
1.111 lukem 1695: go_fetch(const char *url)
1.42 lukem 1696: {
1.186 lukem 1697: char *proxyenv;
1.42 lukem 1698:
1.147 christos 1699: #ifndef NO_ABOUT
1.42 lukem 1700: /*
1701: * Check for about:*
1702: */
1.150 lukem 1703: if (STRNEQUAL(url, ABOUT_URL)) {
1.42 lukem 1704: url += sizeof(ABOUT_URL) -1;
1.144 lukem 1705: if (strcasecmp(url, "ftp") == 0 ||
1706: strcasecmp(url, "tnftp") == 0) {
1.87 lukem 1707: fputs(
1.143 salo 1708: "This version of ftp has been enhanced by Luke Mewburn <lukem@NetBSD.org>\n"
1.87 lukem 1709: "for the NetBSD project. Execute `man ftp' for more details.\n", ttyout);
1710: } else if (strcasecmp(url, "lukem") == 0) {
1711: fputs(
1712: "Luke Mewburn is the author of most of the enhancements in this ftp client.\n"
1.143 salo 1713: "Please email feedback to <lukem@NetBSD.org>.\n", ttyout);
1.42 lukem 1714: } else if (strcasecmp(url, "netbsd") == 0) {
1.87 lukem 1715: fputs(
1716: "NetBSD is a freely available and redistributable UNIX-like operating system.\n"
1.144 lukem 1717: "For more information, see http://www.NetBSD.org/\n", ttyout);
1.92 lukem 1718: } else if (strcasecmp(url, "version") == 0) {
1.115 lukem 1719: fprintf(ttyout, "Version: %s %s%s\n",
1720: FTP_PRODUCT, FTP_VERSION,
1721: #ifdef INET6
1722: ""
1723: #else
1724: " (-IPv6)"
1725: #endif
1726: );
1.42 lukem 1727: } else {
1728: fprintf(ttyout, "`%s' is an interesting topic.\n", url);
1729: }
1.92 lukem 1730: fputs("\n", ttyout);
1.42 lukem 1731: return (0);
1732: }
1.147 christos 1733: #endif
1.42 lukem 1734:
1735: /*
1736: * Check for file:// and http:// URLs.
1737: */
1.150 lukem 1738: if (STRNEQUAL(url, HTTP_URL) || STRNEQUAL(url, FILE_URL))
1.52 lukem 1739: return (fetch_url(url, NULL, NULL, NULL));
1.42 lukem 1740:
1741: /*
1742: * Try FTP URL-style and host:file arguments next.
1743: * If ftpproxy is set with an FTP URL, use fetch_url()
1744: * Othewise, use fetch_ftp().
1745: */
1.186 lukem 1746: proxyenv = getoptionvalue("ftp_proxy");
1747: if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL))
1.52 lukem 1748: return (fetch_url(url, NULL, NULL, NULL));
1.42 lukem 1749:
1.52 lukem 1750: return (fetch_ftp(url));
1.42 lukem 1751: }
1752:
1753: /*
1754: * Retrieve multiple files from the command line,
1755: * calling go_fetch() for each file.
1756: *
1757: * If an ftp path has a trailing "/", the path will be cd-ed into and
1.41 lukem 1758: * the connection remains open, and the function will return -1
1759: * (to indicate the connection is alive).
1.1 lukem 1760: * If an error occurs the return value will be the offset+1 in
1761: * argv[] of the file that caused a problem (i.e, argv[x]
1762: * returns x+1)
1763: * Otherwise, 0 is returned if all files retrieved successfully.
1764: */
1765: int
1.111 lukem 1766: auto_fetch(int argc, char *argv[])
1.1 lukem 1767: {
1.160 lukem 1768: volatile int argpos, rval;
1.22 lukem 1769:
1.160 lukem 1770: argpos = rval = 0;
1.1 lukem 1771:
1.88 lukem 1772: if (sigsetjmp(toplevel, 1)) {
1.2 lukem 1773: if (connected)
1774: disconnect(0, NULL);
1.148 lukem 1775: if (rval > 0)
1776: rval = argpos + 1;
1777: return (rval);
1.2 lukem 1778: }
1.88 lukem 1779: (void)xsignal(SIGINT, intr);
1.91 lukem 1780: (void)xsignal(SIGPIPE, lostpeer);
1.1 lukem 1781:
1782: /*
1783: * Loop through as long as there's files to fetch.
1784: */
1.160 lukem 1785: for (; (rval == 0) && (argpos < argc); argpos++) {
1.1 lukem 1786: if (strchr(argv[argpos], ':') == NULL)
1787: break;
1.42 lukem 1788: redirect_loop = 0;
1.90 lukem 1789: if (!anonftp)
1790: anonftp = 2; /* Handle "automatic" transfers. */
1.52 lukem 1791: rval = go_fetch(argv[argpos]);
1792: if (outfile != NULL && strcmp(outfile, "-") != 0
1793: && outfile[0] != '|')
1794: outfile = NULL;
1.42 lukem 1795: if (rval > 0)
1.1 lukem 1796: rval = argpos + 1;
1.42 lukem 1797: }
1.3 lukem 1798:
1.1 lukem 1799: if (connected && rval != -1)
1800: disconnect(0, NULL);
1.114 lukem 1801: return (rval);
1802: }
1803:
1804:
1.170 lukem 1805: /*
1806: * Upload multiple files from the command line.
1807: *
1808: * If an error occurs the return value will be the offset+1 in
1809: * argv[] of the file that caused a problem (i.e, argv[x]
1810: * returns x+1)
1811: * Otherwise, 0 is returned if all files uploaded successfully.
1812: */
1.114 lukem 1813: int
1814: auto_put(int argc, char **argv, const char *uploadserver)
1815: {
1816: char *uargv[4], *path, *pathsep;
1.170 lukem 1817: int uargc, rval, argpos;
1.159 lukem 1818: size_t len;
1.187 lukem 1819: char cmdbuf[MAX_C_NAME];
1.114 lukem 1820:
1.187 lukem 1821: (void)strlcpy(cmdbuf, "mput", sizeof(cmdbuf));
1822: uargv[0] = cmdbuf;
1823: uargv[1] = argv[0];
1824: uargc = 2;
1.114 lukem 1825: uargv[2] = uargv[3] = NULL;
1826: pathsep = NULL;
1827: rval = 1;
1828:
1.163 christos 1829: DPRINTF("auto_put: target `%s'\n", uploadserver);
1.114 lukem 1830:
1.166 christos 1831: path = ftp_strdup(uploadserver);
1.114 lukem 1832: len = strlen(path);
1833: if (path[len - 1] != '/' && path[len - 1] != ':') {
1834: /*
1835: * make sure we always pass a directory to auto_fetch
1836: */
1837: if (argc > 1) { /* more than one file to upload */
1838: len = strlen(uploadserver) + 2; /* path + "/" + "\0" */
1839: free(path);
1.166 christos 1840: path = (char *)ftp_malloc(len);
1.114 lukem 1841: (void)strlcpy(path, uploadserver, len);
1842: (void)strlcat(path, "/", len);
1843: } else { /* single file to upload */
1.187 lukem 1844: (void)strlcpy(cmdbuf, "put", sizeof(cmdbuf));
1845: uargv[0] = cmdbuf;
1.114 lukem 1846: pathsep = strrchr(path, '/');
1847: if (pathsep == NULL) {
1848: pathsep = strrchr(path, ':');
1849: if (pathsep == NULL) {
1850: warnx("Invalid URL `%s'", path);
1851: goto cleanup_auto_put;
1852: }
1853: pathsep++;
1.166 christos 1854: uargv[2] = ftp_strdup(pathsep);
1.114 lukem 1855: pathsep[0] = '/';
1.157 lukem 1856: } else
1.166 christos 1857: uargv[2] = ftp_strdup(pathsep + 1);
1.114 lukem 1858: pathsep[1] = '\0';
1859: uargc++;
1860: }
1861: }
1.163 christos 1862: DPRINTF("auto_put: URL `%s' argv[2] `%s'\n",
1.183 lukem 1863: path, STRorNULL(uargv[2]));
1.157 lukem 1864:
1865: /* connect and cwd */
1.114 lukem 1866: rval = auto_fetch(1, &path);
1867: if(rval >= 0)
1868: goto cleanup_auto_put;
1869:
1.170 lukem 1870: rval = 0;
1871:
1872: /* target filename provided; upload 1 file */
1.114 lukem 1873: /* XXX : is this the best way? */
1874: if (uargc == 3) {
1875: uargv[1] = argv[0];
1876: put(uargc, uargv);
1.170 lukem 1877: if ((code / 100) != COMPLETE)
1878: rval = 1;
1879: } else { /* otherwise a target dir: upload all files to it */
1880: for(argpos = 0; argv[argpos] != NULL; argpos++) {
1881: uargv[1] = argv[argpos];
1882: mput(uargc, uargv);
1883: if ((code / 100) != COMPLETE) {
1884: rval = argpos + 1;
1885: break;
1886: }
1887: }
1.114 lukem 1888: }
1889:
1.118 lukem 1890: cleanup_auto_put:
1.168 christos 1891: free(path);
1.114 lukem 1892: FREEPTR(uargv[2]);
1.1 lukem 1893: return (rval);
1894: }
CVSweb <webmaster@jp.NetBSD.org>