xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 31132)
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*31132Smckusick static	char sccsid[] = "@(#)ftpcmd.y	5.9 (Berkeley) 05/15/87";
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];
5430945Scsvsj char *fromname;
5510276Ssam 
5610276Ssam char	*index();
5710276Ssam %}
5810276Ssam 
5910276Ssam %token
6010276Ssam 	A	B	C	E	F	I
6110276Ssam 	L	N	P	R	S	T
6210276Ssam 
6310276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
6410276Ssam 
6510276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
6610276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
6710276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
6810276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
6910276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
7010276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
7126045Sminshall 	XCUP	STOU
7210276Ssam 
7310276Ssam 	LEXERR
7410276Ssam 
7510276Ssam %start	cmd_list
7610276Ssam 
7710276Ssam %%
7810276Ssam 
7910276Ssam cmd_list:	/* empty */
8010276Ssam 	|	cmd_list cmd
8130945Scsvsj 		= {
8230945Scsvsj 			fromname = (char *) 0;
8330945Scsvsj 		}
8430945Scsvsj 	|	cmd_list rcmd
8510276Ssam 	;
8610276Ssam 
8710276Ssam cmd:		USER SP username CRLF
8810276Ssam 		= {
8910276Ssam 			extern struct passwd *getpwnam();
9010276Ssam 
9126045Sminshall 			logged_in = 0;
9226494Sminshall 			if (strcmp((char *) $3, "ftp") == 0 ||
9326494Sminshall 			  strcmp((char *) $3, "anonymous") == 0) {
9410324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
9510324Ssam 					guest = 1;
9610324Ssam 					reply(331,
9710276Ssam 				  "Guest login ok, send ident as password.");
9810324Ssam 				}
9926045Sminshall 				else {
10026045Sminshall 					reply(530, "User %s unknown.", $3);
10126045Sminshall 				}
10226494Sminshall 			} else if (checkuser((char *) $3)) {
10310276Ssam 				guest = 0;
10426494Sminshall 				pw = getpwnam((char *) $3);
10526045Sminshall 				if (pw == NULL) {
10626045Sminshall 					reply(530, "User %s unknown.", $3);
10726045Sminshall 				}
10826045Sminshall 				else {
10926045Sminshall 				    reply(331, "Password required for %s.", $3);
11026045Sminshall 				}
11128865Smckusick 			} else {
11228865Smckusick 				reply(530, "User %s access denied.", $3);
11310276Ssam 			}
11426494Sminshall 			free((char *) $3);
11510276Ssam 		}
11610276Ssam 	|	PASS SP password CRLF
11710276Ssam 		= {
11826494Sminshall 			pass((char *) $3);
11926494Sminshall 			free((char *) $3);
12010276Ssam 		}
12110276Ssam 	|	PORT SP host_port CRLF
12210276Ssam 		= {
12310320Ssam 			usedefault = 0;
12426045Sminshall 			if (pdata > 0) {
12526045Sminshall 				(void) close(pdata);
12626045Sminshall 			}
12726045Sminshall 			pdata = -1;
12827107Smckusick 			reply(200, "PORT command successful.");
12910276Ssam 		}
13026045Sminshall 	|	PASV CRLF
13126045Sminshall 		= {
13226045Sminshall 			passive();
13326045Sminshall 		}
13410276Ssam 	|	TYPE SP type_code CRLF
13510276Ssam 		= {
13610276Ssam 			switch (cmd_type) {
13710276Ssam 
13810276Ssam 			case TYPE_A:
13910276Ssam 				if (cmd_form == FORM_N) {
14010276Ssam 					reply(200, "Type set to A.");
14110276Ssam 					type = cmd_type;
14210276Ssam 					form = cmd_form;
14310276Ssam 				} else
14410276Ssam 					reply(504, "Form must be N.");
14510276Ssam 				break;
14610276Ssam 
14710276Ssam 			case TYPE_E:
14810276Ssam 				reply(504, "Type E not implemented.");
14910276Ssam 				break;
15010276Ssam 
15110276Ssam 			case TYPE_I:
15210276Ssam 				reply(200, "Type set to I.");
15310276Ssam 				type = cmd_type;
15410276Ssam 				break;
15510276Ssam 
15610276Ssam 			case TYPE_L:
15710276Ssam 				if (cmd_bytesz == 8) {
15810276Ssam 					reply(200,
15910276Ssam 					    "Type set to L (byte size 8).");
16010276Ssam 					type = cmd_type;
16110276Ssam 				} else
16210276Ssam 					reply(504, "Byte size must be 8.");
16310276Ssam 			}
16410276Ssam 		}
16510276Ssam 	|	STRU SP struct_code CRLF
16610276Ssam 		= {
16710276Ssam 			switch ($3) {
16810276Ssam 
16910276Ssam 			case STRU_F:
17010276Ssam 				reply(200, "STRU F ok.");
17110276Ssam 				break;
17210276Ssam 
17310276Ssam 			default:
17427107Smckusick 				reply(504, "Unimplemented STRU type.");
17510276Ssam 			}
17610276Ssam 		}
17710276Ssam 	|	MODE SP mode_code CRLF
17810276Ssam 		= {
17910276Ssam 			switch ($3) {
18010276Ssam 
18110276Ssam 			case MODE_S:
18210276Ssam 				reply(200, "MODE S ok.");
18310276Ssam 				break;
18410276Ssam 
18510276Ssam 			default:
18610276Ssam 				reply(502, "Unimplemented MODE type.");
18710276Ssam 			}
18810276Ssam 		}
18910276Ssam 	|	ALLO SP NUMBER CRLF
19010276Ssam 		= {
19127107Smckusick 			reply(202, "ALLO command ignored.");
19210276Ssam 		}
19310276Ssam 	|	RETR check_login SP pathname CRLF
19410276Ssam 		= {
19510302Ssam 			if ($2 && $4 != NULL)
19626494Sminshall 				retrieve((char *) 0, (char *) $4);
19710302Ssam 			if ($4 != NULL)
19826494Sminshall 				free((char *) $4);
19910276Ssam 		}
20010276Ssam 	|	STOR check_login SP pathname CRLF
20110276Ssam 		= {
20210302Ssam 			if ($2 && $4 != NULL)
20326494Sminshall 				store((char *) $4, "w");
20410302Ssam 			if ($4 != NULL)
20526494Sminshall 				free((char *) $4);
20610276Ssam 		}
20710276Ssam 	|	APPE check_login SP pathname CRLF
20810276Ssam 		= {
20910302Ssam 			if ($2 && $4 != NULL)
21026494Sminshall 				store((char *) $4, "a");
21110302Ssam 			if ($4 != NULL)
21226494Sminshall 				free((char *) $4);
21310276Ssam 		}
21410276Ssam 	|	NLST check_login CRLF
21510276Ssam 		= {
21610276Ssam 			if ($2)
21711217Ssam 				retrieve("/bin/ls", "");
21810276Ssam 		}
21910276Ssam 	|	NLST check_login SP pathname CRLF
22010276Ssam 		= {
22110302Ssam 			if ($2 && $4 != NULL)
22226494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
22310302Ssam 			if ($4 != NULL)
22426494Sminshall 				free((char *) $4);
22510276Ssam 		}
22610276Ssam 	|	LIST check_login CRLF
22710276Ssam 		= {
22810276Ssam 			if ($2)
22910318Ssam 				retrieve("/bin/ls -lg", "");
23010276Ssam 		}
23110276Ssam 	|	LIST check_login SP pathname CRLF
23210276Ssam 		= {
23310302Ssam 			if ($2 && $4 != NULL)
23426494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
23510302Ssam 			if ($4 != NULL)
23626494Sminshall 				free((char *) $4);
23710276Ssam 		}
23810276Ssam 	|	DELE check_login SP pathname CRLF
23910276Ssam 		= {
24010302Ssam 			if ($2 && $4 != NULL)
24126494Sminshall 				delete((char *) $4);
24210302Ssam 			if ($4 != NULL)
24326494Sminshall 				free((char *) $4);
24410276Ssam 		}
24530945Scsvsj 	|	RNTO SP pathname CRLF
24630945Scsvsj 		= {
24730945Scsvsj 			if (fromname) {
24830945Scsvsj 				renamecmd(fromname, (char *) $3);
24930945Scsvsj 				free(fromname);
25030945Scsvsj 				fromname = (char *) 0;
25130945Scsvsj 			} else {
25230945Scsvsj 				reply(503, "Bad sequence of commands.");
25330945Scsvsj 			}
25430945Scsvsj 			free((char *) $3);
25530945Scsvsj 		}
25626045Sminshall 	|	ABOR CRLF
25726045Sminshall 		= {
25827107Smckusick 			reply(225, "ABOR command successful.");
25926045Sminshall 		}
26010276Ssam 	|	CWD check_login CRLF
26110276Ssam 		= {
26210276Ssam 			if ($2)
26310276Ssam 				cwd(pw->pw_dir);
26410276Ssam 		}
26510276Ssam 	|	CWD check_login SP pathname CRLF
26610276Ssam 		= {
26710302Ssam 			if ($2 && $4 != NULL)
26826494Sminshall 				cwd((char *) $4);
26910302Ssam 			if ($4 != NULL)
27026494Sminshall 				free((char *) $4);
27110276Ssam 		}
27210276Ssam 	|	HELP CRLF
27310276Ssam 		= {
27426494Sminshall 			help((char *) 0);
27510276Ssam 		}
27610276Ssam 	|	HELP SP STRING CRLF
27710276Ssam 		= {
27826494Sminshall 			help((char *) $3);
27910276Ssam 		}
28010276Ssam 	|	NOOP CRLF
28110276Ssam 		= {
28227107Smckusick 			reply(200, "NOOP command successful.");
28310276Ssam 		}
28410276Ssam 	|	XMKD check_login SP pathname CRLF
28510276Ssam 		= {
28610302Ssam 			if ($2 && $4 != NULL)
28726494Sminshall 				makedir((char *) $4);
28810302Ssam 			if ($4 != NULL)
28926494Sminshall 				free((char *) $4);
29010276Ssam 		}
29110276Ssam 	|	XRMD check_login SP pathname CRLF
29210276Ssam 		= {
29310302Ssam 			if ($2 && $4 != NULL)
29426494Sminshall 				removedir((char *) $4);
29510302Ssam 			if ($4 != NULL)
29626494Sminshall 				free((char *) $4);
29710276Ssam 		}
29810276Ssam 	|	XPWD check_login CRLF
29910276Ssam 		= {
30010276Ssam 			if ($2)
30110302Ssam 				pwd();
30210276Ssam 		}
30310276Ssam 	|	XCUP check_login CRLF
30410276Ssam 		= {
30510276Ssam 			if ($2)
30610276Ssam 				cwd("..");
30710276Ssam 		}
30826045Sminshall 	|	STOU check_login SP pathname CRLF
30926045Sminshall 		= {
31026045Sminshall 			if ($2 && $4 != NULL) {
31126045Sminshall 				unique++;
31226494Sminshall 				store((char *) $4, "w");
31326045Sminshall 				unique = 0;
31426045Sminshall 			}
31526045Sminshall 			if ($4 != NULL)
31626494Sminshall 				free((char *) $4);
31726045Sminshall 		}
31810276Ssam 	|	QUIT CRLF
31910276Ssam 		= {
32010276Ssam 			reply(221, "Goodbye.");
32113246Ssam 			dologout(0);
32210276Ssam 		}
32310276Ssam 	|	error CRLF
32410276Ssam 		= {
32510276Ssam 			yyerrok;
32610276Ssam 		}
32710276Ssam 	;
32810276Ssam 
32930945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
33030945Scsvsj 		= {
33130945Scsvsj 			char *renamefrom();
33230945Scsvsj 
33330945Scsvsj 			if ($2 && $4) {
33430945Scsvsj 				fromname = renamefrom((char *) $4);
33530945Scsvsj 				if (fromname == (char *) 0 && $4) {
33630945Scsvsj 					free((char *) $4);
33730945Scsvsj 				}
33830945Scsvsj 			}
33930945Scsvsj 		}
34030945Scsvsj 	;
34130945Scsvsj 
34210276Ssam username:	STRING
34310276Ssam 	;
34410276Ssam 
34510276Ssam password:	STRING
34610276Ssam 	;
34710276Ssam 
34810276Ssam byte_size:	NUMBER
34910276Ssam 	;
35010276Ssam 
35110276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
35210276Ssam 		NUMBER COMMA NUMBER
35310276Ssam 		= {
35410276Ssam 			register char *a, *p;
35510276Ssam 
35610276Ssam 			a = (char *)&data_dest.sin_addr;
35710276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
35810276Ssam 			p = (char *)&data_dest.sin_port;
35910276Ssam 			p[0] = $9; p[1] = $11;
36010324Ssam 			data_dest.sin_family = AF_INET;
36110276Ssam 		}
36210276Ssam 	;
36310276Ssam 
36410276Ssam form_code:	N
36510276Ssam 	= {
36610276Ssam 		$$ = FORM_N;
36710276Ssam 	}
36810276Ssam 	|	T
36910276Ssam 	= {
37010276Ssam 		$$ = FORM_T;
37110276Ssam 	}
37210276Ssam 	|	C
37310276Ssam 	= {
37410276Ssam 		$$ = FORM_C;
37510276Ssam 	}
37610276Ssam 	;
37710276Ssam 
37810276Ssam type_code:	A
37910276Ssam 	= {
38010276Ssam 		cmd_type = TYPE_A;
38110276Ssam 		cmd_form = FORM_N;
38210276Ssam 	}
38310276Ssam 	|	A SP form_code
38410276Ssam 	= {
38510276Ssam 		cmd_type = TYPE_A;
38610276Ssam 		cmd_form = $3;
38710276Ssam 	}
38810276Ssam 	|	E
38910276Ssam 	= {
39010276Ssam 		cmd_type = TYPE_E;
39110276Ssam 		cmd_form = FORM_N;
39210276Ssam 	}
39310276Ssam 	|	E SP form_code
39410276Ssam 	= {
39510276Ssam 		cmd_type = TYPE_E;
39610276Ssam 		cmd_form = $3;
39710276Ssam 	}
39810276Ssam 	|	I
39910276Ssam 	= {
40010276Ssam 		cmd_type = TYPE_I;
40110276Ssam 	}
40210276Ssam 	|	L
40310276Ssam 	= {
40410276Ssam 		cmd_type = TYPE_L;
40510276Ssam 		cmd_bytesz = 8;
40610276Ssam 	}
40710276Ssam 	|	L SP byte_size
40810276Ssam 	= {
40910276Ssam 		cmd_type = TYPE_L;
41010276Ssam 		cmd_bytesz = $3;
41110276Ssam 	}
41210276Ssam 	/* this is for a bug in the BBN ftp */
41310276Ssam 	|	L byte_size
41410276Ssam 	= {
41510276Ssam 		cmd_type = TYPE_L;
41610276Ssam 		cmd_bytesz = $2;
41710276Ssam 	}
41810276Ssam 	;
41910276Ssam 
42010276Ssam struct_code:	F
42110276Ssam 	= {
42210276Ssam 		$$ = STRU_F;
42310276Ssam 	}
42410276Ssam 	|	R
42510276Ssam 	= {
42610276Ssam 		$$ = STRU_R;
42710276Ssam 	}
42810276Ssam 	|	P
42910276Ssam 	= {
43010276Ssam 		$$ = STRU_P;
43110276Ssam 	}
43210276Ssam 	;
43310276Ssam 
43410276Ssam mode_code:	S
43510276Ssam 	= {
43610276Ssam 		$$ = MODE_S;
43710276Ssam 	}
43810276Ssam 	|	B
43910276Ssam 	= {
44010276Ssam 		$$ = MODE_B;
44110276Ssam 	}
44210276Ssam 	|	C
44310276Ssam 	= {
44410276Ssam 		$$ = MODE_C;
44510276Ssam 	}
44610276Ssam 	;
44710276Ssam 
44810276Ssam pathname:	pathstring
44910276Ssam 	= {
45027107Smckusick 		/*
45127107Smckusick 		 * Problem: this production is used for all pathname
45227107Smckusick 		 * processing, but only gives a 550 error reply.
45327107Smckusick 		 * This is a valid reply in some cases but not in others.
45427107Smckusick 		 */
45526494Sminshall 		if ($1 && strncmp((char *) $1, "~", 1) == 0) {
45626494Sminshall 			$$ = (int)*glob((char *) $1);
45710302Ssam 			if (globerr != NULL) {
45810276Ssam 				reply(550, globerr);
45910302Ssam 				$$ = NULL;
46010302Ssam 			}
46126494Sminshall 			free((char *) $1);
46210276Ssam 		} else
46310276Ssam 			$$ = $1;
46410276Ssam 	}
46510276Ssam 	;
46610276Ssam 
46710276Ssam pathstring:	STRING
46810276Ssam 	;
46910276Ssam 
47010276Ssam check_login:	/* empty */
47110276Ssam 	= {
47210276Ssam 		if (logged_in)
47310276Ssam 			$$ = 1;
47410276Ssam 		else {
47510276Ssam 			reply(530, "Please login with USER and PASS.");
47610276Ssam 			$$ = 0;
47710276Ssam 		}
47810276Ssam 	}
47910276Ssam 	;
48010276Ssam 
48110276Ssam %%
48210276Ssam 
48310276Ssam extern jmp_buf errcatch;
48410276Ssam 
48510276Ssam #define	CMD	0	/* beginning of command */
48610276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
48710276Ssam #define	STR1	2	/* expect SP followed by STRING */
48810276Ssam #define	STR2	3	/* expect STRING */
48910276Ssam #define	OSTR	4	/* optional STRING */
49010276Ssam 
49110276Ssam struct tab {
49210276Ssam 	char	*name;
49310276Ssam 	short	token;
49410276Ssam 	short	state;
49510276Ssam 	short	implemented;	/* 1 if command is implemented */
49610276Ssam 	char	*help;
49710276Ssam };
49810276Ssam 
49910276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
50010276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
50110276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
50210276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
50310276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
50410276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
50510276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
50626045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
50710276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
50810276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
50910276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
51010276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
51110276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
51210276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
51310276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
51410276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
51510276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
51610276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
51710276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
51810276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
51910276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
52010276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
52110276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
52210276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
52310276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
52426045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
52510276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
52610276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
52710276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
52810276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
52910276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
53010276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
53110276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
53210276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
53310276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
53426045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
53510276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
53626045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
53710276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
53826045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
53910276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
54026045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54110276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54226045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
54310276Ssam 	{ NULL,   0,    0,    0,	0 }
54410276Ssam };
54510276Ssam 
54610276Ssam struct tab *
54710276Ssam lookup(cmd)
54810276Ssam 	char *cmd;
54910276Ssam {
55010276Ssam 	register struct tab *p;
55110276Ssam 
55210276Ssam 	for (p = cmdtab; p->name != NULL; p++)
55310276Ssam 		if (strcmp(cmd, p->name) == 0)
55410276Ssam 			return (p);
55510276Ssam 	return (0);
55610276Ssam }
55710276Ssam 
55813033Ssam #include <arpa/telnet.h>
55910276Ssam 
56010276Ssam /*
56110276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
56210276Ssam  */
56310276Ssam char *
56410276Ssam getline(s, n, iop)
56510276Ssam 	char *s;
56610276Ssam 	register FILE *iop;
56710276Ssam {
56810276Ssam 	register c;
56926494Sminshall 	register char *cs;
57010276Ssam 
57110276Ssam 	cs = s;
57227751Sminshall /* tmpline may contain saved command from urgent mode interruption */
57326045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
57426045Sminshall 		*cs++ = tmpline[c];
57526045Sminshall 		if (tmpline[c] == '\n') {
57626045Sminshall 			*cs++ = '\0';
57726045Sminshall 			if (debug) {
57826494Sminshall 				syslog(LOG_DEBUG, "FTPD: command: %s", s);
57926045Sminshall 			}
58026045Sminshall 			tmpline[0] = '\0';
58126045Sminshall 			return(s);
58226045Sminshall 		}
58326045Sminshall 		if (c == 0) {
58426045Sminshall 			tmpline[0] = '\0';
58526045Sminshall 		}
58626045Sminshall 	}
58727751Sminshall 	while (--n > 0 && (c = getc(iop)) != EOF) {
58827751Sminshall 		c = 0377 & c;
58910276Ssam 		while (c == IAC) {
59027751Sminshall 			switch (c = 0377 & getc(iop)) {
59127751Sminshall 			case WILL:
59227751Sminshall 			case WONT:
59327751Sminshall 				c = 0377 & getc(iop);
59427751Sminshall 				printf("%c%c%c", IAC, WONT, c);
59527751Sminshall 				(void) fflush(stdout);
59627751Sminshall 				break;
59727751Sminshall 			case DO:
59827751Sminshall 			case DONT:
59927751Sminshall 				c = 0377 & getc(iop);
60027751Sminshall 				printf("%c%c%c", IAC, DONT, c);
60127751Sminshall 				(void) fflush(stdout);
60227751Sminshall 				break;
60327751Sminshall 			default:
60427751Sminshall 				break;
60527751Sminshall 			}
60627751Sminshall 			c = 0377 & getc(iop); /* try next character */
60710276Ssam 		}
60810276Ssam 		*cs++ = c;
60910276Ssam 		if (c=='\n')
61010276Ssam 			break;
61110276Ssam 	}
61227751Sminshall 	if (c == EOF && cs == s)
61318303Sralph 		return (NULL);
61410276Ssam 	*cs++ = '\0';
61511652Ssam 	if (debug) {
61626494Sminshall 		syslog(LOG_DEBUG, "FTPD: command: %s", s);
61711652Ssam 	}
61810276Ssam 	return (s);
61910276Ssam }
62010276Ssam 
62111652Ssam static int
62211652Ssam toolong()
62311652Ssam {
62426494Sminshall 	time_t now;
62511652Ssam 	extern char *ctime();
62626494Sminshall 	extern time_t time();
62711652Ssam 
62811652Ssam 	reply(421,
62911652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
63026494Sminshall 	(void) time(&now);
63111652Ssam 	if (logging) {
63226494Sminshall 		syslog(LOG_INFO,
63311652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
63411652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
63511652Ssam 	}
63613246Ssam 	dologout(1);
63711652Ssam }
63811652Ssam 
63910276Ssam yylex()
64010276Ssam {
64110276Ssam 	static int cpos, state;
64210276Ssam 	register char *cp;
64310276Ssam 	register struct tab *p;
64410276Ssam 	int n;
64510276Ssam 	char c;
64610276Ssam 
64710276Ssam 	for (;;) {
64810276Ssam 		switch (state) {
64910276Ssam 
65010276Ssam 		case CMD:
65126494Sminshall 			(void) signal(SIGALRM, toolong);
65226494Sminshall 			(void) alarm((unsigned) timeout);
65310276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
65410276Ssam 				reply(221, "You could at least say goodbye.");
65513246Ssam 				dologout(0);
65610276Ssam 			}
65726494Sminshall 			(void) alarm(0);
65810276Ssam 			if (index(cbuf, '\r')) {
65910276Ssam 				cp = index(cbuf, '\r');
66010276Ssam 				cp[0] = '\n'; cp[1] = 0;
66110276Ssam 			}
66210276Ssam 			if (index(cbuf, ' '))
66310276Ssam 				cpos = index(cbuf, ' ') - cbuf;
66410276Ssam 			else
66526045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
66626045Sminshall 			if (cpos == 0) {
66710276Ssam 				cpos = 4;
66826045Sminshall 			}
66910276Ssam 			c = cbuf[cpos];
67010276Ssam 			cbuf[cpos] = '\0';
67110276Ssam 			upper(cbuf);
67210276Ssam 			p = lookup(cbuf);
67310276Ssam 			cbuf[cpos] = c;
67410276Ssam 			if (p != 0) {
67510276Ssam 				if (p->implemented == 0) {
67610276Ssam 					nack(p->name);
67726494Sminshall 					longjmp(errcatch,0);
67810276Ssam 					/* NOTREACHED */
67910276Ssam 				}
68010276Ssam 				state = p->state;
68110276Ssam 				yylval = (int) p->name;
68210276Ssam 				return (p->token);
68310276Ssam 			}
68410276Ssam 			break;
68510276Ssam 
68610276Ssam 		case OSTR:
68710276Ssam 			if (cbuf[cpos] == '\n') {
68810276Ssam 				state = CMD;
68910276Ssam 				return (CRLF);
69010276Ssam 			}
69110276Ssam 			/* FALL THRU */
69210276Ssam 
69310276Ssam 		case STR1:
69410276Ssam 			if (cbuf[cpos] == ' ') {
69510276Ssam 				cpos++;
69610276Ssam 				state = STR2;
69710276Ssam 				return (SP);
69810276Ssam 			}
69910276Ssam 			break;
70010276Ssam 
70110276Ssam 		case STR2:
70210276Ssam 			cp = &cbuf[cpos];
70310276Ssam 			n = strlen(cp);
70410276Ssam 			cpos += n - 1;
70510276Ssam 			/*
70610276Ssam 			 * Make sure the string is nonempty and \n terminated.
70710276Ssam 			 */
70810276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
70910276Ssam 				cbuf[cpos] = '\0';
71010276Ssam 				yylval = copy(cp);
71110276Ssam 				cbuf[cpos] = '\n';
71210276Ssam 				state = ARGS;
71310276Ssam 				return (STRING);
71410276Ssam 			}
71510276Ssam 			break;
71610276Ssam 
71710276Ssam 		case ARGS:
71810276Ssam 			if (isdigit(cbuf[cpos])) {
71910276Ssam 				cp = &cbuf[cpos];
72010276Ssam 				while (isdigit(cbuf[++cpos]))
72110276Ssam 					;
72210276Ssam 				c = cbuf[cpos];
72310276Ssam 				cbuf[cpos] = '\0';
72410276Ssam 				yylval = atoi(cp);
72510276Ssam 				cbuf[cpos] = c;
72610276Ssam 				return (NUMBER);
72710276Ssam 			}
72810276Ssam 			switch (cbuf[cpos++]) {
72910276Ssam 
73010276Ssam 			case '\n':
73110276Ssam 				state = CMD;
73210276Ssam 				return (CRLF);
73310276Ssam 
73410276Ssam 			case ' ':
73510276Ssam 				return (SP);
73610276Ssam 
73710276Ssam 			case ',':
73810276Ssam 				return (COMMA);
73910276Ssam 
74010276Ssam 			case 'A':
74110276Ssam 			case 'a':
74210276Ssam 				return (A);
74310276Ssam 
74410276Ssam 			case 'B':
74510276Ssam 			case 'b':
74610276Ssam 				return (B);
74710276Ssam 
74810276Ssam 			case 'C':
74910276Ssam 			case 'c':
75010276Ssam 				return (C);
75110276Ssam 
75210276Ssam 			case 'E':
75310276Ssam 			case 'e':
75410276Ssam 				return (E);
75510276Ssam 
75610276Ssam 			case 'F':
75710276Ssam 			case 'f':
75810276Ssam 				return (F);
75910276Ssam 
76010276Ssam 			case 'I':
76110276Ssam 			case 'i':
76210276Ssam 				return (I);
76310276Ssam 
76410276Ssam 			case 'L':
76510276Ssam 			case 'l':
76610276Ssam 				return (L);
76710276Ssam 
76810276Ssam 			case 'N':
76910276Ssam 			case 'n':
77010276Ssam 				return (N);
77110276Ssam 
77210276Ssam 			case 'P':
77310276Ssam 			case 'p':
77410276Ssam 				return (P);
77510276Ssam 
77610276Ssam 			case 'R':
77710276Ssam 			case 'r':
77810276Ssam 				return (R);
77910276Ssam 
78010276Ssam 			case 'S':
78110276Ssam 			case 's':
78210276Ssam 				return (S);
78310276Ssam 
78410276Ssam 			case 'T':
78510276Ssam 			case 't':
78610276Ssam 				return (T);
78710276Ssam 
78810276Ssam 			}
78910276Ssam 			break;
79010276Ssam 
79110276Ssam 		default:
79210276Ssam 			fatal("Unknown state in scanner.");
79310276Ssam 		}
79426494Sminshall 		yyerror((char *) 0);
79510276Ssam 		state = CMD;
79626494Sminshall 		longjmp(errcatch,0);
79710276Ssam 	}
79810276Ssam }
79910276Ssam 
80010276Ssam upper(s)
80110276Ssam 	char *s;
80210276Ssam {
80310276Ssam 	while (*s != '\0') {
80410276Ssam 		if (islower(*s))
80510276Ssam 			*s = toupper(*s);
80610276Ssam 		s++;
80710276Ssam 	}
80810276Ssam }
80910276Ssam 
81010276Ssam copy(s)
81110276Ssam 	char *s;
81210276Ssam {
81310276Ssam 	char *p;
81426494Sminshall 	extern char *malloc(), *strcpy();
81510276Ssam 
81626494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
81710276Ssam 	if (p == NULL)
81810276Ssam 		fatal("Ran out of memory.");
81926494Sminshall 	(void) strcpy(p, s);
82010276Ssam 	return ((int)p);
82110276Ssam }
82210276Ssam 
82310276Ssam help(s)
82410276Ssam 	char *s;
82510276Ssam {
82610276Ssam 	register struct tab *c;
82710276Ssam 	register int width, NCMDS;
82810276Ssam 
82910276Ssam 	width = 0, NCMDS = 0;
83010276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
831*31132Smckusick 		int len = strlen(c->name) + 1;
83210276Ssam 
83310276Ssam 		if (len > width)
83410276Ssam 			width = len;
83510276Ssam 		NCMDS++;
83610276Ssam 	}
83710276Ssam 	width = (width + 8) &~ 7;
83810276Ssam 	if (s == 0) {
83910276Ssam 		register int i, j, w;
84010276Ssam 		int columns, lines;
84110276Ssam 
84210276Ssam 		lreply(214,
84310276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
84410276Ssam 		columns = 76 / width;
84510276Ssam 		if (columns == 0)
84610276Ssam 			columns = 1;
84710276Ssam 		lines = (NCMDS + columns - 1) / columns;
84810276Ssam 		for (i = 0; i < lines; i++) {
84927107Smckusick 			printf("   ");
85010276Ssam 			for (j = 0; j < columns; j++) {
85110276Ssam 				c = cmdtab + j * lines + i;
85210276Ssam 				printf("%s%c", c->name,
85310276Ssam 					c->implemented ? ' ' : '*');
85410302Ssam 				if (c + lines >= &cmdtab[NCMDS])
85510276Ssam 					break;
856*31132Smckusick 				w = strlen(c->name) + 1;
85710276Ssam 				while (w < width) {
85810276Ssam 					putchar(' ');
85910276Ssam 					w++;
86010276Ssam 				}
86110276Ssam 			}
86210276Ssam 			printf("\r\n");
86310276Ssam 		}
86426494Sminshall 		(void) fflush(stdout);
86510276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
86610276Ssam 		return;
86710276Ssam 	}
86810276Ssam 	upper(s);
86910276Ssam 	c = lookup(s);
87010276Ssam 	if (c == (struct tab *)0) {
87127107Smckusick 		reply(502, "Unknown command %s.", s);
87210276Ssam 		return;
87310276Ssam 	}
87410276Ssam 	if (c->implemented)
87510276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
87610276Ssam 	else
87710276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
87810276Ssam }
879