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