xref: /original-bsd/usr.bin/ftp/main.c (revision 4bbb06ea)
1662bbc18Sdist /*
2679d30f6Spendry  * Copyright (c) 1985, 1989, 1993, 1994
3306c4fb0Sbostic  *	The Regents of the University of California.  All rights reserved.
4c2a61bcaSbostic  *
50cd120c3Sbostic  * %sccs.include.redist.c%
6662bbc18Sdist  */
7662bbc18Sdist 
81b61d977Ssam #ifndef lint
9306c4fb0Sbostic static char copyright[] =
10679d30f6Spendry "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
11306c4fb0Sbostic 	The Regents of the University of California.  All rights reserved.\n";
12c2a61bcaSbostic #endif /* not lint */
13662bbc18Sdist 
14662bbc18Sdist #ifndef lint
15*4bbb06eaSmckusick static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/09/94";
16c2a61bcaSbostic #endif /* not lint */
171b61d977Ssam 
181b61d977Ssam /*
191b61d977Ssam  * FTP User Program -- Command Interface.
201b61d977Ssam  */
212bb83062Spendry /*#include <sys/ioctl.h>*/
22a4f214cfSminshall #include <sys/types.h>
232bb83062Spendry #include <sys/socket.h>
241b61d977Ssam 
2501aaa315Ssam #include <arpa/ftp.h>
2601aaa315Ssam 
271b61d977Ssam #include <ctype.h>
282bb83062Spendry #include <err.h>
291b61d977Ssam #include <netdb.h>
308a4eeb38Ssam #include <pwd.h>
312bb83062Spendry #include <signal.h>
322bb83062Spendry #include <stdio.h>
332bb83062Spendry #include <stdlib.h>
342bb83062Spendry #include <unistd.h>
351b61d977Ssam 
362bb83062Spendry #include "ftp_var.h"
371b61d977Ssam 
382bb83062Spendry int
main(argc,argv)391b61d977Ssam main(argc, argv)
402bb83062Spendry 	int argc;
411b61d977Ssam 	char *argv[];
421b61d977Ssam {
432bb83062Spendry 	int ch, top;
4469c0d6f7Smckusick 	struct passwd *pw = NULL;
452bb83062Spendry 	char *cp, homedir[MAXPATHLEN];
461b61d977Ssam 
471b61d977Ssam 	sp = getservbyname("ftp", "tcp");
482bb83062Spendry 	if (sp == 0)
492bb83062Spendry 		errx(1, "ftp/tcp: unknown service");
506f659679Ssam 	doglob = 1;
518bb385cbSsam 	interactive = 1;
526f659679Ssam 	autologin = 1;
531b61d977Ssam 
542bb83062Spendry 	while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
559b2ce23aSbostic 		switch (ch) {
561b61d977Ssam 		case 'd':
571b61d977Ssam 			options |= SO_DEBUG;
581b61d977Ssam 			debug++;
591b61d977Ssam 			break;
601b61d977Ssam 
612bb83062Spendry 		case 'g':
622bb83062Spendry 			doglob = 0;
631b61d977Ssam 			break;
641b61d977Ssam 
651b61d977Ssam 		case 'i':
668bb385cbSsam 			interactive = 0;
671b61d977Ssam 			break;
681b61d977Ssam 
691b61d977Ssam 		case 'n':
701b61d977Ssam 			autologin = 0;
711b61d977Ssam 			break;
721b61d977Ssam 
732bb83062Spendry 		case 't':
742bb83062Spendry 			trace++;
752bb83062Spendry 			break;
762bb83062Spendry 
772bb83062Spendry 		case 'v':
782bb83062Spendry 			verbose++;
796f659679Ssam 			break;
806f659679Ssam 
811b61d977Ssam 		default:
822bb83062Spendry 			(void)fprintf(stderr,
832bb83062Spendry 				"usage: ftp [-dgintv] [host [port]]\n");
841b61d977Ssam 			exit(1);
851b61d977Ssam 		}
861b61d977Ssam 	}
872bb83062Spendry 	argc -= optind;
882bb83062Spendry 	argv += optind;
892bb83062Spendry 
901b61d977Ssam 	fromatty = isatty(fileno(stdin));
911b61d977Ssam 	if (fromatty)
921b61d977Ssam 		verbose++;
934f2e7c0aSminshall 	cpend = 0;	/* no pending replies */
944f2e7c0aSminshall 	proxy = 0;	/* proxy not active */
95*4bbb06eaSmckusick 	passivemode = 0; /* passive mode not active */
964f2e7c0aSminshall 	crflag = 1;	/* strip c.r. on ascii gets */
979d94ba29Srick 	sendport = -1;	/* not using ports */
988a4eeb38Ssam 	/*
998a4eeb38Ssam 	 * Set up the home directory in case we're globbing.
1008a4eeb38Ssam 	 */
10169c0d6f7Smckusick 	cp = getlogin();
1024f2e7c0aSminshall 	if (cp != NULL) {
10369c0d6f7Smckusick 		pw = getpwnam(cp);
1044f2e7c0aSminshall 	}
1058a4eeb38Ssam 	if (pw == NULL)
1068a4eeb38Ssam 		pw = getpwuid(getuid());
1078a4eeb38Ssam 	if (pw != NULL) {
1088a4eeb38Ssam 		home = homedir;
109a4f214cfSminshall 		(void) strcpy(home, pw->pw_dir);
1108a4eeb38Ssam 	}
1111b61d977Ssam 	if (argc > 0) {
112e8175c4fSpendry 		char *xargv[5];
1132bb83062Spendry 		extern char *__progname;
1142bb83062Spendry 
1151b61d977Ssam 		if (setjmp(toplevel))
1161b61d977Ssam 			exit(0);
117a4f214cfSminshall 		(void) signal(SIGINT, intr);
118a4f214cfSminshall 		(void) signal(SIGPIPE, lostpeer);
1192bb83062Spendry 		xargv[0] = __progname;
1202bb83062Spendry 		xargv[1] = argv[0];
1212bb83062Spendry 		xargv[2] = argv[1];
1222bb83062Spendry 		xargv[3] = argv[2];
123e8175c4fSpendry 		xargv[4] = NULL;
1242bb83062Spendry 		setpeer(argc+1, xargv);
1251b61d977Ssam 	}
1261b61d977Ssam 	top = setjmp(toplevel) == 0;
1271b61d977Ssam 	if (top) {
128a4f214cfSminshall 		(void) signal(SIGINT, intr);
129a4f214cfSminshall 		(void) signal(SIGPIPE, lostpeer);
1301b61d977Ssam 	}
1311b61d977Ssam 	for (;;) {
1321b61d977Ssam 		cmdscanner(top);
1331b61d977Ssam 		top = 1;
1341b61d977Ssam 	}
1351b61d977Ssam }
1361b61d977Ssam 
137dd8ce21eSbostic void
intr()1381b61d977Ssam intr()
1391b61d977Ssam {
1401b61d977Ssam 
1411b61d977Ssam 	longjmp(toplevel, 1);
1421b61d977Ssam }
1431b61d977Ssam 
144dd8ce21eSbostic void
lostpeer()1451b61d977Ssam lostpeer()
1461b61d977Ssam {
1471b61d977Ssam 
1481b61d977Ssam 	if (connected) {
1491b61d977Ssam 		if (cout != NULL) {
150a4f214cfSminshall 			(void) shutdown(fileno(cout), 1+1);
151a4f214cfSminshall 			(void) fclose(cout);
1521b61d977Ssam 			cout = NULL;
1531b61d977Ssam 		}
1541b61d977Ssam 		if (data >= 0) {
155a4f214cfSminshall 			(void) shutdown(data, 1+1);
1561b61d977Ssam 			(void) close(data);
1571b61d977Ssam 			data = -1;
1581b61d977Ssam 		}
1591b61d977Ssam 		connected = 0;
1601b61d977Ssam 	}
1614f2e7c0aSminshall 	pswitch(1);
1624f2e7c0aSminshall 	if (connected) {
1634f2e7c0aSminshall 		if (cout != NULL) {
164a4f214cfSminshall 			(void) shutdown(fileno(cout), 1+1);
165a4f214cfSminshall 			(void) fclose(cout);
1664f2e7c0aSminshall 			cout = NULL;
1674f2e7c0aSminshall 		}
1684f2e7c0aSminshall 		connected = 0;
1694f2e7c0aSminshall 	}
1704f2e7c0aSminshall 	proxflag = 0;
1714f2e7c0aSminshall 	pswitch(0);
1721b61d977Ssam }
1731b61d977Ssam 
1742bb83062Spendry /*
1752bb83062Spendry char *
1761b61d977Ssam tail(filename)
1771b61d977Ssam 	char *filename;
1781b61d977Ssam {
1792bb83062Spendry 	char *s;
1801b61d977Ssam 
1811b61d977Ssam 	while (*filename) {
1822bb83062Spendry 		s = strrchr(filename, '/');
1831b61d977Ssam 		if (s == NULL)
1841b61d977Ssam 			break;
1851b61d977Ssam 		if (s[1])
1861b61d977Ssam 			return (s + 1);
1871b61d977Ssam 		*s = '\0';
1881b61d977Ssam 	}
1891b61d977Ssam 	return (filename);
1901b61d977Ssam }
191a4f214cfSminshall */
1922bb83062Spendry 
1931b61d977Ssam /*
1941b61d977Ssam  * Command parser.
1951b61d977Ssam  */
1962bb83062Spendry void
cmdscanner(top)1971b61d977Ssam cmdscanner(top)
1981b61d977Ssam 	int top;
1991b61d977Ssam {
2002bb83062Spendry 	struct cmd *c;
2012bb83062Spendry 	int l;
2021b61d977Ssam 
2031b61d977Ssam 	if (!top)
204a4f214cfSminshall 		(void) putchar('\n');
2051b61d977Ssam 	for (;;) {
2061b61d977Ssam 		if (fromatty) {
2071b61d977Ssam 			printf("ftp> ");
208a4f214cfSminshall 			(void) fflush(stdout);
2091b61d977Ssam 		}
21066be170bSbostic 		if (fgets(line, sizeof line, stdin) == NULL)
2112bb83062Spendry 			quit(0, 0);
21266be170bSbostic 		l = strlen(line);
21366be170bSbostic 		if (l == 0)
2141b61d977Ssam 			break;
21566be170bSbostic 		if (line[--l] == '\n') {
21666be170bSbostic 			if (l == 0)
2171b61d977Ssam 				break;
21866be170bSbostic 			line[l] = '\0';
21966be170bSbostic 		} else if (l == sizeof(line) - 2) {
22066be170bSbostic 			printf("sorry, input line too long\n");
22166be170bSbostic 			while ((l = getchar()) != '\n' && l != EOF)
22266be170bSbostic 				/* void */;
22366be170bSbostic 			break;
22466be170bSbostic 		} /* else it was a line without a newline */
2251b61d977Ssam 		makeargv();
2264f2e7c0aSminshall 		if (margc == 0) {
22769c0d6f7Smckusick 			continue;
2284f2e7c0aSminshall 		}
2291b61d977Ssam 		c = getcmd(margv[0]);
2301b61d977Ssam 		if (c == (struct cmd *)-1) {
2311b61d977Ssam 			printf("?Ambiguous command\n");
2321b61d977Ssam 			continue;
2331b61d977Ssam 		}
2341b61d977Ssam 		if (c == 0) {
2351b61d977Ssam 			printf("?Invalid command\n");
2361b61d977Ssam 			continue;
2371b61d977Ssam 		}
2388bb385cbSsam 		if (c->c_conn && !connected) {
2398bb385cbSsam 			printf("Not connected.\n");
2408bb385cbSsam 			continue;
2418bb385cbSsam 		}
2421b61d977Ssam 		(*c->c_handler)(margc, margv);
2431b61d977Ssam 		if (bell && c->c_bell)
244e3d8fb40Skarels 			(void) putchar('\007');
2451b61d977Ssam 		if (c->c_handler != help)
2461b61d977Ssam 			break;
2471b61d977Ssam 	}
248a4f214cfSminshall 	(void) signal(SIGINT, intr);
249a4f214cfSminshall 	(void) signal(SIGPIPE, lostpeer);
2501b61d977Ssam }
2511b61d977Ssam 
2521b61d977Ssam struct cmd *
getcmd(name)2531b61d977Ssam getcmd(name)
2542bb83062Spendry 	char *name;
2551b61d977Ssam {
2562bb83062Spendry 	char *p, *q;
2572bb83062Spendry 	struct cmd *c, *found;
2582bb83062Spendry 	int nmatches, longest;
2591b61d977Ssam 
2601b61d977Ssam 	longest = 0;
2611b61d977Ssam 	nmatches = 0;
2621b61d977Ssam 	found = 0;
2631b61d977Ssam 	for (c = cmdtab; p = c->c_name; c++) {
2641b61d977Ssam 		for (q = name; *q == *p++; q++)
2651b61d977Ssam 			if (*q == 0)		/* exact match? */
2661b61d977Ssam 				return (c);
2671b61d977Ssam 		if (!*q) {			/* the name was a prefix */
2681b61d977Ssam 			if (q - name > longest) {
2691b61d977Ssam 				longest = q - name;
2701b61d977Ssam 				nmatches = 1;
2711b61d977Ssam 				found = c;
2721b61d977Ssam 			} else if (q - name == longest)
2731b61d977Ssam 				nmatches++;
2741b61d977Ssam 		}
2751b61d977Ssam 	}
2761b61d977Ssam 	if (nmatches > 1)
2771b61d977Ssam 		return ((struct cmd *)-1);
2781b61d977Ssam 	return (found);
2791b61d977Ssam }
2801b61d977Ssam 
2811b61d977Ssam /*
2821b61d977Ssam  * Slice a string up into argc/argv.
2831b61d977Ssam  */
2844f2e7c0aSminshall 
2854f2e7c0aSminshall int slrflag;
2864f2e7c0aSminshall 
2872bb83062Spendry void
makeargv()2881b61d977Ssam makeargv()
2891b61d977Ssam {
2901b61d977Ssam 	char **argp;
2911b61d977Ssam 
2921b61d977Ssam 	margc = 0;
2931b61d977Ssam 	argp = margv;
2941b61d977Ssam 	stringbase = line;		/* scan from first of buffer */
2951b61d977Ssam 	argbase = argbuf;		/* store from first of buffer */
2964f2e7c0aSminshall 	slrflag = 0;
2971b61d977Ssam 	while (*argp++ = slurpstring())
2981b61d977Ssam 		margc++;
2991b61d977Ssam }
3001b61d977Ssam 
3011b61d977Ssam /*
3021b61d977Ssam  * Parse string into argbuf;
3031b61d977Ssam  * implemented with FSM to
3041b61d977Ssam  * handle quoting and strings
3051b61d977Ssam  */
3061b61d977Ssam char *
slurpstring()3071b61d977Ssam slurpstring()
3081b61d977Ssam {
3091b61d977Ssam 	int got_one = 0;
3102bb83062Spendry 	char *sb = stringbase;
3112bb83062Spendry 	char *ap = argbase;
3121b61d977Ssam 	char *tmp = argbase;		/* will return this if token found */
3131b61d977Ssam 
3144f2e7c0aSminshall 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
3154f2e7c0aSminshall 		switch (slrflag) {	/* and $ as token for macro invoke */
3164f2e7c0aSminshall 			case 0:
3174f2e7c0aSminshall 				slrflag++;
3184f2e7c0aSminshall 				stringbase++;
3194f2e7c0aSminshall 				return ((*sb == '!') ? "!" : "$");
320e3d8fb40Skarels 				/* NOTREACHED */
3214f2e7c0aSminshall 			case 1:
3224f2e7c0aSminshall 				slrflag++;
3234f2e7c0aSminshall 				altarg = stringbase;
3244f2e7c0aSminshall 				break;
3254f2e7c0aSminshall 			default:
3264f2e7c0aSminshall 				break;
3274f2e7c0aSminshall 		}
3284f2e7c0aSminshall 	}
3294f2e7c0aSminshall 
3301b61d977Ssam S0:
3311b61d977Ssam 	switch (*sb) {
3321b61d977Ssam 
3331b61d977Ssam 	case '\0':
3341b61d977Ssam 		goto OUT;
3351b61d977Ssam 
3361b61d977Ssam 	case ' ':
3371b61d977Ssam 	case '\t':
3381b61d977Ssam 		sb++; goto S0;
3391b61d977Ssam 
3401b61d977Ssam 	default:
3414f2e7c0aSminshall 		switch (slrflag) {
3424f2e7c0aSminshall 			case 0:
3434f2e7c0aSminshall 				slrflag++;
3444f2e7c0aSminshall 				break;
3454f2e7c0aSminshall 			case 1:
3464f2e7c0aSminshall 				slrflag++;
3474f2e7c0aSminshall 				altarg = sb;
3484f2e7c0aSminshall 				break;
3494f2e7c0aSminshall 			default:
3504f2e7c0aSminshall 				break;
3514f2e7c0aSminshall 		}
3521b61d977Ssam 		goto S1;
3531b61d977Ssam 	}
3541b61d977Ssam 
3551b61d977Ssam S1:
3561b61d977Ssam 	switch (*sb) {
3571b61d977Ssam 
3581b61d977Ssam 	case ' ':
3591b61d977Ssam 	case '\t':
3601b61d977Ssam 	case '\0':
3611b61d977Ssam 		goto OUT;	/* end of token */
3621b61d977Ssam 
3631b61d977Ssam 	case '\\':
3641b61d977Ssam 		sb++; goto S2;	/* slurp next character */
3651b61d977Ssam 
3661b61d977Ssam 	case '"':
3671b61d977Ssam 		sb++; goto S3;	/* slurp quoted string */
3681b61d977Ssam 
3691b61d977Ssam 	default:
3701b61d977Ssam 		*ap++ = *sb++;	/* add character to token */
3711b61d977Ssam 		got_one = 1;
3721b61d977Ssam 		goto S1;
3731b61d977Ssam 	}
3741b61d977Ssam 
3751b61d977Ssam S2:
3761b61d977Ssam 	switch (*sb) {
3771b61d977Ssam 
3781b61d977Ssam 	case '\0':
3791b61d977Ssam 		goto OUT;
3801b61d977Ssam 
3811b61d977Ssam 	default:
3821b61d977Ssam 		*ap++ = *sb++;
3831b61d977Ssam 		got_one = 1;
3841b61d977Ssam 		goto S1;
3851b61d977Ssam 	}
3861b61d977Ssam 
3871b61d977Ssam S3:
3881b61d977Ssam 	switch (*sb) {
3891b61d977Ssam 
3901b61d977Ssam 	case '\0':
3911b61d977Ssam 		goto OUT;
3921b61d977Ssam 
3931b61d977Ssam 	case '"':
3941b61d977Ssam 		sb++; goto S1;
3951b61d977Ssam 
3961b61d977Ssam 	default:
3971b61d977Ssam 		*ap++ = *sb++;
3981b61d977Ssam 		got_one = 1;
3991b61d977Ssam 		goto S3;
4001b61d977Ssam 	}
4011b61d977Ssam 
4021b61d977Ssam OUT:
4031b61d977Ssam 	if (got_one)
4041b61d977Ssam 		*ap++ = '\0';
4051b61d977Ssam 	argbase = ap;			/* update storage pointer */
4061b61d977Ssam 	stringbase = sb;		/* update scan pointer */
4074f2e7c0aSminshall 	if (got_one) {
4081b61d977Ssam 		return (tmp);
4094f2e7c0aSminshall 	}
4104f2e7c0aSminshall 	switch (slrflag) {
4114f2e7c0aSminshall 		case 0:
4124f2e7c0aSminshall 			slrflag++;
4134f2e7c0aSminshall 			break;
4144f2e7c0aSminshall 		case 1:
4154f2e7c0aSminshall 			slrflag++;
4164f2e7c0aSminshall 			altarg = (char *) 0;
4174f2e7c0aSminshall 			break;
4184f2e7c0aSminshall 		default:
4194f2e7c0aSminshall 			break;
4204f2e7c0aSminshall 	}
4211b61d977Ssam 	return ((char *)0);
4221b61d977Ssam }
4231b61d977Ssam 
4242bb83062Spendry #define HELPINDENT ((int) sizeof ("directory"))
4251b61d977Ssam 
4261b61d977Ssam /*
4271b61d977Ssam  * Help command.
4281b61d977Ssam  * Call each command handler with argc == 0 and argv[0] == name.
4291b61d977Ssam  */
4302bb83062Spendry void
help(argc,argv)4311b61d977Ssam help(argc, argv)
4321b61d977Ssam 	int argc;
4331b61d977Ssam 	char *argv[];
4341b61d977Ssam {
4352bb83062Spendry 	struct cmd *c;
4361b61d977Ssam 
4371b61d977Ssam 	if (argc == 1) {
4382bb83062Spendry 		int i, j, w, k;
4391b61d977Ssam 		int columns, width = 0, lines;
4401b61d977Ssam 
4411b61d977Ssam 		printf("Commands may be abbreviated.  Commands are:\n\n");
4421b61d977Ssam 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
4431b61d977Ssam 			int len = strlen(c->c_name);
4441b61d977Ssam 
4451b61d977Ssam 			if (len > width)
4461b61d977Ssam 				width = len;
4471b61d977Ssam 		}
4481b61d977Ssam 		width = (width + 8) &~ 7;
4491b61d977Ssam 		columns = 80 / width;
4501b61d977Ssam 		if (columns == 0)
4511b61d977Ssam 			columns = 1;
4521b61d977Ssam 		lines = (NCMDS + columns - 1) / columns;
4531b61d977Ssam 		for (i = 0; i < lines; i++) {
4541b61d977Ssam 			for (j = 0; j < columns; j++) {
4551b61d977Ssam 				c = cmdtab + j * lines + i;
4564f2e7c0aSminshall 				if (c->c_name && (!proxy || c->c_proxy)) {
4571b61d977Ssam 					printf("%s", c->c_name);
4584f2e7c0aSminshall 				}
4594f2e7c0aSminshall 				else if (c->c_name) {
4604f2e7c0aSminshall 					for (k=0; k < strlen(c->c_name); k++) {
461a4f214cfSminshall 						(void) putchar(' ');
4624f2e7c0aSminshall 					}
4634f2e7c0aSminshall 				}
4641b61d977Ssam 				if (c + lines >= &cmdtab[NCMDS]) {
4651b61d977Ssam 					printf("\n");
4661b61d977Ssam 					break;
4671b61d977Ssam 				}
4681b61d977Ssam 				w = strlen(c->c_name);
4691b61d977Ssam 				while (w < width) {
4701b61d977Ssam 					w = (w + 8) &~ 7;
471a4f214cfSminshall 					(void) putchar('\t');
4721b61d977Ssam 				}
4731b61d977Ssam 			}
4741b61d977Ssam 		}
4751b61d977Ssam 		return;
4761b61d977Ssam 	}
4771b61d977Ssam 	while (--argc > 0) {
4782bb83062Spendry 		char *arg;
4791b61d977Ssam 		arg = *++argv;
4801b61d977Ssam 		c = getcmd(arg);
4811b61d977Ssam 		if (c == (struct cmd *)-1)
4821b61d977Ssam 			printf("?Ambiguous help command %s\n", arg);
4831b61d977Ssam 		else if (c == (struct cmd *)0)
4841b61d977Ssam 			printf("?Invalid help command %s\n", arg);
4851b61d977Ssam 		else
4861b61d977Ssam 			printf("%-*s\t%s\n", HELPINDENT,
4871b61d977Ssam 				c->c_name, c->c_help);
4881b61d977Ssam 	}
4891b61d977Ssam }
490