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