1 /* $OpenBSD: main.c,v 1.135 2020/09/06 09:49:11 tb Exp $ */ 2 /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ 3 4 /* 5 * Copyright (C) 1997 and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1985, 1989, 1993, 1994 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 /* 63 * FTP User Program -- Command Interface. 64 */ 65 #include <sys/types.h> 66 #include <sys/socket.h> 67 68 #include <ctype.h> 69 #include <err.h> 70 #include <fcntl.h> 71 #include <netdb.h> 72 #include <pwd.h> 73 #include <stdio.h> 74 #include <errno.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <unistd.h> 78 79 #include <tls.h> 80 81 #include "cmds.h" 82 #include "ftp_var.h" 83 84 int trace; 85 int hash; 86 int mark; 87 int sendport; 88 int verbose; 89 int connected; 90 int fromatty; 91 int interactive; 92 #ifndef SMALL 93 int confirmrest; 94 int debug; 95 int bell; 96 char *altarg; 97 #endif /* !SMALL */ 98 int doglob; 99 int autologin; 100 int proxy; 101 int proxflag; 102 int gatemode; 103 char *gateserver; 104 int sunique; 105 int runique; 106 int mcase; 107 int ntflag; 108 int mapflag; 109 int preserve; 110 int progress; 111 int code; 112 int crflag; 113 char pasv[BUFSIZ]; 114 int passivemode; 115 int activefallback; 116 char ntin[17]; 117 char ntout[17]; 118 char mapin[PATH_MAX]; 119 char mapout[PATH_MAX]; 120 char typename[32]; 121 int type; 122 int curtype; 123 char structname[32]; 124 int stru; 125 char formname[32]; 126 int form; 127 char modename[32]; 128 int mode; 129 char bytename[32]; 130 int bytesize; 131 int anonftp; 132 int dirchange; 133 unsigned int retry_connect; 134 int ttywidth; 135 int epsv4; 136 int epsv4bad; 137 138 #ifndef SMALL 139 int editing; 140 EditLine *el; 141 History *hist; 142 char *cursor_pos; 143 size_t cursor_argc; 144 size_t cursor_argo; 145 int resume; 146 char *srcaddr; 147 #endif /* !SMALL */ 148 149 char *cookiefile; 150 151 off_t bytes; 152 off_t filesize; 153 char *direction; 154 155 char *hostname; 156 int unix_server; 157 int unix_proxy; 158 159 char *ftpport; 160 char *httpport; 161 #ifndef NOSSL 162 char *httpsport; 163 #endif /* !SMALL */ 164 char *httpuseragent; 165 char *gateport; 166 167 jmp_buf toplevel; 168 169 #ifndef SMALL 170 char line[FTPBUFLEN]; 171 char *argbase; 172 char *stringbase; 173 char argbuf[FTPBUFLEN]; 174 StringList *marg_sl; 175 int margc; 176 int options; 177 #endif /* !SMALL */ 178 179 int cpend; 180 int mflag; 181 182 #ifndef SMALL 183 int macnum; 184 struct macel macros[16]; 185 char macbuf[4096]; 186 #endif /* !SMALL */ 187 188 FILE *ttyout; 189 190 int connect_timeout; 191 192 #ifndef NOSSL 193 char * const ssl_verify_opts[] = { 194 #define SSL_CAFILE 0 195 "cafile", 196 #define SSL_CAPATH 1 197 "capath", 198 #define SSL_CIPHERS 2 199 "ciphers", 200 #define SSL_DONTVERIFY 3 201 "dont", 202 #define SSL_DOVERIFY 4 203 "do", 204 #define SSL_VERIFYDEPTH 5 205 "depth", 206 #define SSL_MUSTSTAPLE 6 207 "muststaple", 208 #define SSL_NOVERIFYTIME 7 209 "noverifytime", 210 #define SSL_SESSION 8 211 "session", 212 #define SSL_PROTOCOLS 9 213 "protocols", 214 NULL 215 }; 216 217 struct tls_config *tls_config; 218 int tls_session_fd = -1; 219 220 static void 221 process_ssl_options(char *cp) 222 { 223 const char *errstr; 224 char *str; 225 int depth; 226 uint32_t protocols; 227 228 while (*cp) { 229 switch (getsubopt(&cp, ssl_verify_opts, &str)) { 230 case SSL_CAFILE: 231 if (str == NULL) 232 errx(1, "missing CA file"); 233 if (tls_config_set_ca_file(tls_config, str) != 0) 234 errx(1, "tls ca file failed: %s", 235 tls_config_error(tls_config)); 236 break; 237 case SSL_CAPATH: 238 if (str == NULL) 239 errx(1, "missing CA directory path"); 240 if (tls_config_set_ca_path(tls_config, str) != 0) 241 errx(1, "tls ca path failed: %s", 242 tls_config_error(tls_config)); 243 break; 244 case SSL_CIPHERS: 245 if (str == NULL) 246 errx(1, "missing cipher list"); 247 if (tls_config_set_ciphers(tls_config, str) != 0) 248 errx(1, "tls ciphers failed: %s", 249 tls_config_error(tls_config)); 250 break; 251 case SSL_DONTVERIFY: 252 tls_config_insecure_noverifycert(tls_config); 253 tls_config_insecure_noverifyname(tls_config); 254 break; 255 case SSL_DOVERIFY: 256 tls_config_verify(tls_config); 257 break; 258 case SSL_VERIFYDEPTH: 259 if (str == NULL) 260 errx(1, "missing depth"); 261 depth = strtonum(str, 0, INT_MAX, &errstr); 262 if (errstr) 263 errx(1, "certificate validation depth is %s", 264 errstr); 265 tls_config_set_verify_depth(tls_config, depth); 266 break; 267 case SSL_MUSTSTAPLE: 268 tls_config_ocsp_require_stapling(tls_config); 269 break; 270 case SSL_NOVERIFYTIME: 271 tls_config_insecure_noverifytime(tls_config); 272 break; 273 case SSL_SESSION: 274 if (str == NULL) 275 errx(1, "missing session file"); 276 if ((tls_session_fd = open(str, O_RDWR|O_CREAT, 277 0600)) == -1) 278 err(1, "failed to open or create session file " 279 "'%s'", str); 280 if (tls_config_set_session_fd(tls_config, 281 tls_session_fd) == -1) 282 errx(1, "failed to set session: %s", 283 tls_config_error(tls_config)); 284 break; 285 case SSL_PROTOCOLS: 286 if (str == NULL) 287 errx(1, "missing protocol name"); 288 if (tls_config_parse_protocols(&protocols, str) != 0) 289 errx(1, "failed to parse TLS protocols"); 290 if (tls_config_set_protocols(tls_config, protocols) != 0) 291 errx(1, "failed to set TLS protocols: %s", 292 tls_config_error(tls_config)); 293 break; 294 default: 295 errx(1, "unknown -S suboption `%s'", 296 suboptarg ? suboptarg : ""); 297 /* NOTREACHED */ 298 } 299 } 300 } 301 #endif /* !NOSSL */ 302 303 int family = PF_UNSPEC; 304 int pipeout; 305 306 int 307 main(volatile int argc, char *argv[]) 308 { 309 int ch, rval; 310 #ifndef SMALL 311 int top; 312 #endif 313 struct passwd *pw = NULL; 314 char *cp, homedir[PATH_MAX]; 315 char *outfile = NULL; 316 const char *errstr; 317 int dumb_terminal = 0; 318 319 ftpport = "ftp"; 320 httpport = "http"; 321 #ifndef NOSSL 322 httpsport = "https"; 323 #endif /* !NOSSL */ 324 gateport = getenv("FTPSERVERPORT"); 325 if (gateport == NULL || *gateport == '\0') 326 gateport = "ftpgate"; 327 doglob = 1; 328 interactive = 1; 329 autologin = 1; 330 passivemode = 1; 331 activefallback = 1; 332 preserve = 1; 333 verbose = 0; 334 progress = 0; 335 gatemode = 0; 336 #ifndef NOSSL 337 cookiefile = NULL; 338 #endif /* NOSSL */ 339 #ifndef SMALL 340 editing = 0; 341 el = NULL; 342 hist = NULL; 343 resume = 0; 344 srcaddr = NULL; 345 marg_sl = sl_init(); 346 #endif /* !SMALL */ 347 mark = HASHBYTES; 348 epsv4 = 1; 349 epsv4bad = 0; 350 351 /* Set default operation mode based on FTPMODE environment variable */ 352 if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { 353 if (strcmp(cp, "passive") == 0) { 354 passivemode = 1; 355 activefallback = 0; 356 } else if (strcmp(cp, "active") == 0) { 357 passivemode = 0; 358 activefallback = 0; 359 } else if (strcmp(cp, "gate") == 0) { 360 gatemode = 1; 361 } else if (strcmp(cp, "auto") == 0) { 362 passivemode = 1; 363 activefallback = 1; 364 } else 365 warnx("unknown FTPMODE: %s. Using defaults", cp); 366 } 367 368 if (strcmp(__progname, "gate-ftp") == 0) 369 gatemode = 1; 370 gateserver = getenv("FTPSERVER"); 371 if (gateserver == NULL) 372 gateserver = ""; 373 if (gatemode) { 374 if (*gateserver == '\0') { 375 warnx( 376 "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); 377 gatemode = 0; 378 } 379 } 380 381 cp = getenv("TERM"); 382 dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || 383 !strcmp(cp, "emacs") || !strcmp(cp, "su")); 384 fromatty = isatty(fileno(stdin)); 385 if (fromatty) { 386 verbose = 1; /* verbose if from a tty */ 387 #ifndef SMALL 388 if (!dumb_terminal) 389 editing = 1; /* editing mode on if tty is usable */ 390 #endif /* !SMALL */ 391 } 392 393 ttyout = stdout; 394 if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) 395 progress = 1; /* progress bar on if tty is usable */ 396 397 #ifndef NOSSL 398 cookiefile = getenv("http_cookies"); 399 if (tls_init() != 0) 400 errx(1, "tls init failed"); 401 if (tls_config == NULL) { 402 tls_config = tls_config_new(); 403 if (tls_config == NULL) 404 errx(1, "tls config failed"); 405 if (tls_config_set_protocols(tls_config, 406 TLS_PROTOCOLS_ALL) != 0) 407 errx(1, "tls set protocols failed: %s", 408 tls_config_error(tls_config)); 409 if (tls_config_set_ciphers(tls_config, "legacy") != 0) 410 errx(1, "tls set ciphers failed: %s", 411 tls_config_error(tls_config)); 412 } 413 #endif /* !NOSSL */ 414 415 httpuseragent = NULL; 416 417 while ((ch = getopt(argc, argv, 418 "46AaCc:dD:EeN:gik:Mmno:pP:r:S:s:tU:vVw:")) != -1) { 419 switch (ch) { 420 case '4': 421 family = PF_INET; 422 break; 423 case '6': 424 family = PF_INET6; 425 break; 426 case 'A': 427 activefallback = 0; 428 passivemode = 0; 429 break; 430 431 case 'N': 432 setprogname(optarg); 433 break; 434 case 'a': 435 anonftp = 1; 436 break; 437 438 case 'C': 439 #ifndef SMALL 440 resume = 1; 441 #endif /* !SMALL */ 442 break; 443 444 case 'c': 445 #ifndef SMALL 446 cookiefile = optarg; 447 #endif /* !SMALL */ 448 break; 449 450 case 'D': 451 action = optarg; 452 break; 453 case 'd': 454 #ifndef SMALL 455 options |= SO_DEBUG; 456 debug++; 457 #endif /* !SMALL */ 458 break; 459 460 case 'E': 461 epsv4 = 0; 462 break; 463 464 case 'e': 465 #ifndef SMALL 466 editing = 0; 467 #endif /* !SMALL */ 468 break; 469 470 case 'g': 471 doglob = 0; 472 break; 473 474 case 'i': 475 interactive = 0; 476 break; 477 478 case 'k': 479 keep_alive_timeout = strtonum(optarg, 0, INT_MAX, 480 &errstr); 481 if (errstr != NULL) { 482 warnx("keep alive amount is %s: %s", errstr, 483 optarg); 484 usage(); 485 } 486 break; 487 case 'M': 488 progress = 0; 489 break; 490 case 'm': 491 progress = -1; 492 break; 493 494 case 'n': 495 autologin = 0; 496 break; 497 498 case 'o': 499 outfile = optarg; 500 if (*outfile == '\0') { 501 pipeout = 0; 502 outfile = NULL; 503 ttyout = stdout; 504 } else { 505 pipeout = strcmp(outfile, "-") == 0; 506 ttyout = pipeout ? stderr : stdout; 507 } 508 break; 509 510 case 'p': 511 passivemode = 1; 512 activefallback = 0; 513 break; 514 515 case 'P': 516 ftpport = optarg; 517 break; 518 519 case 'r': 520 retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); 521 if (errstr != NULL) { 522 warnx("retry amount is %s: %s", errstr, 523 optarg); 524 usage(); 525 } 526 break; 527 528 case 'S': 529 #ifndef NOSSL 530 process_ssl_options(optarg); 531 #endif /* !NOSSL */ 532 break; 533 534 case 's': 535 #ifndef SMALL 536 srcaddr = optarg; 537 #endif /* !SMALL */ 538 break; 539 540 case 't': 541 trace = 1; 542 break; 543 544 #ifndef SMALL 545 case 'U': 546 free (httpuseragent); 547 if (strcspn(optarg, "\r\n") != strlen(optarg)) 548 errx(1, "Invalid User-Agent: %s.", optarg); 549 if (asprintf(&httpuseragent, "User-Agent: %s", 550 optarg) == -1) 551 errx(1, "Can't allocate memory for HTTP(S) " 552 "User-Agent"); 553 break; 554 #endif /* !SMALL */ 555 556 case 'v': 557 verbose = 1; 558 break; 559 560 case 'V': 561 verbose = 0; 562 break; 563 564 case 'w': 565 connect_timeout = strtonum(optarg, 0, 200, &errstr); 566 if (errstr) 567 errx(1, "-w: %s", errstr); 568 break; 569 default: 570 usage(); 571 } 572 } 573 argc -= optind; 574 argv += optind; 575 576 #ifndef NOSSL 577 cookie_load(); 578 #endif /* !NOSSL */ 579 if (httpuseragent == NULL) 580 httpuseragent = HTTP_USER_AGENT; 581 582 cpend = 0; /* no pending replies */ 583 proxy = 0; /* proxy not active */ 584 crflag = 1; /* strip c.r. on ascii gets */ 585 sendport = -1; /* not using ports */ 586 /* 587 * Set up the home directory in case we're globbing. 588 */ 589 cp = getlogin(); 590 if (cp != NULL) { 591 pw = getpwnam(cp); 592 } 593 if (pw == NULL) 594 pw = getpwuid(getuid()); 595 if (pw != NULL) { 596 (void)strlcpy(homedir, pw->pw_dir, sizeof homedir); 597 home = homedir; 598 } 599 600 setttywidth(0); 601 (void)signal(SIGWINCH, setttywidth); 602 603 if (argc > 0) { 604 if (isurl(argv[0])) { 605 if (pipeout) { 606 #ifndef SMALL 607 if (pledge("stdio rpath dns tty inet proc exec fattr", 608 NULL) == -1) 609 err(1, "pledge"); 610 #else 611 if (pledge("stdio rpath dns tty inet fattr", 612 NULL) == -1) 613 err(1, "pledge"); 614 #endif 615 } else { 616 #ifndef SMALL 617 if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr", 618 NULL) == -1) 619 err(1, "pledge"); 620 #else 621 if (pledge("stdio rpath wpath cpath dns tty inet fattr", 622 NULL) == -1) 623 err(1, "pledge"); 624 #endif 625 } 626 627 rval = auto_fetch(argc, argv, outfile); 628 if (rval >= 0) /* -1 == connected and cd-ed */ 629 exit(rval); 630 } else { 631 #ifndef SMALL 632 char *xargv[5]; 633 634 if (setjmp(toplevel)) 635 exit(0); 636 (void)signal(SIGINT, (sig_t)intr); 637 (void)signal(SIGPIPE, (sig_t)lostpeer); 638 xargv[0] = __progname; 639 xargv[1] = argv[0]; 640 xargv[2] = argv[1]; 641 xargv[3] = argv[2]; 642 xargv[4] = NULL; 643 do { 644 setpeer(argc+1, xargv); 645 if (!retry_connect) 646 break; 647 if (!connected) { 648 macnum = 0; 649 fputs("Retrying...\n", ttyout); 650 sleep(retry_connect); 651 } 652 } while (!connected); 653 retry_connect = 0; /* connected, stop hiding msgs */ 654 #endif /* !SMALL */ 655 } 656 } 657 #ifndef SMALL 658 controlediting(); 659 top = setjmp(toplevel) == 0; 660 if (top) { 661 (void)signal(SIGINT, (sig_t)intr); 662 (void)signal(SIGPIPE, (sig_t)lostpeer); 663 } 664 for (;;) { 665 cmdscanner(top); 666 top = 1; 667 } 668 #else /* !SMALL */ 669 usage(); 670 #endif /* !SMALL */ 671 } 672 673 void 674 intr(void) 675 { 676 int save_errno = errno; 677 678 write(fileno(ttyout), "\n\r", 2); 679 alarmtimer(0); 680 681 errno = save_errno; 682 longjmp(toplevel, 1); 683 } 684 685 void 686 lostpeer(void) 687 { 688 int save_errno = errno; 689 690 alarmtimer(0); 691 if (connected) { 692 if (cout != NULL) { 693 (void)shutdown(fileno(cout), SHUT_RDWR); 694 (void)fclose(cout); 695 cout = NULL; 696 } 697 if (data >= 0) { 698 (void)shutdown(data, SHUT_RDWR); 699 (void)close(data); 700 data = -1; 701 } 702 connected = 0; 703 } 704 pswitch(1); 705 if (connected) { 706 if (cout != NULL) { 707 (void)shutdown(fileno(cout), SHUT_RDWR); 708 (void)fclose(cout); 709 cout = NULL; 710 } 711 connected = 0; 712 } 713 proxflag = 0; 714 pswitch(0); 715 errno = save_errno; 716 } 717 718 #ifndef SMALL 719 /* 720 * Generate a prompt 721 */ 722 char * 723 prompt(void) 724 { 725 return ("ftp> "); 726 } 727 728 /* 729 * Command parser. 730 */ 731 void 732 cmdscanner(int top) 733 { 734 struct cmd *c; 735 int num; 736 HistEvent hev; 737 738 if (!top && !editing) 739 (void)putc('\n', ttyout); 740 for (;;) { 741 if (!editing) { 742 if (fromatty) { 743 fputs(prompt(), ttyout); 744 (void)fflush(ttyout); 745 } 746 if (fgets(line, sizeof(line), stdin) == NULL) 747 quit(0, 0); 748 num = strlen(line); 749 if (num == 0) 750 break; 751 if (line[--num] == '\n') { 752 if (num == 0) 753 break; 754 line[num] = '\0'; 755 } else if (num == sizeof(line) - 2) { 756 fputs("sorry, input line too long.\n", ttyout); 757 while ((num = getchar()) != '\n' && num != EOF) 758 /* void */; 759 break; 760 } /* else it was a line without a newline */ 761 } else { 762 const char *buf; 763 cursor_pos = NULL; 764 765 if ((buf = el_gets(el, &num)) == NULL || num == 0) { 766 putc('\n', ttyout); 767 fflush(ttyout); 768 quit(0, 0); 769 } 770 if (buf[--num] == '\n') { 771 if (num == 0) 772 break; 773 } 774 if (num >= sizeof(line)) { 775 fputs("sorry, input line too long.\n", ttyout); 776 break; 777 } 778 memcpy(line, buf, (size_t)num); 779 line[num] = '\0'; 780 history(hist, &hev, H_ENTER, buf); 781 } 782 783 makeargv(); 784 if (margc == 0) 785 continue; 786 c = getcmd(margv[0]); 787 if (c == (struct cmd *)-1) { 788 fputs("?Ambiguous command.\n", ttyout); 789 continue; 790 } 791 if (c == 0) { 792 /* 793 * Give editline(3) a shot at unknown commands. 794 * XXX - bogus commands with a colon in 795 * them will not elicit an error. 796 */ 797 if (editing && 798 el_parse(el, margc, (const char **)margv) != 0) 799 fputs("?Invalid command.\n", ttyout); 800 continue; 801 } 802 if (c->c_conn && !connected) { 803 fputs("Not connected.\n", ttyout); 804 continue; 805 } 806 confirmrest = 0; 807 (*c->c_handler)(margc, margv); 808 if (bell && c->c_bell) 809 (void)putc('\007', ttyout); 810 if (c->c_handler != help) 811 break; 812 } 813 (void)signal(SIGINT, (sig_t)intr); 814 (void)signal(SIGPIPE, (sig_t)lostpeer); 815 } 816 817 struct cmd * 818 getcmd(const char *name) 819 { 820 const char *p, *q; 821 struct cmd *c, *found; 822 int nmatches, longest; 823 824 if (name == NULL) 825 return (0); 826 827 longest = 0; 828 nmatches = 0; 829 found = 0; 830 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 831 for (q = name; *q == *p++; q++) 832 if (*q == 0) /* exact match? */ 833 return (c); 834 if (!*q) { /* the name was a prefix */ 835 if (q - name > longest) { 836 longest = q - name; 837 nmatches = 1; 838 found = c; 839 } else if (q - name == longest) 840 nmatches++; 841 } 842 } 843 if (nmatches > 1) 844 return ((struct cmd *)-1); 845 return (found); 846 } 847 848 /* 849 * Slice a string up into argc/argv. 850 */ 851 852 int slrflag; 853 854 void 855 makeargv(void) 856 { 857 char *argp; 858 859 stringbase = line; /* scan from first of buffer */ 860 argbase = argbuf; /* store from first of buffer */ 861 slrflag = 0; 862 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 863 for (margc = 0; ; margc++) { 864 argp = slurpstring(); 865 sl_add(marg_sl, argp); 866 if (argp == NULL) 867 break; 868 } 869 if (cursor_pos == line) { 870 cursor_argc = 0; 871 cursor_argo = 0; 872 } else if (cursor_pos != NULL) { 873 cursor_argc = margc; 874 cursor_argo = strlen(margv[margc-1]); 875 } 876 } 877 878 #define INC_CHKCURSOR(x) { (x)++ ; \ 879 if (x == cursor_pos) { \ 880 cursor_argc = margc; \ 881 cursor_argo = ap-argbase; \ 882 cursor_pos = NULL; \ 883 } } 884 885 /* 886 * Parse string into argbuf; 887 * implemented with FSM to 888 * handle quoting and strings 889 */ 890 char * 891 slurpstring(void) 892 { 893 int got_one = 0; 894 char *sb = stringbase; 895 char *ap = argbase; 896 char *tmp = argbase; /* will return this if token found */ 897 898 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 899 switch (slrflag) { /* and $ as token for macro invoke */ 900 case 0: 901 slrflag++; 902 INC_CHKCURSOR(stringbase); 903 return ((*sb == '!') ? "!" : "$"); 904 /* NOTREACHED */ 905 case 1: 906 slrflag++; 907 altarg = stringbase; 908 break; 909 default: 910 break; 911 } 912 } 913 914 S0: 915 switch (*sb) { 916 917 case '\0': 918 goto OUT; 919 920 case ' ': 921 case '\t': 922 INC_CHKCURSOR(sb); 923 goto S0; 924 925 default: 926 switch (slrflag) { 927 case 0: 928 slrflag++; 929 break; 930 case 1: 931 slrflag++; 932 altarg = sb; 933 break; 934 default: 935 break; 936 } 937 goto S1; 938 } 939 940 S1: 941 switch (*sb) { 942 943 case ' ': 944 case '\t': 945 case '\0': 946 goto OUT; /* end of token */ 947 948 case '\\': 949 INC_CHKCURSOR(sb); 950 goto S2; /* slurp next character */ 951 952 case '"': 953 INC_CHKCURSOR(sb); 954 goto S3; /* slurp quoted string */ 955 956 default: 957 *ap = *sb; /* add character to token */ 958 ap++; 959 INC_CHKCURSOR(sb); 960 got_one = 1; 961 goto S1; 962 } 963 964 S2: 965 switch (*sb) { 966 967 case '\0': 968 goto OUT; 969 970 default: 971 *ap = *sb; 972 ap++; 973 INC_CHKCURSOR(sb); 974 got_one = 1; 975 goto S1; 976 } 977 978 S3: 979 switch (*sb) { 980 981 case '\0': 982 goto OUT; 983 984 case '"': 985 INC_CHKCURSOR(sb); 986 goto S1; 987 988 default: 989 *ap = *sb; 990 ap++; 991 INC_CHKCURSOR(sb); 992 got_one = 1; 993 goto S3; 994 } 995 996 OUT: 997 if (got_one) 998 *ap++ = '\0'; 999 argbase = ap; /* update storage pointer */ 1000 stringbase = sb; /* update scan pointer */ 1001 if (got_one) { 1002 return (tmp); 1003 } 1004 switch (slrflag) { 1005 case 0: 1006 slrflag++; 1007 break; 1008 case 1: 1009 slrflag++; 1010 altarg = (char *) 0; 1011 break; 1012 default: 1013 break; 1014 } 1015 return (NULL); 1016 } 1017 1018 /* 1019 * Help command. 1020 * Call each command handler with argc == 0 and argv[0] == name. 1021 */ 1022 void 1023 help(int argc, char *argv[]) 1024 { 1025 struct cmd *c; 1026 1027 if (argc == 1) { 1028 StringList *buf; 1029 1030 buf = sl_init(); 1031 fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", 1032 proxy ? "Proxy c" : "C"); 1033 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) 1034 if (c->c_name && (!proxy || c->c_proxy)) 1035 sl_add(buf, c->c_name); 1036 list_vertical(buf); 1037 sl_free(buf, 0); 1038 return; 1039 } 1040 1041 #define HELPINDENT ((int) sizeof("disconnect")) 1042 1043 while (--argc > 0) { 1044 char *arg; 1045 1046 arg = *++argv; 1047 c = getcmd(arg); 1048 if (c == (struct cmd *)-1) 1049 fprintf(ttyout, "?Ambiguous help command %s\n", arg); 1050 else if (c == NULL) 1051 fprintf(ttyout, "?Invalid help command %s\n", arg); 1052 else 1053 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, 1054 c->c_name, c->c_help); 1055 } 1056 } 1057 #endif /* !SMALL */ 1058 1059 __dead void 1060 usage(void) 1061 { 1062 fprintf(stderr, "usage: " 1063 #ifndef SMALL 1064 "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] " 1065 "[-r seconds]\n" 1066 " [-s sourceaddr] [host [port]]\n" 1067 " ftp [-C] [-N name] [-o output] [-s sourceaddr]\n" 1068 " ftp://[user:password@]host[:port]/file[/] ...\n" 1069 " ftp [-C] [-c cookie] [-N name] [-o output] [-S ssl_options] " 1070 "[-s sourceaddr]\n" 1071 " [-U useragent] [-w seconds] " 1072 "http[s]://[user:password@]host[:port]/file ...\n" 1073 " ftp [-C] [-N name] [-o output] [-s sourceaddr] file:file ...\n" 1074 " ftp [-C] [-N name] [-o output] [-s sourceaddr] host:/file[/] ...\n" 1075 #else /* !SMALL */ 1076 "ftp [-N name] [-o output] " 1077 "ftp://[user:password@]host[:port]/file[/] ...\n" 1078 #ifndef NOSSL 1079 " ftp [-N name] [-o output] [-S ssl_options] [-w seconds] " 1080 "http[s]://[user:password@]host[:port]/file ...\n" 1081 #else 1082 " ftp [-N name] [-o output] [-w seconds] http://host[:port]/file ...\n" 1083 #endif /* NOSSL */ 1084 " ftp [-N name] [-o output] file:file ...\n" 1085 " ftp [-N name] [-o output] host:/file[/] ...\n" 1086 #endif /* !SMALL */ 1087 ); 1088 exit(1); 1089 } 1090