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