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