xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 27107)
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*27107Smckusick static	char sccsid[] = "@(#)ftpcmd.y	5.5 (Berkeley) 04/16/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 				}
10610276Ssam 			}
10726494Sminshall 			free((char *) $3);
10810276Ssam 		}
10910276Ssam 	|	PASS SP password CRLF
11010276Ssam 		= {
11126494Sminshall 			pass((char *) $3);
11226494Sminshall 			free((char *) $3);
11310276Ssam 		}
11410276Ssam 	|	PORT SP host_port CRLF
11510276Ssam 		= {
11610320Ssam 			usedefault = 0;
11726045Sminshall 			if (pdata > 0) {
11826045Sminshall 				(void) close(pdata);
11926045Sminshall 			}
12026045Sminshall 			pdata = -1;
121*27107Smckusick 			reply(200, "PORT command successful.");
12210276Ssam 		}
12326045Sminshall 	|	PASV CRLF
12426045Sminshall 		= {
12526045Sminshall 			passive();
12626045Sminshall 		}
12710276Ssam 	|	TYPE SP type_code CRLF
12810276Ssam 		= {
12910276Ssam 			switch (cmd_type) {
13010276Ssam 
13110276Ssam 			case TYPE_A:
13210276Ssam 				if (cmd_form == FORM_N) {
13310276Ssam 					reply(200, "Type set to A.");
13410276Ssam 					type = cmd_type;
13510276Ssam 					form = cmd_form;
13610276Ssam 				} else
13710276Ssam 					reply(504, "Form must be N.");
13810276Ssam 				break;
13910276Ssam 
14010276Ssam 			case TYPE_E:
14110276Ssam 				reply(504, "Type E not implemented.");
14210276Ssam 				break;
14310276Ssam 
14410276Ssam 			case TYPE_I:
14510276Ssam 				reply(200, "Type set to I.");
14610276Ssam 				type = cmd_type;
14710276Ssam 				break;
14810276Ssam 
14910276Ssam 			case TYPE_L:
15010276Ssam 				if (cmd_bytesz == 8) {
15110276Ssam 					reply(200,
15210276Ssam 					    "Type set to L (byte size 8).");
15310276Ssam 					type = cmd_type;
15410276Ssam 				} else
15510276Ssam 					reply(504, "Byte size must be 8.");
15610276Ssam 			}
15710276Ssam 		}
15810276Ssam 	|	STRU SP struct_code CRLF
15910276Ssam 		= {
16010276Ssam 			switch ($3) {
16110276Ssam 
16210276Ssam 			case STRU_F:
16310276Ssam 				reply(200, "STRU F ok.");
16410276Ssam 				break;
16510276Ssam 
16610276Ssam 			default:
167*27107Smckusick 				reply(504, "Unimplemented STRU type.");
16810276Ssam 			}
16910276Ssam 		}
17010276Ssam 	|	MODE SP mode_code CRLF
17110276Ssam 		= {
17210276Ssam 			switch ($3) {
17310276Ssam 
17410276Ssam 			case MODE_S:
17510276Ssam 				reply(200, "MODE S ok.");
17610276Ssam 				break;
17710276Ssam 
17810276Ssam 			default:
17910276Ssam 				reply(502, "Unimplemented MODE type.");
18010276Ssam 			}
18110276Ssam 		}
18210276Ssam 	|	ALLO SP NUMBER CRLF
18310276Ssam 		= {
184*27107Smckusick 			reply(202, "ALLO command ignored.");
18510276Ssam 		}
18610276Ssam 	|	RETR check_login SP pathname CRLF
18710276Ssam 		= {
18810302Ssam 			if ($2 && $4 != NULL)
18926494Sminshall 				retrieve((char *) 0, (char *) $4);
19010302Ssam 			if ($4 != NULL)
19126494Sminshall 				free((char *) $4);
19210276Ssam 		}
19310276Ssam 	|	STOR check_login SP pathname CRLF
19410276Ssam 		= {
19510302Ssam 			if ($2 && $4 != NULL)
19626494Sminshall 				store((char *) $4, "w");
19710302Ssam 			if ($4 != NULL)
19826494Sminshall 				free((char *) $4);
19910276Ssam 		}
20010276Ssam 	|	APPE check_login SP pathname CRLF
20110276Ssam 		= {
20210302Ssam 			if ($2 && $4 != NULL)
20326494Sminshall 				store((char *) $4, "a");
20410302Ssam 			if ($4 != NULL)
20526494Sminshall 				free((char *) $4);
20610276Ssam 		}
20710276Ssam 	|	NLST check_login CRLF
20810276Ssam 		= {
20910276Ssam 			if ($2)
21011217Ssam 				retrieve("/bin/ls", "");
21110276Ssam 		}
21210276Ssam 	|	NLST check_login SP pathname CRLF
21310276Ssam 		= {
21410302Ssam 			if ($2 && $4 != NULL)
21526494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
21610302Ssam 			if ($4 != NULL)
21726494Sminshall 				free((char *) $4);
21810276Ssam 		}
21910276Ssam 	|	LIST check_login CRLF
22010276Ssam 		= {
22110276Ssam 			if ($2)
22210318Ssam 				retrieve("/bin/ls -lg", "");
22310276Ssam 		}
22410276Ssam 	|	LIST check_login SP pathname CRLF
22510276Ssam 		= {
22610302Ssam 			if ($2 && $4 != NULL)
22726494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
22810302Ssam 			if ($4 != NULL)
22926494Sminshall 				free((char *) $4);
23010276Ssam 		}
23110276Ssam 	|	DELE check_login SP pathname CRLF
23210276Ssam 		= {
23310302Ssam 			if ($2 && $4 != NULL)
23426494Sminshall 				delete((char *) $4);
23510302Ssam 			if ($4 != NULL)
23626494Sminshall 				free((char *) $4);
23710276Ssam 		}
23826045Sminshall 	|	ABOR CRLF
23926045Sminshall 		= {
240*27107Smckusick 			reply(225, "ABOR command successful.");
24126045Sminshall 		}
24210276Ssam 	|	CWD check_login CRLF
24310276Ssam 		= {
24410276Ssam 			if ($2)
24510276Ssam 				cwd(pw->pw_dir);
24610276Ssam 		}
24710276Ssam 	|	CWD check_login SP pathname CRLF
24810276Ssam 		= {
24910302Ssam 			if ($2 && $4 != NULL)
25026494Sminshall 				cwd((char *) $4);
25110302Ssam 			if ($4 != NULL)
25226494Sminshall 				free((char *) $4);
25310276Ssam 		}
25410276Ssam 	|	rename_cmd
25510276Ssam 	|	HELP CRLF
25610276Ssam 		= {
25726494Sminshall 			help((char *) 0);
25810276Ssam 		}
25910276Ssam 	|	HELP SP STRING CRLF
26010276Ssam 		= {
26126494Sminshall 			help((char *) $3);
26210276Ssam 		}
26310276Ssam 	|	NOOP CRLF
26410276Ssam 		= {
265*27107Smckusick 			reply(200, "NOOP command successful.");
26610276Ssam 		}
26710276Ssam 	|	XMKD check_login SP pathname CRLF
26810276Ssam 		= {
26910302Ssam 			if ($2 && $4 != NULL)
27026494Sminshall 				makedir((char *) $4);
27110302Ssam 			if ($4 != NULL)
27226494Sminshall 				free((char *) $4);
27310276Ssam 		}
27410276Ssam 	|	XRMD check_login SP pathname CRLF
27510276Ssam 		= {
27610302Ssam 			if ($2 && $4 != NULL)
27726494Sminshall 				removedir((char *) $4);
27810302Ssam 			if ($4 != NULL)
27926494Sminshall 				free((char *) $4);
28010276Ssam 		}
28110276Ssam 	|	XPWD check_login CRLF
28210276Ssam 		= {
28310276Ssam 			if ($2)
28410302Ssam 				pwd();
28510276Ssam 		}
28610276Ssam 	|	XCUP check_login CRLF
28710276Ssam 		= {
28810276Ssam 			if ($2)
28910276Ssam 				cwd("..");
29010276Ssam 		}
29126045Sminshall 	|	STOU check_login SP pathname CRLF
29226045Sminshall 		= {
29326045Sminshall 			if ($2 && $4 != NULL) {
29426045Sminshall 				unique++;
29526494Sminshall 				store((char *) $4, "w");
29626045Sminshall 				unique = 0;
29726045Sminshall 			}
29826045Sminshall 			if ($4 != NULL)
29926494Sminshall 				free((char *) $4);
30026045Sminshall 		}
30110276Ssam 	|	QUIT CRLF
30210276Ssam 		= {
30310276Ssam 			reply(221, "Goodbye.");
30413246Ssam 			dologout(0);
30510276Ssam 		}
30610276Ssam 	|	error CRLF
30710276Ssam 		= {
30810276Ssam 			yyerrok;
30910276Ssam 		}
31010276Ssam 	;
31110276Ssam 
31210276Ssam username:	STRING
31310276Ssam 	;
31410276Ssam 
31510276Ssam password:	STRING
31610276Ssam 	;
31710276Ssam 
31810276Ssam byte_size:	NUMBER
31910276Ssam 	;
32010276Ssam 
32110276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
32210276Ssam 		NUMBER COMMA NUMBER
32310276Ssam 		= {
32410276Ssam 			register char *a, *p;
32510276Ssam 
32610276Ssam 			a = (char *)&data_dest.sin_addr;
32710276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
32810276Ssam 			p = (char *)&data_dest.sin_port;
32910276Ssam 			p[0] = $9; p[1] = $11;
33010324Ssam 			data_dest.sin_family = AF_INET;
33110276Ssam 		}
33210276Ssam 	;
33310276Ssam 
33410276Ssam form_code:	N
33510276Ssam 	= {
33610276Ssam 		$$ = FORM_N;
33710276Ssam 	}
33810276Ssam 	|	T
33910276Ssam 	= {
34010276Ssam 		$$ = FORM_T;
34110276Ssam 	}
34210276Ssam 	|	C
34310276Ssam 	= {
34410276Ssam 		$$ = FORM_C;
34510276Ssam 	}
34610276Ssam 	;
34710276Ssam 
34810276Ssam type_code:	A
34910276Ssam 	= {
35010276Ssam 		cmd_type = TYPE_A;
35110276Ssam 		cmd_form = FORM_N;
35210276Ssam 	}
35310276Ssam 	|	A SP form_code
35410276Ssam 	= {
35510276Ssam 		cmd_type = TYPE_A;
35610276Ssam 		cmd_form = $3;
35710276Ssam 	}
35810276Ssam 	|	E
35910276Ssam 	= {
36010276Ssam 		cmd_type = TYPE_E;
36110276Ssam 		cmd_form = FORM_N;
36210276Ssam 	}
36310276Ssam 	|	E SP form_code
36410276Ssam 	= {
36510276Ssam 		cmd_type = TYPE_E;
36610276Ssam 		cmd_form = $3;
36710276Ssam 	}
36810276Ssam 	|	I
36910276Ssam 	= {
37010276Ssam 		cmd_type = TYPE_I;
37110276Ssam 	}
37210276Ssam 	|	L
37310276Ssam 	= {
37410276Ssam 		cmd_type = TYPE_L;
37510276Ssam 		cmd_bytesz = 8;
37610276Ssam 	}
37710276Ssam 	|	L SP byte_size
37810276Ssam 	= {
37910276Ssam 		cmd_type = TYPE_L;
38010276Ssam 		cmd_bytesz = $3;
38110276Ssam 	}
38210276Ssam 	/* this is for a bug in the BBN ftp */
38310276Ssam 	|	L byte_size
38410276Ssam 	= {
38510276Ssam 		cmd_type = TYPE_L;
38610276Ssam 		cmd_bytesz = $2;
38710276Ssam 	}
38810276Ssam 	;
38910276Ssam 
39010276Ssam struct_code:	F
39110276Ssam 	= {
39210276Ssam 		$$ = STRU_F;
39310276Ssam 	}
39410276Ssam 	|	R
39510276Ssam 	= {
39610276Ssam 		$$ = STRU_R;
39710276Ssam 	}
39810276Ssam 	|	P
39910276Ssam 	= {
40010276Ssam 		$$ = STRU_P;
40110276Ssam 	}
40210276Ssam 	;
40310276Ssam 
40410276Ssam mode_code:	S
40510276Ssam 	= {
40610276Ssam 		$$ = MODE_S;
40710276Ssam 	}
40810276Ssam 	|	B
40910276Ssam 	= {
41010276Ssam 		$$ = MODE_B;
41110276Ssam 	}
41210276Ssam 	|	C
41310276Ssam 	= {
41410276Ssam 		$$ = MODE_C;
41510276Ssam 	}
41610276Ssam 	;
41710276Ssam 
41810276Ssam pathname:	pathstring
41910276Ssam 	= {
420*27107Smckusick 		/*
421*27107Smckusick 		 * Problem: this production is used for all pathname
422*27107Smckusick 		 * processing, but only gives a 550 error reply.
423*27107Smckusick 		 * This is a valid reply in some cases but not in others.
424*27107Smckusick 		 */
42526494Sminshall 		if ($1 && strncmp((char *) $1, "~", 1) == 0) {
42626494Sminshall 			$$ = (int)*glob((char *) $1);
42710302Ssam 			if (globerr != NULL) {
42810276Ssam 				reply(550, globerr);
42910302Ssam 				$$ = NULL;
43010302Ssam 			}
43126494Sminshall 			free((char *) $1);
43210276Ssam 		} else
43310276Ssam 			$$ = $1;
43410276Ssam 	}
43510276Ssam 	;
43610276Ssam 
43710276Ssam pathstring:	STRING
43810276Ssam 	;
43910276Ssam 
44010276Ssam rename_cmd:	rename_from rename_to
44110276Ssam 	= {
44210276Ssam 		if ($1 && $2)
44326494Sminshall 			renamecmd((char *) $1, (char *) $2);
44410276Ssam 		else
44510276Ssam 			reply(503, "Bad sequence of commands.");
44610276Ssam 		if ($1)
44726494Sminshall 			free((char *) $1);
44810276Ssam 		if ($2)
44926494Sminshall 			free((char *) $2);
45010276Ssam 	}
45110276Ssam 	;
45210276Ssam 
45310276Ssam rename_from:	RNFR check_login SP pathname CRLF
45410276Ssam 	= {
45510276Ssam 		char *from = 0, *renamefrom();
45610276Ssam 
45710302Ssam 		if ($2 && $4)
45826494Sminshall 			from = renamefrom((char *) $4);
45910302Ssam 		if (from == 0 && $4)
46026494Sminshall 			free((char *) $4);
46110276Ssam 		$$ = (int)from;
46210276Ssam 	}
46310276Ssam 	;
46410276Ssam 
46510276Ssam rename_to:	RNTO SP pathname CRLF
46610276Ssam 	= {
46710276Ssam 		$$ = $3;
46810276Ssam 	}
46910276Ssam 	;
47010276Ssam 
47110276Ssam check_login:	/* empty */
47210276Ssam 	= {
47310276Ssam 		if (logged_in)
47410276Ssam 			$$ = 1;
47510276Ssam 		else {
47610276Ssam 			reply(530, "Please login with USER and PASS.");
47710276Ssam 			$$ = 0;
47810276Ssam 		}
47910276Ssam 	}
48010276Ssam 	;
48110276Ssam 
48210276Ssam %%
48310276Ssam 
48410276Ssam extern jmp_buf errcatch;
48510276Ssam 
48610276Ssam #define	CMD	0	/* beginning of command */
48710276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
48810276Ssam #define	STR1	2	/* expect SP followed by STRING */
48910276Ssam #define	STR2	3	/* expect STRING */
49010276Ssam #define	OSTR	4	/* optional STRING */
49110276Ssam 
49210276Ssam struct tab {
49310276Ssam 	char	*name;
49410276Ssam 	short	token;
49510276Ssam 	short	state;
49610276Ssam 	short	implemented;	/* 1 if command is implemented */
49710276Ssam 	char	*help;
49810276Ssam };
49910276Ssam 
50010276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
50110276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
50210276Ssam 	{ "PASS", PASS, STR1, 1,	"<sp> password" },
50310276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
50410276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
50510276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
50610276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
50726045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
50810276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
50910276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
51010276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
51110276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
51210276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
51310276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
51410276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
51510276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
51610276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
51710276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
51810276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
51910276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
52010276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
52110276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
52210276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
52310276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
52410276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
52526045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
52610276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
52710276Ssam 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name]" },
52810276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
52910276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
53010276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
53110276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
53210276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
53310276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
53410276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
53526045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
53610276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
53726045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
53810276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
53926045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
54010276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
54126045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54210276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
54326045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
54410276Ssam 	{ NULL,   0,    0,    0,	0 }
54510276Ssam };
54610276Ssam 
54710276Ssam struct tab *
54810276Ssam lookup(cmd)
54910276Ssam 	char *cmd;
55010276Ssam {
55110276Ssam 	register struct tab *p;
55210276Ssam 
55310276Ssam 	for (p = cmdtab; p->name != NULL; p++)
55410276Ssam 		if (strcmp(cmd, p->name) == 0)
55510276Ssam 			return (p);
55610276Ssam 	return (0);
55710276Ssam }
55810276Ssam 
55913033Ssam #include <arpa/telnet.h>
56010276Ssam 
56110276Ssam /*
56210276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
56310276Ssam  */
56410276Ssam char *
56510276Ssam getline(s, n, iop)
56610276Ssam 	char *s;
56710276Ssam 	register FILE *iop;
56810276Ssam {
56910276Ssam 	register c;
57026494Sminshall 	register char *cs;
57126494Sminshall 	char ch;
57210276Ssam 
57310276Ssam 	cs = s;
57426045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
57526045Sminshall 		*cs++ = tmpline[c];
57626045Sminshall 		if (tmpline[c] == '\n') {
57726045Sminshall 			*cs++ = '\0';
57826045Sminshall 			if (debug) {
57926494Sminshall 				syslog(LOG_DEBUG, "FTPD: command: %s", s);
58026045Sminshall 			}
58126045Sminshall 			tmpline[0] = '\0';
58226045Sminshall 			return(s);
58326045Sminshall 		}
58426045Sminshall 		if (c == 0) {
58526045Sminshall 			tmpline[0] = '\0';
58626045Sminshall 		}
58726045Sminshall 	}
58826045Sminshall 	while (--n > 0 && read(fileno(iop),&ch,1) >= 0) {
58926045Sminshall 		c = 0377 & ch;
59010276Ssam 		while (c == IAC) {
59126494Sminshall 			(void) read(fileno(iop),&ch,1);	/* skip command */
59226494Sminshall 			(void) read(fileno(iop),&ch,1);	/* try next char */
59326045Sminshall 			c = 0377 & ch;
59410276Ssam 		}
59510276Ssam 		*cs++ = c;
59610276Ssam 		if (c=='\n')
59710276Ssam 			break;
59810276Ssam 	}
59910276Ssam 	if (c < 0 && cs == s)
60018303Sralph 		return (NULL);
60110276Ssam 	*cs++ = '\0';
60211652Ssam 	if (debug) {
60326494Sminshall 		syslog(LOG_DEBUG, "FTPD: command: %s", s);
60411652Ssam 	}
60510276Ssam 	return (s);
60610276Ssam }
60710276Ssam 
60811652Ssam static int
60911652Ssam toolong()
61011652Ssam {
61126494Sminshall 	time_t now;
61211652Ssam 	extern char *ctime();
61326494Sminshall 	extern time_t time();
61411652Ssam 
61511652Ssam 	reply(421,
61611652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
61726494Sminshall 	(void) time(&now);
61811652Ssam 	if (logging) {
61926494Sminshall 		syslog(LOG_INFO,
62011652Ssam 			"FTPD: User %s timed out after %d seconds at %s",
62111652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
62211652Ssam 	}
62313246Ssam 	dologout(1);
62411652Ssam }
62511652Ssam 
62610276Ssam yylex()
62710276Ssam {
62810276Ssam 	static int cpos, state;
62910276Ssam 	register char *cp;
63010276Ssam 	register struct tab *p;
63110276Ssam 	int n;
63210276Ssam 	char c;
63310276Ssam 
63410276Ssam 	for (;;) {
63510276Ssam 		switch (state) {
63610276Ssam 
63710276Ssam 		case CMD:
63826494Sminshall 			(void) signal(SIGALRM, toolong);
63926494Sminshall 			(void) alarm((unsigned) timeout);
64010276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
64110276Ssam 				reply(221, "You could at least say goodbye.");
64213246Ssam 				dologout(0);
64310276Ssam 			}
64426494Sminshall 			(void) alarm(0);
64510276Ssam 			if (index(cbuf, '\r')) {
64610276Ssam 				cp = index(cbuf, '\r');
64710276Ssam 				cp[0] = '\n'; cp[1] = 0;
64810276Ssam 			}
64910276Ssam 			if (index(cbuf, ' '))
65010276Ssam 				cpos = index(cbuf, ' ') - cbuf;
65110276Ssam 			else
65226045Sminshall 				cpos = index(cbuf, '\n') - cbuf;
65326045Sminshall 			if (cpos == 0) {
65410276Ssam 				cpos = 4;
65526045Sminshall 			}
65610276Ssam 			c = cbuf[cpos];
65710276Ssam 			cbuf[cpos] = '\0';
65810276Ssam 			upper(cbuf);
65910276Ssam 			p = lookup(cbuf);
66010276Ssam 			cbuf[cpos] = c;
66110276Ssam 			if (p != 0) {
66210276Ssam 				if (p->implemented == 0) {
66310276Ssam 					nack(p->name);
66426494Sminshall 					longjmp(errcatch,0);
66510276Ssam 					/* NOTREACHED */
66610276Ssam 				}
66710276Ssam 				state = p->state;
66810276Ssam 				yylval = (int) p->name;
66910276Ssam 				return (p->token);
67010276Ssam 			}
67110276Ssam 			break;
67210276Ssam 
67310276Ssam 		case OSTR:
67410276Ssam 			if (cbuf[cpos] == '\n') {
67510276Ssam 				state = CMD;
67610276Ssam 				return (CRLF);
67710276Ssam 			}
67810276Ssam 			/* FALL THRU */
67910276Ssam 
68010276Ssam 		case STR1:
68110276Ssam 			if (cbuf[cpos] == ' ') {
68210276Ssam 				cpos++;
68310276Ssam 				state = STR2;
68410276Ssam 				return (SP);
68510276Ssam 			}
68610276Ssam 			break;
68710276Ssam 
68810276Ssam 		case STR2:
68910276Ssam 			cp = &cbuf[cpos];
69010276Ssam 			n = strlen(cp);
69110276Ssam 			cpos += n - 1;
69210276Ssam 			/*
69310276Ssam 			 * Make sure the string is nonempty and \n terminated.
69410276Ssam 			 */
69510276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
69610276Ssam 				cbuf[cpos] = '\0';
69710276Ssam 				yylval = copy(cp);
69810276Ssam 				cbuf[cpos] = '\n';
69910276Ssam 				state = ARGS;
70010276Ssam 				return (STRING);
70110276Ssam 			}
70210276Ssam 			break;
70310276Ssam 
70410276Ssam 		case ARGS:
70510276Ssam 			if (isdigit(cbuf[cpos])) {
70610276Ssam 				cp = &cbuf[cpos];
70710276Ssam 				while (isdigit(cbuf[++cpos]))
70810276Ssam 					;
70910276Ssam 				c = cbuf[cpos];
71010276Ssam 				cbuf[cpos] = '\0';
71110276Ssam 				yylval = atoi(cp);
71210276Ssam 				cbuf[cpos] = c;
71310276Ssam 				return (NUMBER);
71410276Ssam 			}
71510276Ssam 			switch (cbuf[cpos++]) {
71610276Ssam 
71710276Ssam 			case '\n':
71810276Ssam 				state = CMD;
71910276Ssam 				return (CRLF);
72010276Ssam 
72110276Ssam 			case ' ':
72210276Ssam 				return (SP);
72310276Ssam 
72410276Ssam 			case ',':
72510276Ssam 				return (COMMA);
72610276Ssam 
72710276Ssam 			case 'A':
72810276Ssam 			case 'a':
72910276Ssam 				return (A);
73010276Ssam 
73110276Ssam 			case 'B':
73210276Ssam 			case 'b':
73310276Ssam 				return (B);
73410276Ssam 
73510276Ssam 			case 'C':
73610276Ssam 			case 'c':
73710276Ssam 				return (C);
73810276Ssam 
73910276Ssam 			case 'E':
74010276Ssam 			case 'e':
74110276Ssam 				return (E);
74210276Ssam 
74310276Ssam 			case 'F':
74410276Ssam 			case 'f':
74510276Ssam 				return (F);
74610276Ssam 
74710276Ssam 			case 'I':
74810276Ssam 			case 'i':
74910276Ssam 				return (I);
75010276Ssam 
75110276Ssam 			case 'L':
75210276Ssam 			case 'l':
75310276Ssam 				return (L);
75410276Ssam 
75510276Ssam 			case 'N':
75610276Ssam 			case 'n':
75710276Ssam 				return (N);
75810276Ssam 
75910276Ssam 			case 'P':
76010276Ssam 			case 'p':
76110276Ssam 				return (P);
76210276Ssam 
76310276Ssam 			case 'R':
76410276Ssam 			case 'r':
76510276Ssam 				return (R);
76610276Ssam 
76710276Ssam 			case 'S':
76810276Ssam 			case 's':
76910276Ssam 				return (S);
77010276Ssam 
77110276Ssam 			case 'T':
77210276Ssam 			case 't':
77310276Ssam 				return (T);
77410276Ssam 
77510276Ssam 			}
77610276Ssam 			break;
77710276Ssam 
77810276Ssam 		default:
77910276Ssam 			fatal("Unknown state in scanner.");
78010276Ssam 		}
78126494Sminshall 		yyerror((char *) 0);
78210276Ssam 		state = CMD;
78326494Sminshall 		longjmp(errcatch,0);
78410276Ssam 	}
78510276Ssam }
78610276Ssam 
78710276Ssam upper(s)
78810276Ssam 	char *s;
78910276Ssam {
79010276Ssam 	while (*s != '\0') {
79110276Ssam 		if (islower(*s))
79210276Ssam 			*s = toupper(*s);
79310276Ssam 		s++;
79410276Ssam 	}
79510276Ssam }
79610276Ssam 
79710276Ssam copy(s)
79810276Ssam 	char *s;
79910276Ssam {
80010276Ssam 	char *p;
80126494Sminshall 	extern char *malloc(), *strcpy();
80210276Ssam 
80326494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
80410276Ssam 	if (p == NULL)
80510276Ssam 		fatal("Ran out of memory.");
80626494Sminshall 	(void) strcpy(p, s);
80710276Ssam 	return ((int)p);
80810276Ssam }
80910276Ssam 
81010276Ssam help(s)
81110276Ssam 	char *s;
81210276Ssam {
81310276Ssam 	register struct tab *c;
81410276Ssam 	register int width, NCMDS;
81510276Ssam 
81610276Ssam 	width = 0, NCMDS = 0;
81710276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
81810276Ssam 		int len = strlen(c->name);
81910276Ssam 
82010276Ssam 		if (c->implemented == 0)
82110276Ssam 			len++;
82210276Ssam 		if (len > width)
82310276Ssam 			width = len;
82410276Ssam 		NCMDS++;
82510276Ssam 	}
82610276Ssam 	width = (width + 8) &~ 7;
82710276Ssam 	if (s == 0) {
82810276Ssam 		register int i, j, w;
82910276Ssam 		int columns, lines;
83010276Ssam 
83110276Ssam 		lreply(214,
83210276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
83310276Ssam 		columns = 76 / width;
83410276Ssam 		if (columns == 0)
83510276Ssam 			columns = 1;
83610276Ssam 		lines = (NCMDS + columns - 1) / columns;
83710276Ssam 		for (i = 0; i < lines; i++) {
838*27107Smckusick 			printf("   ");
83910276Ssam 			for (j = 0; j < columns; j++) {
84010276Ssam 				c = cmdtab + j * lines + i;
84110276Ssam 				printf("%s%c", c->name,
84210276Ssam 					c->implemented ? ' ' : '*');
84310302Ssam 				if (c + lines >= &cmdtab[NCMDS])
84410276Ssam 					break;
84510276Ssam 				w = strlen(c->name);
84610276Ssam 				while (w < width) {
84710276Ssam 					putchar(' ');
84810276Ssam 					w++;
84910276Ssam 				}
85010276Ssam 			}
85110276Ssam 			printf("\r\n");
85210276Ssam 		}
85326494Sminshall 		(void) fflush(stdout);
85410276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
85510276Ssam 		return;
85610276Ssam 	}
85710276Ssam 	upper(s);
85810276Ssam 	c = lookup(s);
85910276Ssam 	if (c == (struct tab *)0) {
860*27107Smckusick 		reply(502, "Unknown command %s.", s);
86110276Ssam 		return;
86210276Ssam 	}
86310276Ssam 	if (c->implemented)
86410276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
86510276Ssam 	else
86610276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
86710276Ssam }
868