Annotation of src/usr.bin/ftp/main.c, Revision 1.129
1.129 ! florian 1: /* $OpenBSD: main.c,v 1.120 2018/02/10 06:25:16 jsing Exp $ */
! 2: /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
1.122 jasper 3:
1.1 deraadt 4: /*
1.129 ! florian 5: * Copyright (C) 1997 and 1998 WIDE Project.
! 6: * All rights reserved.
1.118 krw 7: *
1.129 ! florian 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.
1.118 krw 19: *
1.129 ! florian 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.
1.44 itojun 31: */
32:
1.129 ! florian 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: */
1.1 deraadt 65: #include <sys/types.h>
66: #include <sys/socket.h>
67:
1.129 ! florian 68: #include <ctype.h>
1.1 deraadt 69: #include <err.h>
1.120 jsing 70: #include <fcntl.h>
1.129 ! florian 71: #include <netdb.h>
! 72: #include <pwd.h>
1.1 deraadt 73: #include <stdio.h>
1.129 ! florian 74: #include <errno.h>
1.18 millert 75: #include <stdlib.h>
1.1 deraadt 76: #include <string.h>
77: #include <unistd.h>
78:
1.129 ! florian 79: #include <tls.h>
1.91 jsing 80:
1.129 ! florian 81: #include "cmds.h"
! 82: #include "ftp_var.h"
! 83:
! 84: int connect_timeout;
! 85:
! 86: #ifndef NOSSL
! 87: char * const ssl_verify_opts[] = {
! 88: #define SSL_CAFILE 0
! 89: "cafile",
! 90: #define SSL_CAPATH 1
! 91: "capath",
! 92: #define SSL_CIPHERS 2
! 93: "ciphers",
! 94: #define SSL_DONTVERIFY 3
! 95: "dont",
! 96: #define SSL_DOVERIFY 4
! 97: "do",
! 98: #define SSL_VERIFYDEPTH 5
! 99: "depth",
! 100: #define SSL_MUSTSTAPLE 6
! 101: "muststaple",
! 102: #define SSL_NOVERIFYTIME 7
! 103: "noverifytime",
! 104: #define SSL_SESSION 8
! 105: "session",
! 106: NULL
! 107: };
! 108:
! 109: struct tls_config *tls_config;
! 110: int tls_session_fd = -1;
! 111:
! 112: static void
! 113: process_ssl_options(char *cp)
! 114: {
! 115: const char *errstr;
! 116: long long depth;
! 117: char *str;
! 118:
! 119: while (*cp) {
! 120: switch (getsubopt(&cp, ssl_verify_opts, &str)) {
! 121: case SSL_CAFILE:
! 122: if (str == NULL)
! 123: errx(1, "missing CA file");
! 124: if (tls_config_set_ca_file(tls_config, str) != 0)
! 125: errx(1, "tls ca file failed: %s",
! 126: tls_config_error(tls_config));
! 127: break;
! 128: case SSL_CAPATH:
! 129: if (str == NULL)
! 130: errx(1, "missing CA directory path");
! 131: if (tls_config_set_ca_path(tls_config, str) != 0)
! 132: errx(1, "tls ca path failed: %s",
! 133: tls_config_error(tls_config));
! 134: break;
! 135: case SSL_CIPHERS:
! 136: if (str == NULL)
! 137: errx(1, "missing cipher list");
! 138: if (tls_config_set_ciphers(tls_config, str) != 0)
! 139: errx(1, "tls ciphers failed: %s",
! 140: tls_config_error(tls_config));
! 141: break;
! 142: case SSL_DONTVERIFY:
! 143: tls_config_insecure_noverifycert(tls_config);
! 144: tls_config_insecure_noverifyname(tls_config);
! 145: break;
! 146: case SSL_DOVERIFY:
! 147: tls_config_verify(tls_config);
! 148: break;
! 149: case SSL_VERIFYDEPTH:
! 150: if (str == NULL)
! 151: errx(1, "missing depth");
! 152: depth = strtonum(str, 0, INT_MAX, &errstr);
! 153: if (errstr)
! 154: errx(1, "certificate validation depth is %s",
! 155: errstr);
! 156: tls_config_set_verify_depth(tls_config, (int)depth);
! 157: break;
! 158: case SSL_MUSTSTAPLE:
! 159: tls_config_ocsp_require_stapling(tls_config);
! 160: break;
! 161: case SSL_NOVERIFYTIME:
! 162: tls_config_insecure_noverifytime(tls_config);
! 163: break;
! 164: case SSL_SESSION:
! 165: if (str == NULL)
! 166: errx(1, "missing session file");
! 167: if ((tls_session_fd = open(str, O_RDWR|O_CREAT,
! 168: 0600)) == -1)
! 169: err(1, "failed to open or create session file "
! 170: "'%s'", str);
! 171: if (tls_config_set_session_fd(tls_config,
! 172: tls_session_fd) == -1)
! 173: errx(1, "failed to set session: %s",
! 174: tls_config_error(tls_config));
! 175: break;
! 176: default:
! 177: errx(1, "unknown -S suboption `%s'",
! 178: suboptarg ? suboptarg : "");
! 179: /* NOTREACHED */
! 180: }
! 181: }
! 182: }
! 183: #endif /* !NOSSL */
! 184:
! 185: int family = PF_UNSPEC;
! 186: int pipeout;
1.49 deraadt 187:
1.1 deraadt 188: int
1.129 ! florian 189: main(volatile int argc, char *argv[])
1.1 deraadt 190: {
1.129 ! florian 191: int ch, rval;
! 192: #ifndef SMALL
! 193: int top;
! 194: #endif
! 195: struct passwd *pw = NULL;
! 196: char *cp, homedir[PATH_MAX];
! 197: char *outfile = NULL;
! 198: const char *errstr;
! 199: int dumb_terminal = 0;
! 200:
! 201: ftpport = "ftp";
! 202: httpport = "http";
! 203: #ifndef NOSSL
! 204: httpsport = "https";
! 205: #endif /* !NOSSL */
! 206: gateport = getenv("FTPSERVERPORT");
! 207: if (gateport == NULL || *gateport == '\0')
! 208: gateport = "ftpgate";
! 209: doglob = 1;
! 210: interactive = 1;
! 211: autologin = 1;
! 212: passivemode = 1;
! 213: activefallback = 1;
! 214: preserve = 1;
! 215: verbose = 0;
! 216: progress = 0;
! 217: gatemode = 0;
! 218: #ifndef NOSSL
! 219: cookiefile = NULL;
! 220: #endif /* NOSSL */
! 221: #ifndef SMALL
! 222: editing = 0;
! 223: el = NULL;
! 224: hist = NULL;
! 225: resume = 0;
! 226: srcaddr = NULL;
! 227: marg_sl = sl_init();
! 228: #endif /* !SMALL */
! 229: mark = HASHBYTES;
! 230: epsv4 = 1;
! 231: epsv4bad = 0;
! 232:
! 233: /* Set default operation mode based on FTPMODE environment variable */
! 234: if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
! 235: if (strcmp(cp, "passive") == 0) {
! 236: passivemode = 1;
! 237: activefallback = 0;
! 238: } else if (strcmp(cp, "active") == 0) {
! 239: passivemode = 0;
! 240: activefallback = 0;
! 241: } else if (strcmp(cp, "gate") == 0) {
! 242: gatemode = 1;
! 243: } else if (strcmp(cp, "auto") == 0) {
! 244: passivemode = 1;
! 245: activefallback = 1;
! 246: } else
! 247: warnx("unknown FTPMODE: %s. Using defaults", cp);
! 248: }
! 249:
! 250: if (strcmp(__progname, "gate-ftp") == 0)
! 251: gatemode = 1;
! 252: gateserver = getenv("FTPSERVER");
! 253: if (gateserver == NULL)
! 254: gateserver = "";
! 255: if (gatemode) {
! 256: if (*gateserver == '\0') {
! 257: warnx(
! 258: "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
! 259: gatemode = 0;
! 260: }
! 261: }
! 262:
! 263: cp = getenv("TERM");
! 264: dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
! 265: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
! 266: fromatty = isatty(fileno(stdin));
! 267: if (fromatty) {
! 268: verbose = 1; /* verbose if from a tty */
! 269: #ifndef SMALL
! 270: if (!dumb_terminal)
! 271: editing = 1; /* editing mode on if tty is usable */
! 272: #endif /* !SMALL */
! 273: }
! 274:
! 275: ttyout = stdout;
! 276: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
! 277: progress = 1; /* progress bar on if tty is usable */
! 278:
! 279: #ifndef NOSSL
! 280: cookiefile = getenv("http_cookies");
! 281: if (tls_init() != 0)
! 282: errx(1, "tls init failed");
! 283: if (tls_config == NULL) {
! 284: tls_config = tls_config_new();
! 285: if (tls_config == NULL)
! 286: errx(1, "tls config failed");
! 287: if (tls_config_set_protocols(tls_config,
! 288: TLS_PROTOCOLS_ALL) != 0)
! 289: errx(1, "tls set protocols failed: %s",
! 290: tls_config_error(tls_config));
! 291: if (tls_config_set_ciphers(tls_config, "legacy") != 0)
! 292: errx(1, "tls set ciphers failed: %s",
! 293: tls_config_error(tls_config));
! 294: }
! 295: #endif /* !NOSSL */
! 296:
! 297: httpuseragent = NULL;
! 298:
1.86 jca 299: while ((ch = getopt(argc, argv,
1.129 ! florian 300: "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vVw:")) != -1) {
1.1 deraadt 301: switch (ch) {
1.49 deraadt 302: case '4':
1.129 ! florian 303: family = PF_INET;
1.49 deraadt 304: break;
305: case '6':
1.129 ! florian 306: family = PF_INET6;
1.49 deraadt 307: break;
1.37 millert 308: case 'A':
1.129 ! florian 309: activefallback = 0;
! 310: passivemode = 0;
! 311: break;
! 312:
! 313: case 'a':
! 314: anonftp = 1;
1.37 millert 315: break;
1.129 ! florian 316:
1.67 martynas 317: case 'C':
1.129 ! florian 318: #ifndef SMALL
1.67 martynas 319: resume = 1;
1.129 ! florian 320: #endif /* !SMALL */
1.63 pyr 321: break;
1.129 ! florian 322:
! 323: case 'c':
! 324: #ifndef SMALL
! 325: cookiefile = optarg;
! 326: #endif /* !SMALL */
! 327: break;
! 328:
1.87 deraadt 329: case 'D':
1.129 ! florian 330: action = optarg;
! 331: break;
! 332: case 'd':
! 333: #ifndef SMALL
! 334: options |= SO_DEBUG;
! 335: debug++;
! 336: #endif /* !SMALL */
! 337: break;
! 338:
! 339: case 'E':
! 340: epsv4 = 0;
! 341: break;
! 342:
! 343: case 'e':
! 344: #ifndef SMALL
! 345: editing = 0;
! 346: #endif /* !SMALL */
! 347: break;
! 348:
! 349: case 'g':
! 350: doglob = 0;
! 351: break;
! 352:
! 353: case 'i':
! 354: interactive = 0;
1.87 deraadt 355: break;
1.129 ! florian 356:
! 357: case 'k':
! 358: keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
! 359: &errstr);
! 360: if (errstr != NULL) {
! 361: warnx("keep alive amount is %s: %s", errstr,
! 362: optarg);
! 363: usage();
! 364: }
1.1 deraadt 365: break;
1.121 kmos 366: case 'M':
1.129 ! florian 367: progress = 0;
1.59 fgsch 368: break;
1.121 kmos 369: case 'm':
1.129 ! florian 370: progress = -1;
1.21 kstailey 371: break;
1.129 ! florian 372:
! 373: case 'n':
! 374: autologin = 0;
! 375: break;
! 376:
! 377: case 'o':
! 378: outfile = optarg;
! 379: if (*outfile == '\0') {
! 380: pipeout = 0;
! 381: outfile = NULL;
! 382: ttyout = stdout;
! 383: } else {
! 384: pipeout = strcmp(outfile, "-") == 0;
! 385: ttyout = pipeout ? stderr : stdout;
! 386: }
! 387: break;
! 388:
! 389: case 'p':
! 390: passivemode = 1;
! 391: activefallback = 0;
! 392: break;
! 393:
! 394: case 'P':
! 395: ftpport = optarg;
! 396: break;
! 397:
! 398: case 'r':
! 399: retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
! 400: if (errstr != NULL) {
! 401: warnx("retry amount is %s: %s", errstr,
! 402: optarg);
! 403: usage();
! 404: }
! 405: break;
! 406:
1.121 kmos 407: case 'S':
1.129 ! florian 408: #ifndef NOSSL
! 409: process_ssl_options(optarg);
! 410: #endif /* !NOSSL */
! 411: break;
! 412:
! 413: case 's':
! 414: #ifndef SMALL
! 415: srcaddr = optarg;
! 416: #endif /* !SMALL */
! 417: break;
! 418:
! 419: case 't':
! 420: trace = 1;
1.1 deraadt 421: break;
1.129 ! florian 422:
! 423: #ifndef SMALL
1.121 kmos 424: case 'U':
1.129 ! florian 425: free (httpuseragent);
! 426: if (strcspn(optarg, "\r\n") != strlen(optarg))
! 427: errx(1, "Invalid User-Agent: %s.", optarg);
! 428: if (asprintf(&httpuseragent, "User-Agent: %s",
! 429: optarg) == -1)
! 430: errx(1, "Can't allocate memory for HTTP(S) "
! 431: "User-Agent");
! 432: break;
! 433: #endif /* !SMALL */
! 434:
! 435: case 'v':
! 436: verbose = 1;
1.41 millert 437: break;
1.129 ! florian 438:
1.121 kmos 439: case 'V':
440: verbose = 0;
1.65 espie 441: break;
1.129 ! florian 442:
1.121 kmos 443: case 'w':
1.129 ! florian 444: connect_timeout = strtonum(optarg, 0, 200, &errstr);
! 445: if (errstr)
! 446: errx(1, "-w: %s", errstr);
1.116 deraadt 447: break;
1.1 deraadt 448: default:
1.17 millert 449: usage();
1.1 deraadt 450: }
451: }
452: argc -= optind;
453: argv += optind;
1.63 pyr 454:
1.129 ! florian 455: #ifndef NOSSL
! 456: cookie_load();
! 457: #endif /* !NOSSL */
! 458: if (httpuseragent == NULL)
! 459: httpuseragent = HTTP_USER_AGENT;
! 460:
! 461: cpend = 0; /* no pending replies */
! 462: proxy = 0; /* proxy not active */
! 463: crflag = 1; /* strip c.r. on ascii gets */
! 464: sendport = -1; /* not using ports */
! 465: /*
! 466: * Set up the home directory in case we're globbing.
! 467: */
! 468: cp = getlogin();
! 469: if (cp != NULL) {
! 470: pw = getpwnam(cp);
! 471: }
! 472: if (pw == NULL)
! 473: pw = getpwuid(getuid());
! 474: if (pw != NULL) {
! 475: (void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
! 476: home = homedir;
! 477: }
! 478:
! 479: setttywidth(0);
! 480: (void)signal(SIGWINCH, setttywidth);
1.9 michaels 481:
1.129 ! florian 482: if (argc > 0) {
! 483: if (isurl(argv[0])) {
! 484: if (pipeout) {
! 485: #ifndef SMALL
! 486: if (pledge("stdio rpath dns tty inet proc exec fattr",
! 487: NULL) == -1)
! 488: err(1, "pledge");
! 489: #else
! 490: if (pledge("stdio rpath dns tty inet fattr",
! 491: NULL) == -1)
! 492: err(1, "pledge");
! 493: #endif
! 494: } else {
1.103 doug 495: #ifndef SMALL
1.129 ! florian 496: if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr",
! 497: NULL) == -1)
! 498: err(1, "pledge");
! 499: #else
! 500: if (pledge("stdio rpath wpath cpath dns tty inet fattr",
! 501: NULL) == -1)
! 502: err(1, "pledge");
! 503: #endif
! 504: }
1.103 doug 505:
1.129 ! florian 506: rval = auto_fetch(argc, argv, outfile);
! 507: if (rval >= 0) /* -1 == connected and cd-ed */
! 508: exit(rval);
! 509: } else {
! 510: #ifndef SMALL
! 511: char *xargv[5];
1.3 deraadt 512:
1.129 ! florian 513: if (setjmp(toplevel))
! 514: exit(0);
! 515: (void)signal(SIGINT, (sig_t)intr);
! 516: (void)signal(SIGPIPE, (sig_t)lostpeer);
! 517: xargv[0] = __progname;
! 518: xargv[1] = argv[0];
! 519: xargv[2] = argv[1];
! 520: xargv[3] = argv[2];
! 521: xargv[4] = NULL;
! 522: do {
! 523: setpeer(argc+1, xargv);
! 524: if (!retry_connect)
! 525: break;
! 526: if (!connected) {
! 527: macnum = 0;
! 528: fputs("Retrying...\n", ttyout);
! 529: sleep(retry_connect);
! 530: }
! 531: } while (!connected);
! 532: retry_connect = 0; /* connected, stop hiding msgs */
! 533: #endif /* !SMALL */
1.3 deraadt 534: }
1.1 deraadt 535: }
1.129 ! florian 536: #ifndef SMALL
! 537: controlediting();
! 538: top = setjmp(toplevel) == 0;
! 539: if (top) {
! 540: (void)signal(SIGINT, (sig_t)intr);
! 541: (void)signal(SIGPIPE, (sig_t)lostpeer);
! 542: }
! 543: for (;;) {
! 544: cmdscanner(top);
! 545: top = 1;
! 546: }
! 547: #else /* !SMALL */
! 548: usage();
! 549: #endif /* !SMALL */
! 550: }
! 551:
! 552: void
! 553: intr(void)
! 554: {
! 555: int save_errno = errno;
! 556:
! 557: write(fileno(ttyout), "\n\r", 2);
! 558: alarmtimer(0);
1.121 kmos 559:
1.129 ! florian 560: errno = save_errno;
! 561: longjmp(toplevel, 1);
1.1 deraadt 562: }
563:
1.129 ! florian 564: void
! 565: lostpeer(void)
1.1 deraadt 566: {
1.129 ! florian 567: int save_errno = errno;
1.121 kmos 568:
1.129 ! florian 569: alarmtimer(0);
! 570: if (connected) {
! 571: if (cout != NULL) {
! 572: (void)shutdown(fileno(cout), SHUT_RDWR);
! 573: (void)fclose(cout);
! 574: cout = NULL;
! 575: }
! 576: if (data >= 0) {
! 577: (void)shutdown(data, SHUT_RDWR);
! 578: (void)close(data);
! 579: data = -1;
! 580: }
! 581: connected = 0;
! 582: }
! 583: pswitch(1);
! 584: if (connected) {
! 585: if (cout != NULL) {
! 586: (void)shutdown(fileno(cout), SHUT_RDWR);
! 587: (void)fclose(cout);
! 588: cout = NULL;
! 589: }
! 590: connected = 0;
1.121 kmos 591: }
1.129 ! florian 592: proxflag = 0;
! 593: pswitch(0);
! 594: errno = save_errno;
! 595: }
1.90 deraadt 596:
1.129 ! florian 597: #ifndef SMALL
! 598: /*
! 599: * Generate a prompt
! 600: */
! 601: char *
! 602: prompt(void)
! 603: {
! 604: return ("ftp> ");
1.1 deraadt 605: }
606:
1.129 ! florian 607: /*
! 608: * Command parser.
! 609: */
! 610: void
! 611: cmdscanner(int top)
1.1 deraadt 612: {
1.129 ! florian 613: struct cmd *c;
! 614: int num;
! 615: HistEvent hev;
1.1 deraadt 616:
1.129 ! florian 617: if (!top && !editing)
! 618: (void)putc('\n', ttyout);
! 619: for (;;) {
! 620: if (!editing) {
! 621: if (fromatty) {
! 622: fputs(prompt(), ttyout);
! 623: (void)fflush(ttyout);
! 624: }
! 625: if (fgets(line, sizeof(line), stdin) == NULL)
! 626: quit(0, 0);
! 627: num = strlen(line);
! 628: if (num == 0)
! 629: break;
! 630: if (line[--num] == '\n') {
! 631: if (num == 0)
! 632: break;
! 633: line[num] = '\0';
! 634: } else if (num == sizeof(line) - 2) {
! 635: fputs("sorry, input line too long.\n", ttyout);
! 636: while ((num = getchar()) != '\n' && num != EOF)
! 637: /* void */;
! 638: break;
! 639: } /* else it was a line without a newline */
! 640: } else {
! 641: const char *buf;
! 642: cursor_pos = NULL;
! 643:
! 644: if ((buf = el_gets(el, &num)) == NULL || num == 0) {
! 645: putc('\n', ttyout);
! 646: fflush(ttyout);
! 647: quit(0, 0);
! 648: }
! 649: if (buf[--num] == '\n') {
! 650: if (num == 0)
! 651: break;
! 652: }
! 653: if (num >= sizeof(line)) {
! 654: fputs("sorry, input line too long.\n", ttyout);
! 655: break;
! 656: }
! 657: memcpy(line, buf, (size_t)num);
! 658: line[num] = '\0';
! 659: history(hist, &hev, H_ENTER, buf);
! 660: }
! 661:
! 662: makeargv();
! 663: if (margc == 0)
! 664: continue;
! 665: c = getcmd(margv[0]);
! 666: if (c == (struct cmd *)-1) {
! 667: fputs("?Ambiguous command.\n", ttyout);
! 668: continue;
! 669: }
! 670: if (c == 0) {
! 671: /*
! 672: * Give editline(3) a shot at unknown commands.
! 673: * XXX - bogus commands with a colon in
! 674: * them will not elicit an error.
! 675: */
! 676: if (editing &&
! 677: el_parse(el, margc, (const char **)margv) != 0)
! 678: fputs("?Invalid command.\n", ttyout);
! 679: continue;
! 680: }
! 681: if (c->c_conn && !connected) {
! 682: fputs("Not connected.\n", ttyout);
! 683: continue;
! 684: }
! 685: confirmrest = 0;
! 686: (*c->c_handler)(margc, margv);
! 687: if (bell && c->c_bell)
! 688: (void)putc('\007', ttyout);
! 689: if (c->c_handler != help)
! 690: break;
! 691: }
! 692: (void)signal(SIGINT, (sig_t)intr);
! 693: (void)signal(SIGPIPE, (sig_t)lostpeer);
! 694: }
1.1 deraadt 695:
1.129 ! florian 696: struct cmd *
! 697: getcmd(const char *name)
! 698: {
! 699: const char *p, *q;
! 700: struct cmd *c, *found;
! 701: int nmatches, longest;
! 702:
! 703: if (name == NULL)
! 704: return (0);
! 705:
! 706: longest = 0;
! 707: nmatches = 0;
! 708: found = 0;
! 709: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
! 710: for (q = name; *q == *p++; q++)
! 711: if (*q == 0) /* exact match? */
! 712: return (c);
! 713: if (!*q) { /* the name was a prefix */
! 714: if (q - name > longest) {
! 715: longest = q - name;
! 716: nmatches = 1;
! 717: found = c;
! 718: } else if (q - name == longest)
! 719: nmatches++;
! 720: }
! 721: }
! 722: if (nmatches > 1)
! 723: return ((struct cmd *)-1);
! 724: return (found);
1.1 deraadt 725: }
726:
1.129 ! florian 727: /*
! 728: * Slice a string up into argc/argv.
! 729: */
! 730:
! 731: int slrflag;
! 732:
! 733: void
! 734: makeargv(void)
1.1 deraadt 735: {
1.129 ! florian 736: char *argp;
1.1 deraadt 737:
1.129 ! florian 738: stringbase = line; /* scan from first of buffer */
! 739: argbase = argbuf; /* store from first of buffer */
! 740: slrflag = 0;
! 741: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
! 742: for (margc = 0; ; margc++) {
! 743: argp = slurpstring();
! 744: sl_add(marg_sl, argp);
! 745: if (argp == NULL)
1.121 kmos 746: break;
1.129 ! florian 747: }
! 748: if (cursor_pos == line) {
! 749: cursor_argc = 0;
! 750: cursor_argo = 0;
! 751: } else if (cursor_pos != NULL) {
! 752: cursor_argc = margc;
! 753: cursor_argo = strlen(margv[margc-1]);
! 754: }
! 755: }
1.121 kmos 756:
1.129 ! florian 757: #define INC_CHKCURSOR(x) { (x)++ ; \
! 758: if (x == cursor_pos) { \
! 759: cursor_argc = margc; \
! 760: cursor_argo = ap-argbase; \
! 761: cursor_pos = NULL; \
! 762: } }
1.121 kmos 763:
1.129 ! florian 764: /*
! 765: * Parse string into argbuf;
! 766: * implemented with FSM to
! 767: * handle quoting and strings
! 768: */
! 769: char *
! 770: slurpstring(void)
! 771: {
! 772: int got_one = 0;
! 773: char *sb = stringbase;
! 774: char *ap = argbase;
! 775: char *tmp = argbase; /* will return this if token found */
! 776:
! 777: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
! 778: switch (slrflag) { /* and $ as token for macro invoke */
! 779: case 0:
! 780: slrflag++;
! 781: INC_CHKCURSOR(stringbase);
! 782: return ((*sb == '!') ? "!" : "$");
! 783: /* NOTREACHED */
! 784: case 1:
! 785: slrflag++;
! 786: altarg = stringbase;
! 787: break;
! 788: default:
! 789: break;
1.17 millert 790: }
1.1 deraadt 791: }
1.121 kmos 792:
1.129 ! florian 793: S0:
! 794: switch (*sb) {
1.121 kmos 795:
1.129 ! florian 796: case '\0':
! 797: goto OUT;
1.121 kmos 798:
1.129 ! florian 799: case ' ':
! 800: case '\t':
! 801: INC_CHKCURSOR(sb);
! 802: goto S0;
1.1 deraadt 803:
1.129 ! florian 804: default:
! 805: switch (slrflag) {
! 806: case 0:
! 807: slrflag++;
! 808: break;
! 809: case 1:
! 810: slrflag++;
! 811: altarg = sb;
! 812: break;
! 813: default:
! 814: break;
! 815: }
! 816: goto S1;
! 817: }
1.1 deraadt 818:
1.129 ! florian 819: S1:
! 820: switch (*sb) {
1.1 deraadt 821:
1.129 ! florian 822: case ' ':
! 823: case '\t':
! 824: case '\0':
! 825: goto OUT; /* end of token */
! 826:
! 827: case '\\':
! 828: INC_CHKCURSOR(sb);
! 829: goto S2; /* slurp next character */
! 830:
! 831: case '"':
! 832: INC_CHKCURSOR(sb);
! 833: goto S3; /* slurp quoted string */
1.1 deraadt 834:
1.129 ! florian 835: default:
! 836: *ap = *sb; /* add character to token */
! 837: ap++;
! 838: INC_CHKCURSOR(sb);
! 839: got_one = 1;
! 840: goto S1;
1.1 deraadt 841: }
842:
1.129 ! florian 843: S2:
! 844: switch (*sb) {
1.1 deraadt 845:
1.129 ! florian 846: case '\0':
! 847: goto OUT;
1.1 deraadt 848:
849: default:
1.129 ! florian 850: *ap = *sb;
! 851: ap++;
! 852: INC_CHKCURSOR(sb);
! 853: got_one = 1;
! 854: goto S1;
1.1 deraadt 855: }
856:
1.129 ! florian 857: S3:
! 858: switch (*sb) {
! 859:
! 860: case '\0':
! 861: goto OUT;
! 862:
! 863: case '"':
! 864: INC_CHKCURSOR(sb);
! 865: goto S1;
! 866:
! 867: default:
! 868: *ap = *sb;
! 869: ap++;
! 870: INC_CHKCURSOR(sb);
! 871: got_one = 1;
! 872: goto S3;
! 873: }
1.1 deraadt 874:
1.129 ! florian 875: OUT:
! 876: if (got_one)
! 877: *ap++ = '\0';
! 878: argbase = ap; /* update storage pointer */
! 879: stringbase = sb; /* update scan pointer */
! 880: if (got_one) {
! 881: return (tmp);
! 882: }
! 883: switch (slrflag) {
! 884: case 0:
! 885: slrflag++;
! 886: break;
! 887: case 1:
! 888: slrflag++;
! 889: altarg = (char *) 0;
! 890: break;
! 891: default:
! 892: break;
! 893: }
! 894: return (NULL);
1.1 deraadt 895: }
896:
1.129 ! florian 897: /*
! 898: * Help command.
! 899: * Call each command handler with argc == 0 and argv[0] == name.
! 900: */
! 901: void
! 902: help(int argc, char *argv[])
1.1 deraadt 903: {
1.129 ! florian 904: struct cmd *c;
1.1 deraadt 905:
1.129 ! florian 906: if (argc == 1) {
! 907: StringList *buf;
1.1 deraadt 908:
1.129 ! florian 909: buf = sl_init();
! 910: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
! 911: proxy ? "Proxy c" : "C");
! 912: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
! 913: if (c->c_name && (!proxy || c->c_proxy))
! 914: sl_add(buf, c->c_name);
! 915: list_vertical(buf);
! 916: sl_free(buf, 0);
! 917: return;
! 918: }
1.17 millert 919:
1.129 ! florian 920: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 921:
1.129 ! florian 922: while (--argc > 0) {
! 923: char *arg;
1.17 millert 924:
1.129 ! florian 925: arg = *++argv;
! 926: c = getcmd(arg);
! 927: if (c == (struct cmd *)-1)
! 928: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
! 929: else if (c == NULL)
! 930: fprintf(ttyout, "?Invalid help command %s\n", arg);
! 931: else
! 932: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
! 933: c->c_name, c->c_help);
! 934: }
1.17 millert 935: }
1.129 ! florian 936: #endif /* !SMALL */
1.17 millert 937:
1.129 ! florian 938: __dead void
1.58 deraadt 939: usage(void)
1.17 millert 940: {
1.129 ! florian 941: fprintf(stderr, "usage: "
! 942: #ifndef SMALL
! 943: "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] "
! 944: "[-r seconds]\n"
! 945: " [-s srcaddr] [host [port]]\n"
! 946: " ftp [-C] [-o output] [-s srcaddr]\n"
! 947: " ftp://[user:password@]host[:port]/file[/] ...\n"
! 948: " ftp [-C] [-c cookie] [-o output] [-S ssl_options] "
! 949: "[-s srcaddr]\n"
! 950: " [-U useragent] [-w seconds] "
! 951: "http[s]://[user:password@]host[:port]/file ...\n"
! 952: " ftp [-C] [-o output] [-s srcaddr] file:file ...\n"
! 953: " ftp [-C] [-o output] [-s srcaddr] host:/file[/] ...\n"
! 954: #else /* !SMALL */
! 955: "ftp [-o output] "
! 956: "ftp://[user:password@]host[:port]/file[/] ...\n"
! 957: #ifndef NOSSL
! 958: " ftp [-o output] [-S ssl_options] [-w seconds] "
! 959: "http[s]://[user:password@]host[:port]/file ...\n"
! 960: #else
! 961: " ftp [-o output] [-w seconds] http://host[:port]/file ...\n"
! 962: #endif /* NOSSL */
! 963: " ftp [-o output] file:file ...\n"
! 964: " ftp [-o output] host:/file[/] ...\n"
! 965: #endif /* !SMALL */
! 966: );
1.17 millert 967: exit(1);
1.1 deraadt 968: }