Annotation of src/usr.bin/ftp/fetch.c, Revision 1.1
1.1 ! millert 1: /* $NetBSD: fetch.c,v 1.2 1997/02/01 10:45:00 lukem Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1997 The NetBSD Foundation, Inc.
! 5: * All rights reserved.
! 6: *
! 7: * This code is derived from software contributed to The NetBSD Foundation
! 8: * by Jason Thorpe and Luke Mewburn.
! 9: *
! 10: * Redistribution and use in source and binary forms, with or without
! 11: * modification, are permitted provided that the following conditions
! 12: * are met:
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: * 2. Redistributions in binary form must reproduce the above copyright
! 16: * notice, this list of conditions and the following disclaimer in the
! 17: * documentation and/or other materials provided with the distribution.
! 18: * 3. All advertising materials mentioning features or use of this software
! 19: * must display the following acknowledgement:
! 20: * This product includes software developed by the NetBSD
! 21: * Foundation, Inc. and its contributors.
! 22: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 23: * contributors may be used to endorse or promote products derived
! 24: * from this software without specific prior written permission.
! 25: *
! 26: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 27: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 28: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 29: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
! 30: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 31: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 32: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 33: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 34: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 35: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 36: * POSSIBILITY OF SUCH DAMAGE.
! 37: */
! 38:
! 39: #ifndef lint
! 40: static char rcsid[] = "$NetBSD: fetch.c,v 1.2 1997/02/01 10:45:00 lukem Exp $";
! 41: #endif /* not lint */
! 42:
! 43: /*
! 44: * FTP User Program -- Command line file retrieval
! 45: */
! 46:
! 47: #include <sys/types.h>
! 48: #include <sys/param.h>
! 49: #include <sys/socket.h>
! 50:
! 51: #include <netinet/in.h>
! 52:
! 53: #include <arpa/ftp.h>
! 54: #include <arpa/inet.h>
! 55:
! 56: #include <ctype.h>
! 57: #include <err.h>
! 58: #include <netdb.h>
! 59: #include <fcntl.h>
! 60: #include <stdio.h>
! 61: #include <stdlib.h>
! 62: #include <string.h>
! 63: #include <unistd.h>
! 64:
! 65: #include "ftp_var.h"
! 66:
! 67: #define FTP_URL "ftp://" /* ftp URL prefix */
! 68: #define HTTP_URL "http://" /* http URL prefix */
! 69: #define HTTP_PROXY "http_proxy" /* env var with http proxy location */
! 70:
! 71:
! 72: #define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
! 73:
! 74: jmp_buf httpabort;
! 75:
! 76: /*
! 77: * Retrieve an http:// URL, via a proxy if necessary.
! 78: * Modifies the string argument given.
! 79: * Returns -1 on failure, 0 on success
! 80: */
! 81: int
! 82: http_get(line)
! 83: char *line;
! 84: {
! 85: struct sockaddr_in sin;
! 86: int i, out, port, s;
! 87: size_t buflen, len;
! 88: char c, *cp, *cp2, *savefile, *portnum, *path, buf[4096];
! 89: char *proxyenv, *proxy, *host;
! 90: sig_t oldintr;
! 91: off_t hashbytes;
! 92:
! 93: s = -1;
! 94: proxy = NULL;
! 95:
! 96: host = line + sizeof(HTTP_URL) - 1;
! 97: path = strchr(host, '/'); /* find path */
! 98: if (EMPTYSTRING(path))
! 99: goto cleanup_http_get;
! 100: *path++ = '\0';
! 101: if (EMPTYSTRING(path))
! 102: goto cleanup_http_get;
! 103:
! 104: savefile = strrchr(path, '/'); /* find savefile */
! 105: if (savefile != NULL)
! 106: savefile++;
! 107: else
! 108: savefile = path;
! 109: if (EMPTYSTRING(savefile))
! 110: goto cleanup_http_get;
! 111:
! 112: proxyenv = getenv(HTTP_PROXY);
! 113: if (proxyenv != NULL) { /* use proxy */
! 114: if (strncmp(proxyenv, HTTP_URL, sizeof(HTTP_URL) - 1) != 0) {
! 115: warnx("Malformed proxy url: %s", proxyenv);
! 116: goto cleanup_http_get;
! 117: }
! 118: proxy = strdup(proxyenv);
! 119: if (proxy == NULL)
! 120: errx(1, "Can't allocate memory for proxy url.");
! 121: host = proxy + sizeof(HTTP_URL) - 1;
! 122: if (EMPTYSTRING(host))
! 123: goto cleanup_http_get;
! 124: *--path = '/'; /* add / back to real path */
! 125: path = strchr(host, '/'); /* remove trailing / on host */
! 126: if (! EMPTYSTRING(path))
! 127: *path++ = '\0';
! 128: path = line;
! 129: }
! 130:
! 131: portnum = strchr(host, ':'); /* find portnum */
! 132: if (portnum != NULL)
! 133: *portnum++ = '\0';
! 134:
! 135: if (debug)
! 136: printf("host %s, port %s, path %s, save as %s.\n",
! 137: host, portnum, path, savefile);
! 138:
! 139: memset(&sin, 0, sizeof(sin));
! 140: sin.sin_family = AF_INET;
! 141:
! 142: if (isdigit(host[0])) {
! 143: if (inet_aton(host, &sin.sin_addr) == 0) {
! 144: warnx("invalid IP address: %s", host);
! 145: goto cleanup_http_get;
! 146: }
! 147: } else {
! 148: struct hostent *hp;
! 149:
! 150: hp = gethostbyname(host);
! 151: if (hp == NULL) {
! 152: warnx("%s: %s", host, hstrerror(h_errno));
! 153: goto cleanup_http_get;
! 154: }
! 155: if (hp->h_addrtype != AF_INET) {
! 156: warnx("%s: not an Internet address?", host);
! 157: goto cleanup_http_get;
! 158: }
! 159: memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
! 160: }
! 161:
! 162: if (! EMPTYSTRING(portnum)) {
! 163: port = atoi(portnum);
! 164: if (port < 1 || (port & 0xffff) != port) {
! 165: warnx("invalid port: %s", portnum);
! 166: goto cleanup_http_get;
! 167: }
! 168: port = htons(port);
! 169: } else
! 170: port = httpport;
! 171: sin.sin_port = port;
! 172:
! 173: s = socket(AF_INET, SOCK_STREAM, 0);
! 174: if (s == -1) {
! 175: warnx("Can't create socket");
! 176: goto cleanup_http_get;
! 177: }
! 178:
! 179: if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
! 180: warn("Can't connect to %s", host);
! 181: goto cleanup_http_get;
! 182: }
! 183:
! 184: /*
! 185: * Construct and send the request. We're expecting a return
! 186: * status of "200". Proxy requests don't want leading /.
! 187: */
! 188: if (!proxy)
! 189: printf("Requesting %s:%d/%s\n", line, ntohs(port), path);
! 190: else
! 191: printf("Requesting %s (via %s)\n", line, proxyenv);
! 192: snprintf(buf, sizeof(buf), "GET %s%s HTTP/1.0\n\n",
! 193: proxy ? "" : "/", path);
! 194: buflen = strlen(buf);
! 195: if (write(s, buf, buflen) < buflen) {
! 196: warn("write");
! 197: goto cleanup_http_get;
! 198: }
! 199: memset(buf, 0, sizeof(buf));
! 200: for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
! 201: if (read(s, cp, 1) != 1)
! 202: goto improper;
! 203: if (*cp == '\n')
! 204: break;
! 205: }
! 206: buf[buflen - 1] = '\0'; /* sanity */
! 207: cp = strchr(buf, ' ');
! 208: if (cp == NULL)
! 209: goto improper;
! 210: else
! 211: cp++;
! 212: if (strncmp(cp, "200", 3)) {
! 213: warnx("Error retrieving file: %s", cp);
! 214: goto cleanup_http_get;
! 215: }
! 216:
! 217: /*
! 218: * Read the rest of the header.
! 219: */
! 220: memset(buf, 0, sizeof(buf));
! 221: c = '\0';
! 222: for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
! 223: if (read(s, cp, 1) != 1)
! 224: goto improper;
! 225: if (*cp == '\n' && c == '\n')
! 226: break;
! 227: c = *cp;
! 228: }
! 229: buf[buflen - 1] = '\0'; /* sanity */
! 230:
! 231: /*
! 232: * Look for the "Content-length: " header.
! 233: */
! 234: #define CONTENTLEN "Content-Length: "
! 235: for (cp = buf; *cp != '\0'; cp++) {
! 236: if (tolower(*cp) == 'c' &&
! 237: strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0)
! 238: break;
! 239: }
! 240: if (*cp == '\0')
! 241: goto improper;
! 242: cp += sizeof(CONTENTLEN) - 1;
! 243: cp2 = strchr(cp, '\n');
! 244: if (cp2 == NULL)
! 245: goto improper;
! 246: else
! 247: *cp2 = '\0';
! 248: filesize = atoi(cp);
! 249: if (filesize < 1)
! 250: goto improper;
! 251:
! 252: /* Open the output file. */
! 253: out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
! 254: if (out < 0) {
! 255: warn("Can't open %s", savefile);
! 256: goto cleanup_http_get;
! 257: }
! 258:
! 259: /* Trap signals */
! 260: oldintr = NULL;
! 261: if (setjmp(httpabort)) {
! 262: if (oldintr)
! 263: (void) signal(SIGINT, oldintr);
! 264: goto cleanup_http_get;
! 265: }
! 266: oldintr = signal(SIGINT, aborthttp);
! 267:
! 268: bytes = 0;
! 269: hashbytes = mark;
! 270: progressmeter(-1);
! 271:
! 272: /* Finally, suck down the file. */
! 273: i = 0;
! 274: while ((len = read(s, buf, sizeof(buf))) > 0) {
! 275: bytes += len;
! 276: for (cp = buf; len > 0; len -= i, cp += i) {
! 277: if ((i = write(out, cp, len)) == -1) {
! 278: warn("Writing %s", savefile);
! 279: goto cleanup_http_get;
! 280: }
! 281: else if (i == 0)
! 282: break;
! 283: }
! 284: if (hash && !progress) {
! 285: while (bytes >= hashbytes) {
! 286: (void) putchar('#');
! 287: hashbytes += mark;
! 288: }
! 289: (void) fflush(stdout);
! 290: }
! 291: }
! 292: if (hash && !progress && bytes > 0) {
! 293: if (bytes < mark)
! 294: (void) putchar('#');
! 295: (void) putchar('\n');
! 296: (void) fflush(stdout);
! 297: }
! 298: if (len != 0) {
! 299: warn("Reading from socket");
! 300: goto cleanup_http_get;
! 301: }
! 302: progressmeter(1);
! 303: if (verbose)
! 304: printf("Successfully retrieved file.\n");
! 305: (void) signal(SIGINT, oldintr);
! 306:
! 307: close(s);
! 308: close(out);
! 309: if (proxy)
! 310: free(proxy);
! 311: return(0);
! 312:
! 313: improper:
! 314: warnx("improper response from %s", host);
! 315: cleanup_http_get:
! 316: if (s != -1)
! 317: close(s);
! 318: if (proxy)
! 319: free(proxy);
! 320: return(-1);
! 321: }
! 322:
! 323: /*
! 324: * Abort a http retrieval
! 325: */
! 326: void
! 327: aborthttp()
! 328: {
! 329:
! 330: alarmtimer(0);
! 331: printf("\nhttp fetch aborted\n");
! 332: (void) fflush(stdout);
! 333: longjmp(httpabort, 1);
! 334: }
! 335:
! 336: /*
! 337: * Retrieve multiple files from the command line, transferring
! 338: * files of the form "host:path", "ftp://host/path" using the
! 339: * ftp protocol, and files of the form "http://host/path" using
! 340: * the http protocol.
! 341: * If path has a trailing "/", then return(-1);
! 342: * the path will be cd-ed into and the connection remains open,
! 343: * and the function will return -1 (to indicate the connection
! 344: * is alive).
! 345: * If an error occurs the return value will be the offset+1 in
! 346: * argv[] of the file that caused a problem (i.e, argv[x]
! 347: * returns x+1)
! 348: * Otherwise, 0 is returned if all files retrieved successfully.
! 349: */
! 350: int
! 351: auto_fetch(argc, argv)
! 352: int argc;
! 353: char *argv[];
! 354: {
! 355: static char lasthost[MAXHOSTNAMELEN];
! 356: char *xargv[5];
! 357: char *cp, *line, *host, *dir, *file, *portnum;
! 358: int rval, xargc, argpos;
! 359:
! 360: argpos = 0;
! 361:
! 362: if (setjmp(toplevel)) {
! 363: if (connected)
! 364: disconnect(0, NULL);
! 365: return(argpos + 1);
! 366: }
! 367: (void) signal(SIGINT, intr);
! 368: (void) signal(SIGPIPE, lostpeer);
! 369:
! 370: /*
! 371: * Loop through as long as there's files to fetch.
! 372: */
! 373: for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) {
! 374: if (strchr(argv[argpos], ':') == NULL)
! 375: break;
! 376: host = dir = file = portnum = NULL;
! 377:
! 378: /*
! 379: * We muck with the string, so we make a copy.
! 380: */
! 381: line = strdup(argv[argpos]);
! 382: if (line == NULL)
! 383: errx(1, "Can't allocate memory for auto-fetch.");
! 384:
! 385: /*
! 386: * Try HTTP URL-style arguments first.
! 387: */
! 388: if (strncmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
! 389: if (http_get(line) == -1)
! 390: rval = argpos + 1;
! 391: continue;
! 392: }
! 393:
! 394: /*
! 395: * Try FTP URL-style arguments next, then host:file.
! 396: */
! 397: host = line;
! 398: if (strncmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
! 399: host += sizeof(FTP_URL) - 1;
! 400: cp = strchr(host, '/');
! 401:
! 402: /* Look for a port number after the host name. */
! 403: portnum = strchr(host, ':');
! 404: if (portnum != NULL)
! 405: *portnum++ = '\0';
! 406: } else /* classic style `host:file' */
! 407: cp = strchr(host, ':');
! 408: if (EMPTYSTRING(host)) {
! 409: rval = argpos + 1;
! 410: continue;
! 411: }
! 412:
! 413: /*
! 414: * If cp is NULL, the file wasn't specified
! 415: * (URL looked something like ftp://host)
! 416: */
! 417: if (cp != NULL)
! 418: *cp++ = '\0';
! 419:
! 420: /*
! 421: * Extract the file and (if present) directory name.
! 422: */
! 423: dir = cp;
! 424: if (! EMPTYSTRING(dir)) {
! 425: cp = strrchr(cp, '/');
! 426: if (cp != NULL) {
! 427: *cp++ = '\0';
! 428: file = cp;
! 429: } else {
! 430: file = dir;
! 431: dir = NULL;
! 432: }
! 433: }
! 434: if (debug)
! 435: printf("host '%s', dir '%s', file '%s'\n",
! 436: host, dir, file);
! 437:
! 438: /*
! 439: * Set up the connection if we don't have one.
! 440: */
! 441: if (strcmp(host, lasthost) != 0) {
! 442: strcpy(lasthost, host);
! 443: if (connected)
! 444: disconnect(0, NULL);
! 445: xargv[0] = __progname;
! 446: xargv[1] = host;
! 447: xargv[2] = NULL;
! 448: xargc = 2;
! 449: if (portnum != NULL) {
! 450: xargv[2] = portnum;
! 451: xargv[3] = NULL;
! 452: xargc = 3;
! 453: }
! 454: setpeer(xargc, xargv);
! 455: if (connected == 0) {
! 456: warnx("Can't connect to host `%s'", host);
! 457: rval = argpos + 1;
! 458: continue;
! 459: }
! 460:
! 461: /* Always use binary transfers. */
! 462: setbinary(0, NULL);
! 463: }
! 464: else /* already have connection, cd back to '/' */
! 465: {
! 466: xargv[0] = "cd";
! 467: xargv[1] = "/";
! 468: xargv[2] = NULL;
! 469: cd(2, xargv);
! 470: if (! dirchange) {
! 471: rval = argpos + 1;
! 472: continue;
! 473: }
! 474: }
! 475:
! 476: /* Change directories, if necessary. */
! 477: if (! EMPTYSTRING(dir)) {
! 478: xargv[0] = "cd";
! 479: xargv[1] = dir;
! 480: xargv[2] = NULL;
! 481: cd(2, xargv);
! 482: if (! dirchange) {
! 483: rval = argpos + 1;
! 484: continue;
! 485: }
! 486: }
! 487:
! 488: if (EMPTYSTRING(file)) {
! 489: rval = -1;
! 490: continue;
! 491: }
! 492:
! 493: if (!verbose)
! 494: printf("Retrieving %s/%s\n", dir ? dir : "", file);
! 495:
! 496: /* Fetch the file. */
! 497: xargv[0] = "get";
! 498: xargv[1] = file;
! 499: xargv[2] = NULL;
! 500: get(2, xargv);
! 501:
! 502: if ((code / 100) != COMPLETE) /* XXX: is this valid? */
! 503: rval = argpos + 1;
! 504: }
! 505: if (connected && rval != -1)
! 506: disconnect(0, NULL);
! 507: return (rval);
! 508: }