xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 34769)
110276Ssam /*
226045Sminshall  * Copyright (c) 1985 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
533738Sbostic  * Redistribution and use in source and binary forms are permitted
6*34769Sbostic  * provided that the above copyright notice and this paragraph are
7*34769Sbostic  * duplicated in all such forms and that any documentation,
8*34769Sbostic  * advertising materials, and other materials related to such
9*34769Sbostic  * distribution and use acknowledge that the software was developed
10*34769Sbostic  * by the University of California, Berkeley.  The name of the
11*34769Sbostic  * University may not be used to endorse or promote products derived
12*34769Sbostic  * from this software without specific prior written permission.
13*34769Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34769Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34769Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16*34769Sbostic  *
17*34769Sbostic  *	@(#)ftpcmd.y	5.11 (Berkeley) 06/18/88
1822501Sdist  */
1922501Sdist 
2022501Sdist /*
2110276Ssam  * Grammar for FTP commands.
2210276Ssam  * See RFC 765.
2310276Ssam  */
2410276Ssam 
2510276Ssam %{
2610276Ssam 
2710276Ssam #ifndef lint
28*34769Sbostic static char sccsid[] = "@(#)ftpcmd.y	5.11 (Berkeley) 06/18/88";
2933738Sbostic #endif /* not lint */
3010276Ssam 
3110276Ssam #include <sys/types.h>
3210276Ssam #include <sys/socket.h>
3310276Ssam 
3410276Ssam #include <netinet/in.h>
3510276Ssam 
3613033Ssam #include <arpa/ftp.h>
3713033Ssam 
3810276Ssam #include <stdio.h>
3911652Ssam #include <signal.h>
4010276Ssam #include <ctype.h>
4110276Ssam #include <pwd.h>
4210276Ssam #include <setjmp.h>
4326494Sminshall #include <syslog.h>
4410276Ssam 
4510276Ssam extern	struct sockaddr_in data_dest;
4610276Ssam extern	int logged_in;
4710276Ssam extern	struct passwd *pw;
4810276Ssam extern	int guest;
4910276Ssam extern	int logging;
5010276Ssam extern	int type;
5110276Ssam extern	int form;
5210276Ssam extern	int debug;
5311652Ssam extern	int timeout;
5426045Sminshall extern  int pdata;
5510276Ssam extern	char hostname[];
5610276Ssam extern	char *globerr;
5710320Ssam extern	int usedefault;
5826045Sminshall extern	int unique;
5926045Sminshall extern  int transflag;
6026045Sminshall extern  char tmpline[];
6110276Ssam char	**glob();
6210276Ssam 
6310276Ssam static	int cmd_type;
6410276Ssam static	int cmd_form;
6510276Ssam static	int cmd_bytesz;
6626045Sminshall char cbuf[512];
6730945Scsvsj char *fromname;
6810276Ssam 
6910276Ssam char	*index();
7010276Ssam %}
7110276Ssam 
7210276Ssam %token
7310276Ssam 	A	B	C	E	F	I
7410276Ssam 	L	N	P	R	S	T
7510276Ssam 
7610276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
7710276Ssam 
7810276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
7910276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
8010276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
8110276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
8210276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
8310276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
8426045Sminshall 	XCUP	STOU
8510276Ssam 
8610276Ssam 	LEXERR
8710276Ssam 
8810276Ssam %start	cmd_list
8910276Ssam 
9010276Ssam %%
9110276Ssam 
9210276Ssam cmd_list:	/* empty */
9310276Ssam 	|	cmd_list cmd
9430945Scsvsj 		= {
9530945Scsvsj 			fromname = (char *) 0;
9630945Scsvsj 		}
9730945Scsvsj 	|	cmd_list rcmd
9810276Ssam 	;
9910276Ssam 
10010276Ssam cmd:		USER SP username CRLF
10110276Ssam 		= {
10210276Ssam 			extern struct passwd *getpwnam();
10310276Ssam 
10426045Sminshall 			logged_in = 0;
10526494Sminshall 			if (strcmp((char *) $3, "ftp") == 0 ||
10626494Sminshall 			  strcmp((char *) $3, "anonymous") == 0) {
10710324Ssam 				if ((pw = getpwnam("ftp")) != NULL) {
10810324Ssam 					guest = 1;
10910324Ssam 					reply(331,
11010276Ssam 				  "Guest login ok, send ident as password.");
11110324Ssam 				}
11226045Sminshall 				else {
11326045Sminshall 					reply(530, "User %s unknown.", $3);
11426045Sminshall 				}
11526494Sminshall 			} else if (checkuser((char *) $3)) {
11610276Ssam 				guest = 0;
11726494Sminshall 				pw = getpwnam((char *) $3);
11826045Sminshall 				if (pw == NULL) {
11926045Sminshall 					reply(530, "User %s unknown.", $3);
12026045Sminshall 				}
12126045Sminshall 				else {
12226045Sminshall 				    reply(331, "Password required for %s.", $3);
12326045Sminshall 				}
12428865Smckusick 			} else {
12528865Smckusick 				reply(530, "User %s access denied.", $3);
12610276Ssam 			}
12726494Sminshall 			free((char *) $3);
12810276Ssam 		}
12910276Ssam 	|	PASS SP password CRLF
13010276Ssam 		= {
13126494Sminshall 			pass((char *) $3);
13226494Sminshall 			free((char *) $3);
13310276Ssam 		}
13410276Ssam 	|	PORT SP host_port CRLF
13510276Ssam 		= {
13610320Ssam 			usedefault = 0;
13726045Sminshall 			if (pdata > 0) {
13826045Sminshall 				(void) close(pdata);
13926045Sminshall 			}
14026045Sminshall 			pdata = -1;
14127107Smckusick 			reply(200, "PORT command successful.");
14210276Ssam 		}
14326045Sminshall 	|	PASV CRLF
14426045Sminshall 		= {
14526045Sminshall 			passive();
14626045Sminshall 		}
14710276Ssam 	|	TYPE SP type_code CRLF
14810276Ssam 		= {
14910276Ssam 			switch (cmd_type) {
15010276Ssam 
15110276Ssam 			case TYPE_A:
15210276Ssam 				if (cmd_form == FORM_N) {
15310276Ssam 					reply(200, "Type set to A.");
15410276Ssam 					type = cmd_type;
15510276Ssam 					form = cmd_form;
15610276Ssam 				} else
15710276Ssam 					reply(504, "Form must be N.");
15810276Ssam 				break;
15910276Ssam 
16010276Ssam 			case TYPE_E:
16110276Ssam 				reply(504, "Type E not implemented.");
16210276Ssam 				break;
16310276Ssam 
16410276Ssam 			case TYPE_I:
16510276Ssam 				reply(200, "Type set to I.");
16610276Ssam 				type = cmd_type;
16710276Ssam 				break;
16810276Ssam 
16910276Ssam 			case TYPE_L:
17010276Ssam 				if (cmd_bytesz == 8) {
17110276Ssam 					reply(200,
17210276Ssam 					    "Type set to L (byte size 8).");
17310276Ssam 					type = cmd_type;
17410276Ssam 				} else
17510276Ssam 					reply(504, "Byte size must be 8.");
17610276Ssam 			}
17710276Ssam 		}
17810276Ssam 	|	STRU SP struct_code CRLF
17910276Ssam 		= {
18010276Ssam 			switch ($3) {
18110276Ssam 
18210276Ssam 			case STRU_F:
18310276Ssam 				reply(200, "STRU F ok.");
18410276Ssam 				break;
18510276Ssam 
18610276Ssam 			default:
18727107Smckusick 				reply(504, "Unimplemented STRU type.");
18810276Ssam 			}
18910276Ssam 		}
19010276Ssam 	|	MODE SP mode_code CRLF
19110276Ssam 		= {
19210276Ssam 			switch ($3) {
19310276Ssam 
19410276Ssam 			case MODE_S:
19510276Ssam 				reply(200, "MODE S ok.");
19610276Ssam 				break;
19710276Ssam 
19810276Ssam 			default:
19910276Ssam 				reply(502, "Unimplemented MODE type.");
20010276Ssam 			}
20110276Ssam 		}
20210276Ssam 	|	ALLO SP NUMBER CRLF
20310276Ssam 		= {
20427107Smckusick 			reply(202, "ALLO command ignored.");
20510276Ssam 		}
20610276Ssam 	|	RETR check_login SP pathname CRLF
20710276Ssam 		= {
20810302Ssam 			if ($2 && $4 != NULL)
20926494Sminshall 				retrieve((char *) 0, (char *) $4);
21010302Ssam 			if ($4 != NULL)
21126494Sminshall 				free((char *) $4);
21210276Ssam 		}
21310276Ssam 	|	STOR check_login SP pathname CRLF
21410276Ssam 		= {
21510302Ssam 			if ($2 && $4 != NULL)
21626494Sminshall 				store((char *) $4, "w");
21710302Ssam 			if ($4 != NULL)
21826494Sminshall 				free((char *) $4);
21910276Ssam 		}
22010276Ssam 	|	APPE check_login SP pathname CRLF
22110276Ssam 		= {
22210302Ssam 			if ($2 && $4 != NULL)
22326494Sminshall 				store((char *) $4, "a");
22410302Ssam 			if ($4 != NULL)
22526494Sminshall 				free((char *) $4);
22610276Ssam 		}
22710276Ssam 	|	NLST check_login CRLF
22810276Ssam 		= {
22910276Ssam 			if ($2)
23011217Ssam 				retrieve("/bin/ls", "");
23110276Ssam 		}
23210276Ssam 	|	NLST check_login SP pathname CRLF
23310276Ssam 		= {
23410302Ssam 			if ($2 && $4 != NULL)
23526494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
23610302Ssam 			if ($4 != NULL)
23726494Sminshall 				free((char *) $4);
23810276Ssam 		}
23910276Ssam 	|	LIST check_login CRLF
24010276Ssam 		= {
24110276Ssam 			if ($2)
24210318Ssam 				retrieve("/bin/ls -lg", "");
24310276Ssam 		}
24410276Ssam 	|	LIST check_login SP pathname CRLF
24510276Ssam 		= {
24610302Ssam 			if ($2 && $4 != NULL)
24726494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
24810302Ssam 			if ($4 != NULL)
24926494Sminshall 				free((char *) $4);
25010276Ssam 		}
25110276Ssam 	|	DELE check_login SP pathname CRLF
25210276Ssam 		= {
25310302Ssam 			if ($2 && $4 != NULL)
25426494Sminshall 				delete((char *) $4);
25510302Ssam 			if ($4 != NULL)
25626494Sminshall 				free((char *) $4);
25710276Ssam 		}
25830945Scsvsj 	|	RNTO SP pathname CRLF
25930945Scsvsj 		= {
26030945Scsvsj 			if (fromname) {
26130945Scsvsj 				renamecmd(fromname, (char *) $3);
26230945Scsvsj 				free(fromname);
26330945Scsvsj 				fromname = (char *) 0;
26430945Scsvsj 			} else {
26530945Scsvsj 				reply(503, "Bad sequence of commands.");
26630945Scsvsj 			}
26730945Scsvsj 			free((char *) $3);
26830945Scsvsj 		}
26926045Sminshall 	|	ABOR CRLF
27026045Sminshall 		= {
27127107Smckusick 			reply(225, "ABOR command successful.");
27226045Sminshall 		}
27310276Ssam 	|	CWD check_login CRLF
27410276Ssam 		= {
27510276Ssam 			if ($2)
27610276Ssam 				cwd(pw->pw_dir);
27710276Ssam 		}
27810276Ssam 	|	CWD check_login SP pathname CRLF
27910276Ssam 		= {
28010302Ssam 			if ($2 && $4 != NULL)
28126494Sminshall 				cwd((char *) $4);
28210302Ssam 			if ($4 != NULL)
28326494Sminshall 				free((char *) $4);
28410276Ssam 		}
28510276Ssam 	|	HELP CRLF
28610276Ssam 		= {
28726494Sminshall 			help((char *) 0);
28810276Ssam 		}
28910276Ssam 	|	HELP SP STRING CRLF
29010276Ssam 		= {
29126494Sminshall 			help((char *) $3);
29210276Ssam 		}
29310276Ssam 	|	NOOP CRLF
29410276Ssam 		= {
29527107Smckusick 			reply(200, "NOOP command successful.");
29610276Ssam 		}
29710276Ssam 	|	XMKD check_login SP pathname CRLF
29810276Ssam 		= {
29910302Ssam 			if ($2 && $4 != NULL)
30026494Sminshall 				makedir((char *) $4);
30110302Ssam 			if ($4 != NULL)
30226494Sminshall 				free((char *) $4);
30310276Ssam 		}
30410276Ssam 	|	XRMD check_login SP pathname CRLF
30510276Ssam 		= {
30610302Ssam 			if ($2 && $4 != NULL)
30726494Sminshall 				removedir((char *) $4);
30810302Ssam 			if ($4 != NULL)
30926494Sminshall 				free((char *) $4);
31010276Ssam 		}
31110276Ssam 	|	XPWD check_login CRLF
31210276Ssam 		= {
31310276Ssam 			if ($2)
31410302Ssam 				pwd();
31510276Ssam 		}
31610276Ssam 	|	XCUP check_login CRLF
31710276Ssam 		= {
31810276Ssam 			if ($2)
31910276Ssam 				cwd("..");
32010276Ssam 		}
32126045Sminshall 	|	STOU check_login SP pathname CRLF
32226045Sminshall 		= {
32326045Sminshall 			if ($2 && $4 != NULL) {
32426045Sminshall 				unique++;
32526494Sminshall 				store((char *) $4, "w");
32626045Sminshall 				unique = 0;
32726045Sminshall 			}
32826045Sminshall 			if ($4 != NULL)
32926494Sminshall 				free((char *) $4);
33026045Sminshall 		}
33110276Ssam 	|	QUIT CRLF
33210276Ssam 		= {
33310276Ssam 			reply(221, "Goodbye.");
33413246Ssam 			dologout(0);
33510276Ssam 		}
33610276Ssam 	|	error CRLF
33710276Ssam 		= {
33810276Ssam 			yyerrok;
33910276Ssam 		}
34010276Ssam 	;
34110276Ssam 
34230945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
34330945Scsvsj 		= {
34430945Scsvsj 			char *renamefrom();
34530945Scsvsj 
34630945Scsvsj 			if ($2 && $4) {
34730945Scsvsj 				fromname = renamefrom((char *) $4);
34830945Scsvsj 				if (fromname == (char *) 0 && $4) {
34930945Scsvsj 					free((char *) $4);
35030945Scsvsj 				}
35130945Scsvsj 			}
35230945Scsvsj 		}
35330945Scsvsj 	;
35430945Scsvsj 
35510276Ssam username:	STRING
35610276Ssam 	;
35710276Ssam 
35810276Ssam password:	STRING
35910276Ssam 	;
36010276Ssam 
36110276Ssam byte_size:	NUMBER
36210276Ssam 	;
36310276Ssam 
36410276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
36510276Ssam 		NUMBER COMMA NUMBER
36610276Ssam 		= {
36710276Ssam 			register char *a, *p;
36810276Ssam 
36910276Ssam 			a = (char *)&data_dest.sin_addr;
37010276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
37110276Ssam 			p = (char *)&data_dest.sin_port;
37210276Ssam 			p[0] = $9; p[1] = $11;
37310324Ssam 			data_dest.sin_family = AF_INET;
37410276Ssam 		}
37510276Ssam 	;
37610276Ssam 
37710276Ssam form_code:	N
37810276Ssam 	= {
37910276Ssam 		$$ = FORM_N;
38010276Ssam 	}
38110276Ssam 	|	T
38210276Ssam 	= {
38310276Ssam 		$$ = FORM_T;
38410276Ssam 	}
38510276Ssam 	|	C
38610276Ssam 	= {
38710276Ssam 		$$ = FORM_C;
38810276Ssam 	}
38910276Ssam 	;
39010276Ssam 
39110276Ssam type_code:	A
39210276Ssam 	= {
39310276Ssam 		cmd_type = TYPE_A;
39410276Ssam 		cmd_form = FORM_N;
39510276Ssam 	}
39610276Ssam 	|	A SP form_code
39710276Ssam 	= {
39810276Ssam 		cmd_type = TYPE_A;
39910276Ssam 		cmd_form = $3;
40010276Ssam 	}
40110276Ssam 	|	E
40210276Ssam 	= {
40310276Ssam 		cmd_type = TYPE_E;
40410276Ssam 		cmd_form = FORM_N;
40510276Ssam 	}
40610276Ssam 	|	E SP form_code
40710276Ssam 	= {
40810276Ssam 		cmd_type = TYPE_E;
40910276Ssam 		cmd_form = $3;
41010276Ssam 	}
41110276Ssam 	|	I
41210276Ssam 	= {
41310276Ssam 		cmd_type = TYPE_I;
41410276Ssam 	}
41510276Ssam 	|	L
41610276Ssam 	= {
41710276Ssam 		cmd_type = TYPE_L;
41810276Ssam 		cmd_bytesz = 8;
41910276Ssam 	}
42010276Ssam 	|	L SP byte_size
42110276Ssam 	= {
42210276Ssam 		cmd_type = TYPE_L;
42310276Ssam 		cmd_bytesz = $3;
42410276Ssam 	}
42510276Ssam 	/* this is for a bug in the BBN ftp */
42610276Ssam 	|	L byte_size
42710276Ssam 	= {
42810276Ssam 		cmd_type = TYPE_L;
42910276Ssam 		cmd_bytesz = $2;
43010276Ssam 	}
43110276Ssam 	;
43210276Ssam 
43310276Ssam struct_code:	F
43410276Ssam 	= {
43510276Ssam 		$$ = STRU_F;
43610276Ssam 	}
43710276Ssam 	|	R
43810276Ssam 	= {
43910276Ssam 		$$ = STRU_R;
44010276Ssam 	}
44110276Ssam 	|	P
44210276Ssam 	= {
44310276Ssam 		$$ = STRU_P;
44410276Ssam 	}
44510276Ssam 	;
44610276Ssam 
44710276Ssam mode_code:	S
44810276Ssam 	= {
44910276Ssam 		$$ = MODE_S;
45010276Ssam 	}
45110276Ssam 	|	B
45210276Ssam 	= {
45310276Ssam 		$$ = MODE_B;
45410276Ssam 	}
45510276Ssam 	|	C
45610276Ssam 	= {
45710276Ssam 		$$ = MODE_C;
45810276Ssam 	}
45910276Ssam 	;
46010276Ssam 
46110276Ssam pathname:	pathstring
46210276Ssam 	= {
46327107Smckusick 		/*
46427107Smckusick 		 * Problem: this production is used for all pathname
46527107Smckusick 		 * processing, but only gives a 550 error reply.
46627107Smckusick 		 * This is a valid reply in some cases but not in others.
46727107Smckusick 		 */
46826494Sminshall 		if ($1 && strncmp((char *) $1, "~", 1) == 0) {
46926494Sminshall 			$$ = (int)*glob((char *) $1);
47010302Ssam 			if (globerr != NULL) {
47110276Ssam 				reply(550, globerr);
47210302Ssam 				$$ = NULL;
47310302Ssam 			}
47426494Sminshall 			free((char *) $1);
47510276Ssam 		} else
47610276Ssam 			$$ = $1;
47710276Ssam 	}
47810276Ssam 	;
47910276Ssam 
48010276Ssam pathstring:	STRING
48110276Ssam 	;
48210276Ssam 
48310276Ssam check_login:	/* empty */
48410276Ssam 	= {
48510276Ssam 		if (logged_in)
48610276Ssam 			$$ = 1;
48710276Ssam 		else {
48810276Ssam 			reply(530, "Please login with USER and PASS.");
48910276Ssam 			$$ = 0;
49010276Ssam 		}
49110276Ssam 	}
49210276Ssam 	;
49310276Ssam 
49410276Ssam %%
49510276Ssam 
49610276Ssam extern jmp_buf errcatch;
49710276Ssam 
49810276Ssam #define	CMD	0	/* beginning of command */
49910276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
50010276Ssam #define	STR1	2	/* expect SP followed by STRING */
50110276Ssam #define	STR2	3	/* expect STRING */
50210276Ssam #define	OSTR	4	/* optional STRING */
50310276Ssam 
50410276Ssam struct tab {
50510276Ssam 	char	*name;
50610276Ssam 	short	token;
50710276Ssam 	short	state;
50810276Ssam 	short	implemented;	/* 1 if command is implemented */
50910276Ssam 	char	*help;
51010276Ssam };
51110276Ssam 
51210276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
51310276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
51410276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
51510276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
51610276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
51710276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
51810276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
51926045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
52010276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
52110276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
52210276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
52310276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
52410276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
52510276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
52610276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
52710276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
52810276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
52910276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
53010276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
53110276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
53210276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
53310276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
53410276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
53510276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
53610276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
53726045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
53810276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
53910276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
54010276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
54110276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
54210276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
54310276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
54410276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
54510276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
54610276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
54726045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
54810276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
54926045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
55010276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
55126045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
55210276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
55326045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
55410276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
55526045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
55610276Ssam 	{ NULL,   0,    0,    0,	0 }
55710276Ssam };
55810276Ssam 
55910276Ssam struct tab *
56010276Ssam lookup(cmd)
56110276Ssam 	char *cmd;
56210276Ssam {
56310276Ssam 	register struct tab *p;
56410276Ssam 
56510276Ssam 	for (p = cmdtab; p->name != NULL; p++)
56610276Ssam 		if (strcmp(cmd, p->name) == 0)
56710276Ssam 			return (p);
56810276Ssam 	return (0);
56910276Ssam }
57010276Ssam 
57113033Ssam #include <arpa/telnet.h>
57210276Ssam 
57310276Ssam /*
57410276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
57510276Ssam  */
57610276Ssam char *
57710276Ssam getline(s, n, iop)
57810276Ssam 	char *s;
57910276Ssam 	register FILE *iop;
58010276Ssam {
58110276Ssam 	register c;
58226494Sminshall 	register char *cs;
58310276Ssam 
58410276Ssam 	cs = s;
58527751Sminshall /* tmpline may contain saved command from urgent mode interruption */
58626045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
58726045Sminshall 		*cs++ = tmpline[c];
58826045Sminshall 		if (tmpline[c] == '\n') {
58926045Sminshall 			*cs++ = '\0';
59026045Sminshall 			if (debug) {
59126494Sminshall 				syslog(LOG_DEBUG, "FTPD: command: %s", s);
59226045Sminshall 			}
59326045Sminshall 			tmpline[0] = '\0';
59426045Sminshall 			return(s);
59526045Sminshall 		}
59626045Sminshall 		if (c == 0) {
59726045Sminshall 			tmpline[0] = '\0';
59826045Sminshall 		}
59926045Sminshall 	}
60027751Sminshall 	while (--n > 0 && (c = getc(iop)) != EOF) {
60127751Sminshall 		c = 0377 & c;
60210276Ssam 		while (c == IAC) {
60327751Sminshall 			switch (c = 0377 & getc(iop)) {
60427751Sminshall 			case WILL:
60527751Sminshall 			case WONT:
60627751Sminshall 				c = 0377 & getc(iop);
60727751Sminshall 				printf("%c%c%c", IAC, WONT, c);
60827751Sminshall 				(void) fflush(stdout);
60927751Sminshall 				break;
61027751Sminshall 			case DO:
61127751Sminshall 			case DONT:
61227751Sminshall 				c = 0377 & getc(iop);
61327751Sminshall 				printf("%c%c%c", IAC, DONT, c);
61427751Sminshall 				(void) fflush(stdout);
61527751Sminshall 				break;
61627751Sminshall 			default:
61727751Sminshall 				break;
61827751Sminshall 			}
61927751Sminshall 			c = 0377 & getc(iop); /* try next character */
62010276Ssam 		}
62110276Ssam 		*cs++ = c;
62210276Ssam 		if (c=='\n')
62310276Ssam 			break;
62410276Ssam 	}
62527751Sminshall 	if (c == EOF && cs == s)
62618303Sralph 		return (NULL);
62710276Ssam 	*cs++ = '\0';
62811652Ssam 	if (debug) {
62926494Sminshall 		syslog(LOG_DEBUG, "FTPD: command: %s", s);
63011652Ssam 	}
63110276Ssam 	return (s);
63210276Ssam }
63310276Ssam 
63411652Ssam static int
63511652Ssam toolong()
63611652Ssam {
63726494Sminshall 	time_t now;
63811652Ssam 	extern char *ctime();
63926494Sminshall 	extern time_t time();
64011652Ssam 
64111652Ssam 	reply(421,
64211652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
64326494Sminshall 	(void) time(&now);
64411652Ssam 	if (logging) {
64526494Sminshall 		syslog(LOG_INFO,
64611652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
64711652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
64811652Ssam 	}
64913246Ssam 	dologout(1);
65011652Ssam }
65111652Ssam 
65210276Ssam yylex()
65310276Ssam {
65410276Ssam 	static int cpos, state;
65510276Ssam 	register char *cp;
65610276Ssam 	register struct tab *p;
65710276Ssam 	int n;
65810276Ssam 	char c;
65910276Ssam 
66010276Ssam 	for (;;) {
66110276Ssam 		switch (state) {
66210276Ssam 
66310276Ssam 		case CMD:
66426494Sminshall 			(void) signal(SIGALRM, toolong);
66526494Sminshall 			(void) alarm((unsigned) timeout);
66610276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
66710276Ssam 				reply(221, "You could at least say goodbye.");
66813246Ssam 				dologout(0);
66910276Ssam 			}
67026494Sminshall 			(void) alarm(0);
67110276Ssam 			if (index(cbuf, '\r')) {
67210276Ssam 				cp = index(cbuf, '\r');
67310276Ssam 				cp[0] = '\n'; cp[1] = 0;
67410276Ssam 			}
67510276Ssam 			if (index(cbuf, ' '))
67610276Ssam 				cpos = index(cbuf, ' ') - cbuf;
67710276Ssam 			else
67826045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
67926045Sminshall 			if (cpos == 0) {
68010276Ssam 				cpos = 4;
68126045Sminshall 			}
68210276Ssam 			c = cbuf[cpos];
68310276Ssam 			cbuf[cpos] = '\0';
68410276Ssam 			upper(cbuf);
68510276Ssam 			p = lookup(cbuf);
68610276Ssam 			cbuf[cpos] = c;
68710276Ssam 			if (p != 0) {
68810276Ssam 				if (p->implemented == 0) {
68910276Ssam 					nack(p->name);
69026494Sminshall 					longjmp(errcatch,0);
69110276Ssam 					/* NOTREACHED */
69210276Ssam 				}
69310276Ssam 				state = p->state;
69410276Ssam 				yylval = (int) p->name;
69510276Ssam 				return (p->token);
69610276Ssam 			}
69710276Ssam 			break;
69810276Ssam 
69910276Ssam 		case OSTR:
70010276Ssam 			if (cbuf[cpos] == '\n') {
70110276Ssam 				state = CMD;
70210276Ssam 				return (CRLF);
70310276Ssam 			}
70410276Ssam 			/* FALL THRU */
70510276Ssam 
70610276Ssam 		case STR1:
70710276Ssam 			if (cbuf[cpos] == ' ') {
70810276Ssam 				cpos++;
70910276Ssam 				state = STR2;
71010276Ssam 				return (SP);
71110276Ssam 			}
71210276Ssam 			break;
71310276Ssam 
71410276Ssam 		case STR2:
71510276Ssam 			cp = &cbuf[cpos];
71610276Ssam 			n = strlen(cp);
71710276Ssam 			cpos += n - 1;
71810276Ssam 			/*
71910276Ssam 			 * Make sure the string is nonempty and \n terminated.
72010276Ssam 			 */
72110276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
72210276Ssam 				cbuf[cpos] = '\0';
72310276Ssam 				yylval = copy(cp);
72410276Ssam 				cbuf[cpos] = '\n';
72510276Ssam 				state = ARGS;
72610276Ssam 				return (STRING);
72710276Ssam 			}
72810276Ssam 			break;
72910276Ssam 
73010276Ssam 		case ARGS:
73110276Ssam 			if (isdigit(cbuf[cpos])) {
73210276Ssam 				cp = &cbuf[cpos];
73310276Ssam 				while (isdigit(cbuf[++cpos]))
73410276Ssam 					;
73510276Ssam 				c = cbuf[cpos];
73610276Ssam 				cbuf[cpos] = '\0';
73710276Ssam 				yylval = atoi(cp);
73810276Ssam 				cbuf[cpos] = c;
73910276Ssam 				return (NUMBER);
74010276Ssam 			}
74110276Ssam 			switch (cbuf[cpos++]) {
74210276Ssam 
74310276Ssam 			case '\n':
74410276Ssam 				state = CMD;
74510276Ssam 				return (CRLF);
74610276Ssam 
74710276Ssam 			case ' ':
74810276Ssam 				return (SP);
74910276Ssam 
75010276Ssam 			case ',':
75110276Ssam 				return (COMMA);
75210276Ssam 
75310276Ssam 			case 'A':
75410276Ssam 			case 'a':
75510276Ssam 				return (A);
75610276Ssam 
75710276Ssam 			case 'B':
75810276Ssam 			case 'b':
75910276Ssam 				return (B);
76010276Ssam 
76110276Ssam 			case 'C':
76210276Ssam 			case 'c':
76310276Ssam 				return (C);
76410276Ssam 
76510276Ssam 			case 'E':
76610276Ssam 			case 'e':
76710276Ssam 				return (E);
76810276Ssam 
76910276Ssam 			case 'F':
77010276Ssam 			case 'f':
77110276Ssam 				return (F);
77210276Ssam 
77310276Ssam 			case 'I':
77410276Ssam 			case 'i':
77510276Ssam 				return (I);
77610276Ssam 
77710276Ssam 			case 'L':
77810276Ssam 			case 'l':
77910276Ssam 				return (L);
78010276Ssam 
78110276Ssam 			case 'N':
78210276Ssam 			case 'n':
78310276Ssam 				return (N);
78410276Ssam 
78510276Ssam 			case 'P':
78610276Ssam 			case 'p':
78710276Ssam 				return (P);
78810276Ssam 
78910276Ssam 			case 'R':
79010276Ssam 			case 'r':
79110276Ssam 				return (R);
79210276Ssam 
79310276Ssam 			case 'S':
79410276Ssam 			case 's':
79510276Ssam 				return (S);
79610276Ssam 
79710276Ssam 			case 'T':
79810276Ssam 			case 't':
79910276Ssam 				return (T);
80010276Ssam 
80110276Ssam 			}
80210276Ssam 			break;
80310276Ssam 
80410276Ssam 		default:
80510276Ssam 			fatal("Unknown state in scanner.");
80610276Ssam 		}
80726494Sminshall 		yyerror((char *) 0);
80810276Ssam 		state = CMD;
80926494Sminshall 		longjmp(errcatch,0);
81010276Ssam 	}
81110276Ssam }
81210276Ssam 
81310276Ssam upper(s)
81410276Ssam 	char *s;
81510276Ssam {
81610276Ssam 	while (*s != '\0') {
81710276Ssam 		if (islower(*s))
81810276Ssam 			*s = toupper(*s);
81910276Ssam 		s++;
82010276Ssam 	}
82110276Ssam }
82210276Ssam 
82310276Ssam copy(s)
82410276Ssam 	char *s;
82510276Ssam {
82610276Ssam 	char *p;
82726494Sminshall 	extern char *malloc(), *strcpy();
82810276Ssam 
82926494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
83010276Ssam 	if (p == NULL)
83110276Ssam 		fatal("Ran out of memory.");
83226494Sminshall 	(void) strcpy(p, s);
83310276Ssam 	return ((int)p);
83410276Ssam }
83510276Ssam 
83610276Ssam help(s)
83710276Ssam 	char *s;
83810276Ssam {
83910276Ssam 	register struct tab *c;
84010276Ssam 	register int width, NCMDS;
84110276Ssam 
84210276Ssam 	width = 0, NCMDS = 0;
84310276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
84431132Smckusick 		int len = strlen(c->name) + 1;
84510276Ssam 
84610276Ssam 		if (len > width)
84710276Ssam 			width = len;
84810276Ssam 		NCMDS++;
84910276Ssam 	}
85010276Ssam 	width = (width + 8) &~ 7;
85110276Ssam 	if (s == 0) {
85210276Ssam 		register int i, j, w;
85310276Ssam 		int columns, lines;
85410276Ssam 
85510276Ssam 		lreply(214,
85610276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
85710276Ssam 		columns = 76 / width;
85810276Ssam 		if (columns == 0)
85910276Ssam 			columns = 1;
86010276Ssam 		lines = (NCMDS + columns - 1) / columns;
86110276Ssam 		for (i = 0; i < lines; i++) {
86227107Smckusick 			printf("   ");
86310276Ssam 			for (j = 0; j < columns; j++) {
86410276Ssam 				c = cmdtab + j * lines + i;
86510276Ssam 				printf("%s%c", c->name,
86610276Ssam 					c->implemented ? ' ' : '*');
86710302Ssam 				if (c + lines >= &cmdtab[NCMDS])
86810276Ssam 					break;
86931132Smckusick 				w = strlen(c->name) + 1;
87010276Ssam 				while (w < width) {
87110276Ssam 					putchar(' ');
87210276Ssam 					w++;
87310276Ssam 				}
87410276Ssam 			}
87510276Ssam 			printf("\r\n");
87610276Ssam 		}
87726494Sminshall 		(void) fflush(stdout);
87810276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
87910276Ssam 		return;
88010276Ssam 	}
88110276Ssam 	upper(s);
88210276Ssam 	c = lookup(s);
88310276Ssam 	if (c == (struct tab *)0) {
88427107Smckusick 		reply(502, "Unknown command %s.", s);
88510276Ssam 		return;
88610276Ssam 	}
88710276Ssam 	if (c->implemented)
88810276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
88910276Ssam 	else
89010276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
89110276Ssam }
892