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