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