xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 33738)
110276Ssam /*
226045Sminshall  * Copyright (c) 1985 Regents of the University of California.
3*33738Sbostic  * All rights reserved.
4*33738Sbostic  *
5*33738Sbostic  * Redistribution and use in source and binary forms are permitted
6*33738Sbostic  * provided that this notice is preserved and that due credit is given
7*33738Sbostic  * to the University of California at Berkeley. The name of the University
8*33738Sbostic  * may not be used to endorse or promote products derived from this
9*33738Sbostic  * software without specific prior written permission. This software
10*33738Sbostic  * is provided ``as is'' without express or implied warranty.
1122501Sdist  */
1222501Sdist 
1322501Sdist /*
1410276Ssam  * Grammar for FTP commands.
1510276Ssam  * See RFC 765.
1610276Ssam  */
1710276Ssam 
1810276Ssam %{
1910276Ssam 
2010276Ssam #ifndef lint
21*33738Sbostic static char sccsid[] = "@(#)ftpcmd.y	5.10 (Berkeley) 03/14/88";
22*33738Sbostic #endif /* not lint */
2310276Ssam 
2410276Ssam #include <sys/types.h>
2510276Ssam #include <sys/socket.h>
2610276Ssam 
2710276Ssam #include <netinet/in.h>
2810276Ssam 
2913033Ssam #include <arpa/ftp.h>
3013033Ssam 
3110276Ssam #include <stdio.h>
3211652Ssam #include <signal.h>
3310276Ssam #include <ctype.h>
3410276Ssam #include <pwd.h>
3510276Ssam #include <setjmp.h>
3626494Sminshall #include <syslog.h>
3710276Ssam 
3810276Ssam extern	struct sockaddr_in data_dest;
3910276Ssam extern	int logged_in;
4010276Ssam extern	struct passwd *pw;
4110276Ssam extern	int guest;
4210276Ssam extern	int logging;
4310276Ssam extern	int type;
4410276Ssam extern	int form;
4510276Ssam extern	int debug;
4611652Ssam extern	int timeout;
4726045Sminshall extern  int pdata;
4810276Ssam extern	char hostname[];
4910276Ssam extern	char *globerr;
5010320Ssam extern	int usedefault;
5126045Sminshall extern	int unique;
5226045Sminshall extern  int transflag;
5326045Sminshall extern  char tmpline[];
5410276Ssam char	**glob();
5510276Ssam 
5610276Ssam static	int cmd_type;
5710276Ssam static	int cmd_form;
5810276Ssam static	int cmd_bytesz;
5926045Sminshall char cbuf[512];
6030945Scsvsj char *fromname;
6110276Ssam 
6210276Ssam char	*index();
6310276Ssam %}
6410276Ssam 
6510276Ssam %token
6610276Ssam 	A	B	C	E	F	I
6710276Ssam 	L	N	P	R	S	T
6810276Ssam 
6910276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
7010276Ssam 
7110276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
7210276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
7310276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
7410276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
7510276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
7610276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
7726045Sminshall 	XCUP	STOU
7810276Ssam 
7910276Ssam 	LEXERR
8010276Ssam 
8110276Ssam %start	cmd_list
8210276Ssam 
8310276Ssam %%
8410276Ssam 
8510276Ssam cmd_list:	/* empty */
8610276Ssam 	|	cmd_list cmd
8730945Scsvsj 		= {
8830945Scsvsj 			fromname = (char *) 0;
8930945Scsvsj 		}
9030945Scsvsj 	|	cmd_list rcmd
9110276Ssam 	;
9210276Ssam 
9310276Ssam cmd:		USER SP username CRLF
9410276Ssam 		= {
9510276Ssam 			extern struct passwd *getpwnam();
9610276Ssam 
9726045Sminshall 			logged_in = 0;
9826494Sminshall 			if (strcmp((char *) $3, "ftp") == 0 ||
9926494Sminshall 			  strcmp((char *) $3, "anonymous") == 0) {
10010324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
10110324Ssam 					guest = 1;
10210324Ssam 					reply(331,
10310276Ssam 				  "Guest login ok, send ident as password.");
10410324Ssam 				}
10526045Sminshall 				else {
10626045Sminshall 					reply(530, "User %s unknown.", $3);
10726045Sminshall 				}
10826494Sminshall 			} else if (checkuser((char *) $3)) {
10910276Ssam 				guest = 0;
11026494Sminshall 				pw = getpwnam((char *) $3);
11126045Sminshall 				if (pw == NULL) {
11226045Sminshall 					reply(530, "User %s unknown.", $3);
11326045Sminshall 				}
11426045Sminshall 				else {
11526045Sminshall 				    reply(331, "Password required for %s.", $3);
11626045Sminshall 				}
11728865Smckusick 			} else {
11828865Smckusick 				reply(530, "User %s access denied.", $3);
11910276Ssam 			}
12026494Sminshall 			free((char *) $3);
12110276Ssam 		}
12210276Ssam 	|	PASS SP password CRLF
12310276Ssam 		= {
12426494Sminshall 			pass((char *) $3);
12526494Sminshall 			free((char *) $3);
12610276Ssam 		}
12710276Ssam 	|	PORT SP host_port CRLF
12810276Ssam 		= {
12910320Ssam 			usedefault = 0;
13026045Sminshall 			if (pdata > 0) {
13126045Sminshall 				(void) close(pdata);
13226045Sminshall 			}
13326045Sminshall 			pdata = -1;
13427107Smckusick 			reply(200, "PORT command successful.");
13510276Ssam 		}
13626045Sminshall 	|	PASV CRLF
13726045Sminshall 		= {
13826045Sminshall 			passive();
13926045Sminshall 		}
14010276Ssam 	|	TYPE SP type_code CRLF
14110276Ssam 		= {
14210276Ssam 			switch (cmd_type) {
14310276Ssam 
14410276Ssam 			case TYPE_A:
14510276Ssam 				if (cmd_form == FORM_N) {
14610276Ssam 					reply(200, "Type set to A.");
14710276Ssam 					type = cmd_type;
14810276Ssam 					form = cmd_form;
14910276Ssam 				} else
15010276Ssam 					reply(504, "Form must be N.");
15110276Ssam 				break;
15210276Ssam 
15310276Ssam 			case TYPE_E:
15410276Ssam 				reply(504, "Type E not implemented.");
15510276Ssam 				break;
15610276Ssam 
15710276Ssam 			case TYPE_I:
15810276Ssam 				reply(200, "Type set to I.");
15910276Ssam 				type = cmd_type;
16010276Ssam 				break;
16110276Ssam 
16210276Ssam 			case TYPE_L:
16310276Ssam 				if (cmd_bytesz == 8) {
16410276Ssam 					reply(200,
16510276Ssam 					    "Type set to L (byte size 8).");
16610276Ssam 					type = cmd_type;
16710276Ssam 				} else
16810276Ssam 					reply(504, "Byte size must be 8.");
16910276Ssam 			}
17010276Ssam 		}
17110276Ssam 	|	STRU SP struct_code CRLF
17210276Ssam 		= {
17310276Ssam 			switch ($3) {
17410276Ssam 
17510276Ssam 			case STRU_F:
17610276Ssam 				reply(200, "STRU F ok.");
17710276Ssam 				break;
17810276Ssam 
17910276Ssam 			default:
18027107Smckusick 				reply(504, "Unimplemented STRU type.");
18110276Ssam 			}
18210276Ssam 		}
18310276Ssam 	|	MODE SP mode_code CRLF
18410276Ssam 		= {
18510276Ssam 			switch ($3) {
18610276Ssam 
18710276Ssam 			case MODE_S:
18810276Ssam 				reply(200, "MODE S ok.");
18910276Ssam 				break;
19010276Ssam 
19110276Ssam 			default:
19210276Ssam 				reply(502, "Unimplemented MODE type.");
19310276Ssam 			}
19410276Ssam 		}
19510276Ssam 	|	ALLO SP NUMBER CRLF
19610276Ssam 		= {
19727107Smckusick 			reply(202, "ALLO command ignored.");
19810276Ssam 		}
19910276Ssam 	|	RETR check_login SP pathname CRLF
20010276Ssam 		= {
20110302Ssam 			if ($2 && $4 != NULL)
20226494Sminshall 				retrieve((char *) 0, (char *) $4);
20310302Ssam 			if ($4 != NULL)
20426494Sminshall 				free((char *) $4);
20510276Ssam 		}
20610276Ssam 	|	STOR check_login SP pathname CRLF
20710276Ssam 		= {
20810302Ssam 			if ($2 && $4 != NULL)
20926494Sminshall 				store((char *) $4, "w");
21010302Ssam 			if ($4 != NULL)
21126494Sminshall 				free((char *) $4);
21210276Ssam 		}
21310276Ssam 	|	APPE check_login SP pathname CRLF
21410276Ssam 		= {
21510302Ssam 			if ($2 && $4 != NULL)
21626494Sminshall 				store((char *) $4, "a");
21710302Ssam 			if ($4 != NULL)
21826494Sminshall 				free((char *) $4);
21910276Ssam 		}
22010276Ssam 	|	NLST check_login CRLF
22110276Ssam 		= {
22210276Ssam 			if ($2)
22311217Ssam 				retrieve("/bin/ls", "");
22410276Ssam 		}
22510276Ssam 	|	NLST check_login SP pathname CRLF
22610276Ssam 		= {
22710302Ssam 			if ($2 && $4 != NULL)
22826494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
22910302Ssam 			if ($4 != NULL)
23026494Sminshall 				free((char *) $4);
23110276Ssam 		}
23210276Ssam 	|	LIST check_login CRLF
23310276Ssam 		= {
23410276Ssam 			if ($2)
23510318Ssam 				retrieve("/bin/ls -lg", "");
23610276Ssam 		}
23710276Ssam 	|	LIST check_login SP pathname CRLF
23810276Ssam 		= {
23910302Ssam 			if ($2 && $4 != NULL)
24026494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
24110302Ssam 			if ($4 != NULL)
24226494Sminshall 				free((char *) $4);
24310276Ssam 		}
24410276Ssam 	|	DELE check_login SP pathname CRLF
24510276Ssam 		= {
24610302Ssam 			if ($2 && $4 != NULL)
24726494Sminshall 				delete((char *) $4);
24810302Ssam 			if ($4 != NULL)
24926494Sminshall 				free((char *) $4);
25010276Ssam 		}
25130945Scsvsj 	|	RNTO SP pathname CRLF
25230945Scsvsj 		= {
25330945Scsvsj 			if (fromname) {
25430945Scsvsj 				renamecmd(fromname, (char *) $3);
25530945Scsvsj 				free(fromname);
25630945Scsvsj 				fromname = (char *) 0;
25730945Scsvsj 			} else {
25830945Scsvsj 				reply(503, "Bad sequence of commands.");
25930945Scsvsj 			}
26030945Scsvsj 			free((char *) $3);
26130945Scsvsj 		}
26226045Sminshall 	|	ABOR CRLF
26326045Sminshall 		= {
26427107Smckusick 			reply(225, "ABOR command successful.");
26526045Sminshall 		}
26610276Ssam 	|	CWD check_login CRLF
26710276Ssam 		= {
26810276Ssam 			if ($2)
26910276Ssam 				cwd(pw->pw_dir);
27010276Ssam 		}
27110276Ssam 	|	CWD check_login SP pathname CRLF
27210276Ssam 		= {
27310302Ssam 			if ($2 && $4 != NULL)
27426494Sminshall 				cwd((char *) $4);
27510302Ssam 			if ($4 != NULL)
27626494Sminshall 				free((char *) $4);
27710276Ssam 		}
27810276Ssam 	|	HELP CRLF
27910276Ssam 		= {
28026494Sminshall 			help((char *) 0);
28110276Ssam 		}
28210276Ssam 	|	HELP SP STRING CRLF
28310276Ssam 		= {
28426494Sminshall 			help((char *) $3);
28510276Ssam 		}
28610276Ssam 	|	NOOP CRLF
28710276Ssam 		= {
28827107Smckusick 			reply(200, "NOOP command successful.");
28910276Ssam 		}
29010276Ssam 	|	XMKD check_login SP pathname CRLF
29110276Ssam 		= {
29210302Ssam 			if ($2 && $4 != NULL)
29326494Sminshall 				makedir((char *) $4);
29410302Ssam 			if ($4 != NULL)
29526494Sminshall 				free((char *) $4);
29610276Ssam 		}
29710276Ssam 	|	XRMD check_login SP pathname CRLF
29810276Ssam 		= {
29910302Ssam 			if ($2 && $4 != NULL)
30026494Sminshall 				removedir((char *) $4);
30110302Ssam 			if ($4 != NULL)
30226494Sminshall 				free((char *) $4);
30310276Ssam 		}
30410276Ssam 	|	XPWD check_login CRLF
30510276Ssam 		= {
30610276Ssam 			if ($2)
30710302Ssam 				pwd();
30810276Ssam 		}
30910276Ssam 	|	XCUP check_login CRLF
31010276Ssam 		= {
31110276Ssam 			if ($2)
31210276Ssam 				cwd("..");
31310276Ssam 		}
31426045Sminshall 	|	STOU check_login SP pathname CRLF
31526045Sminshall 		= {
31626045Sminshall 			if ($2 && $4 != NULL) {
31726045Sminshall 				unique++;
31826494Sminshall 				store((char *) $4, "w");
31926045Sminshall 				unique = 0;
32026045Sminshall 			}
32126045Sminshall 			if ($4 != NULL)
32226494Sminshall 				free((char *) $4);
32326045Sminshall 		}
32410276Ssam 	|	QUIT CRLF
32510276Ssam 		= {
32610276Ssam 			reply(221, "Goodbye.");
32713246Ssam 			dologout(0);
32810276Ssam 		}
32910276Ssam 	|	error CRLF
33010276Ssam 		= {
33110276Ssam 			yyerrok;
33210276Ssam 		}
33310276Ssam 	;
33410276Ssam 
33530945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
33630945Scsvsj 		= {
33730945Scsvsj 			char *renamefrom();
33830945Scsvsj 
33930945Scsvsj 			if ($2 && $4) {
34030945Scsvsj 				fromname = renamefrom((char *) $4);
34130945Scsvsj 				if (fromname == (char *) 0 && $4) {
34230945Scsvsj 					free((char *) $4);
34330945Scsvsj 				}
34430945Scsvsj 			}
34530945Scsvsj 		}
34630945Scsvsj 	;
34730945Scsvsj 
34810276Ssam username:	STRING
34910276Ssam 	;
35010276Ssam 
35110276Ssam password:	STRING
35210276Ssam 	;
35310276Ssam 
35410276Ssam byte_size:	NUMBER
35510276Ssam 	;
35610276Ssam 
35710276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
35810276Ssam 		NUMBER COMMA NUMBER
35910276Ssam 		= {
36010276Ssam 			register char *a, *p;
36110276Ssam 
36210276Ssam 			a = (char *)&data_dest.sin_addr;
36310276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
36410276Ssam 			p = (char *)&data_dest.sin_port;
36510276Ssam 			p[0] = $9; p[1] = $11;
36610324Ssam 			data_dest.sin_family = AF_INET;
36710276Ssam 		}
36810276Ssam 	;
36910276Ssam 
37010276Ssam form_code:	N
37110276Ssam 	= {
37210276Ssam 		$$ = FORM_N;
37310276Ssam 	}
37410276Ssam 	|	T
37510276Ssam 	= {
37610276Ssam 		$$ = FORM_T;
37710276Ssam 	}
37810276Ssam 	|	C
37910276Ssam 	= {
38010276Ssam 		$$ = FORM_C;
38110276Ssam 	}
38210276Ssam 	;
38310276Ssam 
38410276Ssam type_code:	A
38510276Ssam 	= {
38610276Ssam 		cmd_type = TYPE_A;
38710276Ssam 		cmd_form = FORM_N;
38810276Ssam 	}
38910276Ssam 	|	A SP form_code
39010276Ssam 	= {
39110276Ssam 		cmd_type = TYPE_A;
39210276Ssam 		cmd_form = $3;
39310276Ssam 	}
39410276Ssam 	|	E
39510276Ssam 	= {
39610276Ssam 		cmd_type = TYPE_E;
39710276Ssam 		cmd_form = FORM_N;
39810276Ssam 	}
39910276Ssam 	|	E SP form_code
40010276Ssam 	= {
40110276Ssam 		cmd_type = TYPE_E;
40210276Ssam 		cmd_form = $3;
40310276Ssam 	}
40410276Ssam 	|	I
40510276Ssam 	= {
40610276Ssam 		cmd_type = TYPE_I;
40710276Ssam 	}
40810276Ssam 	|	L
40910276Ssam 	= {
41010276Ssam 		cmd_type = TYPE_L;
41110276Ssam 		cmd_bytesz = 8;
41210276Ssam 	}
41310276Ssam 	|	L SP byte_size
41410276Ssam 	= {
41510276Ssam 		cmd_type = TYPE_L;
41610276Ssam 		cmd_bytesz = $3;
41710276Ssam 	}
41810276Ssam 	/* this is for a bug in the BBN ftp */
41910276Ssam 	|	L byte_size
42010276Ssam 	= {
42110276Ssam 		cmd_type = TYPE_L;
42210276Ssam 		cmd_bytesz = $2;
42310276Ssam 	}
42410276Ssam 	;
42510276Ssam 
42610276Ssam struct_code:	F
42710276Ssam 	= {
42810276Ssam 		$$ = STRU_F;
42910276Ssam 	}
43010276Ssam 	|	R
43110276Ssam 	= {
43210276Ssam 		$$ = STRU_R;
43310276Ssam 	}
43410276Ssam 	|	P
43510276Ssam 	= {
43610276Ssam 		$$ = STRU_P;
43710276Ssam 	}
43810276Ssam 	;
43910276Ssam 
44010276Ssam mode_code:	S
44110276Ssam 	= {
44210276Ssam 		$$ = MODE_S;
44310276Ssam 	}
44410276Ssam 	|	B
44510276Ssam 	= {
44610276Ssam 		$$ = MODE_B;
44710276Ssam 	}
44810276Ssam 	|	C
44910276Ssam 	= {
45010276Ssam 		$$ = MODE_C;
45110276Ssam 	}
45210276Ssam 	;
45310276Ssam 
45410276Ssam pathname:	pathstring
45510276Ssam 	= {
45627107Smckusick 		/*
45727107Smckusick 		 * Problem: this production is used for all pathname
45827107Smckusick 		 * processing, but only gives a 550 error reply.
45927107Smckusick 		 * This is a valid reply in some cases but not in others.
46027107Smckusick 		 */
46126494Sminshall 		if ($1 && strncmp((char *) $1, "~", 1) == 0) {
46226494Sminshall 			$$ = (int)*glob((char *) $1);
46310302Ssam 			if (globerr != NULL) {
46410276Ssam 				reply(550, globerr);
46510302Ssam 				$$ = NULL;
46610302Ssam 			}
46726494Sminshall 			free((char *) $1);
46810276Ssam 		} else
46910276Ssam 			$$ = $1;
47010276Ssam 	}
47110276Ssam 	;
47210276Ssam 
47310276Ssam pathstring:	STRING
47410276Ssam 	;
47510276Ssam 
47610276Ssam check_login:	/* empty */
47710276Ssam 	= {
47810276Ssam 		if (logged_in)
47910276Ssam 			$$ = 1;
48010276Ssam 		else {
48110276Ssam 			reply(530, "Please login with USER and PASS.");
48210276Ssam 			$$ = 0;
48310276Ssam 		}
48410276Ssam 	}
48510276Ssam 	;
48610276Ssam 
48710276Ssam %%
48810276Ssam 
48910276Ssam extern jmp_buf errcatch;
49010276Ssam 
49110276Ssam #define	CMD	0	/* beginning of command */
49210276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
49310276Ssam #define	STR1	2	/* expect SP followed by STRING */
49410276Ssam #define	STR2	3	/* expect STRING */
49510276Ssam #define	OSTR	4	/* optional STRING */
49610276Ssam 
49710276Ssam struct tab {
49810276Ssam 	char	*name;
49910276Ssam 	short	token;
50010276Ssam 	short	state;
50110276Ssam 	short	implemented;	/* 1 if command is implemented */
50210276Ssam 	char	*help;
50310276Ssam };
50410276Ssam 
50510276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
50610276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
50710276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
50810276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
50910276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
51010276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
51110276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
51226045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
51310276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
51410276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
51510276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
51610276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
51710276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
51810276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
51910276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
52010276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
52110276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
52210276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
52310276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
52410276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
52510276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
52610276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
52710276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
52810276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
52910276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
53026045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
53110276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
53210276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
53310276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
53410276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
53510276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
53610276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
53710276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
53810276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
53910276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
54026045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
54110276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
54226045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
54310276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
54426045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
54510276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
54626045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54710276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54826045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
54910276Ssam 	{ NULL,   0,    0,    0,	0 }
55010276Ssam };
55110276Ssam 
55210276Ssam struct tab *
55310276Ssam lookup(cmd)
55410276Ssam 	char *cmd;
55510276Ssam {
55610276Ssam 	register struct tab *p;
55710276Ssam 
55810276Ssam 	for (p = cmdtab; p->name != NULL; p++)
55910276Ssam 		if (strcmp(cmd, p->name) == 0)
56010276Ssam 			return (p);
56110276Ssam 	return (0);
56210276Ssam }
56310276Ssam 
56413033Ssam #include <arpa/telnet.h>
56510276Ssam 
56610276Ssam /*
56710276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
56810276Ssam  */
56910276Ssam char *
57010276Ssam getline(s, n, iop)
57110276Ssam 	char *s;
57210276Ssam 	register FILE *iop;
57310276Ssam {
57410276Ssam 	register c;
57526494Sminshall 	register char *cs;
57610276Ssam 
57710276Ssam 	cs = s;
57827751Sminshall /* tmpline may contain saved command from urgent mode interruption */
57926045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
58026045Sminshall 		*cs++ = tmpline[c];
58126045Sminshall 		if (tmpline[c] == '\n') {
58226045Sminshall 			*cs++ = '\0';
58326045Sminshall 			if (debug) {
58426494Sminshall 				syslog(LOG_DEBUG, "FTPD: command: %s", s);
58526045Sminshall 			}
58626045Sminshall 			tmpline[0] = '\0';
58726045Sminshall 			return(s);
58826045Sminshall 		}
58926045Sminshall 		if (c == 0) {
59026045Sminshall 			tmpline[0] = '\0';
59126045Sminshall 		}
59226045Sminshall 	}
59327751Sminshall 	while (--n > 0 && (c = getc(iop)) != EOF) {
59427751Sminshall 		c = 0377 & c;
59510276Ssam 		while (c == IAC) {
59627751Sminshall 			switch (c = 0377 & getc(iop)) {
59727751Sminshall 			case WILL:
59827751Sminshall 			case WONT:
59927751Sminshall 				c = 0377 & getc(iop);
60027751Sminshall 				printf("%c%c%c", IAC, WONT, c);
60127751Sminshall 				(void) fflush(stdout);
60227751Sminshall 				break;
60327751Sminshall 			case DO:
60427751Sminshall 			case DONT:
60527751Sminshall 				c = 0377 & getc(iop);
60627751Sminshall 				printf("%c%c%c", IAC, DONT, c);
60727751Sminshall 				(void) fflush(stdout);
60827751Sminshall 				break;
60927751Sminshall 			default:
61027751Sminshall 				break;
61127751Sminshall 			}
61227751Sminshall 			c = 0377 & getc(iop); /* try next character */
61310276Ssam 		}
61410276Ssam 		*cs++ = c;
61510276Ssam 		if (c=='\n')
61610276Ssam 			break;
61710276Ssam 	}
61827751Sminshall 	if (c == EOF && cs == s)
61918303Sralph 		return (NULL);
62010276Ssam 	*cs++ = '\0';
62111652Ssam 	if (debug) {
62226494Sminshall 		syslog(LOG_DEBUG, "FTPD: command: %s", s);
62311652Ssam 	}
62410276Ssam 	return (s);
62510276Ssam }
62610276Ssam 
62711652Ssam static int
62811652Ssam toolong()
62911652Ssam {
63026494Sminshall 	time_t now;
63111652Ssam 	extern char *ctime();
63226494Sminshall 	extern time_t time();
63311652Ssam 
63411652Ssam 	reply(421,
63511652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
63626494Sminshall 	(void) time(&now);
63711652Ssam 	if (logging) {
63826494Sminshall 		syslog(LOG_INFO,
63911652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
64011652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
64111652Ssam 	}
64213246Ssam 	dologout(1);
64311652Ssam }
64411652Ssam 
64510276Ssam yylex()
64610276Ssam {
64710276Ssam 	static int cpos, state;
64810276Ssam 	register char *cp;
64910276Ssam 	register struct tab *p;
65010276Ssam 	int n;
65110276Ssam 	char c;
65210276Ssam 
65310276Ssam 	for (;;) {
65410276Ssam 		switch (state) {
65510276Ssam 
65610276Ssam 		case CMD:
65726494Sminshall 			(void) signal(SIGALRM, toolong);
65826494Sminshall 			(void) alarm((unsigned) timeout);
65910276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
66010276Ssam 				reply(221, "You could at least say goodbye.");
66113246Ssam 				dologout(0);
66210276Ssam 			}
66326494Sminshall 			(void) alarm(0);
66410276Ssam 			if (index(cbuf, '\r')) {
66510276Ssam 				cp = index(cbuf, '\r');
66610276Ssam 				cp[0] = '\n'; cp[1] = 0;
66710276Ssam 			}
66810276Ssam 			if (index(cbuf, ' '))
66910276Ssam 				cpos = index(cbuf, ' ') - cbuf;
67010276Ssam 			else
67126045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
67226045Sminshall 			if (cpos == 0) {
67310276Ssam 				cpos = 4;
67426045Sminshall 			}
67510276Ssam 			c = cbuf[cpos];
67610276Ssam 			cbuf[cpos] = '\0';
67710276Ssam 			upper(cbuf);
67810276Ssam 			p = lookup(cbuf);
67910276Ssam 			cbuf[cpos] = c;
68010276Ssam 			if (p != 0) {
68110276Ssam 				if (p->implemented == 0) {
68210276Ssam 					nack(p->name);
68326494Sminshall 					longjmp(errcatch,0);
68410276Ssam 					/* NOTREACHED */
68510276Ssam 				}
68610276Ssam 				state = p->state;
68710276Ssam 				yylval = (int) p->name;
68810276Ssam 				return (p->token);
68910276Ssam 			}
69010276Ssam 			break;
69110276Ssam 
69210276Ssam 		case OSTR:
69310276Ssam 			if (cbuf[cpos] == '\n') {
69410276Ssam 				state = CMD;
69510276Ssam 				return (CRLF);
69610276Ssam 			}
69710276Ssam 			/* FALL THRU */
69810276Ssam 
69910276Ssam 		case STR1:
70010276Ssam 			if (cbuf[cpos] == ' ') {
70110276Ssam 				cpos++;
70210276Ssam 				state = STR2;
70310276Ssam 				return (SP);
70410276Ssam 			}
70510276Ssam 			break;
70610276Ssam 
70710276Ssam 		case STR2:
70810276Ssam 			cp = &cbuf[cpos];
70910276Ssam 			n = strlen(cp);
71010276Ssam 			cpos += n - 1;
71110276Ssam 			/*
71210276Ssam 			 * Make sure the string is nonempty and \n terminated.
71310276Ssam 			 */
71410276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
71510276Ssam 				cbuf[cpos] = '\0';
71610276Ssam 				yylval = copy(cp);
71710276Ssam 				cbuf[cpos] = '\n';
71810276Ssam 				state = ARGS;
71910276Ssam 				return (STRING);
72010276Ssam 			}
72110276Ssam 			break;
72210276Ssam 
72310276Ssam 		case ARGS:
72410276Ssam 			if (isdigit(cbuf[cpos])) {
72510276Ssam 				cp = &cbuf[cpos];
72610276Ssam 				while (isdigit(cbuf[++cpos]))
72710276Ssam 					;
72810276Ssam 				c = cbuf[cpos];
72910276Ssam 				cbuf[cpos] = '\0';
73010276Ssam 				yylval = atoi(cp);
73110276Ssam 				cbuf[cpos] = c;
73210276Ssam 				return (NUMBER);
73310276Ssam 			}
73410276Ssam 			switch (cbuf[cpos++]) {
73510276Ssam 
73610276Ssam 			case '\n':
73710276Ssam 				state = CMD;
73810276Ssam 				return (CRLF);
73910276Ssam 
74010276Ssam 			case ' ':
74110276Ssam 				return (SP);
74210276Ssam 
74310276Ssam 			case ',':
74410276Ssam 				return (COMMA);
74510276Ssam 
74610276Ssam 			case 'A':
74710276Ssam 			case 'a':
74810276Ssam 				return (A);
74910276Ssam 
75010276Ssam 			case 'B':
75110276Ssam 			case 'b':
75210276Ssam 				return (B);
75310276Ssam 
75410276Ssam 			case 'C':
75510276Ssam 			case 'c':
75610276Ssam 				return (C);
75710276Ssam 
75810276Ssam 			case 'E':
75910276Ssam 			case 'e':
76010276Ssam 				return (E);
76110276Ssam 
76210276Ssam 			case 'F':
76310276Ssam 			case 'f':
76410276Ssam 				return (F);
76510276Ssam 
76610276Ssam 			case 'I':
76710276Ssam 			case 'i':
76810276Ssam 				return (I);
76910276Ssam 
77010276Ssam 			case 'L':
77110276Ssam 			case 'l':
77210276Ssam 				return (L);
77310276Ssam 
77410276Ssam 			case 'N':
77510276Ssam 			case 'n':
77610276Ssam 				return (N);
77710276Ssam 
77810276Ssam 			case 'P':
77910276Ssam 			case 'p':
78010276Ssam 				return (P);
78110276Ssam 
78210276Ssam 			case 'R':
78310276Ssam 			case 'r':
78410276Ssam 				return (R);
78510276Ssam 
78610276Ssam 			case 'S':
78710276Ssam 			case 's':
78810276Ssam 				return (S);
78910276Ssam 
79010276Ssam 			case 'T':
79110276Ssam 			case 't':
79210276Ssam 				return (T);
79310276Ssam 
79410276Ssam 			}
79510276Ssam 			break;
79610276Ssam 
79710276Ssam 		default:
79810276Ssam 			fatal("Unknown state in scanner.");
79910276Ssam 		}
80026494Sminshall 		yyerror((char *) 0);
80110276Ssam 		state = CMD;
80226494Sminshall 		longjmp(errcatch,0);
80310276Ssam 	}
80410276Ssam }
80510276Ssam 
80610276Ssam upper(s)
80710276Ssam 	char *s;
80810276Ssam {
80910276Ssam 	while (*s != '\0') {
81010276Ssam 		if (islower(*s))
81110276Ssam 			*s = toupper(*s);
81210276Ssam 		s++;
81310276Ssam 	}
81410276Ssam }
81510276Ssam 
81610276Ssam copy(s)
81710276Ssam 	char *s;
81810276Ssam {
81910276Ssam 	char *p;
82026494Sminshall 	extern char *malloc(), *strcpy();
82110276Ssam 
82226494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
82310276Ssam 	if (p == NULL)
82410276Ssam 		fatal("Ran out of memory.");
82526494Sminshall 	(void) strcpy(p, s);
82610276Ssam 	return ((int)p);
82710276Ssam }
82810276Ssam 
82910276Ssam help(s)
83010276Ssam 	char *s;
83110276Ssam {
83210276Ssam 	register struct tab *c;
83310276Ssam 	register int width, NCMDS;
83410276Ssam 
83510276Ssam 	width = 0, NCMDS = 0;
83610276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
83731132Smckusick 		int len = strlen(c->name) + 1;
83810276Ssam 
83910276Ssam 		if (len > width)
84010276Ssam 			width = len;
84110276Ssam 		NCMDS++;
84210276Ssam 	}
84310276Ssam 	width = (width + 8) &~ 7;
84410276Ssam 	if (s == 0) {
84510276Ssam 		register int i, j, w;
84610276Ssam 		int columns, lines;
84710276Ssam 
84810276Ssam 		lreply(214,
84910276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
85010276Ssam 		columns = 76 / width;
85110276Ssam 		if (columns == 0)
85210276Ssam 			columns = 1;
85310276Ssam 		lines = (NCMDS + columns - 1) / columns;
85410276Ssam 		for (i = 0; i < lines; i++) {
85527107Smckusick 			printf("   ");
85610276Ssam 			for (j = 0; j < columns; j++) {
85710276Ssam 				c = cmdtab + j * lines + i;
85810276Ssam 				printf("%s%c", c->name,
85910276Ssam 					c->implemented ? ' ' : '*');
86010302Ssam 				if (c + lines >= &cmdtab[NCMDS])
86110276Ssam 					break;
86231132Smckusick 				w = strlen(c->name) + 1;
86310276Ssam 				while (w < width) {
86410276Ssam 					putchar(' ');
86510276Ssam 					w++;
86610276Ssam 				}
86710276Ssam 			}
86810276Ssam 			printf("\r\n");
86910276Ssam 		}
87026494Sminshall 		(void) fflush(stdout);
87110276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
87210276Ssam 		return;
87310276Ssam 	}
87410276Ssam 	upper(s);
87510276Ssam 	c = lookup(s);
87610276Ssam 	if (c == (struct tab *)0) {
87727107Smckusick 		reply(502, "Unknown command %s.", s);
87810276Ssam 		return;
87910276Ssam 	}
88010276Ssam 	if (c->implemented)
88110276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
88210276Ssam 	else
88310276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
88410276Ssam }
885