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