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