Annotation of src/usr.bin/ftp/main.c, Revision 1.135
1.135 ! tb 1: /* $OpenBSD: main.c,v 1.134 2020/09/06 09:03:13 tb 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: char *str;
1.134 tb 225: int depth;
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);
1.134 tb 265: tls_config_set_verify_depth(tls_config, depth);
1.129 florian 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)
1.135 ! tb 291: errx(1, "failed to set TLS protocols: %s",
! 292: tls_config_error(tls_config));
1.129 florian 293: break;
294: default:
295: errx(1, "unknown -S suboption `%s'",
296: suboptarg ? suboptarg : "");
297: /* NOTREACHED */
298: }
299: }
300: }
301: #endif /* !NOSSL */
302:
303: int family = PF_UNSPEC;
304: int pipeout;
1.49 deraadt 305:
1.1 deraadt 306: int
1.129 florian 307: main(volatile int argc, char *argv[])
1.1 deraadt 308: {
1.129 florian 309: int ch, rval;
310: #ifndef SMALL
311: int top;
312: #endif
313: struct passwd *pw = NULL;
314: char *cp, homedir[PATH_MAX];
315: char *outfile = NULL;
316: const char *errstr;
317: int dumb_terminal = 0;
318:
319: ftpport = "ftp";
320: httpport = "http";
321: #ifndef NOSSL
322: httpsport = "https";
323: #endif /* !NOSSL */
324: gateport = getenv("FTPSERVERPORT");
325: if (gateport == NULL || *gateport == '\0')
326: gateport = "ftpgate";
327: doglob = 1;
328: interactive = 1;
329: autologin = 1;
330: passivemode = 1;
331: activefallback = 1;
332: preserve = 1;
333: verbose = 0;
334: progress = 0;
335: gatemode = 0;
336: #ifndef NOSSL
337: cookiefile = NULL;
338: #endif /* NOSSL */
339: #ifndef SMALL
340: editing = 0;
341: el = NULL;
342: hist = NULL;
343: resume = 0;
344: srcaddr = NULL;
345: marg_sl = sl_init();
346: #endif /* !SMALL */
347: mark = HASHBYTES;
348: epsv4 = 1;
349: epsv4bad = 0;
350:
351: /* Set default operation mode based on FTPMODE environment variable */
352: if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
353: if (strcmp(cp, "passive") == 0) {
354: passivemode = 1;
355: activefallback = 0;
356: } else if (strcmp(cp, "active") == 0) {
357: passivemode = 0;
358: activefallback = 0;
359: } else if (strcmp(cp, "gate") == 0) {
360: gatemode = 1;
361: } else if (strcmp(cp, "auto") == 0) {
362: passivemode = 1;
363: activefallback = 1;
364: } else
365: warnx("unknown FTPMODE: %s. Using defaults", cp);
366: }
367:
368: if (strcmp(__progname, "gate-ftp") == 0)
369: gatemode = 1;
370: gateserver = getenv("FTPSERVER");
371: if (gateserver == NULL)
372: gateserver = "";
373: if (gatemode) {
374: if (*gateserver == '\0') {
375: warnx(
376: "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
377: gatemode = 0;
378: }
379: }
380:
381: cp = getenv("TERM");
382: dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
383: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
384: fromatty = isatty(fileno(stdin));
385: if (fromatty) {
386: verbose = 1; /* verbose if from a tty */
387: #ifndef SMALL
388: if (!dumb_terminal)
389: editing = 1; /* editing mode on if tty is usable */
390: #endif /* !SMALL */
391: }
392:
393: ttyout = stdout;
394: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
395: progress = 1; /* progress bar on if tty is usable */
396:
397: #ifndef NOSSL
398: cookiefile = getenv("http_cookies");
399: if (tls_init() != 0)
400: errx(1, "tls init failed");
401: if (tls_config == NULL) {
402: tls_config = tls_config_new();
403: if (tls_config == NULL)
404: errx(1, "tls config failed");
405: if (tls_config_set_protocols(tls_config,
406: TLS_PROTOCOLS_ALL) != 0)
407: errx(1, "tls set protocols failed: %s",
408: tls_config_error(tls_config));
409: if (tls_config_set_ciphers(tls_config, "legacy") != 0)
410: errx(1, "tls set ciphers failed: %s",
411: tls_config_error(tls_config));
412: }
413: #endif /* !NOSSL */
414:
415: httpuseragent = NULL;
416:
1.86 jca 417: while ((ch = getopt(argc, argv,
1.130 deraadt 418: "46AaCc:dD:EeN:gik:Mmno:pP:r:S:s:tU:vVw:")) != -1) {
1.1 deraadt 419: switch (ch) {
1.49 deraadt 420: case '4':
1.129 florian 421: family = PF_INET;
1.49 deraadt 422: break;
423: case '6':
1.129 florian 424: family = PF_INET6;
1.49 deraadt 425: break;
1.37 millert 426: case 'A':
1.129 florian 427: activefallback = 0;
428: passivemode = 0;
429: break;
430:
1.130 deraadt 431: case 'N':
432: setprogname(optarg);
433: break;
1.129 florian 434: case 'a':
435: anonftp = 1;
1.37 millert 436: break;
1.129 florian 437:
1.67 martynas 438: case 'C':
1.129 florian 439: #ifndef SMALL
1.67 martynas 440: resume = 1;
1.129 florian 441: #endif /* !SMALL */
1.63 pyr 442: break;
1.129 florian 443:
444: case 'c':
445: #ifndef SMALL
446: cookiefile = optarg;
447: #endif /* !SMALL */
448: break;
449:
1.87 deraadt 450: case 'D':
1.129 florian 451: action = optarg;
452: break;
453: case 'd':
454: #ifndef SMALL
455: options |= SO_DEBUG;
456: debug++;
457: #endif /* !SMALL */
458: break;
459:
460: case 'E':
461: epsv4 = 0;
462: break;
463:
464: case 'e':
465: #ifndef SMALL
466: editing = 0;
467: #endif /* !SMALL */
468: break;
469:
470: case 'g':
471: doglob = 0;
472: break;
473:
474: case 'i':
475: interactive = 0;
1.87 deraadt 476: break;
1.129 florian 477:
478: case 'k':
479: keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
480: &errstr);
481: if (errstr != NULL) {
482: warnx("keep alive amount is %s: %s", errstr,
483: optarg);
484: usage();
485: }
1.1 deraadt 486: break;
1.121 kmos 487: case 'M':
1.129 florian 488: progress = 0;
1.59 fgsch 489: break;
1.121 kmos 490: case 'm':
1.129 florian 491: progress = -1;
1.21 kstailey 492: break;
1.129 florian 493:
494: case 'n':
495: autologin = 0;
496: break;
497:
498: case 'o':
499: outfile = optarg;
500: if (*outfile == '\0') {
501: pipeout = 0;
502: outfile = NULL;
503: ttyout = stdout;
504: } else {
505: pipeout = strcmp(outfile, "-") == 0;
506: ttyout = pipeout ? stderr : stdout;
507: }
508: break;
509:
510: case 'p':
511: passivemode = 1;
512: activefallback = 0;
513: break;
514:
515: case 'P':
516: ftpport = optarg;
517: break;
518:
519: case 'r':
520: retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
521: if (errstr != NULL) {
522: warnx("retry amount is %s: %s", errstr,
523: optarg);
524: usage();
525: }
526: break;
527:
1.121 kmos 528: case 'S':
1.129 florian 529: #ifndef NOSSL
530: process_ssl_options(optarg);
531: #endif /* !NOSSL */
532: break;
533:
534: case 's':
535: #ifndef SMALL
536: srcaddr = optarg;
537: #endif /* !SMALL */
538: break;
539:
540: case 't':
541: trace = 1;
1.1 deraadt 542: break;
1.129 florian 543:
544: #ifndef SMALL
1.121 kmos 545: case 'U':
1.129 florian 546: free (httpuseragent);
547: if (strcspn(optarg, "\r\n") != strlen(optarg))
548: errx(1, "Invalid User-Agent: %s.", optarg);
549: if (asprintf(&httpuseragent, "User-Agent: %s",
550: optarg) == -1)
551: errx(1, "Can't allocate memory for HTTP(S) "
552: "User-Agent");
553: break;
554: #endif /* !SMALL */
555:
556: case 'v':
557: verbose = 1;
1.41 millert 558: break;
1.129 florian 559:
1.121 kmos 560: case 'V':
561: verbose = 0;
1.65 espie 562: break;
1.129 florian 563:
1.121 kmos 564: case 'w':
1.129 florian 565: connect_timeout = strtonum(optarg, 0, 200, &errstr);
566: if (errstr)
567: errx(1, "-w: %s", errstr);
1.116 deraadt 568: break;
1.1 deraadt 569: default:
1.17 millert 570: usage();
1.1 deraadt 571: }
572: }
573: argc -= optind;
574: argv += optind;
1.63 pyr 575:
1.129 florian 576: #ifndef NOSSL
577: cookie_load();
578: #endif /* !NOSSL */
579: if (httpuseragent == NULL)
580: httpuseragent = HTTP_USER_AGENT;
581:
582: cpend = 0; /* no pending replies */
583: proxy = 0; /* proxy not active */
584: crflag = 1; /* strip c.r. on ascii gets */
585: sendport = -1; /* not using ports */
586: /*
587: * Set up the home directory in case we're globbing.
588: */
589: cp = getlogin();
590: if (cp != NULL) {
591: pw = getpwnam(cp);
592: }
593: if (pw == NULL)
594: pw = getpwuid(getuid());
595: if (pw != NULL) {
596: (void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
597: home = homedir;
598: }
599:
600: setttywidth(0);
601: (void)signal(SIGWINCH, setttywidth);
1.9 michaels 602:
1.129 florian 603: if (argc > 0) {
604: if (isurl(argv[0])) {
605: if (pipeout) {
606: #ifndef SMALL
607: if (pledge("stdio rpath dns tty inet proc exec fattr",
608: NULL) == -1)
609: err(1, "pledge");
610: #else
611: if (pledge("stdio rpath dns tty inet fattr",
612: NULL) == -1)
613: err(1, "pledge");
614: #endif
615: } else {
1.103 doug 616: #ifndef SMALL
1.129 florian 617: if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr",
618: NULL) == -1)
619: err(1, "pledge");
620: #else
621: if (pledge("stdio rpath wpath cpath dns tty inet fattr",
622: NULL) == -1)
623: err(1, "pledge");
624: #endif
625: }
1.103 doug 626:
1.129 florian 627: rval = auto_fetch(argc, argv, outfile);
628: if (rval >= 0) /* -1 == connected and cd-ed */
629: exit(rval);
630: } else {
631: #ifndef SMALL
632: char *xargv[5];
1.3 deraadt 633:
1.129 florian 634: if (setjmp(toplevel))
635: exit(0);
636: (void)signal(SIGINT, (sig_t)intr);
637: (void)signal(SIGPIPE, (sig_t)lostpeer);
638: xargv[0] = __progname;
639: xargv[1] = argv[0];
640: xargv[2] = argv[1];
641: xargv[3] = argv[2];
642: xargv[4] = NULL;
643: do {
644: setpeer(argc+1, xargv);
645: if (!retry_connect)
646: break;
647: if (!connected) {
648: macnum = 0;
649: fputs("Retrying...\n", ttyout);
650: sleep(retry_connect);
651: }
652: } while (!connected);
653: retry_connect = 0; /* connected, stop hiding msgs */
654: #endif /* !SMALL */
1.3 deraadt 655: }
1.1 deraadt 656: }
1.129 florian 657: #ifndef SMALL
658: controlediting();
659: top = setjmp(toplevel) == 0;
660: if (top) {
661: (void)signal(SIGINT, (sig_t)intr);
662: (void)signal(SIGPIPE, (sig_t)lostpeer);
663: }
664: for (;;) {
665: cmdscanner(top);
666: top = 1;
667: }
668: #else /* !SMALL */
669: usage();
670: #endif /* !SMALL */
671: }
672:
673: void
674: intr(void)
675: {
676: int save_errno = errno;
677:
678: write(fileno(ttyout), "\n\r", 2);
679: alarmtimer(0);
1.121 kmos 680:
1.129 florian 681: errno = save_errno;
682: longjmp(toplevel, 1);
1.1 deraadt 683: }
684:
1.129 florian 685: void
686: lostpeer(void)
1.1 deraadt 687: {
1.129 florian 688: int save_errno = errno;
1.121 kmos 689:
1.129 florian 690: alarmtimer(0);
691: if (connected) {
692: if (cout != NULL) {
693: (void)shutdown(fileno(cout), SHUT_RDWR);
694: (void)fclose(cout);
695: cout = NULL;
696: }
697: if (data >= 0) {
698: (void)shutdown(data, SHUT_RDWR);
699: (void)close(data);
700: data = -1;
701: }
702: connected = 0;
703: }
704: pswitch(1);
705: if (connected) {
706: if (cout != NULL) {
707: (void)shutdown(fileno(cout), SHUT_RDWR);
708: (void)fclose(cout);
709: cout = NULL;
710: }
711: connected = 0;
1.121 kmos 712: }
1.129 florian 713: proxflag = 0;
714: pswitch(0);
715: errno = save_errno;
716: }
1.90 deraadt 717:
1.129 florian 718: #ifndef SMALL
719: /*
720: * Generate a prompt
721: */
722: char *
723: prompt(void)
724: {
725: return ("ftp> ");
1.1 deraadt 726: }
727:
1.129 florian 728: /*
729: * Command parser.
730: */
731: void
732: cmdscanner(int top)
1.1 deraadt 733: {
1.129 florian 734: struct cmd *c;
735: int num;
736: HistEvent hev;
1.1 deraadt 737:
1.129 florian 738: if (!top && !editing)
739: (void)putc('\n', ttyout);
740: for (;;) {
741: if (!editing) {
742: if (fromatty) {
743: fputs(prompt(), ttyout);
744: (void)fflush(ttyout);
745: }
746: if (fgets(line, sizeof(line), stdin) == NULL)
747: quit(0, 0);
748: num = strlen(line);
749: if (num == 0)
750: break;
751: if (line[--num] == '\n') {
752: if (num == 0)
753: break;
754: line[num] = '\0';
755: } else if (num == sizeof(line) - 2) {
756: fputs("sorry, input line too long.\n", ttyout);
757: while ((num = getchar()) != '\n' && num != EOF)
758: /* void */;
759: break;
760: } /* else it was a line without a newline */
761: } else {
762: const char *buf;
763: cursor_pos = NULL;
764:
765: if ((buf = el_gets(el, &num)) == NULL || num == 0) {
766: putc('\n', ttyout);
767: fflush(ttyout);
768: quit(0, 0);
769: }
770: if (buf[--num] == '\n') {
771: if (num == 0)
772: break;
773: }
774: if (num >= sizeof(line)) {
775: fputs("sorry, input line too long.\n", ttyout);
776: break;
777: }
778: memcpy(line, buf, (size_t)num);
779: line[num] = '\0';
780: history(hist, &hev, H_ENTER, buf);
781: }
782:
783: makeargv();
784: if (margc == 0)
785: continue;
786: c = getcmd(margv[0]);
787: if (c == (struct cmd *)-1) {
788: fputs("?Ambiguous command.\n", ttyout);
789: continue;
790: }
791: if (c == 0) {
792: /*
793: * Give editline(3) a shot at unknown commands.
794: * XXX - bogus commands with a colon in
795: * them will not elicit an error.
796: */
797: if (editing &&
798: el_parse(el, margc, (const char **)margv) != 0)
799: fputs("?Invalid command.\n", ttyout);
800: continue;
801: }
802: if (c->c_conn && !connected) {
803: fputs("Not connected.\n", ttyout);
804: continue;
805: }
806: confirmrest = 0;
807: (*c->c_handler)(margc, margv);
808: if (bell && c->c_bell)
809: (void)putc('\007', ttyout);
810: if (c->c_handler != help)
811: break;
812: }
813: (void)signal(SIGINT, (sig_t)intr);
814: (void)signal(SIGPIPE, (sig_t)lostpeer);
815: }
1.1 deraadt 816:
1.129 florian 817: struct cmd *
818: getcmd(const char *name)
819: {
820: const char *p, *q;
821: struct cmd *c, *found;
822: int nmatches, longest;
823:
824: if (name == NULL)
825: return (0);
826:
827: longest = 0;
828: nmatches = 0;
829: found = 0;
830: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
831: for (q = name; *q == *p++; q++)
832: if (*q == 0) /* exact match? */
833: return (c);
834: if (!*q) { /* the name was a prefix */
835: if (q - name > longest) {
836: longest = q - name;
837: nmatches = 1;
838: found = c;
839: } else if (q - name == longest)
840: nmatches++;
841: }
842: }
843: if (nmatches > 1)
844: return ((struct cmd *)-1);
845: return (found);
1.1 deraadt 846: }
847:
1.129 florian 848: /*
849: * Slice a string up into argc/argv.
850: */
851:
852: int slrflag;
853:
854: void
855: makeargv(void)
1.1 deraadt 856: {
1.129 florian 857: char *argp;
1.1 deraadt 858:
1.129 florian 859: stringbase = line; /* scan from first of buffer */
860: argbase = argbuf; /* store from first of buffer */
861: slrflag = 0;
862: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
863: for (margc = 0; ; margc++) {
864: argp = slurpstring();
865: sl_add(marg_sl, argp);
866: if (argp == NULL)
1.121 kmos 867: break;
1.129 florian 868: }
869: if (cursor_pos == line) {
870: cursor_argc = 0;
871: cursor_argo = 0;
872: } else if (cursor_pos != NULL) {
873: cursor_argc = margc;
874: cursor_argo = strlen(margv[margc-1]);
875: }
876: }
1.121 kmos 877:
1.129 florian 878: #define INC_CHKCURSOR(x) { (x)++ ; \
879: if (x == cursor_pos) { \
880: cursor_argc = margc; \
881: cursor_argo = ap-argbase; \
882: cursor_pos = NULL; \
883: } }
1.121 kmos 884:
1.129 florian 885: /*
886: * Parse string into argbuf;
887: * implemented with FSM to
888: * handle quoting and strings
889: */
890: char *
891: slurpstring(void)
892: {
893: int got_one = 0;
894: char *sb = stringbase;
895: char *ap = argbase;
896: char *tmp = argbase; /* will return this if token found */
897:
898: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
899: switch (slrflag) { /* and $ as token for macro invoke */
900: case 0:
901: slrflag++;
902: INC_CHKCURSOR(stringbase);
903: return ((*sb == '!') ? "!" : "$");
904: /* NOTREACHED */
905: case 1:
906: slrflag++;
907: altarg = stringbase;
908: break;
909: default:
910: break;
1.17 millert 911: }
1.1 deraadt 912: }
1.121 kmos 913:
1.129 florian 914: S0:
915: switch (*sb) {
1.121 kmos 916:
1.129 florian 917: case '\0':
918: goto OUT;
1.121 kmos 919:
1.129 florian 920: case ' ':
921: case '\t':
922: INC_CHKCURSOR(sb);
923: goto S0;
1.1 deraadt 924:
1.129 florian 925: default:
926: switch (slrflag) {
927: case 0:
928: slrflag++;
929: break;
930: case 1:
931: slrflag++;
932: altarg = sb;
933: break;
934: default:
935: break;
936: }
937: goto S1;
938: }
1.1 deraadt 939:
1.129 florian 940: S1:
941: switch (*sb) {
1.1 deraadt 942:
1.129 florian 943: case ' ':
944: case '\t':
945: case '\0':
946: goto OUT; /* end of token */
947:
948: case '\\':
949: INC_CHKCURSOR(sb);
950: goto S2; /* slurp next character */
951:
952: case '"':
953: INC_CHKCURSOR(sb);
954: goto S3; /* slurp quoted string */
1.1 deraadt 955:
1.129 florian 956: default:
957: *ap = *sb; /* add character to token */
958: ap++;
959: INC_CHKCURSOR(sb);
960: got_one = 1;
961: goto S1;
1.1 deraadt 962: }
963:
1.129 florian 964: S2:
965: switch (*sb) {
1.1 deraadt 966:
1.129 florian 967: case '\0':
968: goto OUT;
1.1 deraadt 969:
970: default:
1.129 florian 971: *ap = *sb;
972: ap++;
973: INC_CHKCURSOR(sb);
974: got_one = 1;
975: goto S1;
1.1 deraadt 976: }
977:
1.129 florian 978: S3:
979: switch (*sb) {
980:
981: case '\0':
982: goto OUT;
983:
984: case '"':
985: INC_CHKCURSOR(sb);
986: goto S1;
987:
988: default:
989: *ap = *sb;
990: ap++;
991: INC_CHKCURSOR(sb);
992: got_one = 1;
993: goto S3;
994: }
1.1 deraadt 995:
1.129 florian 996: OUT:
997: if (got_one)
998: *ap++ = '\0';
999: argbase = ap; /* update storage pointer */
1000: stringbase = sb; /* update scan pointer */
1001: if (got_one) {
1002: return (tmp);
1003: }
1004: switch (slrflag) {
1005: case 0:
1006: slrflag++;
1007: break;
1008: case 1:
1009: slrflag++;
1010: altarg = (char *) 0;
1011: break;
1012: default:
1013: break;
1014: }
1015: return (NULL);
1.1 deraadt 1016: }
1017:
1.129 florian 1018: /*
1019: * Help command.
1020: * Call each command handler with argc == 0 and argv[0] == name.
1021: */
1022: void
1023: help(int argc, char *argv[])
1.1 deraadt 1024: {
1.129 florian 1025: struct cmd *c;
1.1 deraadt 1026:
1.129 florian 1027: if (argc == 1) {
1028: StringList *buf;
1.1 deraadt 1029:
1.129 florian 1030: buf = sl_init();
1031: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1032: proxy ? "Proxy c" : "C");
1033: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
1034: if (c->c_name && (!proxy || c->c_proxy))
1035: sl_add(buf, c->c_name);
1036: list_vertical(buf);
1037: sl_free(buf, 0);
1038: return;
1039: }
1.17 millert 1040:
1.129 florian 1041: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 1042:
1.129 florian 1043: while (--argc > 0) {
1044: char *arg;
1.17 millert 1045:
1.129 florian 1046: arg = *++argv;
1047: c = getcmd(arg);
1048: if (c == (struct cmd *)-1)
1049: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1050: else if (c == NULL)
1051: fprintf(ttyout, "?Invalid help command %s\n", arg);
1052: else
1053: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1054: c->c_name, c->c_help);
1055: }
1.17 millert 1056: }
1.129 florian 1057: #endif /* !SMALL */
1.17 millert 1058:
1.129 florian 1059: __dead void
1.58 deraadt 1060: usage(void)
1.17 millert 1061: {
1.129 florian 1062: fprintf(stderr, "usage: "
1063: #ifndef SMALL
1064: "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] "
1065: "[-r seconds]\n"
1.131 deraadt 1066: " [-s sourceaddr] [host [port]]\n"
1067: " ftp [-C] [-N name] [-o output] [-s sourceaddr]\n"
1.129 florian 1068: " ftp://[user:password@]host[:port]/file[/] ...\n"
1.130 deraadt 1069: " ftp [-C] [-c cookie] [-N name] [-o output] [-S ssl_options] "
1.131 deraadt 1070: "[-s sourceaddr]\n"
1.129 florian 1071: " [-U useragent] [-w seconds] "
1072: "http[s]://[user:password@]host[:port]/file ...\n"
1.131 deraadt 1073: " ftp [-C] [-N name] [-o output] [-s sourceaddr] file:file ...\n"
1074: " ftp [-C] [-N name] [-o output] [-s sourceaddr] host:/file[/] ...\n"
1.129 florian 1075: #else /* !SMALL */
1.130 deraadt 1076: "ftp [-N name] [-o output] "
1.129 florian 1077: "ftp://[user:password@]host[:port]/file[/] ...\n"
1078: #ifndef NOSSL
1.130 deraadt 1079: " ftp [-N name] [-o output] [-S ssl_options] [-w seconds] "
1.129 florian 1080: "http[s]://[user:password@]host[:port]/file ...\n"
1081: #else
1.130 deraadt 1082: " ftp [-N name] [-o output] [-w seconds] http://host[:port]/file ...\n"
1.129 florian 1083: #endif /* NOSSL */
1.130 deraadt 1084: " ftp [-N name] [-o output] file:file ...\n"
1085: " ftp [-N name] [-o output] host:/file[/] ...\n"
1.129 florian 1086: #endif /* !SMALL */
1087: );
1.17 millert 1088: exit(1);
1.1 deraadt 1089: }