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