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