Annotation of src/usr.bin/ftp/main.c, Revision 1.30
1.30 ! deraadt 1: /* $OpenBSD: main.c,v 1.29 1997/04/11 23:14:04 millert Exp $ */
1.28 millert 2: /* $NetBSD: main.c,v 1.21 1997/04/05 03:27:39 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.30 ! deraadt 47: static char rcsid[] = "$OpenBSD: main.c,v 1.29 1997/04/11 23:14:04 millert 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:
68: int
69: main(argc, argv)
70: int argc;
71: char *argv[];
72: {
1.17 millert 73: struct servent *sp;
74: int ch, top, port, rval;
1.1 deraadt 75: struct passwd *pw = NULL;
76: char *cp, homedir[MAXPATHLEN];
1.28 millert 77: int dumb_terminal = 0;
1.30 ! deraadt 78: int outfd = -1;
1.1 deraadt 79:
80: sp = getservbyname("ftp", "tcp");
81: if (sp == 0)
1.17 millert 82: ftpport = htons(FTP_PORT); /* good fallback */
83: else
84: ftpport = sp->s_port;
85: sp = getservbyname("http", "tcp");
86: if (sp == 0)
87: httpport = htons(HTTP_PORT); /* good fallback */
88: else
89: httpport = sp->s_port;
1.1 deraadt 90: doglob = 1;
91: interactive = 1;
92: autologin = 1;
1.17 millert 93: passivemode = 0;
94: preserve = 1;
95: verbose = 0;
96: progress = 0;
1.25 millert 97: #ifndef SMALL
1.22 millert 98: editing = 0;
1.28 millert 99: el = NULL;
100: hist = NULL;
1.22 millert 101: #endif
1.8 kstailey 102: mark = HASHBYTES;
1.17 millert 103: marg_sl = sl_init();
1.1 deraadt 104:
1.17 millert 105: cp = strrchr(argv[0], '/');
106: cp = (cp == NULL) ? argv[0] : cp + 1;
107: if (strcmp(cp, "pftp") == 0)
108: passivemode = 1;
109:
1.29 millert 110: dumb_terminal = (!strcmp(getenv("TERM"), "dumb") ||
111: !strcmp(getenv("TERM"), "emacs") || !strcmp(getenv("TERM"), "su"));
1.17 millert 112: fromatty = isatty(fileno(stdin));
1.22 millert 113: if (fromatty) {
1.17 millert 114: verbose = 1; /* verbose if from a tty */
1.25 millert 115: #ifndef SMALL
1.28 millert 116: if (!dumb_terminal)
1.27 kstailey 117: editing = 1; /* editing mode on if from a tty */
1.22 millert 118: #endif
119: }
1.30 ! deraadt 120:
! 121: ttyout = stdout;
! 122: if (isatty(fileno(ttyout)) && !dumb_terminal)
1.22 millert 123: progress = 1; /* progress bar on if going to a tty */
1.30 ! deraadt 124: else {
! 125: ttyout = stderr;
! 126: outfd = fileno(stdout);
! 127: }
1.17 millert 128:
1.21 kstailey 129: while ((ch = getopt(argc, argv, "adeginpPr:tvV")) != -1) {
1.1 deraadt 130: switch (ch) {
1.17 millert 131: case 'a':
132: anonftp = 1;
133: break;
134:
1.1 deraadt 135: case 'd':
136: options |= SO_DEBUG;
137: debug++;
138: break;
1.17 millert 139:
1.28 millert 140: case 'e':
141: #ifndef SMALL
1.22 millert 142: editing = 0;
143: #endif
1.21 kstailey 144: break;
145:
1.1 deraadt 146: case 'g':
147: doglob = 0;
148: break;
149:
150: case 'i':
151: interactive = 0;
152: break;
153:
154: case 'n':
155: autologin = 0;
156: break;
157:
1.3 deraadt 158: case 'p':
1.17 millert 159: passivemode = 1;
1.3 deraadt 160: break;
161:
1.17 millert 162: case 'P':
163: port = atoi(optarg);
164: if (port <= 0)
1.18 millert 165: warnx("bad port number: %s (ignored)", optarg);
1.17 millert 166: else
167: ftpport = htons(port);
1.7 mickey 168: break;
169:
1.18 millert 170: case 'r':
171: if (isdigit(*optarg))
172: retry_connect = atoi(optarg);
173: else
174: errx(1, "-r requires numeric argument");
175: break;
176:
1.1 deraadt 177: case 't':
1.17 millert 178: trace = 1;
1.1 deraadt 179: break;
180:
181: case 'v':
1.17 millert 182: verbose = 1;
183: break;
184:
185: case 'V':
186: verbose = 0;
1.1 deraadt 187: break;
188:
189: default:
1.17 millert 190: usage();
1.1 deraadt 191: }
192: }
193: argc -= optind;
194: argv += optind;
195:
196: cpend = 0; /* no pending replies */
197: proxy = 0; /* proxy not active */
198: crflag = 1; /* strip c.r. on ascii gets */
199: sendport = -1; /* not using ports */
200: /*
201: * Set up the home directory in case we're globbing.
202: */
203: cp = getlogin();
204: if (cp != NULL) {
205: pw = getpwnam(cp);
206: }
207: if (pw == NULL)
208: pw = getpwuid(getuid());
209: if (pw != NULL) {
210: home = homedir;
1.18 millert 211: (void)strcpy(home, pw->pw_dir);
1.3 deraadt 212: }
1.9 michaels 213:
1.17 millert 214: setttywidth(0);
1.18 millert 215: (void)signal(SIGWINCH, setttywidth);
1.17 millert 216:
217: if (argc > 0) {
218: if (strchr(argv[0], ':') != NULL) {
219: anonftp = 1; /* Handle "automatic" transfers. */
1.30 ! deraadt 220: rval = auto_fetch(argc, argv, outfd);
1.17 millert 221: if (rval >= 0) /* -1 == connected and cd-ed */
222: exit(rval);
223: } else {
1.3 deraadt 224: char *xargv[5];
225:
226: if (setjmp(toplevel))
227: exit(0);
1.20 millert 228: (void)signal(SIGINT, (sig_t)intr);
229: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 230: xargv[0] = __progname;
1.17 millert 231: xargv[1] = argv[0];
232: xargv[2] = argv[1];
233: xargv[3] = argv[2];
234: xargv[4] = NULL;
1.18 millert 235: do {
236: setpeer(argc+1, xargv);
237: if (!retry_connect)
238: break;
239: if (!connected) {
240: macnum = 0;
1.30 ! deraadt 241: fputs("Retrying...\n", ttyout);
1.18 millert 242: sleep(retry_connect);
243: }
244: } while (!connected);
1.3 deraadt 245: }
1.1 deraadt 246: }
1.28 millert 247: #ifndef SMALL
248: controlediting();
249: #endif /* !SMALL */
1.1 deraadt 250: top = setjmp(toplevel) == 0;
251: if (top) {
1.20 millert 252: (void)signal(SIGINT, (sig_t)intr);
253: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 254: }
255: for (;;) {
256: cmdscanner(top);
257: top = 1;
258: }
259: }
260:
261: void
262: intr()
263: {
264:
1.17 millert 265: alarmtimer(0);
1.1 deraadt 266: longjmp(toplevel, 1);
267: }
268:
269: void
270: lostpeer()
271: {
272:
1.17 millert 273: alarmtimer(0);
1.1 deraadt 274: if (connected) {
275: if (cout != NULL) {
1.18 millert 276: (void)shutdown(fileno(cout), 1+1);
277: (void)fclose(cout);
1.1 deraadt 278: cout = NULL;
279: }
280: if (data >= 0) {
1.18 millert 281: (void)shutdown(data, 1+1);
282: (void)close(data);
1.1 deraadt 283: data = -1;
284: }
285: connected = 0;
286: }
287: pswitch(1);
288: if (connected) {
289: if (cout != NULL) {
1.18 millert 290: (void)shutdown(fileno(cout), 1+1);
291: (void)fclose(cout);
1.1 deraadt 292: cout = NULL;
293: }
294: connected = 0;
295: }
296: proxflag = 0;
297: pswitch(0);
298: }
299:
300: /*
1.17 millert 301: * Generate a prompt
302: */
1.1 deraadt 303: char *
1.17 millert 304: prompt()
1.1 deraadt 305: {
1.17 millert 306: return ("ftp> ");
1.1 deraadt 307: }
308:
309: /*
310: * Command parser.
311: */
312: void
313: cmdscanner(top)
314: int top;
315: {
316: struct cmd *c;
1.17 millert 317: int num;
1.1 deraadt 318:
1.17 millert 319: if (!top
1.25 millert 320: #ifndef SMALL
1.17 millert 321: && !editing
1.25 millert 322: #endif /* !SMALL */
1.17 millert 323: )
1.30 ! deraadt 324: (void)putc('\n', ttyout);
1.1 deraadt 325: for (;;) {
1.25 millert 326: #ifndef SMALL
1.17 millert 327: if (!editing) {
1.25 millert 328: #endif /* !SMALL */
1.17 millert 329: if (fromatty) {
1.30 ! deraadt 330: fputs(prompt(), ttyout);
! 331: (void)fflush(ttyout);
1.17 millert 332: }
333: if (fgets(line, sizeof(line), stdin) == NULL)
334: quit(0, 0);
335: num = strlen(line);
336: if (num == 0)
337: break;
338: if (line[--num] == '\n') {
339: if (num == 0)
340: break;
341: line[num] = '\0';
342: } else if (num == sizeof(line) - 2) {
1.30 ! deraadt 343: fputs("sorry, input line too long.\n", ttyout);
1.17 millert 344: while ((num = getchar()) != '\n' && num != EOF)
345: /* void */;
346: break;
347: } /* else it was a line without a newline */
1.25 millert 348: #ifndef SMALL
1.17 millert 349: } else {
350: const char *buf;
351: cursor_pos = NULL;
352:
353: if ((buf = el_gets(el, &num)) == NULL || num == 0)
354: quit(0, 0);
355: if (line[--num] == '\n') {
356: if (num == 0)
357: break;
358: } else if (num >= sizeof(line)) {
1.30 ! deraadt 359: fputs("sorry, input line too long.\n", ttyout);
1.1 deraadt 360: break;
1.17 millert 361: }
362: memcpy(line, buf, num);
363: line[num] = '\0';
364: history(hist, H_ENTER, buf);
365: }
1.25 millert 366: #endif /* !SMALL */
1.17 millert 367:
1.1 deraadt 368: makeargv();
1.17 millert 369: if (margc == 0)
1.1 deraadt 370: continue;
371: c = getcmd(margv[0]);
372: if (c == (struct cmd *)-1) {
1.30 ! deraadt 373: fputs("?Ambiguous command.\n", ttyout);
1.1 deraadt 374: continue;
375: }
376: if (c == 0) {
1.25 millert 377: #ifndef SMALL
1.24 millert 378: /*
379: * Give editline(3) a shot at unknown commands.
380: * XXX - bogus commands with a colon in
381: * them will not elicit an error.
382: */
383: if (el_parse(el, margc, margv) != 0)
1.25 millert 384: #endif /* !SMALL */
1.30 ! deraadt 385: fputs("?Invalid command.\n", ttyout);
1.1 deraadt 386: continue;
387: }
388: if (c->c_conn && !connected) {
1.30 ! deraadt 389: fputs("Not connected.\n", ttyout);
1.1 deraadt 390: continue;
391: }
1.17 millert 392: confirmrest = 0;
1.1 deraadt 393: (*c->c_handler)(margc, margv);
394: if (bell && c->c_bell)
1.30 ! deraadt 395: (void)putc('\007', ttyout);
1.1 deraadt 396: if (c->c_handler != help)
397: break;
398: }
1.20 millert 399: (void)signal(SIGINT, (sig_t)intr);
400: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 401: }
402:
403: struct cmd *
404: getcmd(name)
1.17 millert 405: const char *name;
1.1 deraadt 406: {
1.17 millert 407: const char *p, *q;
1.1 deraadt 408: struct cmd *c, *found;
409: int nmatches, longest;
1.2 deraadt 410:
411: if (name == NULL)
412: return (0);
1.1 deraadt 413:
414: longest = 0;
415: nmatches = 0;
416: found = 0;
1.17 millert 417: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 418: for (q = name; *q == *p++; q++)
419: if (*q == 0) /* exact match? */
420: return (c);
421: if (!*q) { /* the name was a prefix */
422: if (q - name > longest) {
423: longest = q - name;
424: nmatches = 1;
425: found = c;
426: } else if (q - name == longest)
427: nmatches++;
428: }
429: }
430: if (nmatches > 1)
431: return ((struct cmd *)-1);
432: return (found);
433: }
434:
435: /*
436: * Slice a string up into argc/argv.
437: */
438:
439: int slrflag;
440:
441: void
442: makeargv()
443: {
1.17 millert 444: char *argp;
1.1 deraadt 445:
446: stringbase = line; /* scan from first of buffer */
447: argbase = argbuf; /* store from first of buffer */
448: slrflag = 0;
1.17 millert 449: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 450: for (margc = 0; ; margc++) {
1.17 millert 451: argp = slurpstring();
452: sl_add(marg_sl, argp);
453: if (argp == NULL)
1.1 deraadt 454: break;
455: }
1.25 millert 456: #ifndef SMALL
1.17 millert 457: if (cursor_pos == line) {
458: cursor_argc = 0;
459: cursor_argo = 0;
460: } else if (cursor_pos != NULL) {
461: cursor_argc = margc;
462: cursor_argo = strlen(margv[margc-1]);
463: }
1.25 millert 464: #endif /* !SMALL */
1.17 millert 465: }
1.1 deraadt 466:
1.25 millert 467: #ifdef SMALL
1.17 millert 468: #define INC_CHKCURSOR(x) (x)++
1.25 millert 469: #else /* !SMALL */
1.17 millert 470: #define INC_CHKCURSOR(x) { (x)++ ; \
471: if (x == cursor_pos) { \
472: cursor_argc = margc; \
473: cursor_argo = ap-argbase; \
474: cursor_pos = NULL; \
475: } }
476:
1.25 millert 477: #endif /* !SMALL */
1.1 deraadt 478:
479: /*
480: * Parse string into argbuf;
481: * implemented with FSM to
482: * handle quoting and strings
483: */
484: char *
485: slurpstring()
486: {
487: int got_one = 0;
488: char *sb = stringbase;
489: char *ap = argbase;
490: char *tmp = argbase; /* will return this if token found */
491:
492: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
493: switch (slrflag) { /* and $ as token for macro invoke */
494: case 0:
495: slrflag++;
1.17 millert 496: INC_CHKCURSOR(stringbase);
1.1 deraadt 497: return ((*sb == '!') ? "!" : "$");
498: /* NOTREACHED */
499: case 1:
500: slrflag++;
501: altarg = stringbase;
502: break;
503: default:
504: break;
505: }
506: }
507:
508: S0:
509: switch (*sb) {
510:
511: case '\0':
512: goto OUT;
513:
514: case ' ':
515: case '\t':
1.17 millert 516: INC_CHKCURSOR(sb);
517: goto S0;
1.1 deraadt 518:
519: default:
520: switch (slrflag) {
521: case 0:
522: slrflag++;
523: break;
524: case 1:
525: slrflag++;
526: altarg = sb;
527: break;
528: default:
529: break;
530: }
531: goto S1;
532: }
533:
534: S1:
535: switch (*sb) {
536:
537: case ' ':
538: case '\t':
539: case '\0':
540: goto OUT; /* end of token */
541:
542: case '\\':
1.17 millert 543: INC_CHKCURSOR(sb);
544: goto S2; /* slurp next character */
1.1 deraadt 545:
546: case '"':
1.17 millert 547: INC_CHKCURSOR(sb);
548: goto S3; /* slurp quoted string */
1.1 deraadt 549:
550: default:
1.17 millert 551: *ap = *sb; /* add character to token */
552: ap++;
553: INC_CHKCURSOR(sb);
1.1 deraadt 554: got_one = 1;
555: goto S1;
556: }
557:
558: S2:
559: switch (*sb) {
560:
561: case '\0':
562: goto OUT;
563:
564: default:
1.17 millert 565: *ap = *sb;
566: ap++;
567: INC_CHKCURSOR(sb);
1.1 deraadt 568: got_one = 1;
569: goto S1;
570: }
571:
572: S3:
573: switch (*sb) {
574:
575: case '\0':
576: goto OUT;
577:
578: case '"':
1.17 millert 579: INC_CHKCURSOR(sb);
580: goto S1;
1.1 deraadt 581:
582: default:
1.17 millert 583: *ap = *sb;
584: ap++;
585: INC_CHKCURSOR(sb);
1.1 deraadt 586: got_one = 1;
587: goto S3;
588: }
589:
590: OUT:
591: if (got_one)
592: *ap++ = '\0';
593: argbase = ap; /* update storage pointer */
594: stringbase = sb; /* update scan pointer */
595: if (got_one) {
596: return (tmp);
597: }
598: switch (slrflag) {
599: case 0:
600: slrflag++;
601: break;
602: case 1:
603: slrflag++;
604: altarg = (char *) 0;
605: break;
606: default:
607: break;
608: }
609: return ((char *)0);
610: }
611:
612: /*
613: * Help command.
614: * Call each command handler with argc == 0 and argv[0] == name.
615: */
616: void
617: help(argc, argv)
618: int argc;
619: char *argv[];
620: {
621: struct cmd *c;
622:
623: if (argc == 1) {
1.17 millert 624: StringList *buf;
1.1 deraadt 625:
1.17 millert 626: buf = sl_init();
1.30 ! deraadt 627: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1.17 millert 628: proxy ? "Proxy c" : "C");
629: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
630: if (c->c_name && (!proxy || c->c_proxy))
631: sl_add(buf, c->c_name);
632: list_vertical(buf);
633: sl_free(buf, 0);
1.1 deraadt 634: return;
635: }
1.17 millert 636:
1.18 millert 637: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 638:
1.1 deraadt 639: while (--argc > 0) {
640: char *arg;
1.17 millert 641:
1.1 deraadt 642: arg = *++argv;
643: c = getcmd(arg);
644: if (c == (struct cmd *)-1)
1.30 ! deraadt 645: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1.1 deraadt 646: else if (c == (struct cmd *)0)
1.30 ! deraadt 647: fprintf(ttyout, "?Invalid help command %s\n", arg);
1.1 deraadt 648: else
1.30 ! deraadt 649: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1.1 deraadt 650: c->c_name, c->c_help);
651: }
1.17 millert 652: }
653:
654: void
655: usage()
656: {
657: (void)fprintf(stderr,
1.22 millert 658: "usage: %s [-adeginprtvV] [host [port]]\n"
1.17 millert 659: " %s host:path[/]\n"
660: " %s ftp://host[:port]/path[/]\n"
661: " %s http://host[:port]/file\n",
662: __progname, __progname, __progname, __progname);
663: exit(1);
1.1 deraadt 664: }