Annotation of src/usr.bin/ftp/main.c, Revision 1.133
1.133 ! tb 1: /* $OpenBSD: main.c,v 1.132 2020/09/01 12:33:48 jca Exp $ */
1.129 florian 2: /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
1.122 jasper 3:
1.1 deraadt 4: /*
1.129 florian 5: * Copyright (C) 1997 and 1998 WIDE Project.
6: * All rights reserved.
1.118 krw 7: *
1.129 florian 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.129 florian 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.
1.44 itojun 31: */
32:
1.129 florian 33: /*
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.
45: * 3. Neither the name of the University nor the names of its contributors
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: */
1.1 deraadt 65: #include <sys/types.h>
66: #include <sys/socket.h>
67:
1.129 florian 68: #include <ctype.h>
1.1 deraadt 69: #include <err.h>
1.120 jsing 70: #include <fcntl.h>
1.129 florian 71: #include <netdb.h>
72: #include <pwd.h>
1.1 deraadt 73: #include <stdio.h>
1.129 florian 74: #include <errno.h>
1.18 millert 75: #include <stdlib.h>
1.1 deraadt 76: #include <string.h>
77: #include <unistd.h>
78:
1.129 florian 79: #include <tls.h>
1.91 jsing 80:
1.129 florian 81: #include "cmds.h"
82: #include "ftp_var.h"
1.132 jca 83:
84: int trace;
85: int hash;
86: int mark;
87: int sendport;
88: int verbose;
89: int connected;
90: int fromatty;
91: int interactive;
92: #ifndef SMALL
93: int confirmrest;
94: int debug;
95: int bell;
96: char *altarg;
97: #endif /* !SMALL */
98: int doglob;
99: int autologin;
100: int proxy;
101: int proxflag;
102: int gatemode;
103: char *gateserver;
104: int sunique;
105: int runique;
106: int mcase;
107: int ntflag;
108: int mapflag;
109: int preserve;
110: int progress;
111: int code;
112: int crflag;
113: char pasv[BUFSIZ];
114: int passivemode;
115: int activefallback;
116: char ntin[17];
117: char ntout[17];
118: char mapin[PATH_MAX];
119: char mapout[PATH_MAX];
120: char typename[32];
121: int type;
122: int curtype;
123: char structname[32];
124: int stru;
125: char formname[32];
126: int form;
127: char modename[32];
128: int mode;
129: char bytename[32];
130: int bytesize;
131: int anonftp;
132: int dirchange;
133: unsigned int retry_connect;
134: int ttywidth;
135: int epsv4;
136: int epsv4bad;
137:
138: #ifndef SMALL
139: int editing;
140: EditLine *el;
141: History *hist;
142: char *cursor_pos;
143: size_t cursor_argc;
144: size_t cursor_argo;
145: int resume;
146: char *srcaddr;
147: #endif /* !SMALL */
148:
149: char *cookiefile;
150:
151: off_t bytes;
152: off_t filesize;
153: char *direction;
154:
155: char *hostname;
156: int unix_server;
157: int unix_proxy;
158:
159: char *ftpport;
160: char *httpport;
161: #ifndef NOSSL
162: char *httpsport;
163: #endif /* !SMALL */
164: char *httpuseragent;
165: char *gateport;
166:
167: jmp_buf toplevel;
168:
169: #ifndef SMALL
170: char line[FTPBUFLEN];
171: char *argbase;
172: char *stringbase;
173: char argbuf[FTPBUFLEN];
174: StringList *marg_sl;
175: int margc;
176: int options;
177: #endif /* !SMALL */
178:
179: int cpend;
180: int mflag;
181:
182: #ifndef SMALL
183: int macnum;
184: struct macel macros[16];
185: char macbuf[4096];
186: #endif /* !SMALL */
187:
188: FILE *ttyout;
1.129 florian 189:
190: int connect_timeout;
191:
192: #ifndef NOSSL
193: char * const ssl_verify_opts[] = {
194: #define SSL_CAFILE 0
195: "cafile",
196: #define SSL_CAPATH 1
197: "capath",
198: #define SSL_CIPHERS 2
199: "ciphers",
200: #define SSL_DONTVERIFY 3
201: "dont",
202: #define SSL_DOVERIFY 4
203: "do",
204: #define SSL_VERIFYDEPTH 5
205: "depth",
206: #define SSL_MUSTSTAPLE 6
207: "muststaple",
208: #define SSL_NOVERIFYTIME 7
209: "noverifytime",
210: #define SSL_SESSION 8
211: "session",
1.133 ! tb 212: #define SSL_PROTOCOLS 9
! 213: "protocols",
1.129 florian 214: NULL
215: };
216:
217: struct tls_config *tls_config;
218: int tls_session_fd = -1;
219:
220: static void
221: process_ssl_options(char *cp)
222: {
223: const char *errstr;
224: long long depth;
225: char *str;
1.133 ! tb 226: uint32_t protocols;
1.129 florian 227:
228: while (*cp) {
229: switch (getsubopt(&cp, ssl_verify_opts, &str)) {
230: case SSL_CAFILE:
231: if (str == NULL)
232: errx(1, "missing CA file");
233: if (tls_config_set_ca_file(tls_config, str) != 0)
234: errx(1, "tls ca file failed: %s",
235: tls_config_error(tls_config));
236: break;
237: case SSL_CAPATH:
238: if (str == NULL)
239: errx(1, "missing CA directory path");
240: if (tls_config_set_ca_path(tls_config, str) != 0)
241: errx(1, "tls ca path failed: %s",
242: tls_config_error(tls_config));
243: break;
244: case SSL_CIPHERS:
245: if (str == NULL)
246: errx(1, "missing cipher list");
247: if (tls_config_set_ciphers(tls_config, str) != 0)
248: errx(1, "tls ciphers failed: %s",
249: tls_config_error(tls_config));
250: break;
251: case SSL_DONTVERIFY:
252: tls_config_insecure_noverifycert(tls_config);
253: tls_config_insecure_noverifyname(tls_config);
254: break;
255: case SSL_DOVERIFY:
256: tls_config_verify(tls_config);
257: break;
258: case SSL_VERIFYDEPTH:
259: if (str == NULL)
260: errx(1, "missing depth");
261: depth = strtonum(str, 0, INT_MAX, &errstr);
262: if (errstr)
263: errx(1, "certificate validation depth is %s",
264: errstr);
265: tls_config_set_verify_depth(tls_config, (int)depth);
266: break;
267: case SSL_MUSTSTAPLE:
268: tls_config_ocsp_require_stapling(tls_config);
269: break;
270: case SSL_NOVERIFYTIME:
271: tls_config_insecure_noverifytime(tls_config);
272: break;
273: case SSL_SESSION:
274: if (str == NULL)
275: errx(1, "missing session file");
276: if ((tls_session_fd = open(str, O_RDWR|O_CREAT,
277: 0600)) == -1)
278: err(1, "failed to open or create session file "
279: "'%s'", str);
280: if (tls_config_set_session_fd(tls_config,
281: tls_session_fd) == -1)
282: errx(1, "failed to set session: %s",
283: tls_config_error(tls_config));
1.133 ! tb 284: break;
! 285: case SSL_PROTOCOLS:
! 286: if (str == NULL)
! 287: errx(1, "missing protocol name");
! 288: if (tls_config_parse_protocols(&protocols, str) != 0)
! 289: errx(1, "failed to parse TLS protocols");
! 290: if (tls_config_set_protocols(tls_config, protocols) != 0)
! 291: errx(1, "failed to set TLS protocols");
1.129 florian 292: break;
293: default:
294: errx(1, "unknown -S suboption `%s'",
295: suboptarg ? suboptarg : "");
296: /* NOTREACHED */
297: }
298: }
299: }
300: #endif /* !NOSSL */
301:
302: int family = PF_UNSPEC;
303: int pipeout;
1.49 deraadt 304:
1.1 deraadt 305: int
1.129 florian 306: main(volatile int argc, char *argv[])
1.1 deraadt 307: {
1.129 florian 308: int ch, rval;
309: #ifndef SMALL
310: int top;
311: #endif
312: struct passwd *pw = NULL;
313: char *cp, homedir[PATH_MAX];
314: char *outfile = NULL;
315: const char *errstr;
316: int dumb_terminal = 0;
317:
318: ftpport = "ftp";
319: httpport = "http";
320: #ifndef NOSSL
321: httpsport = "https";
322: #endif /* !NOSSL */
323: gateport = getenv("FTPSERVERPORT");
324: if (gateport == NULL || *gateport == '\0')
325: gateport = "ftpgate";
326: doglob = 1;
327: interactive = 1;
328: autologin = 1;
329: passivemode = 1;
330: activefallback = 1;
331: preserve = 1;
332: verbose = 0;
333: progress = 0;
334: gatemode = 0;
335: #ifndef NOSSL
336: cookiefile = NULL;
337: #endif /* NOSSL */
338: #ifndef SMALL
339: editing = 0;
340: el = NULL;
341: hist = NULL;
342: resume = 0;
343: srcaddr = NULL;
344: marg_sl = sl_init();
345: #endif /* !SMALL */
346: mark = HASHBYTES;
347: epsv4 = 1;
348: epsv4bad = 0;
349:
350: /* Set default operation mode based on FTPMODE environment variable */
351: if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
352: if (strcmp(cp, "passive") == 0) {
353: passivemode = 1;
354: activefallback = 0;
355: } else if (strcmp(cp, "active") == 0) {
356: passivemode = 0;
357: activefallback = 0;
358: } else if (strcmp(cp, "gate") == 0) {
359: gatemode = 1;
360: } else if (strcmp(cp, "auto") == 0) {
361: passivemode = 1;
362: activefallback = 1;
363: } else
364: warnx("unknown FTPMODE: %s. Using defaults", cp);
365: }
366:
367: if (strcmp(__progname, "gate-ftp") == 0)
368: gatemode = 1;
369: gateserver = getenv("FTPSERVER");
370: if (gateserver == NULL)
371: gateserver = "";
372: if (gatemode) {
373: if (*gateserver == '\0') {
374: warnx(
375: "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
376: gatemode = 0;
377: }
378: }
379:
380: cp = getenv("TERM");
381: dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
382: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
383: fromatty = isatty(fileno(stdin));
384: if (fromatty) {
385: verbose = 1; /* verbose if from a tty */
386: #ifndef SMALL
387: if (!dumb_terminal)
388: editing = 1; /* editing mode on if tty is usable */
389: #endif /* !SMALL */
390: }
391:
392: ttyout = stdout;
393: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
394: progress = 1; /* progress bar on if tty is usable */
395:
396: #ifndef NOSSL
397: cookiefile = getenv("http_cookies");
398: if (tls_init() != 0)
399: errx(1, "tls init failed");
400: if (tls_config == NULL) {
401: tls_config = tls_config_new();
402: if (tls_config == NULL)
403: errx(1, "tls config failed");
404: if (tls_config_set_protocols(tls_config,
405: TLS_PROTOCOLS_ALL) != 0)
406: errx(1, "tls set protocols failed: %s",
407: tls_config_error(tls_config));
408: if (tls_config_set_ciphers(tls_config, "legacy") != 0)
409: errx(1, "tls set ciphers failed: %s",
410: tls_config_error(tls_config));
411: }
412: #endif /* !NOSSL */
413:
414: httpuseragent = NULL;
415:
1.86 jca 416: while ((ch = getopt(argc, argv,
1.130 deraadt 417: "46AaCc:dD:EeN:gik:Mmno:pP:r:S:s:tU:vVw:")) != -1) {
1.1 deraadt 418: switch (ch) {
1.49 deraadt 419: case '4':
1.129 florian 420: family = PF_INET;
1.49 deraadt 421: break;
422: case '6':
1.129 florian 423: family = PF_INET6;
1.49 deraadt 424: break;
1.37 millert 425: case 'A':
1.129 florian 426: activefallback = 0;
427: passivemode = 0;
428: break;
429:
1.130 deraadt 430: case 'N':
431: setprogname(optarg);
432: break;
1.129 florian 433: case 'a':
434: anonftp = 1;
1.37 millert 435: break;
1.129 florian 436:
1.67 martynas 437: case 'C':
1.129 florian 438: #ifndef SMALL
1.67 martynas 439: resume = 1;
1.129 florian 440: #endif /* !SMALL */
1.63 pyr 441: break;
1.129 florian 442:
443: case 'c':
444: #ifndef SMALL
445: cookiefile = optarg;
446: #endif /* !SMALL */
447: break;
448:
1.87 deraadt 449: case 'D':
1.129 florian 450: action = optarg;
451: break;
452: case 'd':
453: #ifndef SMALL
454: options |= SO_DEBUG;
455: debug++;
456: #endif /* !SMALL */
457: break;
458:
459: case 'E':
460: epsv4 = 0;
461: break;
462:
463: case 'e':
464: #ifndef SMALL
465: editing = 0;
466: #endif /* !SMALL */
467: break;
468:
469: case 'g':
470: doglob = 0;
471: break;
472:
473: case 'i':
474: interactive = 0;
1.87 deraadt 475: break;
1.129 florian 476:
477: case 'k':
478: keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
479: &errstr);
480: if (errstr != NULL) {
481: warnx("keep alive amount is %s: %s", errstr,
482: optarg);
483: usage();
484: }
1.1 deraadt 485: break;
1.121 kmos 486: case 'M':
1.129 florian 487: progress = 0;
1.59 fgsch 488: break;
1.121 kmos 489: case 'm':
1.129 florian 490: progress = -1;
1.21 kstailey 491: break;
1.129 florian 492:
493: case 'n':
494: autologin = 0;
495: break;
496:
497: case 'o':
498: outfile = optarg;
499: if (*outfile == '\0') {
500: pipeout = 0;
501: outfile = NULL;
502: ttyout = stdout;
503: } else {
504: pipeout = strcmp(outfile, "-") == 0;
505: ttyout = pipeout ? stderr : stdout;
506: }
507: break;
508:
509: case 'p':
510: passivemode = 1;
511: activefallback = 0;
512: break;
513:
514: case 'P':
515: ftpport = optarg;
516: break;
517:
518: case 'r':
519: retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
520: if (errstr != NULL) {
521: warnx("retry amount is %s: %s", errstr,
522: optarg);
523: usage();
524: }
525: break;
526:
1.121 kmos 527: case 'S':
1.129 florian 528: #ifndef NOSSL
529: process_ssl_options(optarg);
530: #endif /* !NOSSL */
531: break;
532:
533: case 's':
534: #ifndef SMALL
535: srcaddr = optarg;
536: #endif /* !SMALL */
537: break;
538:
539: case 't':
540: trace = 1;
1.1 deraadt 541: break;
1.129 florian 542:
543: #ifndef SMALL
1.121 kmos 544: case 'U':
1.129 florian 545: free (httpuseragent);
546: if (strcspn(optarg, "\r\n") != strlen(optarg))
547: errx(1, "Invalid User-Agent: %s.", optarg);
548: if (asprintf(&httpuseragent, "User-Agent: %s",
549: optarg) == -1)
550: errx(1, "Can't allocate memory for HTTP(S) "
551: "User-Agent");
552: break;
553: #endif /* !SMALL */
554:
555: case 'v':
556: verbose = 1;
1.41 millert 557: break;
1.129 florian 558:
1.121 kmos 559: case 'V':
560: verbose = 0;
1.65 espie 561: break;
1.129 florian 562:
1.121 kmos 563: case 'w':
1.129 florian 564: connect_timeout = strtonum(optarg, 0, 200, &errstr);
565: if (errstr)
566: errx(1, "-w: %s", errstr);
1.116 deraadt 567: break;
1.1 deraadt 568: default:
1.17 millert 569: usage();
1.1 deraadt 570: }
571: }
572: argc -= optind;
573: argv += optind;
1.63 pyr 574:
1.129 florian 575: #ifndef NOSSL
576: cookie_load();
577: #endif /* !NOSSL */
578: if (httpuseragent == NULL)
579: httpuseragent = HTTP_USER_AGENT;
580:
581: cpend = 0; /* no pending replies */
582: proxy = 0; /* proxy not active */
583: crflag = 1; /* strip c.r. on ascii gets */
584: sendport = -1; /* not using ports */
585: /*
586: * Set up the home directory in case we're globbing.
587: */
588: cp = getlogin();
589: if (cp != NULL) {
590: pw = getpwnam(cp);
591: }
592: if (pw == NULL)
593: pw = getpwuid(getuid());
594: if (pw != NULL) {
595: (void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
596: home = homedir;
597: }
598:
599: setttywidth(0);
600: (void)signal(SIGWINCH, setttywidth);
1.9 michaels 601:
1.129 florian 602: if (argc > 0) {
603: if (isurl(argv[0])) {
604: if (pipeout) {
605: #ifndef SMALL
606: if (pledge("stdio rpath dns tty inet proc exec fattr",
607: NULL) == -1)
608: err(1, "pledge");
609: #else
610: if (pledge("stdio rpath dns tty inet fattr",
611: NULL) == -1)
612: err(1, "pledge");
613: #endif
614: } else {
1.103 doug 615: #ifndef SMALL
1.129 florian 616: if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr",
617: NULL) == -1)
618: err(1, "pledge");
619: #else
620: if (pledge("stdio rpath wpath cpath dns tty inet fattr",
621: NULL) == -1)
622: err(1, "pledge");
623: #endif
624: }
1.103 doug 625:
1.129 florian 626: rval = auto_fetch(argc, argv, outfile);
627: if (rval >= 0) /* -1 == connected and cd-ed */
628: exit(rval);
629: } else {
630: #ifndef SMALL
631: char *xargv[5];
1.3 deraadt 632:
1.129 florian 633: if (setjmp(toplevel))
634: exit(0);
635: (void)signal(SIGINT, (sig_t)intr);
636: (void)signal(SIGPIPE, (sig_t)lostpeer);
637: xargv[0] = __progname;
638: xargv[1] = argv[0];
639: xargv[2] = argv[1];
640: xargv[3] = argv[2];
641: xargv[4] = NULL;
642: do {
643: setpeer(argc+1, xargv);
644: if (!retry_connect)
645: break;
646: if (!connected) {
647: macnum = 0;
648: fputs("Retrying...\n", ttyout);
649: sleep(retry_connect);
650: }
651: } while (!connected);
652: retry_connect = 0; /* connected, stop hiding msgs */
653: #endif /* !SMALL */
1.3 deraadt 654: }
1.1 deraadt 655: }
1.129 florian 656: #ifndef SMALL
657: controlediting();
658: top = setjmp(toplevel) == 0;
659: if (top) {
660: (void)signal(SIGINT, (sig_t)intr);
661: (void)signal(SIGPIPE, (sig_t)lostpeer);
662: }
663: for (;;) {
664: cmdscanner(top);
665: top = 1;
666: }
667: #else /* !SMALL */
668: usage();
669: #endif /* !SMALL */
670: }
671:
672: void
673: intr(void)
674: {
675: int save_errno = errno;
676:
677: write(fileno(ttyout), "\n\r", 2);
678: alarmtimer(0);
1.121 kmos 679:
1.129 florian 680: errno = save_errno;
681: longjmp(toplevel, 1);
1.1 deraadt 682: }
683:
1.129 florian 684: void
685: lostpeer(void)
1.1 deraadt 686: {
1.129 florian 687: int save_errno = errno;
1.121 kmos 688:
1.129 florian 689: alarmtimer(0);
690: if (connected) {
691: if (cout != NULL) {
692: (void)shutdown(fileno(cout), SHUT_RDWR);
693: (void)fclose(cout);
694: cout = NULL;
695: }
696: if (data >= 0) {
697: (void)shutdown(data, SHUT_RDWR);
698: (void)close(data);
699: data = -1;
700: }
701: connected = 0;
702: }
703: pswitch(1);
704: if (connected) {
705: if (cout != NULL) {
706: (void)shutdown(fileno(cout), SHUT_RDWR);
707: (void)fclose(cout);
708: cout = NULL;
709: }
710: connected = 0;
1.121 kmos 711: }
1.129 florian 712: proxflag = 0;
713: pswitch(0);
714: errno = save_errno;
715: }
1.90 deraadt 716:
1.129 florian 717: #ifndef SMALL
718: /*
719: * Generate a prompt
720: */
721: char *
722: prompt(void)
723: {
724: return ("ftp> ");
1.1 deraadt 725: }
726:
1.129 florian 727: /*
728: * Command parser.
729: */
730: void
731: cmdscanner(int top)
1.1 deraadt 732: {
1.129 florian 733: struct cmd *c;
734: int num;
735: HistEvent hev;
1.1 deraadt 736:
1.129 florian 737: if (!top && !editing)
738: (void)putc('\n', ttyout);
739: for (;;) {
740: if (!editing) {
741: if (fromatty) {
742: fputs(prompt(), ttyout);
743: (void)fflush(ttyout);
744: }
745: if (fgets(line, sizeof(line), stdin) == NULL)
746: quit(0, 0);
747: num = strlen(line);
748: if (num == 0)
749: break;
750: if (line[--num] == '\n') {
751: if (num == 0)
752: break;
753: line[num] = '\0';
754: } else if (num == sizeof(line) - 2) {
755: fputs("sorry, input line too long.\n", ttyout);
756: while ((num = getchar()) != '\n' && num != EOF)
757: /* void */;
758: break;
759: } /* else it was a line without a newline */
760: } else {
761: const char *buf;
762: cursor_pos = NULL;
763:
764: if ((buf = el_gets(el, &num)) == NULL || num == 0) {
765: putc('\n', ttyout);
766: fflush(ttyout);
767: quit(0, 0);
768: }
769: if (buf[--num] == '\n') {
770: if (num == 0)
771: break;
772: }
773: if (num >= sizeof(line)) {
774: fputs("sorry, input line too long.\n", ttyout);
775: break;
776: }
777: memcpy(line, buf, (size_t)num);
778: line[num] = '\0';
779: history(hist, &hev, H_ENTER, buf);
780: }
781:
782: makeargv();
783: if (margc == 0)
784: continue;
785: c = getcmd(margv[0]);
786: if (c == (struct cmd *)-1) {
787: fputs("?Ambiguous command.\n", ttyout);
788: continue;
789: }
790: if (c == 0) {
791: /*
792: * Give editline(3) a shot at unknown commands.
793: * XXX - bogus commands with a colon in
794: * them will not elicit an error.
795: */
796: if (editing &&
797: el_parse(el, margc, (const char **)margv) != 0)
798: fputs("?Invalid command.\n", ttyout);
799: continue;
800: }
801: if (c->c_conn && !connected) {
802: fputs("Not connected.\n", ttyout);
803: continue;
804: }
805: confirmrest = 0;
806: (*c->c_handler)(margc, margv);
807: if (bell && c->c_bell)
808: (void)putc('\007', ttyout);
809: if (c->c_handler != help)
810: break;
811: }
812: (void)signal(SIGINT, (sig_t)intr);
813: (void)signal(SIGPIPE, (sig_t)lostpeer);
814: }
1.1 deraadt 815:
1.129 florian 816: struct cmd *
817: getcmd(const char *name)
818: {
819: const char *p, *q;
820: struct cmd *c, *found;
821: int nmatches, longest;
822:
823: if (name == NULL)
824: return (0);
825:
826: longest = 0;
827: nmatches = 0;
828: found = 0;
829: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
830: for (q = name; *q == *p++; q++)
831: if (*q == 0) /* exact match? */
832: return (c);
833: if (!*q) { /* the name was a prefix */
834: if (q - name > longest) {
835: longest = q - name;
836: nmatches = 1;
837: found = c;
838: } else if (q - name == longest)
839: nmatches++;
840: }
841: }
842: if (nmatches > 1)
843: return ((struct cmd *)-1);
844: return (found);
1.1 deraadt 845: }
846:
1.129 florian 847: /*
848: * Slice a string up into argc/argv.
849: */
850:
851: int slrflag;
852:
853: void
854: makeargv(void)
1.1 deraadt 855: {
1.129 florian 856: char *argp;
1.1 deraadt 857:
1.129 florian 858: stringbase = line; /* scan from first of buffer */
859: argbase = argbuf; /* store from first of buffer */
860: slrflag = 0;
861: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
862: for (margc = 0; ; margc++) {
863: argp = slurpstring();
864: sl_add(marg_sl, argp);
865: if (argp == NULL)
1.121 kmos 866: break;
1.129 florian 867: }
868: if (cursor_pos == line) {
869: cursor_argc = 0;
870: cursor_argo = 0;
871: } else if (cursor_pos != NULL) {
872: cursor_argc = margc;
873: cursor_argo = strlen(margv[margc-1]);
874: }
875: }
1.121 kmos 876:
1.129 florian 877: #define INC_CHKCURSOR(x) { (x)++ ; \
878: if (x == cursor_pos) { \
879: cursor_argc = margc; \
880: cursor_argo = ap-argbase; \
881: cursor_pos = NULL; \
882: } }
1.121 kmos 883:
1.129 florian 884: /*
885: * Parse string into argbuf;
886: * implemented with FSM to
887: * handle quoting and strings
888: */
889: char *
890: slurpstring(void)
891: {
892: int got_one = 0;
893: char *sb = stringbase;
894: char *ap = argbase;
895: char *tmp = argbase; /* will return this if token found */
896:
897: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
898: switch (slrflag) { /* and $ as token for macro invoke */
899: case 0:
900: slrflag++;
901: INC_CHKCURSOR(stringbase);
902: return ((*sb == '!') ? "!" : "$");
903: /* NOTREACHED */
904: case 1:
905: slrflag++;
906: altarg = stringbase;
907: break;
908: default:
909: break;
1.17 millert 910: }
1.1 deraadt 911: }
1.121 kmos 912:
1.129 florian 913: S0:
914: switch (*sb) {
1.121 kmos 915:
1.129 florian 916: case '\0':
917: goto OUT;
1.121 kmos 918:
1.129 florian 919: case ' ':
920: case '\t':
921: INC_CHKCURSOR(sb);
922: goto S0;
1.1 deraadt 923:
1.129 florian 924: default:
925: switch (slrflag) {
926: case 0:
927: slrflag++;
928: break;
929: case 1:
930: slrflag++;
931: altarg = sb;
932: break;
933: default:
934: break;
935: }
936: goto S1;
937: }
1.1 deraadt 938:
1.129 florian 939: S1:
940: switch (*sb) {
1.1 deraadt 941:
1.129 florian 942: case ' ':
943: case '\t':
944: case '\0':
945: goto OUT; /* end of token */
946:
947: case '\\':
948: INC_CHKCURSOR(sb);
949: goto S2; /* slurp next character */
950:
951: case '"':
952: INC_CHKCURSOR(sb);
953: goto S3; /* slurp quoted string */
1.1 deraadt 954:
1.129 florian 955: default:
956: *ap = *sb; /* add character to token */
957: ap++;
958: INC_CHKCURSOR(sb);
959: got_one = 1;
960: goto S1;
1.1 deraadt 961: }
962:
1.129 florian 963: S2:
964: switch (*sb) {
1.1 deraadt 965:
1.129 florian 966: case '\0':
967: goto OUT;
1.1 deraadt 968:
969: default:
1.129 florian 970: *ap = *sb;
971: ap++;
972: INC_CHKCURSOR(sb);
973: got_one = 1;
974: goto S1;
1.1 deraadt 975: }
976:
1.129 florian 977: S3:
978: switch (*sb) {
979:
980: case '\0':
981: goto OUT;
982:
983: case '"':
984: INC_CHKCURSOR(sb);
985: goto S1;
986:
987: default:
988: *ap = *sb;
989: ap++;
990: INC_CHKCURSOR(sb);
991: got_one = 1;
992: goto S3;
993: }
1.1 deraadt 994:
1.129 florian 995: OUT:
996: if (got_one)
997: *ap++ = '\0';
998: argbase = ap; /* update storage pointer */
999: stringbase = sb; /* update scan pointer */
1000: if (got_one) {
1001: return (tmp);
1002: }
1003: switch (slrflag) {
1004: case 0:
1005: slrflag++;
1006: break;
1007: case 1:
1008: slrflag++;
1009: altarg = (char *) 0;
1010: break;
1011: default:
1012: break;
1013: }
1014: return (NULL);
1.1 deraadt 1015: }
1016:
1.129 florian 1017: /*
1018: * Help command.
1019: * Call each command handler with argc == 0 and argv[0] == name.
1020: */
1021: void
1022: help(int argc, char *argv[])
1.1 deraadt 1023: {
1.129 florian 1024: struct cmd *c;
1.1 deraadt 1025:
1.129 florian 1026: if (argc == 1) {
1027: StringList *buf;
1.1 deraadt 1028:
1.129 florian 1029: buf = sl_init();
1030: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1031: proxy ? "Proxy c" : "C");
1032: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
1033: if (c->c_name && (!proxy || c->c_proxy))
1034: sl_add(buf, c->c_name);
1035: list_vertical(buf);
1036: sl_free(buf, 0);
1037: return;
1038: }
1.17 millert 1039:
1.129 florian 1040: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 1041:
1.129 florian 1042: while (--argc > 0) {
1043: char *arg;
1.17 millert 1044:
1.129 florian 1045: arg = *++argv;
1046: c = getcmd(arg);
1047: if (c == (struct cmd *)-1)
1048: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1049: else if (c == NULL)
1050: fprintf(ttyout, "?Invalid help command %s\n", arg);
1051: else
1052: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1053: c->c_name, c->c_help);
1054: }
1.17 millert 1055: }
1.129 florian 1056: #endif /* !SMALL */
1.17 millert 1057:
1.129 florian 1058: __dead void
1.58 deraadt 1059: usage(void)
1.17 millert 1060: {
1.129 florian 1061: fprintf(stderr, "usage: "
1062: #ifndef SMALL
1063: "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] "
1064: "[-r seconds]\n"
1.131 deraadt 1065: " [-s sourceaddr] [host [port]]\n"
1066: " ftp [-C] [-N name] [-o output] [-s sourceaddr]\n"
1.129 florian 1067: " ftp://[user:password@]host[:port]/file[/] ...\n"
1.130 deraadt 1068: " ftp [-C] [-c cookie] [-N name] [-o output] [-S ssl_options] "
1.131 deraadt 1069: "[-s sourceaddr]\n"
1.129 florian 1070: " [-U useragent] [-w seconds] "
1071: "http[s]://[user:password@]host[:port]/file ...\n"
1.131 deraadt 1072: " ftp [-C] [-N name] [-o output] [-s sourceaddr] file:file ...\n"
1073: " ftp [-C] [-N name] [-o output] [-s sourceaddr] host:/file[/] ...\n"
1.129 florian 1074: #else /* !SMALL */
1.130 deraadt 1075: "ftp [-N name] [-o output] "
1.129 florian 1076: "ftp://[user:password@]host[:port]/file[/] ...\n"
1077: #ifndef NOSSL
1.130 deraadt 1078: " ftp [-N name] [-o output] [-S ssl_options] [-w seconds] "
1.129 florian 1079: "http[s]://[user:password@]host[:port]/file ...\n"
1080: #else
1.130 deraadt 1081: " ftp [-N name] [-o output] [-w seconds] http://host[:port]/file ...\n"
1.129 florian 1082: #endif /* NOSSL */
1.130 deraadt 1083: " ftp [-N name] [-o output] file:file ...\n"
1084: " ftp [-N name] [-o output] host:/file[/] ...\n"
1.129 florian 1085: #endif /* !SMALL */
1086: );
1.17 millert 1087: exit(1);
1.1 deraadt 1088: }