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