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