xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 22501)
110276Ssam /*
2*22501Sdist  * Copyright (c) 1980 Regents of the University of California.
3*22501Sdist  * All rights reserved.  The Berkeley software License Agreement
4*22501Sdist  * specifies the terms and conditions for redistribution.
5*22501Sdist  */
6*22501Sdist 
7*22501Sdist /*
810276Ssam  * Grammar for FTP commands.
910276Ssam  * See RFC 765.
1010276Ssam  */
1110276Ssam 
1210276Ssam %{
1310276Ssam 
1410276Ssam #ifndef lint
15*22501Sdist static	char sccsid[] = "@(#)ftpcmd.y	5.2 (Berkeley) 06/06/85";
1610276Ssam #endif
1710276Ssam 
1810276Ssam #include <sys/types.h>
1910276Ssam #include <sys/socket.h>
2010276Ssam 
2110276Ssam #include <netinet/in.h>
2210276Ssam 
2313033Ssam #include <arpa/ftp.h>
2413033Ssam 
2510276Ssam #include <stdio.h>
2611652Ssam #include <signal.h>
2710276Ssam #include <ctype.h>
2810276Ssam #include <pwd.h>
2910276Ssam #include <setjmp.h>
3010276Ssam 
3110276Ssam extern	struct sockaddr_in data_dest;
3210276Ssam extern	int logged_in;
3310276Ssam extern	struct passwd *pw;
3410276Ssam extern	int guest;
3510276Ssam extern	int logging;
3610276Ssam extern	int type;
3710276Ssam extern	int form;
3810276Ssam extern	int debug;
3911652Ssam extern	int timeout;
4010276Ssam extern	char hostname[];
4110276Ssam extern	char *globerr;
4210320Ssam extern	int usedefault;
4310276Ssam char	**glob();
4410276Ssam 
4510276Ssam static	int cmd_type;
4610276Ssam static	int cmd_form;
4710276Ssam static	int cmd_bytesz;
4810276Ssam 
4910276Ssam char	*index();
5010276Ssam %}
5110276Ssam 
5210276Ssam %token
5310276Ssam 	A	B	C	E	F	I
5410276Ssam 	L	N	P	R	S	T
5510276Ssam 
5610276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
5710276Ssam 
5810276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
5910276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
6010276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
6110276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
6210276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
6310276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
6410276Ssam 	XCUP
6510276Ssam 
6610276Ssam 	LEXERR
6710276Ssam 
6810276Ssam %start	cmd_list
6910276Ssam 
7010276Ssam %%
7110276Ssam 
7210276Ssam cmd_list:	/* empty */
7310276Ssam 	|	cmd_list cmd
7410276Ssam 	;
7510276Ssam 
7610276Ssam cmd:		USER SP username CRLF
7710276Ssam 		= {
7810276Ssam 			extern struct passwd *getpwnam();
7910276Ssam 
8010324Ssam 			if (strcmp($3, "ftp") == 0 ||
8110324Ssam 			  strcmp($3, "anonymous") == 0) {
8210324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
8310324Ssam 					guest = 1;
8410324Ssam 					reply(331,
8510276Ssam 				  "Guest login ok, send ident as password.");
8610324Ssam 				}
8710694Ssam 			} else if (checkuser($3)) {
8810276Ssam 				guest = 0;
8910276Ssam 				pw = getpwnam($3);
9010276Ssam 				reply(331, "Password required for %s.", $3);
9110276Ssam 			}
9210319Ssam 			if (pw == NULL)
9310319Ssam 				reply(530, "User %s unknown.", $3);
9410276Ssam 			free($3);
9510276Ssam 		}
9610276Ssam 	|	PASS SP password CRLF
9710276Ssam 		= {
9810276Ssam 			pass($3);
9910276Ssam 			free($3);
10010276Ssam 		}
10110276Ssam 	|	PORT SP host_port CRLF
10210276Ssam 		= {
10310320Ssam 			usedefault = 0;
10410276Ssam 			ack($1);
10510276Ssam 		}
10610276Ssam 	|	TYPE SP type_code CRLF
10710276Ssam 		= {
10810276Ssam 			switch (cmd_type) {
10910276Ssam 
11010276Ssam 			case TYPE_A:
11110276Ssam 				if (cmd_form == FORM_N) {
11210276Ssam 					reply(200, "Type set to A.");
11310276Ssam 					type = cmd_type;
11410276Ssam 					form = cmd_form;
11510276Ssam 				} else
11610276Ssam 					reply(504, "Form must be N.");
11710276Ssam 				break;
11810276Ssam 
11910276Ssam 			case TYPE_E:
12010276Ssam 				reply(504, "Type E not implemented.");
12110276Ssam 				break;
12210276Ssam 
12310276Ssam 			case TYPE_I:
12410276Ssam 				reply(200, "Type set to I.");
12510276Ssam 				type = cmd_type;
12610276Ssam 				break;
12710276Ssam 
12810276Ssam 			case TYPE_L:
12910276Ssam 				if (cmd_bytesz == 8) {
13010276Ssam 					reply(200,
13110276Ssam 					    "Type set to L (byte size 8).");
13210276Ssam 					type = cmd_type;
13310276Ssam 				} else
13410276Ssam 					reply(504, "Byte size must be 8.");
13510276Ssam 			}
13610276Ssam 		}
13710276Ssam 	|	STRU SP struct_code CRLF
13810276Ssam 		= {
13910276Ssam 			switch ($3) {
14010276Ssam 
14110276Ssam 			case STRU_F:
14210276Ssam 				reply(200, "STRU F ok.");
14310276Ssam 				break;
14410276Ssam 
14510276Ssam 			default:
14610276Ssam 				reply(502, "Unimplemented STRU type.");
14710276Ssam 			}
14810276Ssam 		}
14910276Ssam 	|	MODE SP mode_code CRLF
15010276Ssam 		= {
15110276Ssam 			switch ($3) {
15210276Ssam 
15310276Ssam 			case MODE_S:
15410276Ssam 				reply(200, "MODE S ok.");
15510276Ssam 				break;
15610276Ssam 
15710276Ssam 			default:
15810276Ssam 				reply(502, "Unimplemented MODE type.");
15910276Ssam 			}
16010276Ssam 		}
16110276Ssam 	|	ALLO SP NUMBER CRLF
16210276Ssam 		= {
16310276Ssam 			ack($1);
16410276Ssam 		}
16510276Ssam 	|	RETR check_login SP pathname CRLF
16610276Ssam 		= {
16710302Ssam 			if ($2 && $4 != NULL)
16810276Ssam 				retrieve(0, $4);
16910302Ssam 			if ($4 != NULL)
17010302Ssam 				free($4);
17110276Ssam 		}
17210276Ssam 	|	STOR check_login SP pathname CRLF
17310276Ssam 		= {
17410302Ssam 			if ($2 && $4 != NULL)
17510276Ssam 				store($4, "w");
17610302Ssam 			if ($4 != NULL)
17710302Ssam 				free($4);
17810276Ssam 		}
17910276Ssam 	|	APPE check_login SP pathname CRLF
18010276Ssam 		= {
18110302Ssam 			if ($2 && $4 != NULL)
18210276Ssam 				store($4, "a");
18310302Ssam 			if ($4 != NULL)
18410302Ssam 				free($4);
18510276Ssam 		}
18610276Ssam 	|	NLST check_login CRLF
18710276Ssam 		= {
18810276Ssam 			if ($2)
18911217Ssam 				retrieve("/bin/ls", "");
19010276Ssam 		}
19110276Ssam 	|	NLST check_login SP pathname CRLF
19210276Ssam 		= {
19310302Ssam 			if ($2 && $4 != NULL)
19411217Ssam 				retrieve("/bin/ls %s", $4);
19510302Ssam 			if ($4 != NULL)
19610302Ssam 				free($4);
19710276Ssam 		}
19810276Ssam 	|	LIST check_login CRLF
19910276Ssam 		= {
20010276Ssam 			if ($2)
20110318Ssam 				retrieve("/bin/ls -lg", "");
20210276Ssam 		}
20310276Ssam 	|	LIST check_login SP pathname CRLF
20410276Ssam 		= {
20510302Ssam 			if ($2 && $4 != NULL)
20610318Ssam 				retrieve("/bin/ls -lg %s", $4);
20710302Ssam 			if ($4 != NULL)
20810302Ssam 				free($4);
20910276Ssam 		}
21010276Ssam 	|	DELE check_login SP pathname CRLF
21110276Ssam 		= {
21210302Ssam 			if ($2 && $4 != NULL)
21310276Ssam 				delete($4);
21410302Ssam 			if ($4 != NULL)
21510302Ssam 				free($4);
21610276Ssam 		}
21710276Ssam 	|	CWD check_login CRLF
21810276Ssam 		= {
21910276Ssam 			if ($2)
22010276Ssam 				cwd(pw->pw_dir);
22110276Ssam 		}
22210276Ssam 	|	CWD check_login SP pathname CRLF
22310276Ssam 		= {
22410302Ssam 			if ($2 && $4 != NULL)
22510276Ssam 				cwd($4);
22610302Ssam 			if ($4 != NULL)
22710302Ssam 				free($4);
22810276Ssam 		}
22910276Ssam 	|	rename_cmd
23010276Ssam 	|	HELP CRLF
23110276Ssam 		= {
23210276Ssam 			help(0);
23310276Ssam 		}
23410276Ssam 	|	HELP SP STRING CRLF
23510276Ssam 		= {
23610276Ssam 			help($3);
23710276Ssam 		}
23810276Ssam 	|	NOOP CRLF
23910276Ssam 		= {
24010276Ssam 			ack($1);
24110276Ssam 		}
24210276Ssam 	|	XMKD check_login SP pathname CRLF
24310276Ssam 		= {
24410302Ssam 			if ($2 && $4 != NULL)
24510302Ssam 				makedir($4);
24610302Ssam 			if ($4 != NULL)
24710302Ssam 				free($4);
24810276Ssam 		}
24910276Ssam 	|	XRMD check_login SP pathname CRLF
25010276Ssam 		= {
25110302Ssam 			if ($2 && $4 != NULL)
25210302Ssam 				removedir($4);
25310302Ssam 			if ($4 != NULL)
25410302Ssam 				free($4);
25510276Ssam 		}
25610276Ssam 	|	XPWD check_login CRLF
25710276Ssam 		= {
25810276Ssam 			if ($2)
25910302Ssam 				pwd();
26010276Ssam 		}
26110276Ssam 	|	XCUP check_login CRLF
26210276Ssam 		= {
26310276Ssam 			if ($2)
26410276Ssam 				cwd("..");
26510276Ssam 		}
26610276Ssam 	|	QUIT CRLF
26710276Ssam 		= {
26810276Ssam 			reply(221, "Goodbye.");
26913246Ssam 			dologout(0);
27010276Ssam 		}
27110276Ssam 	|	error CRLF
27210276Ssam 		= {
27310276Ssam 			yyerrok;
27410276Ssam 		}
27510276Ssam 	;
27610276Ssam 
27710276Ssam username:	STRING
27810276Ssam 	;
27910276Ssam 
28010276Ssam password:	STRING
28110276Ssam 	;
28210276Ssam 
28310276Ssam byte_size:	NUMBER
28410276Ssam 	;
28510276Ssam 
28610276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
28710276Ssam 		NUMBER COMMA NUMBER
28810276Ssam 		= {
28910276Ssam 			register char *a, *p;
29010276Ssam 
29110276Ssam 			a = (char *)&data_dest.sin_addr;
29210276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
29310276Ssam 			p = (char *)&data_dest.sin_port;
29410276Ssam 			p[0] = $9; p[1] = $11;
29510324Ssam 			data_dest.sin_family = AF_INET;
29610276Ssam 		}
29710276Ssam 	;
29810276Ssam 
29910276Ssam form_code:	N
30010276Ssam 	= {
30110276Ssam 		$$ = FORM_N;
30210276Ssam 	}
30310276Ssam 	|	T
30410276Ssam 	= {
30510276Ssam 		$$ = FORM_T;
30610276Ssam 	}
30710276Ssam 	|	C
30810276Ssam 	= {
30910276Ssam 		$$ = FORM_C;
31010276Ssam 	}
31110276Ssam 	;
31210276Ssam 
31310276Ssam type_code:	A
31410276Ssam 	= {
31510276Ssam 		cmd_type = TYPE_A;
31610276Ssam 		cmd_form = FORM_N;
31710276Ssam 	}
31810276Ssam 	|	A SP form_code
31910276Ssam 	= {
32010276Ssam 		cmd_type = TYPE_A;
32110276Ssam 		cmd_form = $3;
32210276Ssam 	}
32310276Ssam 	|	E
32410276Ssam 	= {
32510276Ssam 		cmd_type = TYPE_E;
32610276Ssam 		cmd_form = FORM_N;
32710276Ssam 	}
32810276Ssam 	|	E SP form_code
32910276Ssam 	= {
33010276Ssam 		cmd_type = TYPE_E;
33110276Ssam 		cmd_form = $3;
33210276Ssam 	}
33310276Ssam 	|	I
33410276Ssam 	= {
33510276Ssam 		cmd_type = TYPE_I;
33610276Ssam 	}
33710276Ssam 	|	L
33810276Ssam 	= {
33910276Ssam 		cmd_type = TYPE_L;
34010276Ssam 		cmd_bytesz = 8;
34110276Ssam 	}
34210276Ssam 	|	L SP byte_size
34310276Ssam 	= {
34410276Ssam 		cmd_type = TYPE_L;
34510276Ssam 		cmd_bytesz = $3;
34610276Ssam 	}
34710276Ssam 	/* this is for a bug in the BBN ftp */
34810276Ssam 	|	L byte_size
34910276Ssam 	= {
35010276Ssam 		cmd_type = TYPE_L;
35110276Ssam 		cmd_bytesz = $2;
35210276Ssam 	}
35310276Ssam 	;
35410276Ssam 
35510276Ssam struct_code:	F
35610276Ssam 	= {
35710276Ssam 		$$ = STRU_F;
35810276Ssam 	}
35910276Ssam 	|	R
36010276Ssam 	= {
36110276Ssam 		$$ = STRU_R;
36210276Ssam 	}
36310276Ssam 	|	P
36410276Ssam 	= {
36510276Ssam 		$$ = STRU_P;
36610276Ssam 	}
36710276Ssam 	;
36810276Ssam 
36910276Ssam mode_code:	S
37010276Ssam 	= {
37110276Ssam 		$$ = MODE_S;
37210276Ssam 	}
37310276Ssam 	|	B
37410276Ssam 	= {
37510276Ssam 		$$ = MODE_B;
37610276Ssam 	}
37710276Ssam 	|	C
37810276Ssam 	= {
37910276Ssam 		$$ = MODE_C;
38010276Ssam 	}
38110276Ssam 	;
38210276Ssam 
38310276Ssam pathname:	pathstring
38410276Ssam 	= {
38510276Ssam 		if ($1 && strncmp($1, "~", 1) == 0) {
38610276Ssam 			$$ = (int)*glob($1);
38710302Ssam 			if (globerr != NULL) {
38810276Ssam 				reply(550, globerr);
38910302Ssam 				$$ = NULL;
39010302Ssam 			}
39110276Ssam 			free($1);
39210276Ssam 		} else
39310276Ssam 			$$ = $1;
39410276Ssam 	}
39510276Ssam 	;
39610276Ssam 
39710276Ssam pathstring:	STRING
39810276Ssam 	;
39910276Ssam 
40010276Ssam rename_cmd:	rename_from rename_to
40110276Ssam 	= {
40210276Ssam 		if ($1 && $2)
40310276Ssam 			renamecmd($1, $2);
40410276Ssam 		else
40510276Ssam 			reply(503, "Bad sequence of commands.");
40610276Ssam 		if ($1)
40710276Ssam 			free($1);
40810276Ssam 		if ($2)
40910276Ssam 			free($2);
41010276Ssam 	}
41110276Ssam 	;
41210276Ssam 
41310276Ssam rename_from:	RNFR check_login SP pathname CRLF
41410276Ssam 	= {
41510276Ssam 		char *from = 0, *renamefrom();
41610276Ssam 
41710302Ssam 		if ($2 && $4)
41810276Ssam 			from = renamefrom($4);
41910302Ssam 		if (from == 0 && $4)
42010276Ssam 			free($4);
42110276Ssam 		$$ = (int)from;
42210276Ssam 	}
42310276Ssam 	;
42410276Ssam 
42510276Ssam rename_to:	RNTO SP pathname CRLF
42610276Ssam 	= {
42710276Ssam 		$$ = $3;
42810276Ssam 	}
42910276Ssam 	;
43010276Ssam 
43110276Ssam check_login:	/* empty */
43210276Ssam 	= {
43310276Ssam 		if (logged_in)
43410276Ssam 			$$ = 1;
43510276Ssam 		else {
43610276Ssam 			reply(530, "Please login with USER and PASS.");
43710276Ssam 			$$ = 0;
43810276Ssam 		}
43910276Ssam 	}
44010276Ssam 	;
44110276Ssam 
44210276Ssam %%
44310276Ssam 
44410276Ssam extern jmp_buf errcatch;
44510276Ssam 
44610276Ssam #define	CMD	0	/* beginning of command */
44710276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
44810276Ssam #define	STR1	2	/* expect SP followed by STRING */
44910276Ssam #define	STR2	3	/* expect STRING */
45010276Ssam #define	OSTR	4	/* optional STRING */
45110276Ssam 
45210276Ssam struct tab {
45310276Ssam 	char	*name;
45410276Ssam 	short	token;
45510276Ssam 	short	state;
45610276Ssam 	short	implemented;	/* 1 if command is implemented */
45710276Ssam 	char	*help;
45810276Ssam };
45910276Ssam 
46010276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
46110276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
46210276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
46310276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
46410276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
46510276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
46610276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
46710276Ssam 	{ "PASV", PASV, ARGS, 0,	"(set server in passive mode)" },
46810276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
46910276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
47010276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
47110276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
47210276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
47310276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
47410276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
47510276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
47610276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
47710276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
47810276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
47910276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
48010276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
48110276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
48210276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
48310276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
48410276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
48510276Ssam 	{ "ABOR", ABOR, ARGS, 0,	"(abort operation)" },
48610276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
48710276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
48810276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
48910276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
49010276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
49110276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
49210276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
49310276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
49410276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
49510276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
49610276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
49710276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
49810276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
49910276Ssam 	{ NULL,   0,    0,    0,	0 }
50010276Ssam };
50110276Ssam 
50210276Ssam struct tab *
50310276Ssam lookup(cmd)
50410276Ssam 	char *cmd;
50510276Ssam {
50610276Ssam 	register struct tab *p;
50710276Ssam 
50810276Ssam 	for (p = cmdtab; p->name != NULL; p++)
50910276Ssam 		if (strcmp(cmd, p->name) == 0)
51010276Ssam 			return (p);
51110276Ssam 	return (0);
51210276Ssam }
51310276Ssam 
51413033Ssam #include <arpa/telnet.h>
51510276Ssam 
51610276Ssam /*
51710276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
51810276Ssam  */
51910276Ssam char *
52010276Ssam getline(s, n, iop)
52110276Ssam 	char *s;
52210276Ssam 	register FILE *iop;
52310276Ssam {
52410276Ssam 	register c;
52510276Ssam 	register char *cs;
52610276Ssam 
52710276Ssam 	cs = s;
52810276Ssam 	while (--n > 0 && (c = getc(iop)) >= 0) {
52910276Ssam 		while (c == IAC) {
53010276Ssam 			c = getc(iop);	/* skip command */
53110276Ssam 			c = getc(iop);	/* try next char */
53210276Ssam 		}
53310276Ssam 		*cs++ = c;
53410276Ssam 		if (c=='\n')
53510276Ssam 			break;
53610276Ssam 	}
53710276Ssam 	if (c < 0 && cs == s)
53818303Sralph 		return (NULL);
53910276Ssam 	*cs++ = '\0';
54011652Ssam 	if (debug) {
54111652Ssam 		fprintf(stderr, "FTPD: command: %s", s);
54211652Ssam 		if (c != '\n')
54311652Ssam 			putc('\n', stderr);
54411652Ssam 		fflush(stderr);
54511652Ssam 	}
54610276Ssam 	return (s);
54710276Ssam }
54810276Ssam 
54911652Ssam static int
55011652Ssam toolong()
55111652Ssam {
55211652Ssam 	long now;
55311652Ssam 	extern char *ctime();
55411652Ssam 
55511652Ssam 	reply(421,
55611652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
55711652Ssam 	time(&now);
55811652Ssam 	if (logging) {
55911652Ssam 		fprintf(stderr,
56011652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
56111652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
56211652Ssam 		fflush(stderr);
56311652Ssam 	}
56413246Ssam 	dologout(1);
56511652Ssam }
56611652Ssam 
56710276Ssam yylex()
56810276Ssam {
56910276Ssam 	static char cbuf[512];
57010276Ssam 	static int cpos, state;
57110276Ssam 	register char *cp;
57210276Ssam 	register struct tab *p;
57310276Ssam 	int n;
57410276Ssam 	char c;
57510276Ssam 
57610276Ssam 	for (;;) {
57710276Ssam 		switch (state) {
57810276Ssam 
57910276Ssam 		case CMD:
58011652Ssam 			signal(SIGALRM, toolong);
58111652Ssam 			alarm(timeout);
58210276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
58310276Ssam 				reply(221, "You could at least say goodbye.");
58413246Ssam 				dologout(0);
58510276Ssam 			}
58611652Ssam 			alarm(0);
58710276Ssam 			if (index(cbuf, '\r')) {
58810276Ssam 				cp = index(cbuf, '\r');
58910276Ssam 				cp[0] = '\n'; cp[1] = 0;
59010276Ssam 			}
59110276Ssam 			if (index(cbuf, ' '))
59210276Ssam 				cpos = index(cbuf, ' ') - cbuf;
59310276Ssam 			else
59410276Ssam 				cpos = 4;
59510276Ssam 			c = cbuf[cpos];
59610276Ssam 			cbuf[cpos] = '\0';
59710276Ssam 			upper(cbuf);
59810276Ssam 			p = lookup(cbuf);
59910276Ssam 			cbuf[cpos] = c;
60010276Ssam 			if (p != 0) {
60110276Ssam 				if (p->implemented == 0) {
60210276Ssam 					nack(p->name);
60310276Ssam 					longjmp(errcatch);
60410276Ssam 					/* NOTREACHED */
60510276Ssam 				}
60610276Ssam 				state = p->state;
60710276Ssam 				yylval = (int) p->name;
60810276Ssam 				return (p->token);
60910276Ssam 			}
61010276Ssam 			break;
61110276Ssam 
61210276Ssam 		case OSTR:
61310276Ssam 			if (cbuf[cpos] == '\n') {
61410276Ssam 				state = CMD;
61510276Ssam 				return (CRLF);
61610276Ssam 			}
61710276Ssam 			/* FALL THRU */
61810276Ssam 
61910276Ssam 		case STR1:
62010276Ssam 			if (cbuf[cpos] == ' ') {
62110276Ssam 				cpos++;
62210276Ssam 				state = STR2;
62310276Ssam 				return (SP);
62410276Ssam 			}
62510276Ssam 			break;
62610276Ssam 
62710276Ssam 		case STR2:
62810276Ssam 			cp = &cbuf[cpos];
62910276Ssam 			n = strlen(cp);
63010276Ssam 			cpos += n - 1;
63110276Ssam 			/*
63210276Ssam 			 * Make sure the string is nonempty and \n terminated.
63310276Ssam 			 */
63410276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
63510276Ssam 				cbuf[cpos] = '\0';
63610276Ssam 				yylval = copy(cp);
63710276Ssam 				cbuf[cpos] = '\n';
63810276Ssam 				state = ARGS;
63910276Ssam 				return (STRING);
64010276Ssam 			}
64110276Ssam 			break;
64210276Ssam 
64310276Ssam 		case ARGS:
64410276Ssam 			if (isdigit(cbuf[cpos])) {
64510276Ssam 				cp = &cbuf[cpos];
64610276Ssam 				while (isdigit(cbuf[++cpos]))
64710276Ssam 					;
64810276Ssam 				c = cbuf[cpos];
64910276Ssam 				cbuf[cpos] = '\0';
65010276Ssam 				yylval = atoi(cp);
65110276Ssam 				cbuf[cpos] = c;
65210276Ssam 				return (NUMBER);
65310276Ssam 			}
65410276Ssam 			switch (cbuf[cpos++]) {
65510276Ssam 
65610276Ssam 			case '\n':
65710276Ssam 				state = CMD;
65810276Ssam 				return (CRLF);
65910276Ssam 
66010276Ssam 			case ' ':
66110276Ssam 				return (SP);
66210276Ssam 
66310276Ssam 			case ',':
66410276Ssam 				return (COMMA);
66510276Ssam 
66610276Ssam 			case 'A':
66710276Ssam 			case 'a':
66810276Ssam 				return (A);
66910276Ssam 
67010276Ssam 			case 'B':
67110276Ssam 			case 'b':
67210276Ssam 				return (B);
67310276Ssam 
67410276Ssam 			case 'C':
67510276Ssam 			case 'c':
67610276Ssam 				return (C);
67710276Ssam 
67810276Ssam 			case 'E':
67910276Ssam 			case 'e':
68010276Ssam 				return (E);
68110276Ssam 
68210276Ssam 			case 'F':
68310276Ssam 			case 'f':
68410276Ssam 				return (F);
68510276Ssam 
68610276Ssam 			case 'I':
68710276Ssam 			case 'i':
68810276Ssam 				return (I);
68910276Ssam 
69010276Ssam 			case 'L':
69110276Ssam 			case 'l':
69210276Ssam 				return (L);
69310276Ssam 
69410276Ssam 			case 'N':
69510276Ssam 			case 'n':
69610276Ssam 				return (N);
69710276Ssam 
69810276Ssam 			case 'P':
69910276Ssam 			case 'p':
70010276Ssam 				return (P);
70110276Ssam 
70210276Ssam 			case 'R':
70310276Ssam 			case 'r':
70410276Ssam 				return (R);
70510276Ssam 
70610276Ssam 			case 'S':
70710276Ssam 			case 's':
70810276Ssam 				return (S);
70910276Ssam 
71010276Ssam 			case 'T':
71110276Ssam 			case 't':
71210276Ssam 				return (T);
71310276Ssam 
71410276Ssam 			}
71510276Ssam 			break;
71610276Ssam 
71710276Ssam 		default:
71810276Ssam 			fatal("Unknown state in scanner.");
71910276Ssam 		}
72010276Ssam 		yyerror();
72110276Ssam 		state = CMD;
72210276Ssam 		longjmp(errcatch);
72310276Ssam 	}
72410276Ssam }
72510276Ssam 
72610276Ssam upper(s)
72710276Ssam 	char *s;
72810276Ssam {
72910276Ssam 	while (*s != '\0') {
73010276Ssam 		if (islower(*s))
73110276Ssam 			*s = toupper(*s);
73210276Ssam 		s++;
73310276Ssam 	}
73410276Ssam }
73510276Ssam 
73610276Ssam copy(s)
73710276Ssam 	char *s;
73810276Ssam {
73910276Ssam 	char *p;
74010276Ssam 	extern char *malloc();
74110276Ssam 
74210276Ssam 	p = malloc(strlen(s) + 1);
74310276Ssam 	if (p == NULL)
74410276Ssam 		fatal("Ran out of memory.");
74510276Ssam 	strcpy(p, s);
74610276Ssam 	return ((int)p);
74710276Ssam }
74810276Ssam 
74910276Ssam help(s)
75010276Ssam 	char *s;
75110276Ssam {
75210276Ssam 	register struct tab *c;
75310276Ssam 	register int width, NCMDS;
75410276Ssam 
75510276Ssam 	width = 0, NCMDS = 0;
75610276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
75710276Ssam 		int len = strlen(c->name);
75810276Ssam 
75910276Ssam 		if (c->implemented == 0)
76010276Ssam 			len++;
76110276Ssam 		if (len > width)
76210276Ssam 			width = len;
76310276Ssam 		NCMDS++;
76410276Ssam 	}
76510276Ssam 	width = (width + 8) &~ 7;
76610276Ssam 	if (s == 0) {
76710276Ssam 		register int i, j, w;
76810276Ssam 		int columns, lines;
76910276Ssam 
77010276Ssam 		lreply(214,
77110276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
77210276Ssam 		columns = 76 / width;
77310276Ssam 		if (columns == 0)
77410276Ssam 			columns = 1;
77510276Ssam 		lines = (NCMDS + columns - 1) / columns;
77610276Ssam 		for (i = 0; i < lines; i++) {
77710276Ssam 			printf("    ");
77810276Ssam 			for (j = 0; j < columns; j++) {
77910276Ssam 				c = cmdtab + j * lines + i;
78010276Ssam 				printf("%s%c", c->name,
78110276Ssam 					c->implemented ? ' ' : '*');
78210302Ssam 				if (c + lines >= &cmdtab[NCMDS])
78310276Ssam 					break;
78410276Ssam 				w = strlen(c->name);
78510276Ssam 				while (w < width) {
78610276Ssam 					putchar(' ');
78710276Ssam 					w++;
78810276Ssam 				}
78910276Ssam 			}
79010276Ssam 			printf("\r\n");
79110276Ssam 		}
79210276Ssam 		fflush(stdout);
79310276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
79410276Ssam 		return;
79510276Ssam 	}
79610276Ssam 	upper(s);
79710276Ssam 	c = lookup(s);
79810276Ssam 	if (c == (struct tab *)0) {
79910276Ssam 		reply(504, "Unknown command %s.", s);
80010276Ssam 		return;
80110276Ssam 	}
80210276Ssam 	if (c->implemented)
80310276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
80410276Ssam 	else
80510276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
80610276Ssam }
807