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