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