xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 26045)
110276Ssam /*
2*26045Sminshall  * Copyright (c) 1985 Regents of the University of California.
322501Sdist  * All rights reserved.  The Berkeley software License Agreement
422501Sdist  * specifies the terms and conditions for redistribution.
522501Sdist  */
622501Sdist 
722501Sdist /*
810276Ssam  * Grammar for FTP commands.
910276Ssam  * See RFC 765.
1010276Ssam  */
1110276Ssam 
1210276Ssam %{
1310276Ssam 
1410276Ssam #ifndef lint
15*26045Sminshall static	char sccsid[] = "@(#)ftpcmd.y	5.3 (Berkeley) 02/03/86";
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;
40*26045Sminshall extern  int pdata;
4110276Ssam extern	char hostname[];
4210276Ssam extern	char *globerr;
4310320Ssam extern	int usedefault;
44*26045Sminshall extern	int unique;
45*26045Sminshall extern  int transflag;
46*26045Sminshall extern  char tmpline[];
4710276Ssam char	**glob();
4810276Ssam 
4910276Ssam static	int cmd_type;
5010276Ssam static	int cmd_form;
5110276Ssam static	int cmd_bytesz;
52*26045Sminshall char cbuf[512];
5310276Ssam 
5410276Ssam char	*index();
5510276Ssam %}
5610276Ssam 
5710276Ssam %token
5810276Ssam 	A	B	C	E	F	I
5910276Ssam 	L	N	P	R	S	T
6010276Ssam 
6110276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
6210276Ssam 
6310276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
6410276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
6510276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
6610276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
6710276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
6810276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
69*26045Sminshall 	XCUP	STOU
7010276Ssam 
7110276Ssam 	LEXERR
7210276Ssam 
7310276Ssam %start	cmd_list
7410276Ssam 
7510276Ssam %%
7610276Ssam 
7710276Ssam cmd_list:	/* empty */
7810276Ssam 	|	cmd_list cmd
7910276Ssam 	;
8010276Ssam 
8110276Ssam cmd:		USER SP username CRLF
8210276Ssam 		= {
8310276Ssam 			extern struct passwd *getpwnam();
8410276Ssam 
85*26045Sminshall 			logged_in = 0;
8610324Ssam 			if (strcmp($3, "ftp") == 0 ||
8710324Ssam 			  strcmp($3, "anonymous") == 0) {
8810324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
8910324Ssam 					guest = 1;
9010324Ssam 					reply(331,
9110276Ssam 				  "Guest login ok, send ident as password.");
9210324Ssam 				}
93*26045Sminshall 				else {
94*26045Sminshall 					reply(530, "User %s unknown.", $3);
95*26045Sminshall 				}
9610694Ssam 			} else if (checkuser($3)) {
9710276Ssam 				guest = 0;
9810276Ssam 				pw = getpwnam($3);
99*26045Sminshall 				if (pw == NULL) {
100*26045Sminshall 					reply(530, "User %s unknown.", $3);
101*26045Sminshall 				}
102*26045Sminshall 				else {
103*26045Sminshall 				    reply(331, "Password required for %s.", $3);
104*26045Sminshall 				}
10510276Ssam 			}
10610276Ssam 			free($3);
10710276Ssam 		}
10810276Ssam 	|	PASS SP password CRLF
10910276Ssam 		= {
11010276Ssam 			pass($3);
11110276Ssam 			free($3);
11210276Ssam 		}
11310276Ssam 	|	PORT SP host_port CRLF
11410276Ssam 		= {
11510320Ssam 			usedefault = 0;
116*26045Sminshall 			if (pdata > 0) {
117*26045Sminshall 				(void) close(pdata);
118*26045Sminshall 			}
119*26045Sminshall 			pdata = -1;
12010276Ssam 			ack($1);
12110276Ssam 		}
122*26045Sminshall 	|	PASV CRLF
123*26045Sminshall 		= {
124*26045Sminshall 			passive();
125*26045Sminshall 		}
12610276Ssam 	|	TYPE SP type_code CRLF
12710276Ssam 		= {
12810276Ssam 			switch (cmd_type) {
12910276Ssam 
13010276Ssam 			case TYPE_A:
13110276Ssam 				if (cmd_form == FORM_N) {
13210276Ssam 					reply(200, "Type set to A.");
13310276Ssam 					type = cmd_type;
13410276Ssam 					form = cmd_form;
13510276Ssam 				} else
13610276Ssam 					reply(504, "Form must be N.");
13710276Ssam 				break;
13810276Ssam 
13910276Ssam 			case TYPE_E:
14010276Ssam 				reply(504, "Type E not implemented.");
14110276Ssam 				break;
14210276Ssam 
14310276Ssam 			case TYPE_I:
14410276Ssam 				reply(200, "Type set to I.");
14510276Ssam 				type = cmd_type;
14610276Ssam 				break;
14710276Ssam 
14810276Ssam 			case TYPE_L:
14910276Ssam 				if (cmd_bytesz == 8) {
15010276Ssam 					reply(200,
15110276Ssam 					    "Type set to L (byte size 8).");
15210276Ssam 					type = cmd_type;
15310276Ssam 				} else
15410276Ssam 					reply(504, "Byte size must be 8.");
15510276Ssam 			}
15610276Ssam 		}
15710276Ssam 	|	STRU SP struct_code CRLF
15810276Ssam 		= {
15910276Ssam 			switch ($3) {
16010276Ssam 
16110276Ssam 			case STRU_F:
16210276Ssam 				reply(200, "STRU F ok.");
16310276Ssam 				break;
16410276Ssam 
16510276Ssam 			default:
16610276Ssam 				reply(502, "Unimplemented STRU type.");
16710276Ssam 			}
16810276Ssam 		}
16910276Ssam 	|	MODE SP mode_code CRLF
17010276Ssam 		= {
17110276Ssam 			switch ($3) {
17210276Ssam 
17310276Ssam 			case MODE_S:
17410276Ssam 				reply(200, "MODE S ok.");
17510276Ssam 				break;
17610276Ssam 
17710276Ssam 			default:
17810276Ssam 				reply(502, "Unimplemented MODE type.");
17910276Ssam 			}
18010276Ssam 		}
18110276Ssam 	|	ALLO SP NUMBER CRLF
18210276Ssam 		= {
18310276Ssam 			ack($1);
18410276Ssam 		}
18510276Ssam 	|	RETR check_login SP pathname CRLF
18610276Ssam 		= {
18710302Ssam 			if ($2 && $4 != NULL)
18810276Ssam 				retrieve(0, $4);
18910302Ssam 			if ($4 != NULL)
19010302Ssam 				free($4);
19110276Ssam 		}
19210276Ssam 	|	STOR check_login SP pathname CRLF
19310276Ssam 		= {
19410302Ssam 			if ($2 && $4 != NULL)
19510276Ssam 				store($4, "w");
19610302Ssam 			if ($4 != NULL)
19710302Ssam 				free($4);
19810276Ssam 		}
19910276Ssam 	|	APPE check_login SP pathname CRLF
20010276Ssam 		= {
20110302Ssam 			if ($2 && $4 != NULL)
20210276Ssam 				store($4, "a");
20310302Ssam 			if ($4 != NULL)
20410302Ssam 				free($4);
20510276Ssam 		}
20610276Ssam 	|	NLST check_login CRLF
20710276Ssam 		= {
20810276Ssam 			if ($2)
20911217Ssam 				retrieve("/bin/ls", "");
21010276Ssam 		}
21110276Ssam 	|	NLST check_login SP pathname CRLF
21210276Ssam 		= {
21310302Ssam 			if ($2 && $4 != NULL)
21411217Ssam 				retrieve("/bin/ls %s", $4);
21510302Ssam 			if ($4 != NULL)
21610302Ssam 				free($4);
21710276Ssam 		}
21810276Ssam 	|	LIST check_login CRLF
21910276Ssam 		= {
22010276Ssam 			if ($2)
22110318Ssam 				retrieve("/bin/ls -lg", "");
22210276Ssam 		}
22310276Ssam 	|	LIST check_login SP pathname CRLF
22410276Ssam 		= {
22510302Ssam 			if ($2 && $4 != NULL)
22610318Ssam 				retrieve("/bin/ls -lg %s", $4);
22710302Ssam 			if ($4 != NULL)
22810302Ssam 				free($4);
22910276Ssam 		}
23010276Ssam 	|	DELE check_login SP pathname CRLF
23110276Ssam 		= {
23210302Ssam 			if ($2 && $4 != NULL)
23310276Ssam 				delete($4);
23410302Ssam 			if ($4 != NULL)
23510302Ssam 				free($4);
23610276Ssam 		}
237*26045Sminshall 	|	ABOR CRLF
238*26045Sminshall 		= {
239*26045Sminshall 			ack($1);
240*26045Sminshall 		}
24110276Ssam 	|	CWD check_login CRLF
24210276Ssam 		= {
24310276Ssam 			if ($2)
24410276Ssam 				cwd(pw->pw_dir);
24510276Ssam 		}
24610276Ssam 	|	CWD check_login SP pathname CRLF
24710276Ssam 		= {
24810302Ssam 			if ($2 && $4 != NULL)
24910276Ssam 				cwd($4);
25010302Ssam 			if ($4 != NULL)
25110302Ssam 				free($4);
25210276Ssam 		}
25310276Ssam 	|	rename_cmd
25410276Ssam 	|	HELP CRLF
25510276Ssam 		= {
25610276Ssam 			help(0);
25710276Ssam 		}
25810276Ssam 	|	HELP SP STRING CRLF
25910276Ssam 		= {
26010276Ssam 			help($3);
26110276Ssam 		}
26210276Ssam 	|	NOOP CRLF
26310276Ssam 		= {
26410276Ssam 			ack($1);
26510276Ssam 		}
26610276Ssam 	|	XMKD check_login SP pathname CRLF
26710276Ssam 		= {
26810302Ssam 			if ($2 && $4 != NULL)
26910302Ssam 				makedir($4);
27010302Ssam 			if ($4 != NULL)
27110302Ssam 				free($4);
27210276Ssam 		}
27310276Ssam 	|	XRMD check_login SP pathname CRLF
27410276Ssam 		= {
27510302Ssam 			if ($2 && $4 != NULL)
27610302Ssam 				removedir($4);
27710302Ssam 			if ($4 != NULL)
27810302Ssam 				free($4);
27910276Ssam 		}
28010276Ssam 	|	XPWD check_login CRLF
28110276Ssam 		= {
28210276Ssam 			if ($2)
28310302Ssam 				pwd();
28410276Ssam 		}
28510276Ssam 	|	XCUP check_login CRLF
28610276Ssam 		= {
28710276Ssam 			if ($2)
28810276Ssam 				cwd("..");
28910276Ssam 		}
290*26045Sminshall 	|	STOU check_login SP pathname CRLF
291*26045Sminshall 		= {
292*26045Sminshall 			if ($2 && $4 != NULL) {
293*26045Sminshall 				unique++;
294*26045Sminshall 				store($4, "w");
295*26045Sminshall 				unique = 0;
296*26045Sminshall 			}
297*26045Sminshall 			if ($4 != NULL)
298*26045Sminshall 				free($4);
299*26045Sminshall 		}
30010276Ssam 	|	QUIT CRLF
30110276Ssam 		= {
30210276Ssam 			reply(221, "Goodbye.");
30313246Ssam 			dologout(0);
30410276Ssam 		}
30510276Ssam 	|	error CRLF
30610276Ssam 		= {
30710276Ssam 			yyerrok;
30810276Ssam 		}
30910276Ssam 	;
31010276Ssam 
31110276Ssam username:	STRING
31210276Ssam 	;
31310276Ssam 
31410276Ssam password:	STRING
31510276Ssam 	;
31610276Ssam 
31710276Ssam byte_size:	NUMBER
31810276Ssam 	;
31910276Ssam 
32010276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
32110276Ssam 		NUMBER COMMA NUMBER
32210276Ssam 		= {
32310276Ssam 			register char *a, *p;
32410276Ssam 
32510276Ssam 			a = (char *)&data_dest.sin_addr;
32610276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
32710276Ssam 			p = (char *)&data_dest.sin_port;
32810276Ssam 			p[0] = $9; p[1] = $11;
32910324Ssam 			data_dest.sin_family = AF_INET;
33010276Ssam 		}
33110276Ssam 	;
33210276Ssam 
33310276Ssam form_code:	N
33410276Ssam 	= {
33510276Ssam 		$$ = FORM_N;
33610276Ssam 	}
33710276Ssam 	|	T
33810276Ssam 	= {
33910276Ssam 		$$ = FORM_T;
34010276Ssam 	}
34110276Ssam 	|	C
34210276Ssam 	= {
34310276Ssam 		$$ = FORM_C;
34410276Ssam 	}
34510276Ssam 	;
34610276Ssam 
34710276Ssam type_code:	A
34810276Ssam 	= {
34910276Ssam 		cmd_type = TYPE_A;
35010276Ssam 		cmd_form = FORM_N;
35110276Ssam 	}
35210276Ssam 	|	A SP form_code
35310276Ssam 	= {
35410276Ssam 		cmd_type = TYPE_A;
35510276Ssam 		cmd_form = $3;
35610276Ssam 	}
35710276Ssam 	|	E
35810276Ssam 	= {
35910276Ssam 		cmd_type = TYPE_E;
36010276Ssam 		cmd_form = FORM_N;
36110276Ssam 	}
36210276Ssam 	|	E SP form_code
36310276Ssam 	= {
36410276Ssam 		cmd_type = TYPE_E;
36510276Ssam 		cmd_form = $3;
36610276Ssam 	}
36710276Ssam 	|	I
36810276Ssam 	= {
36910276Ssam 		cmd_type = TYPE_I;
37010276Ssam 	}
37110276Ssam 	|	L
37210276Ssam 	= {
37310276Ssam 		cmd_type = TYPE_L;
37410276Ssam 		cmd_bytesz = 8;
37510276Ssam 	}
37610276Ssam 	|	L SP byte_size
37710276Ssam 	= {
37810276Ssam 		cmd_type = TYPE_L;
37910276Ssam 		cmd_bytesz = $3;
38010276Ssam 	}
38110276Ssam 	/* this is for a bug in the BBN ftp */
38210276Ssam 	|	L byte_size
38310276Ssam 	= {
38410276Ssam 		cmd_type = TYPE_L;
38510276Ssam 		cmd_bytesz = $2;
38610276Ssam 	}
38710276Ssam 	;
38810276Ssam 
38910276Ssam struct_code:	F
39010276Ssam 	= {
39110276Ssam 		$$ = STRU_F;
39210276Ssam 	}
39310276Ssam 	|	R
39410276Ssam 	= {
39510276Ssam 		$$ = STRU_R;
39610276Ssam 	}
39710276Ssam 	|	P
39810276Ssam 	= {
39910276Ssam 		$$ = STRU_P;
40010276Ssam 	}
40110276Ssam 	;
40210276Ssam 
40310276Ssam mode_code:	S
40410276Ssam 	= {
40510276Ssam 		$$ = MODE_S;
40610276Ssam 	}
40710276Ssam 	|	B
40810276Ssam 	= {
40910276Ssam 		$$ = MODE_B;
41010276Ssam 	}
41110276Ssam 	|	C
41210276Ssam 	= {
41310276Ssam 		$$ = MODE_C;
41410276Ssam 	}
41510276Ssam 	;
41610276Ssam 
41710276Ssam pathname:	pathstring
41810276Ssam 	= {
41910276Ssam 		if ($1 && strncmp($1, "~", 1) == 0) {
42010276Ssam 			$$ = (int)*glob($1);
42110302Ssam 			if (globerr != NULL) {
42210276Ssam 				reply(550, globerr);
42310302Ssam 				$$ = NULL;
42410302Ssam 			}
42510276Ssam 			free($1);
42610276Ssam 		} else
42710276Ssam 			$$ = $1;
42810276Ssam 	}
42910276Ssam 	;
43010276Ssam 
43110276Ssam pathstring:	STRING
43210276Ssam 	;
43310276Ssam 
43410276Ssam rename_cmd:	rename_from rename_to
43510276Ssam 	= {
43610276Ssam 		if ($1 && $2)
43710276Ssam 			renamecmd($1, $2);
43810276Ssam 		else
43910276Ssam 			reply(503, "Bad sequence of commands.");
44010276Ssam 		if ($1)
44110276Ssam 			free($1);
44210276Ssam 		if ($2)
44310276Ssam 			free($2);
44410276Ssam 	}
44510276Ssam 	;
44610276Ssam 
44710276Ssam rename_from:	RNFR check_login SP pathname CRLF
44810276Ssam 	= {
44910276Ssam 		char *from = 0, *renamefrom();
45010276Ssam 
45110302Ssam 		if ($2 && $4)
45210276Ssam 			from = renamefrom($4);
45310302Ssam 		if (from == 0 && $4)
45410276Ssam 			free($4);
45510276Ssam 		$$ = (int)from;
45610276Ssam 	}
45710276Ssam 	;
45810276Ssam 
45910276Ssam rename_to:	RNTO SP pathname CRLF
46010276Ssam 	= {
46110276Ssam 		$$ = $3;
46210276Ssam 	}
46310276Ssam 	;
46410276Ssam 
46510276Ssam check_login:	/* empty */
46610276Ssam 	= {
46710276Ssam 		if (logged_in)
46810276Ssam 			$$ = 1;
46910276Ssam 		else {
47010276Ssam 			reply(530, "Please login with USER and PASS.");
47110276Ssam 			$$ = 0;
47210276Ssam 		}
47310276Ssam 	}
47410276Ssam 	;
47510276Ssam 
47610276Ssam %%
47710276Ssam 
47810276Ssam extern jmp_buf errcatch;
47910276Ssam 
48010276Ssam #define	CMD	0	/* beginning of command */
48110276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
48210276Ssam #define	STR1	2	/* expect SP followed by STRING */
48310276Ssam #define	STR2	3	/* expect STRING */
48410276Ssam #define	OSTR	4	/* optional STRING */
48510276Ssam 
48610276Ssam struct tab {
48710276Ssam 	char	*name;
48810276Ssam 	short	token;
48910276Ssam 	short	state;
49010276Ssam 	short	implemented;	/* 1 if command is implemented */
49110276Ssam 	char	*help;
49210276Ssam };
49310276Ssam 
49410276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
49510276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
49610276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
49710276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
49810276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
49910276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
50010276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
501*26045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
50210276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
50310276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
50410276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
50510276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
50610276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
50710276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
50810276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
50910276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
51010276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
51110276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
51210276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
51310276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
51410276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
51510276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
51610276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
51710276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
51810276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
519*26045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
52010276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
52110276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
52210276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
52310276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
52410276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
52510276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
52610276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
52710276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
52810276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
529*26045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
53010276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
531*26045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
53210276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
533*26045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
53410276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
535*26045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
53610276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
537*26045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
53810276Ssam 	{ NULL,   0,    0,    0,	0 }
53910276Ssam };
54010276Ssam 
54110276Ssam struct tab *
54210276Ssam lookup(cmd)
54310276Ssam 	char *cmd;
54410276Ssam {
54510276Ssam 	register struct tab *p;
54610276Ssam 
54710276Ssam 	for (p = cmdtab; p->name != NULL; p++)
54810276Ssam 		if (strcmp(cmd, p->name) == 0)
54910276Ssam 			return (p);
55010276Ssam 	return (0);
55110276Ssam }
55210276Ssam 
55313033Ssam #include <arpa/telnet.h>
55410276Ssam 
55510276Ssam /*
55610276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
55710276Ssam  */
55810276Ssam char *
55910276Ssam getline(s, n, iop)
56010276Ssam 	char *s;
56110276Ssam 	register FILE *iop;
56210276Ssam {
56310276Ssam 	register c;
564*26045Sminshall 	register char *cs, ch;
56510276Ssam 
56610276Ssam 	cs = s;
567*26045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
568*26045Sminshall 		*cs++ = tmpline[c];
569*26045Sminshall 		if (tmpline[c] == '\n') {
570*26045Sminshall 			*cs++ = '\0';
571*26045Sminshall 			if (debug) {
572*26045Sminshall 				fprintf(stderr, "FTPD: command: %s", s);
573*26045Sminshall 			}
574*26045Sminshall 			tmpline[0] = '\0';
575*26045Sminshall 			return(s);
576*26045Sminshall 		}
577*26045Sminshall 		if (c == 0) {
578*26045Sminshall 			tmpline[0] = '\0';
579*26045Sminshall 		}
580*26045Sminshall 	}
581*26045Sminshall 	while (--n > 0 && read(fileno(iop),&ch,1) >= 0) {
582*26045Sminshall 		c = 0377 & ch;
58310276Ssam 		while (c == IAC) {
584*26045Sminshall 			read(fileno(iop),&ch,1);	/* skip command */
585*26045Sminshall 			read(fileno(iop),&ch,1);	/* try next char */
586*26045Sminshall 			c = 0377 & ch;
58710276Ssam 		}
58810276Ssam 		*cs++ = c;
58910276Ssam 		if (c=='\n')
59010276Ssam 			break;
59110276Ssam 	}
59210276Ssam 	if (c < 0 && cs == s)
59318303Sralph 		return (NULL);
59410276Ssam 	*cs++ = '\0';
59511652Ssam 	if (debug) {
59611652Ssam 		fprintf(stderr, "FTPD: command: %s", s);
59711652Ssam 		if (c != '\n')
59811652Ssam 			putc('\n', stderr);
59911652Ssam 		fflush(stderr);
60011652Ssam 	}
60110276Ssam 	return (s);
60210276Ssam }
60310276Ssam 
60411652Ssam static int
60511652Ssam toolong()
60611652Ssam {
60711652Ssam 	long now;
60811652Ssam 	extern char *ctime();
60911652Ssam 
61011652Ssam 	reply(421,
61111652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
61211652Ssam 	time(&now);
61311652Ssam 	if (logging) {
61411652Ssam 		fprintf(stderr,
61511652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
61611652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
61711652Ssam 		fflush(stderr);
61811652Ssam 	}
61913246Ssam 	dologout(1);
62011652Ssam }
62111652Ssam 
62210276Ssam yylex()
62310276Ssam {
62410276Ssam 	static int cpos, state;
62510276Ssam 	register char *cp;
62610276Ssam 	register struct tab *p;
62710276Ssam 	int n;
62810276Ssam 	char c;
62910276Ssam 
63010276Ssam 	for (;;) {
63110276Ssam 		switch (state) {
63210276Ssam 
63310276Ssam 		case CMD:
63411652Ssam 			signal(SIGALRM, toolong);
63511652Ssam 			alarm(timeout);
63610276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
63710276Ssam 				reply(221, "You could at least say goodbye.");
63813246Ssam 				dologout(0);
63910276Ssam 			}
64011652Ssam 			alarm(0);
64110276Ssam 			if (index(cbuf, '\r')) {
64210276Ssam 				cp = index(cbuf, '\r');
64310276Ssam 				cp[0] = '\n'; cp[1] = 0;
64410276Ssam 			}
64510276Ssam 			if (index(cbuf, ' '))
64610276Ssam 				cpos = index(cbuf, ' ') - cbuf;
64710276Ssam 			else
648*26045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
649*26045Sminshall 			if (cpos == 0) {
65010276Ssam 				cpos = 4;
651*26045Sminshall 			}
65210276Ssam 			c = cbuf[cpos];
65310276Ssam 			cbuf[cpos] = '\0';
65410276Ssam 			upper(cbuf);
65510276Ssam 			p = lookup(cbuf);
65610276Ssam 			cbuf[cpos] = c;
65710276Ssam 			if (p != 0) {
65810276Ssam 				if (p->implemented == 0) {
65910276Ssam 					nack(p->name);
66010276Ssam 					longjmp(errcatch);
66110276Ssam 					/* NOTREACHED */
66210276Ssam 				}
66310276Ssam 				state = p->state;
66410276Ssam 				yylval = (int) p->name;
66510276Ssam 				return (p->token);
66610276Ssam 			}
66710276Ssam 			break;
66810276Ssam 
66910276Ssam 		case OSTR:
67010276Ssam 			if (cbuf[cpos] == '\n') {
67110276Ssam 				state = CMD;
67210276Ssam 				return (CRLF);
67310276Ssam 			}
67410276Ssam 			/* FALL THRU */
67510276Ssam 
67610276Ssam 		case STR1:
67710276Ssam 			if (cbuf[cpos] == ' ') {
67810276Ssam 				cpos++;
67910276Ssam 				state = STR2;
68010276Ssam 				return (SP);
68110276Ssam 			}
68210276Ssam 			break;
68310276Ssam 
68410276Ssam 		case STR2:
68510276Ssam 			cp = &cbuf[cpos];
68610276Ssam 			n = strlen(cp);
68710276Ssam 			cpos += n - 1;
68810276Ssam 			/*
68910276Ssam 			 * Make sure the string is nonempty and \n terminated.
69010276Ssam 			 */
69110276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
69210276Ssam 				cbuf[cpos] = '\0';
69310276Ssam 				yylval = copy(cp);
69410276Ssam 				cbuf[cpos] = '\n';
69510276Ssam 				state = ARGS;
69610276Ssam 				return (STRING);
69710276Ssam 			}
69810276Ssam 			break;
69910276Ssam 
70010276Ssam 		case ARGS:
70110276Ssam 			if (isdigit(cbuf[cpos])) {
70210276Ssam 				cp = &cbuf[cpos];
70310276Ssam 				while (isdigit(cbuf[++cpos]))
70410276Ssam 					;
70510276Ssam 				c = cbuf[cpos];
70610276Ssam 				cbuf[cpos] = '\0';
70710276Ssam 				yylval = atoi(cp);
70810276Ssam 				cbuf[cpos] = c;
70910276Ssam 				return (NUMBER);
71010276Ssam 			}
71110276Ssam 			switch (cbuf[cpos++]) {
71210276Ssam 
71310276Ssam 			case '\n':
71410276Ssam 				state = CMD;
71510276Ssam 				return (CRLF);
71610276Ssam 
71710276Ssam 			case ' ':
71810276Ssam 				return (SP);
71910276Ssam 
72010276Ssam 			case ',':
72110276Ssam 				return (COMMA);
72210276Ssam 
72310276Ssam 			case 'A':
72410276Ssam 			case 'a':
72510276Ssam 				return (A);
72610276Ssam 
72710276Ssam 			case 'B':
72810276Ssam 			case 'b':
72910276Ssam 				return (B);
73010276Ssam 
73110276Ssam 			case 'C':
73210276Ssam 			case 'c':
73310276Ssam 				return (C);
73410276Ssam 
73510276Ssam 			case 'E':
73610276Ssam 			case 'e':
73710276Ssam 				return (E);
73810276Ssam 
73910276Ssam 			case 'F':
74010276Ssam 			case 'f':
74110276Ssam 				return (F);
74210276Ssam 
74310276Ssam 			case 'I':
74410276Ssam 			case 'i':
74510276Ssam 				return (I);
74610276Ssam 
74710276Ssam 			case 'L':
74810276Ssam 			case 'l':
74910276Ssam 				return (L);
75010276Ssam 
75110276Ssam 			case 'N':
75210276Ssam 			case 'n':
75310276Ssam 				return (N);
75410276Ssam 
75510276Ssam 			case 'P':
75610276Ssam 			case 'p':
75710276Ssam 				return (P);
75810276Ssam 
75910276Ssam 			case 'R':
76010276Ssam 			case 'r':
76110276Ssam 				return (R);
76210276Ssam 
76310276Ssam 			case 'S':
76410276Ssam 			case 's':
76510276Ssam 				return (S);
76610276Ssam 
76710276Ssam 			case 'T':
76810276Ssam 			case 't':
76910276Ssam 				return (T);
77010276Ssam 
77110276Ssam 			}
77210276Ssam 			break;
77310276Ssam 
77410276Ssam 		default:
77510276Ssam 			fatal("Unknown state in scanner.");
77610276Ssam 		}
77710276Ssam 		yyerror();
77810276Ssam 		state = CMD;
77910276Ssam 		longjmp(errcatch);
78010276Ssam 	}
78110276Ssam }
78210276Ssam 
78310276Ssam upper(s)
78410276Ssam 	char *s;
78510276Ssam {
78610276Ssam 	while (*s != '\0') {
78710276Ssam 		if (islower(*s))
78810276Ssam 			*s = toupper(*s);
78910276Ssam 		s++;
79010276Ssam 	}
79110276Ssam }
79210276Ssam 
79310276Ssam copy(s)
79410276Ssam 	char *s;
79510276Ssam {
79610276Ssam 	char *p;
79710276Ssam 	extern char *malloc();
79810276Ssam 
79910276Ssam 	p = malloc(strlen(s) + 1);
80010276Ssam 	if (p == NULL)
80110276Ssam 		fatal("Ran out of memory.");
80210276Ssam 	strcpy(p, s);
80310276Ssam 	return ((int)p);
80410276Ssam }
80510276Ssam 
80610276Ssam help(s)
80710276Ssam 	char *s;
80810276Ssam {
80910276Ssam 	register struct tab *c;
81010276Ssam 	register int width, NCMDS;
81110276Ssam 
81210276Ssam 	width = 0, NCMDS = 0;
81310276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
81410276Ssam 		int len = strlen(c->name);
81510276Ssam 
81610276Ssam 		if (c->implemented == 0)
81710276Ssam 			len++;
81810276Ssam 		if (len > width)
81910276Ssam 			width = len;
82010276Ssam 		NCMDS++;
82110276Ssam 	}
82210276Ssam 	width = (width + 8) &~ 7;
82310276Ssam 	if (s == 0) {
82410276Ssam 		register int i, j, w;
82510276Ssam 		int columns, lines;
82610276Ssam 
82710276Ssam 		lreply(214,
82810276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
82910276Ssam 		columns = 76 / width;
83010276Ssam 		if (columns == 0)
83110276Ssam 			columns = 1;
83210276Ssam 		lines = (NCMDS + columns - 1) / columns;
83310276Ssam 		for (i = 0; i < lines; i++) {
83410276Ssam 			printf("    ");
83510276Ssam 			for (j = 0; j < columns; j++) {
83610276Ssam 				c = cmdtab + j * lines + i;
83710276Ssam 				printf("%s%c", c->name,
83810276Ssam 					c->implemented ? ' ' : '*');
83910302Ssam 				if (c + lines >= &cmdtab[NCMDS])
84010276Ssam 					break;
84110276Ssam 				w = strlen(c->name);
84210276Ssam 				while (w < width) {
84310276Ssam 					putchar(' ');
84410276Ssam 					w++;
84510276Ssam 				}
84610276Ssam 			}
84710276Ssam 			printf("\r\n");
84810276Ssam 		}
84910276Ssam 		fflush(stdout);
85010276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
85110276Ssam 		return;
85210276Ssam 	}
85310276Ssam 	upper(s);
85410276Ssam 	c = lookup(s);
85510276Ssam 	if (c == (struct tab *)0) {
85610276Ssam 		reply(504, "Unknown command %s.", s);
85710276Ssam 		return;
85810276Ssam 	}
85910276Ssam 	if (c->implemented)
86010276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
86110276Ssam 	else
86210276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
86310276Ssam }
864