xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 28865)
110276Ssam /*
226045Sminshall  * 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*28865Smckusick static	char sccsid[] = "@(#)ftpcmd.y	5.7 (Berkeley) 05/28/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>
3026494Sminshall #include <syslog.h>
3110276Ssam 
3210276Ssam extern	struct sockaddr_in data_dest;
3310276Ssam extern	int logged_in;
3410276Ssam extern	struct passwd *pw;
3510276Ssam extern	int guest;
3610276Ssam extern	int logging;
3710276Ssam extern	int type;
3810276Ssam extern	int form;
3910276Ssam extern	int debug;
4011652Ssam extern	int timeout;
4126045Sminshall extern  int pdata;
4210276Ssam extern	char hostname[];
4310276Ssam extern	char *globerr;
4410320Ssam extern	int usedefault;
4526045Sminshall extern	int unique;
4626045Sminshall extern  int transflag;
4726045Sminshall extern  char tmpline[];
4810276Ssam char	**glob();
4910276Ssam 
5010276Ssam static	int cmd_type;
5110276Ssam static	int cmd_form;
5210276Ssam static	int cmd_bytesz;
5326045Sminshall char cbuf[512];
5410276Ssam 
5510276Ssam char	*index();
5610276Ssam %}
5710276Ssam 
5810276Ssam %token
5910276Ssam 	A	B	C	E	F	I
6010276Ssam 	L	N	P	R	S	T
6110276Ssam 
6210276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
6310276Ssam 
6410276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
6510276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
6610276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
6710276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
6810276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
6910276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
7026045Sminshall 	XCUP	STOU
7110276Ssam 
7210276Ssam 	LEXERR
7310276Ssam 
7410276Ssam %start	cmd_list
7510276Ssam 
7610276Ssam %%
7710276Ssam 
7810276Ssam cmd_list:	/* empty */
7910276Ssam 	|	cmd_list cmd
8010276Ssam 	;
8110276Ssam 
8210276Ssam cmd:		USER SP username CRLF
8310276Ssam 		= {
8410276Ssam 			extern struct passwd *getpwnam();
8510276Ssam 
8626045Sminshall 			logged_in = 0;
8726494Sminshall 			if (strcmp((char *) $3, "ftp") == 0 ||
8826494Sminshall 			  strcmp((char *) $3, "anonymous") == 0) {
8910324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
9010324Ssam 					guest = 1;
9110324Ssam 					reply(331,
9210276Ssam 				  "Guest login ok, send ident as password.");
9310324Ssam 				}
9426045Sminshall 				else {
9526045Sminshall 					reply(530, "User %s unknown.", $3);
9626045Sminshall 				}
9726494Sminshall 			} else if (checkuser((char *) $3)) {
9810276Ssam 				guest = 0;
9926494Sminshall 				pw = getpwnam((char *) $3);
10026045Sminshall 				if (pw == NULL) {
10126045Sminshall 					reply(530, "User %s unknown.", $3);
10226045Sminshall 				}
10326045Sminshall 				else {
10426045Sminshall 				    reply(331, "Password required for %s.", $3);
10526045Sminshall 				}
106*28865Smckusick 			} else {
107*28865Smckusick 				reply(530, "User %s access denied.", $3);
10810276Ssam 			}
10926494Sminshall 			free((char *) $3);
11010276Ssam 		}
11110276Ssam 	|	PASS SP password CRLF
11210276Ssam 		= {
11326494Sminshall 			pass((char *) $3);
11426494Sminshall 			free((char *) $3);
11510276Ssam 		}
11610276Ssam 	|	PORT SP host_port CRLF
11710276Ssam 		= {
11810320Ssam 			usedefault = 0;
11926045Sminshall 			if (pdata > 0) {
12026045Sminshall 				(void) close(pdata);
12126045Sminshall 			}
12226045Sminshall 			pdata = -1;
12327107Smckusick 			reply(200, "PORT command successful.");
12410276Ssam 		}
12526045Sminshall 	|	PASV CRLF
12626045Sminshall 		= {
12726045Sminshall 			passive();
12826045Sminshall 		}
12910276Ssam 	|	TYPE SP type_code CRLF
13010276Ssam 		= {
13110276Ssam 			switch (cmd_type) {
13210276Ssam 
13310276Ssam 			case TYPE_A:
13410276Ssam 				if (cmd_form == FORM_N) {
13510276Ssam 					reply(200, "Type set to A.");
13610276Ssam 					type = cmd_type;
13710276Ssam 					form = cmd_form;
13810276Ssam 				} else
13910276Ssam 					reply(504, "Form must be N.");
14010276Ssam 				break;
14110276Ssam 
14210276Ssam 			case TYPE_E:
14310276Ssam 				reply(504, "Type E not implemented.");
14410276Ssam 				break;
14510276Ssam 
14610276Ssam 			case TYPE_I:
14710276Ssam 				reply(200, "Type set to I.");
14810276Ssam 				type = cmd_type;
14910276Ssam 				break;
15010276Ssam 
15110276Ssam 			case TYPE_L:
15210276Ssam 				if (cmd_bytesz == 8) {
15310276Ssam 					reply(200,
15410276Ssam 					    "Type set to L (byte size 8).");
15510276Ssam 					type = cmd_type;
15610276Ssam 				} else
15710276Ssam 					reply(504, "Byte size must be 8.");
15810276Ssam 			}
15910276Ssam 		}
16010276Ssam 	|	STRU SP struct_code CRLF
16110276Ssam 		= {
16210276Ssam 			switch ($3) {
16310276Ssam 
16410276Ssam 			case STRU_F:
16510276Ssam 				reply(200, "STRU F ok.");
16610276Ssam 				break;
16710276Ssam 
16810276Ssam 			default:
16927107Smckusick 				reply(504, "Unimplemented STRU type.");
17010276Ssam 			}
17110276Ssam 		}
17210276Ssam 	|	MODE SP mode_code CRLF
17310276Ssam 		= {
17410276Ssam 			switch ($3) {
17510276Ssam 
17610276Ssam 			case MODE_S:
17710276Ssam 				reply(200, "MODE S ok.");
17810276Ssam 				break;
17910276Ssam 
18010276Ssam 			default:
18110276Ssam 				reply(502, "Unimplemented MODE type.");
18210276Ssam 			}
18310276Ssam 		}
18410276Ssam 	|	ALLO SP NUMBER CRLF
18510276Ssam 		= {
18627107Smckusick 			reply(202, "ALLO command ignored.");
18710276Ssam 		}
18810276Ssam 	|	RETR check_login SP pathname CRLF
18910276Ssam 		= {
19010302Ssam 			if ($2 && $4 != NULL)
19126494Sminshall 				retrieve((char *) 0, (char *) $4);
19210302Ssam 			if ($4 != NULL)
19326494Sminshall 				free((char *) $4);
19410276Ssam 		}
19510276Ssam 	|	STOR check_login SP pathname CRLF
19610276Ssam 		= {
19710302Ssam 			if ($2 && $4 != NULL)
19826494Sminshall 				store((char *) $4, "w");
19910302Ssam 			if ($4 != NULL)
20026494Sminshall 				free((char *) $4);
20110276Ssam 		}
20210276Ssam 	|	APPE check_login SP pathname CRLF
20310276Ssam 		= {
20410302Ssam 			if ($2 && $4 != NULL)
20526494Sminshall 				store((char *) $4, "a");
20610302Ssam 			if ($4 != NULL)
20726494Sminshall 				free((char *) $4);
20810276Ssam 		}
20910276Ssam 	|	NLST check_login CRLF
21010276Ssam 		= {
21110276Ssam 			if ($2)
21211217Ssam 				retrieve("/bin/ls", "");
21310276Ssam 		}
21410276Ssam 	|	NLST check_login SP pathname CRLF
21510276Ssam 		= {
21610302Ssam 			if ($2 && $4 != NULL)
21726494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
21810302Ssam 			if ($4 != NULL)
21926494Sminshall 				free((char *) $4);
22010276Ssam 		}
22110276Ssam 	|	LIST check_login CRLF
22210276Ssam 		= {
22310276Ssam 			if ($2)
22410318Ssam 				retrieve("/bin/ls -lg", "");
22510276Ssam 		}
22610276Ssam 	|	LIST check_login SP pathname CRLF
22710276Ssam 		= {
22810302Ssam 			if ($2 && $4 != NULL)
22926494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
23010302Ssam 			if ($4 != NULL)
23126494Sminshall 				free((char *) $4);
23210276Ssam 		}
23310276Ssam 	|	DELE check_login SP pathname CRLF
23410276Ssam 		= {
23510302Ssam 			if ($2 && $4 != NULL)
23626494Sminshall 				delete((char *) $4);
23710302Ssam 			if ($4 != NULL)
23826494Sminshall 				free((char *) $4);
23910276Ssam 		}
24026045Sminshall 	|	ABOR CRLF
24126045Sminshall 		= {
24227107Smckusick 			reply(225, "ABOR command successful.");
24326045Sminshall 		}
24410276Ssam 	|	CWD check_login CRLF
24510276Ssam 		= {
24610276Ssam 			if ($2)
24710276Ssam 				cwd(pw->pw_dir);
24810276Ssam 		}
24910276Ssam 	|	CWD check_login SP pathname CRLF
25010276Ssam 		= {
25110302Ssam 			if ($2 && $4 != NULL)
25226494Sminshall 				cwd((char *) $4);
25310302Ssam 			if ($4 != NULL)
25426494Sminshall 				free((char *) $4);
25510276Ssam 		}
25610276Ssam 	|	rename_cmd
25710276Ssam 	|	HELP CRLF
25810276Ssam 		= {
25926494Sminshall 			help((char *) 0);
26010276Ssam 		}
26110276Ssam 	|	HELP SP STRING CRLF
26210276Ssam 		= {
26326494Sminshall 			help((char *) $3);
26410276Ssam 		}
26510276Ssam 	|	NOOP CRLF
26610276Ssam 		= {
26727107Smckusick 			reply(200, "NOOP command successful.");
26810276Ssam 		}
26910276Ssam 	|	XMKD check_login SP pathname CRLF
27010276Ssam 		= {
27110302Ssam 			if ($2 && $4 != NULL)
27226494Sminshall 				makedir((char *) $4);
27310302Ssam 			if ($4 != NULL)
27426494Sminshall 				free((char *) $4);
27510276Ssam 		}
27610276Ssam 	|	XRMD check_login SP pathname CRLF
27710276Ssam 		= {
27810302Ssam 			if ($2 && $4 != NULL)
27926494Sminshall 				removedir((char *) $4);
28010302Ssam 			if ($4 != NULL)
28126494Sminshall 				free((char *) $4);
28210276Ssam 		}
28310276Ssam 	|	XPWD check_login CRLF
28410276Ssam 		= {
28510276Ssam 			if ($2)
28610302Ssam 				pwd();
28710276Ssam 		}
28810276Ssam 	|	XCUP check_login CRLF
28910276Ssam 		= {
29010276Ssam 			if ($2)
29110276Ssam 				cwd("..");
29210276Ssam 		}
29326045Sminshall 	|	STOU check_login SP pathname CRLF
29426045Sminshall 		= {
29526045Sminshall 			if ($2 && $4 != NULL) {
29626045Sminshall 				unique++;
29726494Sminshall 				store((char *) $4, "w");
29826045Sminshall 				unique = 0;
29926045Sminshall 			}
30026045Sminshall 			if ($4 != NULL)
30126494Sminshall 				free((char *) $4);
30226045Sminshall 		}
30310276Ssam 	|	QUIT CRLF
30410276Ssam 		= {
30510276Ssam 			reply(221, "Goodbye.");
30613246Ssam 			dologout(0);
30710276Ssam 		}
30810276Ssam 	|	error CRLF
30910276Ssam 		= {
31010276Ssam 			yyerrok;
31110276Ssam 		}
31210276Ssam 	;
31310276Ssam 
31410276Ssam username:	STRING
31510276Ssam 	;
31610276Ssam 
31710276Ssam password:	STRING
31810276Ssam 	;
31910276Ssam 
32010276Ssam byte_size:	NUMBER
32110276Ssam 	;
32210276Ssam 
32310276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
32410276Ssam 		NUMBER COMMA NUMBER
32510276Ssam 		= {
32610276Ssam 			register char *a, *p;
32710276Ssam 
32810276Ssam 			a = (char *)&data_dest.sin_addr;
32910276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
33010276Ssam 			p = (char *)&data_dest.sin_port;
33110276Ssam 			p[0] = $9; p[1] = $11;
33210324Ssam 			data_dest.sin_family = AF_INET;
33310276Ssam 		}
33410276Ssam 	;
33510276Ssam 
33610276Ssam form_code:	N
33710276Ssam 	= {
33810276Ssam 		$$ = FORM_N;
33910276Ssam 	}
34010276Ssam 	|	T
34110276Ssam 	= {
34210276Ssam 		$$ = FORM_T;
34310276Ssam 	}
34410276Ssam 	|	C
34510276Ssam 	= {
34610276Ssam 		$$ = FORM_C;
34710276Ssam 	}
34810276Ssam 	;
34910276Ssam 
35010276Ssam type_code:	A
35110276Ssam 	= {
35210276Ssam 		cmd_type = TYPE_A;
35310276Ssam 		cmd_form = FORM_N;
35410276Ssam 	}
35510276Ssam 	|	A SP form_code
35610276Ssam 	= {
35710276Ssam 		cmd_type = TYPE_A;
35810276Ssam 		cmd_form = $3;
35910276Ssam 	}
36010276Ssam 	|	E
36110276Ssam 	= {
36210276Ssam 		cmd_type = TYPE_E;
36310276Ssam 		cmd_form = FORM_N;
36410276Ssam 	}
36510276Ssam 	|	E SP form_code
36610276Ssam 	= {
36710276Ssam 		cmd_type = TYPE_E;
36810276Ssam 		cmd_form = $3;
36910276Ssam 	}
37010276Ssam 	|	I
37110276Ssam 	= {
37210276Ssam 		cmd_type = TYPE_I;
37310276Ssam 	}
37410276Ssam 	|	L
37510276Ssam 	= {
37610276Ssam 		cmd_type = TYPE_L;
37710276Ssam 		cmd_bytesz = 8;
37810276Ssam 	}
37910276Ssam 	|	L SP byte_size
38010276Ssam 	= {
38110276Ssam 		cmd_type = TYPE_L;
38210276Ssam 		cmd_bytesz = $3;
38310276Ssam 	}
38410276Ssam 	/* this is for a bug in the BBN ftp */
38510276Ssam 	|	L byte_size
38610276Ssam 	= {
38710276Ssam 		cmd_type = TYPE_L;
38810276Ssam 		cmd_bytesz = $2;
38910276Ssam 	}
39010276Ssam 	;
39110276Ssam 
39210276Ssam struct_code:	F
39310276Ssam 	= {
39410276Ssam 		$$ = STRU_F;
39510276Ssam 	}
39610276Ssam 	|	R
39710276Ssam 	= {
39810276Ssam 		$$ = STRU_R;
39910276Ssam 	}
40010276Ssam 	|	P
40110276Ssam 	= {
40210276Ssam 		$$ = STRU_P;
40310276Ssam 	}
40410276Ssam 	;
40510276Ssam 
40610276Ssam mode_code:	S
40710276Ssam 	= {
40810276Ssam 		$$ = MODE_S;
40910276Ssam 	}
41010276Ssam 	|	B
41110276Ssam 	= {
41210276Ssam 		$$ = MODE_B;
41310276Ssam 	}
41410276Ssam 	|	C
41510276Ssam 	= {
41610276Ssam 		$$ = MODE_C;
41710276Ssam 	}
41810276Ssam 	;
41910276Ssam 
42010276Ssam pathname:	pathstring
42110276Ssam 	= {
42227107Smckusick 		/*
42327107Smckusick 		 * Problem: this production is used for all pathname
42427107Smckusick 		 * processing, but only gives a 550 error reply.
42527107Smckusick 		 * This is a valid reply in some cases but not in others.
42627107Smckusick 		 */
42726494Sminshall 		if ($1 && strncmp((char *) $1, "~", 1) == 0) {
42826494Sminshall 			$$ = (int)*glob((char *) $1);
42910302Ssam 			if (globerr != NULL) {
43010276Ssam 				reply(550, globerr);
43110302Ssam 				$$ = NULL;
43210302Ssam 			}
43326494Sminshall 			free((char *) $1);
43410276Ssam 		} else
43510276Ssam 			$$ = $1;
43610276Ssam 	}
43710276Ssam 	;
43810276Ssam 
43910276Ssam pathstring:	STRING
44010276Ssam 	;
44110276Ssam 
44210276Ssam rename_cmd:	rename_from rename_to
44310276Ssam 	= {
44410276Ssam 		if ($1 && $2)
44526494Sminshall 			renamecmd((char *) $1, (char *) $2);
44610276Ssam 		else
44710276Ssam 			reply(503, "Bad sequence of commands.");
44810276Ssam 		if ($1)
44926494Sminshall 			free((char *) $1);
45010276Ssam 		if ($2)
45126494Sminshall 			free((char *) $2);
45210276Ssam 	}
45310276Ssam 	;
45410276Ssam 
45510276Ssam rename_from:	RNFR check_login SP pathname CRLF
45610276Ssam 	= {
45710276Ssam 		char *from = 0, *renamefrom();
45810276Ssam 
45910302Ssam 		if ($2 && $4)
46026494Sminshall 			from = renamefrom((char *) $4);
46110302Ssam 		if (from == 0 && $4)
46226494Sminshall 			free((char *) $4);
46310276Ssam 		$$ = (int)from;
46410276Ssam 	}
46510276Ssam 	;
46610276Ssam 
46710276Ssam rename_to:	RNTO SP pathname CRLF
46810276Ssam 	= {
46910276Ssam 		$$ = $3;
47010276Ssam 	}
47110276Ssam 	;
47210276Ssam 
47310276Ssam check_login:	/* empty */
47410276Ssam 	= {
47510276Ssam 		if (logged_in)
47610276Ssam 			$$ = 1;
47710276Ssam 		else {
47810276Ssam 			reply(530, "Please login with USER and PASS.");
47910276Ssam 			$$ = 0;
48010276Ssam 		}
48110276Ssam 	}
48210276Ssam 	;
48310276Ssam 
48410276Ssam %%
48510276Ssam 
48610276Ssam extern jmp_buf errcatch;
48710276Ssam 
48810276Ssam #define	CMD	0	/* beginning of command */
48910276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
49010276Ssam #define	STR1	2	/* expect SP followed by STRING */
49110276Ssam #define	STR2	3	/* expect STRING */
49210276Ssam #define	OSTR	4	/* optional STRING */
49310276Ssam 
49410276Ssam struct tab {
49510276Ssam 	char	*name;
49610276Ssam 	short	token;
49710276Ssam 	short	state;
49810276Ssam 	short	implemented;	/* 1 if command is implemented */
49910276Ssam 	char	*help;
50010276Ssam };
50110276Ssam 
50210276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
50310276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
50410276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
50510276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
50610276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
50710276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
50810276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
50926045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
51010276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
51110276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
51210276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
51310276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
51410276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
51510276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
51610276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
51710276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
51810276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
51910276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
52010276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
52110276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
52210276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
52310276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
52410276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
52510276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
52610276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
52726045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
52810276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
52910276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
53010276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
53110276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
53210276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
53310276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
53410276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
53510276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
53610276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
53726045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
53810276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
53926045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
54010276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
54126045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
54210276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
54326045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54410276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54526045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
54610276Ssam 	{ NULL,   0,    0,    0,	0 }
54710276Ssam };
54810276Ssam 
54910276Ssam struct tab *
55010276Ssam lookup(cmd)
55110276Ssam 	char *cmd;
55210276Ssam {
55310276Ssam 	register struct tab *p;
55410276Ssam 
55510276Ssam 	for (p = cmdtab; p->name != NULL; p++)
55610276Ssam 		if (strcmp(cmd, p->name) == 0)
55710276Ssam 			return (p);
55810276Ssam 	return (0);
55910276Ssam }
56010276Ssam 
56113033Ssam #include <arpa/telnet.h>
56210276Ssam 
56310276Ssam /*
56410276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
56510276Ssam  */
56610276Ssam char *
56710276Ssam getline(s, n, iop)
56810276Ssam 	char *s;
56910276Ssam 	register FILE *iop;
57010276Ssam {
57110276Ssam 	register c;
57226494Sminshall 	register char *cs;
57310276Ssam 
57410276Ssam 	cs = s;
57527751Sminshall /* tmpline may contain saved command from urgent mode interruption */
57626045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
57726045Sminshall 		*cs++ = tmpline[c];
57826045Sminshall 		if (tmpline[c] == '\n') {
57926045Sminshall 			*cs++ = '\0';
58026045Sminshall 			if (debug) {
58126494Sminshall 				syslog(LOG_DEBUG, "FTPD: command: %s", s);
58226045Sminshall 			}
58326045Sminshall 			tmpline[0] = '\0';
58426045Sminshall 			return(s);
58526045Sminshall 		}
58626045Sminshall 		if (c == 0) {
58726045Sminshall 			tmpline[0] = '\0';
58826045Sminshall 		}
58926045Sminshall 	}
59027751Sminshall 	while (--n > 0 && (c = getc(iop)) != EOF) {
59127751Sminshall 		c = 0377 & c;
59210276Ssam 		while (c == IAC) {
59327751Sminshall 			switch (c = 0377 & getc(iop)) {
59427751Sminshall 			case WILL:
59527751Sminshall 			case WONT:
59627751Sminshall 				c = 0377 & getc(iop);
59727751Sminshall 				printf("%c%c%c", IAC, WONT, c);
59827751Sminshall 				(void) fflush(stdout);
59927751Sminshall 				break;
60027751Sminshall 			case DO:
60127751Sminshall 			case DONT:
60227751Sminshall 				c = 0377 & getc(iop);
60327751Sminshall 				printf("%c%c%c", IAC, DONT, c);
60427751Sminshall 				(void) fflush(stdout);
60527751Sminshall 				break;
60627751Sminshall 			default:
60727751Sminshall 				break;
60827751Sminshall 			}
60927751Sminshall 			c = 0377 & getc(iop); /* try next character */
61010276Ssam 		}
61110276Ssam 		*cs++ = c;
61210276Ssam 		if (c=='\n')
61310276Ssam 			break;
61410276Ssam 	}
61527751Sminshall 	if (c == EOF && cs == s)
61618303Sralph 		return (NULL);
61710276Ssam 	*cs++ = '\0';
61811652Ssam 	if (debug) {
61926494Sminshall 		syslog(LOG_DEBUG, "FTPD: command: %s", s);
62011652Ssam 	}
62110276Ssam 	return (s);
62210276Ssam }
62310276Ssam 
62411652Ssam static int
62511652Ssam toolong()
62611652Ssam {
62726494Sminshall 	time_t now;
62811652Ssam 	extern char *ctime();
62926494Sminshall 	extern time_t time();
63011652Ssam 
63111652Ssam 	reply(421,
63211652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
63326494Sminshall 	(void) time(&now);
63411652Ssam 	if (logging) {
63526494Sminshall 		syslog(LOG_INFO,
63611652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
63711652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
63811652Ssam 	}
63913246Ssam 	dologout(1);
64011652Ssam }
64111652Ssam 
64210276Ssam yylex()
64310276Ssam {
64410276Ssam 	static int cpos, state;
64510276Ssam 	register char *cp;
64610276Ssam 	register struct tab *p;
64710276Ssam 	int n;
64810276Ssam 	char c;
64910276Ssam 
65010276Ssam 	for (;;) {
65110276Ssam 		switch (state) {
65210276Ssam 
65310276Ssam 		case CMD:
65426494Sminshall 			(void) signal(SIGALRM, toolong);
65526494Sminshall 			(void) alarm((unsigned) timeout);
65610276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
65710276Ssam 				reply(221, "You could at least say goodbye.");
65813246Ssam 				dologout(0);
65910276Ssam 			}
66026494Sminshall 			(void) alarm(0);
66110276Ssam 			if (index(cbuf, '\r')) {
66210276Ssam 				cp = index(cbuf, '\r');
66310276Ssam 				cp[0] = '\n'; cp[1] = 0;
66410276Ssam 			}
66510276Ssam 			if (index(cbuf, ' '))
66610276Ssam 				cpos = index(cbuf, ' ') - cbuf;
66710276Ssam 			else
66826045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
66926045Sminshall 			if (cpos == 0) {
67010276Ssam 				cpos = 4;
67126045Sminshall 			}
67210276Ssam 			c = cbuf[cpos];
67310276Ssam 			cbuf[cpos] = '\0';
67410276Ssam 			upper(cbuf);
67510276Ssam 			p = lookup(cbuf);
67610276Ssam 			cbuf[cpos] = c;
67710276Ssam 			if (p != 0) {
67810276Ssam 				if (p->implemented == 0) {
67910276Ssam 					nack(p->name);
68026494Sminshall 					longjmp(errcatch,0);
68110276Ssam 					/* NOTREACHED */
68210276Ssam 				}
68310276Ssam 				state = p->state;
68410276Ssam 				yylval = (int) p->name;
68510276Ssam 				return (p->token);
68610276Ssam 			}
68710276Ssam 			break;
68810276Ssam 
68910276Ssam 		case OSTR:
69010276Ssam 			if (cbuf[cpos] == '\n') {
69110276Ssam 				state = CMD;
69210276Ssam 				return (CRLF);
69310276Ssam 			}
69410276Ssam 			/* FALL THRU */
69510276Ssam 
69610276Ssam 		case STR1:
69710276Ssam 			if (cbuf[cpos] == ' ') {
69810276Ssam 				cpos++;
69910276Ssam 				state = STR2;
70010276Ssam 				return (SP);
70110276Ssam 			}
70210276Ssam 			break;
70310276Ssam 
70410276Ssam 		case STR2:
70510276Ssam 			cp = &cbuf[cpos];
70610276Ssam 			n = strlen(cp);
70710276Ssam 			cpos += n - 1;
70810276Ssam 			/*
70910276Ssam 			 * Make sure the string is nonempty and \n terminated.
71010276Ssam 			 */
71110276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
71210276Ssam 				cbuf[cpos] = '\0';
71310276Ssam 				yylval = copy(cp);
71410276Ssam 				cbuf[cpos] = '\n';
71510276Ssam 				state = ARGS;
71610276Ssam 				return (STRING);
71710276Ssam 			}
71810276Ssam 			break;
71910276Ssam 
72010276Ssam 		case ARGS:
72110276Ssam 			if (isdigit(cbuf[cpos])) {
72210276Ssam 				cp = &cbuf[cpos];
72310276Ssam 				while (isdigit(cbuf[++cpos]))
72410276Ssam 					;
72510276Ssam 				c = cbuf[cpos];
72610276Ssam 				cbuf[cpos] = '\0';
72710276Ssam 				yylval = atoi(cp);
72810276Ssam 				cbuf[cpos] = c;
72910276Ssam 				return (NUMBER);
73010276Ssam 			}
73110276Ssam 			switch (cbuf[cpos++]) {
73210276Ssam 
73310276Ssam 			case '\n':
73410276Ssam 				state = CMD;
73510276Ssam 				return (CRLF);
73610276Ssam 
73710276Ssam 			case ' ':
73810276Ssam 				return (SP);
73910276Ssam 
74010276Ssam 			case ',':
74110276Ssam 				return (COMMA);
74210276Ssam 
74310276Ssam 			case 'A':
74410276Ssam 			case 'a':
74510276Ssam 				return (A);
74610276Ssam 
74710276Ssam 			case 'B':
74810276Ssam 			case 'b':
74910276Ssam 				return (B);
75010276Ssam 
75110276Ssam 			case 'C':
75210276Ssam 			case 'c':
75310276Ssam 				return (C);
75410276Ssam 
75510276Ssam 			case 'E':
75610276Ssam 			case 'e':
75710276Ssam 				return (E);
75810276Ssam 
75910276Ssam 			case 'F':
76010276Ssam 			case 'f':
76110276Ssam 				return (F);
76210276Ssam 
76310276Ssam 			case 'I':
76410276Ssam 			case 'i':
76510276Ssam 				return (I);
76610276Ssam 
76710276Ssam 			case 'L':
76810276Ssam 			case 'l':
76910276Ssam 				return (L);
77010276Ssam 
77110276Ssam 			case 'N':
77210276Ssam 			case 'n':
77310276Ssam 				return (N);
77410276Ssam 
77510276Ssam 			case 'P':
77610276Ssam 			case 'p':
77710276Ssam 				return (P);
77810276Ssam 
77910276Ssam 			case 'R':
78010276Ssam 			case 'r':
78110276Ssam 				return (R);
78210276Ssam 
78310276Ssam 			case 'S':
78410276Ssam 			case 's':
78510276Ssam 				return (S);
78610276Ssam 
78710276Ssam 			case 'T':
78810276Ssam 			case 't':
78910276Ssam 				return (T);
79010276Ssam 
79110276Ssam 			}
79210276Ssam 			break;
79310276Ssam 
79410276Ssam 		default:
79510276Ssam 			fatal("Unknown state in scanner.");
79610276Ssam 		}
79726494Sminshall 		yyerror((char *) 0);
79810276Ssam 		state = CMD;
79926494Sminshall 		longjmp(errcatch,0);
80010276Ssam 	}
80110276Ssam }
80210276Ssam 
80310276Ssam upper(s)
80410276Ssam 	char *s;
80510276Ssam {
80610276Ssam 	while (*s != '\0') {
80710276Ssam 		if (islower(*s))
80810276Ssam 			*s = toupper(*s);
80910276Ssam 		s++;
81010276Ssam 	}
81110276Ssam }
81210276Ssam 
81310276Ssam copy(s)
81410276Ssam 	char *s;
81510276Ssam {
81610276Ssam 	char *p;
81726494Sminshall 	extern char *malloc(), *strcpy();
81810276Ssam 
81926494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
82010276Ssam 	if (p == NULL)
82110276Ssam 		fatal("Ran out of memory.");
82226494Sminshall 	(void) strcpy(p, s);
82310276Ssam 	return ((int)p);
82410276Ssam }
82510276Ssam 
82610276Ssam help(s)
82710276Ssam 	char *s;
82810276Ssam {
82910276Ssam 	register struct tab *c;
83010276Ssam 	register int width, NCMDS;
83110276Ssam 
83210276Ssam 	width = 0, NCMDS = 0;
83310276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
83410276Ssam 		int len = strlen(c->name);
83510276Ssam 
83610276Ssam 		if (c->implemented == 0)
83710276Ssam 			len++;
83810276Ssam 		if (len > width)
83910276Ssam 			width = len;
84010276Ssam 		NCMDS++;
84110276Ssam 	}
84210276Ssam 	width = (width + 8) &~ 7;
84310276Ssam 	if (s == 0) {
84410276Ssam 		register int i, j, w;
84510276Ssam 		int columns, lines;
84610276Ssam 
84710276Ssam 		lreply(214,
84810276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
84910276Ssam 		columns = 76 / width;
85010276Ssam 		if (columns == 0)
85110276Ssam 			columns = 1;
85210276Ssam 		lines = (NCMDS + columns - 1) / columns;
85310276Ssam 		for (i = 0; i < lines; i++) {
85427107Smckusick 			printf("   ");
85510276Ssam 			for (j = 0; j < columns; j++) {
85610276Ssam 				c = cmdtab + j * lines + i;
85710276Ssam 				printf("%s%c", c->name,
85810276Ssam 					c->implemented ? ' ' : '*');
85910302Ssam 				if (c + lines >= &cmdtab[NCMDS])
86010276Ssam 					break;
86110276Ssam 				w = strlen(c->name);
86210276Ssam 				while (w < width) {
86310276Ssam 					putchar(' ');
86410276Ssam 					w++;
86510276Ssam 				}
86610276Ssam 			}
86710276Ssam 			printf("\r\n");
86810276Ssam 		}
86926494Sminshall 		(void) fflush(stdout);
87010276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
87110276Ssam 		return;
87210276Ssam 	}
87310276Ssam 	upper(s);
87410276Ssam 	c = lookup(s);
87510276Ssam 	if (c == (struct tab *)0) {
87627107Smckusick 		reply(502, "Unknown command %s.", s);
87710276Ssam 		return;
87810276Ssam 	}
87910276Ssam 	if (c->implemented)
88010276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
88110276Ssam 	else
88210276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
88310276Ssam }
884