Annotation of src/usr.bin/ftp/main.c, Revision 1.36
1.36 ! millert 1: /* $OpenBSD: main.c,v 1.35 1997/08/06 17:35:42 mickey Exp $ */
! 2: /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
1.5 deraadt 3:
1.1 deraadt 4: /*
5: * Copyright (c) 1985, 1989, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
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. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
38: static char copyright[] =
39: "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
40: The Regents of the University of California. All rights reserved.\n";
41: #endif /* not lint */
42:
43: #ifndef lint
44: #if 0
45: static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
46: #else
1.36 ! millert 47: static char rcsid[] = "$OpenBSD: main.c,v 1.35 1997/08/06 17:35:42 mickey Exp $";
1.1 deraadt 48: #endif
49: #endif /* not lint */
50:
51: /*
52: * FTP User Program -- Command Interface.
53: */
54: #include <sys/types.h>
55: #include <sys/socket.h>
56:
1.18 millert 57: #include <ctype.h>
1.1 deraadt 58: #include <err.h>
59: #include <netdb.h>
60: #include <pwd.h>
61: #include <stdio.h>
1.18 millert 62: #include <stdlib.h>
1.1 deraadt 63: #include <string.h>
64: #include <unistd.h>
65:
66: #include "ftp_var.h"
67:
1.34 millert 68: int main __P((int, char **));
69:
1.1 deraadt 70: int
71: main(argc, argv)
72: int argc;
73: char *argv[];
74: {
1.17 millert 75: struct servent *sp;
1.34 millert 76: int ch, top, rval;
77: long port;
1.1 deraadt 78: struct passwd *pw = NULL;
1.36 ! millert 79: char *cp, *ep, homedir[MAXPATHLEN];
1.28 millert 80: int dumb_terminal = 0;
1.30 deraadt 81: int outfd = -1;
1.1 deraadt 82:
83: sp = getservbyname("ftp", "tcp");
84: if (sp == 0)
1.17 millert 85: ftpport = htons(FTP_PORT); /* good fallback */
86: else
87: ftpport = sp->s_port;
88: sp = getservbyname("http", "tcp");
89: if (sp == 0)
90: httpport = htons(HTTP_PORT); /* good fallback */
91: else
92: httpport = sp->s_port;
1.36 ! millert 93: gateport = 0;
! 94: cp = getenv("FTPSERVERPORT");
! 95: if (cp != NULL) {
! 96: port = strtol(cp, &ep, 10);
! 97: if (port < 1 || port > USHRT_MAX || *ep != '\0')
! 98: warnx("bad FTPSERVERPORT port number: %s (ignored)",
! 99: cp);
! 100: else
! 101: gateport = htons(port);
! 102: }
! 103: if (gateport == 0) {
! 104: sp = getservbyname("ftpgate", "tcp");
! 105: if (sp == 0)
! 106: gateport = htons(GATE_PORT);
! 107: else
! 108: gateport = sp->s_port;
! 109: }
1.1 deraadt 110: doglob = 1;
111: interactive = 1;
112: autologin = 1;
1.17 millert 113: passivemode = 0;
114: preserve = 1;
115: verbose = 0;
116: progress = 0;
1.36 ! millert 117: gatemode = 0;
1.25 millert 118: #ifndef SMALL
1.22 millert 119: editing = 0;
1.28 millert 120: el = NULL;
121: hist = NULL;
1.22 millert 122: #endif
1.8 kstailey 123: mark = HASHBYTES;
1.17 millert 124: marg_sl = sl_init();
1.1 deraadt 125:
1.17 millert 126: cp = strrchr(argv[0], '/');
127: cp = (cp == NULL) ? argv[0] : cp + 1;
128: if (strcmp(cp, "pftp") == 0)
129: passivemode = 1;
1.36 ! millert 130: else if (strcmp(cp, "gate-ftp") == 0)
! 131: gatemode = 1;
! 132:
! 133: gateserver = getenv("FTPSERVER");
! 134: if (gateserver == NULL || *gateserver == '\0')
! 135: gateserver = GATE_SERVER;
! 136: if (gatemode) {
! 137: if (*gateserver == '\0') {
! 138: warnx(
! 139: "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp");
! 140: gatemode = 0;
! 141: }
! 142: }
1.17 millert 143:
1.31 millert 144: cp = getenv("TERM");
145: dumb_terminal = (cp == NULL || !strcmp(cp, "dumb") ||
146: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
1.17 millert 147: fromatty = isatty(fileno(stdin));
1.22 millert 148: if (fromatty) {
1.17 millert 149: verbose = 1; /* verbose if from a tty */
1.25 millert 150: #ifndef SMALL
1.28 millert 151: if (!dumb_terminal)
1.34 millert 152: editing = 1; /* editing mode on if tty is usable */
1.22 millert 153: #endif
154: }
1.30 deraadt 155:
156: ttyout = stdout;
1.33 millert 157: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
1.34 millert 158: progress = 1; /* progress bar on if tty is usable */
1.32 deraadt 159:
160: if (!isatty(fileno(ttyout))) {
161: outfd = fileno(stdout);
1.30 deraadt 162: ttyout = stderr;
163: }
1.17 millert 164:
1.21 kstailey 165: while ((ch = getopt(argc, argv, "adeginpPr:tvV")) != -1) {
1.1 deraadt 166: switch (ch) {
1.17 millert 167: case 'a':
168: anonftp = 1;
169: break;
170:
1.1 deraadt 171: case 'd':
172: options |= SO_DEBUG;
173: debug++;
174: break;
1.17 millert 175:
1.28 millert 176: case 'e':
177: #ifndef SMALL
1.22 millert 178: editing = 0;
179: #endif
1.21 kstailey 180: break;
181:
1.1 deraadt 182: case 'g':
183: doglob = 0;
184: break;
185:
186: case 'i':
187: interactive = 0;
188: break;
189:
190: case 'n':
191: autologin = 0;
192: break;
193:
1.3 deraadt 194: case 'p':
1.17 millert 195: passivemode = 1;
1.3 deraadt 196: break;
197:
1.17 millert 198: case 'P':
1.36 ! millert 199: port = strtol(optarg, &ep, 10);
! 200: if (port < 1 || port > USHRT_MAX || *ep != '\0')
1.18 millert 201: warnx("bad port number: %s (ignored)", optarg);
1.17 millert 202: else
1.34 millert 203: ftpport = htons((in_port_t)port);
1.7 mickey 204: break;
205:
1.18 millert 206: case 'r':
207: if (isdigit(*optarg))
208: retry_connect = atoi(optarg);
209: else
210: errx(1, "-r requires numeric argument");
211: break;
212:
1.1 deraadt 213: case 't':
1.17 millert 214: trace = 1;
1.1 deraadt 215: break;
216:
217: case 'v':
1.17 millert 218: verbose = 1;
219: break;
220:
221: case 'V':
222: verbose = 0;
1.1 deraadt 223: break;
224:
225: default:
1.17 millert 226: usage();
1.1 deraadt 227: }
228: }
229: argc -= optind;
230: argv += optind;
231:
232: cpend = 0; /* no pending replies */
233: proxy = 0; /* proxy not active */
234: crflag = 1; /* strip c.r. on ascii gets */
235: sendport = -1; /* not using ports */
236: /*
237: * Set up the home directory in case we're globbing.
238: */
239: cp = getlogin();
240: if (cp != NULL) {
241: pw = getpwnam(cp);
242: }
243: if (pw == NULL)
244: pw = getpwuid(getuid());
245: if (pw != NULL) {
246: home = homedir;
1.18 millert 247: (void)strcpy(home, pw->pw_dir);
1.3 deraadt 248: }
1.9 michaels 249:
1.17 millert 250: setttywidth(0);
1.18 millert 251: (void)signal(SIGWINCH, setttywidth);
1.17 millert 252:
1.34 millert 253: #ifdef __GNUC__ /* XXX: to shut up gcc warnings */
254: (void)&argc;
255: (void)&argv;
256: #endif
257:
1.17 millert 258: if (argc > 0) {
259: if (strchr(argv[0], ':') != NULL) {
260: anonftp = 1; /* Handle "automatic" transfers. */
1.30 deraadt 261: rval = auto_fetch(argc, argv, outfd);
1.17 millert 262: if (rval >= 0) /* -1 == connected and cd-ed */
263: exit(rval);
264: } else {
1.3 deraadt 265: char *xargv[5];
266:
267: if (setjmp(toplevel))
268: exit(0);
1.20 millert 269: (void)signal(SIGINT, (sig_t)intr);
270: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 271: xargv[0] = __progname;
1.17 millert 272: xargv[1] = argv[0];
273: xargv[2] = argv[1];
274: xargv[3] = argv[2];
275: xargv[4] = NULL;
1.18 millert 276: do {
277: setpeer(argc+1, xargv);
278: if (!retry_connect)
279: break;
280: if (!connected) {
281: macnum = 0;
1.30 deraadt 282: fputs("Retrying...\n", ttyout);
1.18 millert 283: sleep(retry_connect);
284: }
285: } while (!connected);
1.35 mickey 286: retry_connect = 0; /* connected, stop hiding msgs */
1.3 deraadt 287: }
1.1 deraadt 288: }
1.28 millert 289: #ifndef SMALL
290: controlediting();
291: #endif /* !SMALL */
1.1 deraadt 292: top = setjmp(toplevel) == 0;
293: if (top) {
1.20 millert 294: (void)signal(SIGINT, (sig_t)intr);
295: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 296: }
297: for (;;) {
298: cmdscanner(top);
299: top = 1;
300: }
301: }
302:
303: void
304: intr()
305: {
306:
1.17 millert 307: alarmtimer(0);
1.1 deraadt 308: longjmp(toplevel, 1);
309: }
310:
311: void
312: lostpeer()
313: {
314:
1.17 millert 315: alarmtimer(0);
1.1 deraadt 316: if (connected) {
317: if (cout != NULL) {
1.18 millert 318: (void)shutdown(fileno(cout), 1+1);
319: (void)fclose(cout);
1.1 deraadt 320: cout = NULL;
321: }
322: if (data >= 0) {
1.18 millert 323: (void)shutdown(data, 1+1);
324: (void)close(data);
1.1 deraadt 325: data = -1;
326: }
327: connected = 0;
328: }
329: pswitch(1);
330: if (connected) {
331: if (cout != NULL) {
1.18 millert 332: (void)shutdown(fileno(cout), 1+1);
333: (void)fclose(cout);
1.1 deraadt 334: cout = NULL;
335: }
336: connected = 0;
337: }
338: proxflag = 0;
339: pswitch(0);
340: }
341:
342: /*
1.17 millert 343: * Generate a prompt
344: */
1.1 deraadt 345: char *
1.17 millert 346: prompt()
1.1 deraadt 347: {
1.17 millert 348: return ("ftp> ");
1.1 deraadt 349: }
350:
351: /*
352: * Command parser.
353: */
354: void
355: cmdscanner(top)
356: int top;
357: {
358: struct cmd *c;
1.17 millert 359: int num;
1.1 deraadt 360:
1.17 millert 361: if (!top
1.25 millert 362: #ifndef SMALL
1.17 millert 363: && !editing
1.25 millert 364: #endif /* !SMALL */
1.17 millert 365: )
1.30 deraadt 366: (void)putc('\n', ttyout);
1.1 deraadt 367: for (;;) {
1.25 millert 368: #ifndef SMALL
1.17 millert 369: if (!editing) {
1.25 millert 370: #endif /* !SMALL */
1.17 millert 371: if (fromatty) {
1.30 deraadt 372: fputs(prompt(), ttyout);
373: (void)fflush(ttyout);
1.17 millert 374: }
375: if (fgets(line, sizeof(line), stdin) == NULL)
376: quit(0, 0);
377: num = strlen(line);
378: if (num == 0)
379: break;
380: if (line[--num] == '\n') {
381: if (num == 0)
382: break;
383: line[num] = '\0';
384: } else if (num == sizeof(line) - 2) {
1.30 deraadt 385: fputs("sorry, input line too long.\n", ttyout);
1.17 millert 386: while ((num = getchar()) != '\n' && num != EOF)
387: /* void */;
388: break;
389: } /* else it was a line without a newline */
1.25 millert 390: #ifndef SMALL
1.17 millert 391: } else {
392: const char *buf;
393: cursor_pos = NULL;
394:
395: if ((buf = el_gets(el, &num)) == NULL || num == 0)
396: quit(0, 0);
397: if (line[--num] == '\n') {
398: if (num == 0)
399: break;
400: } else if (num >= sizeof(line)) {
1.30 deraadt 401: fputs("sorry, input line too long.\n", ttyout);
1.1 deraadt 402: break;
1.17 millert 403: }
1.34 millert 404: memcpy(line, buf, (size_t)num);
1.17 millert 405: line[num] = '\0';
406: history(hist, H_ENTER, buf);
407: }
1.25 millert 408: #endif /* !SMALL */
1.17 millert 409:
1.1 deraadt 410: makeargv();
1.17 millert 411: if (margc == 0)
1.1 deraadt 412: continue;
413: c = getcmd(margv[0]);
414: if (c == (struct cmd *)-1) {
1.30 deraadt 415: fputs("?Ambiguous command.\n", ttyout);
1.1 deraadt 416: continue;
417: }
418: if (c == 0) {
1.25 millert 419: #ifndef SMALL
1.24 millert 420: /*
421: * Give editline(3) a shot at unknown commands.
422: * XXX - bogus commands with a colon in
423: * them will not elicit an error.
424: */
425: if (el_parse(el, margc, margv) != 0)
1.25 millert 426: #endif /* !SMALL */
1.30 deraadt 427: fputs("?Invalid command.\n", ttyout);
1.1 deraadt 428: continue;
429: }
430: if (c->c_conn && !connected) {
1.30 deraadt 431: fputs("Not connected.\n", ttyout);
1.1 deraadt 432: continue;
433: }
1.17 millert 434: confirmrest = 0;
1.1 deraadt 435: (*c->c_handler)(margc, margv);
436: if (bell && c->c_bell)
1.30 deraadt 437: (void)putc('\007', ttyout);
1.1 deraadt 438: if (c->c_handler != help)
439: break;
440: }
1.20 millert 441: (void)signal(SIGINT, (sig_t)intr);
442: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 443: }
444:
445: struct cmd *
446: getcmd(name)
1.17 millert 447: const char *name;
1.1 deraadt 448: {
1.17 millert 449: const char *p, *q;
1.1 deraadt 450: struct cmd *c, *found;
451: int nmatches, longest;
1.2 deraadt 452:
453: if (name == NULL)
454: return (0);
1.1 deraadt 455:
456: longest = 0;
457: nmatches = 0;
458: found = 0;
1.17 millert 459: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 460: for (q = name; *q == *p++; q++)
461: if (*q == 0) /* exact match? */
462: return (c);
463: if (!*q) { /* the name was a prefix */
464: if (q - name > longest) {
465: longest = q - name;
466: nmatches = 1;
467: found = c;
468: } else if (q - name == longest)
469: nmatches++;
470: }
471: }
472: if (nmatches > 1)
473: return ((struct cmd *)-1);
474: return (found);
475: }
476:
477: /*
478: * Slice a string up into argc/argv.
479: */
480:
481: int slrflag;
482:
483: void
484: makeargv()
485: {
1.17 millert 486: char *argp;
1.1 deraadt 487:
488: stringbase = line; /* scan from first of buffer */
489: argbase = argbuf; /* store from first of buffer */
490: slrflag = 0;
1.17 millert 491: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 492: for (margc = 0; ; margc++) {
1.17 millert 493: argp = slurpstring();
494: sl_add(marg_sl, argp);
495: if (argp == NULL)
1.1 deraadt 496: break;
497: }
1.25 millert 498: #ifndef SMALL
1.17 millert 499: if (cursor_pos == line) {
500: cursor_argc = 0;
501: cursor_argo = 0;
502: } else if (cursor_pos != NULL) {
503: cursor_argc = margc;
504: cursor_argo = strlen(margv[margc-1]);
505: }
1.25 millert 506: #endif /* !SMALL */
1.17 millert 507: }
1.1 deraadt 508:
1.25 millert 509: #ifdef SMALL
1.17 millert 510: #define INC_CHKCURSOR(x) (x)++
1.25 millert 511: #else /* !SMALL */
1.17 millert 512: #define INC_CHKCURSOR(x) { (x)++ ; \
513: if (x == cursor_pos) { \
514: cursor_argc = margc; \
515: cursor_argo = ap-argbase; \
516: cursor_pos = NULL; \
517: } }
518:
1.25 millert 519: #endif /* !SMALL */
1.1 deraadt 520:
521: /*
522: * Parse string into argbuf;
523: * implemented with FSM to
524: * handle quoting and strings
525: */
526: char *
527: slurpstring()
528: {
529: int got_one = 0;
530: char *sb = stringbase;
531: char *ap = argbase;
532: char *tmp = argbase; /* will return this if token found */
533:
534: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
535: switch (slrflag) { /* and $ as token for macro invoke */
536: case 0:
537: slrflag++;
1.17 millert 538: INC_CHKCURSOR(stringbase);
1.1 deraadt 539: return ((*sb == '!') ? "!" : "$");
540: /* NOTREACHED */
541: case 1:
542: slrflag++;
543: altarg = stringbase;
544: break;
545: default:
546: break;
547: }
548: }
549:
550: S0:
551: switch (*sb) {
552:
553: case '\0':
554: goto OUT;
555:
556: case ' ':
557: case '\t':
1.17 millert 558: INC_CHKCURSOR(sb);
559: goto S0;
1.1 deraadt 560:
561: default:
562: switch (slrflag) {
563: case 0:
564: slrflag++;
565: break;
566: case 1:
567: slrflag++;
568: altarg = sb;
569: break;
570: default:
571: break;
572: }
573: goto S1;
574: }
575:
576: S1:
577: switch (*sb) {
578:
579: case ' ':
580: case '\t':
581: case '\0':
582: goto OUT; /* end of token */
583:
584: case '\\':
1.17 millert 585: INC_CHKCURSOR(sb);
586: goto S2; /* slurp next character */
1.1 deraadt 587:
588: case '"':
1.17 millert 589: INC_CHKCURSOR(sb);
590: goto S3; /* slurp quoted string */
1.1 deraadt 591:
592: default:
1.17 millert 593: *ap = *sb; /* add character to token */
594: ap++;
595: INC_CHKCURSOR(sb);
1.1 deraadt 596: got_one = 1;
597: goto S1;
598: }
599:
600: S2:
601: switch (*sb) {
602:
603: case '\0':
604: goto OUT;
605:
606: default:
1.17 millert 607: *ap = *sb;
608: ap++;
609: INC_CHKCURSOR(sb);
1.1 deraadt 610: got_one = 1;
611: goto S1;
612: }
613:
614: S3:
615: switch (*sb) {
616:
617: case '\0':
618: goto OUT;
619:
620: case '"':
1.17 millert 621: INC_CHKCURSOR(sb);
622: goto S1;
1.1 deraadt 623:
624: default:
1.17 millert 625: *ap = *sb;
626: ap++;
627: INC_CHKCURSOR(sb);
1.1 deraadt 628: got_one = 1;
629: goto S3;
630: }
631:
632: OUT:
633: if (got_one)
634: *ap++ = '\0';
635: argbase = ap; /* update storage pointer */
636: stringbase = sb; /* update scan pointer */
637: if (got_one) {
638: return (tmp);
639: }
640: switch (slrflag) {
641: case 0:
642: slrflag++;
643: break;
644: case 1:
645: slrflag++;
646: altarg = (char *) 0;
647: break;
648: default:
649: break;
650: }
651: return ((char *)0);
652: }
653:
654: /*
655: * Help command.
656: * Call each command handler with argc == 0 and argv[0] == name.
657: */
658: void
659: help(argc, argv)
660: int argc;
661: char *argv[];
662: {
663: struct cmd *c;
664:
665: if (argc == 1) {
1.17 millert 666: StringList *buf;
1.1 deraadt 667:
1.17 millert 668: buf = sl_init();
1.30 deraadt 669: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1.17 millert 670: proxy ? "Proxy c" : "C");
671: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
672: if (c->c_name && (!proxy || c->c_proxy))
673: sl_add(buf, c->c_name);
674: list_vertical(buf);
675: sl_free(buf, 0);
1.1 deraadt 676: return;
677: }
1.17 millert 678:
1.18 millert 679: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 680:
1.1 deraadt 681: while (--argc > 0) {
682: char *arg;
1.17 millert 683:
1.1 deraadt 684: arg = *++argv;
685: c = getcmd(arg);
686: if (c == (struct cmd *)-1)
1.30 deraadt 687: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1.1 deraadt 688: else if (c == (struct cmd *)0)
1.30 deraadt 689: fprintf(ttyout, "?Invalid help command %s\n", arg);
1.1 deraadt 690: else
1.30 deraadt 691: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1.1 deraadt 692: c->c_name, c->c_help);
693: }
1.17 millert 694: }
695:
696: void
697: usage()
698: {
699: (void)fprintf(stderr,
1.35 mickey 700: "usage: %s [-adeginptvV] [-r <seconds>] [host [port]]\n"
1.17 millert 701: " %s host:path[/]\n"
702: " %s ftp://host[:port]/path[/]\n"
703: " %s http://host[:port]/file\n",
704: __progname, __progname, __progname, __progname);
705: exit(1);
1.1 deraadt 706: }