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