xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 36620)
110276Ssam /*
236304Skarels  * Copyright (c) 1985, 1988 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
533738Sbostic  * Redistribution and use in source and binary forms are permitted
634769Sbostic  * provided that the above copyright notice and this paragraph are
734769Sbostic  * duplicated in all such forms and that any documentation,
834769Sbostic  * advertising materials, and other materials related to such
934769Sbostic  * distribution and use acknowledge that the software was developed
1034769Sbostic  * by the University of California, Berkeley.  The name of the
1134769Sbostic  * University may not be used to endorse or promote products derived
1234769Sbostic  * from this software without specific prior written permission.
1334769Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434769Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534769Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1634769Sbostic  *
17*36620Srick  *	@(#)ftpcmd.y	5.18 (Berkeley) 01/25/89
1822501Sdist  */
1922501Sdist 
2022501Sdist /*
2110276Ssam  * Grammar for FTP commands.
2210276Ssam  * See RFC 765.
2310276Ssam  */
2410276Ssam 
2510276Ssam %{
2610276Ssam 
2710276Ssam #ifndef lint
28*36620Srick static char sccsid[] = "@(#)ftpcmd.y	5.18	(Berkeley) 01/25/89";
2933738Sbostic #endif /* not lint */
3010276Ssam 
3136552Sbostic #include <sys/param.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;
55*36620Srick extern	char hostname[], remotehost[];
5610276Ssam extern	char *globerr;
5710320Ssam extern	int usedefault;
5826045Sminshall extern  int transflag;
5926045Sminshall extern  char tmpline[];
6010276Ssam char	**glob();
6110276Ssam 
6236552Sbostic off_t	restart_point;
6336552Sbostic 
6410276Ssam static	int cmd_type;
6510276Ssam static	int cmd_form;
6610276Ssam static	int cmd_bytesz;
6736304Skarels char	cbuf[512];
6836304Skarels char	*fromname;
6910276Ssam 
7010276Ssam char	*index();
7110276Ssam %}
7210276Ssam 
7310276Ssam %token
7410276Ssam 	A	B	C	E	F	I
7510276Ssam 	L	N	P	R	S	T
7610276Ssam 
7710276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
7810276Ssam 
7910276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
8010276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
8110276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
8210276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
8310276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
84*36620Srick 	STAT	HELP	NOOP	MKD	RMD	PWD
85*36620Srick 	CDUP	STOU	SYST
8610276Ssam 
8710276Ssam 	LEXERR
8810276Ssam 
8910276Ssam %start	cmd_list
9010276Ssam 
9110276Ssam %%
9210276Ssam 
9310276Ssam cmd_list:	/* empty */
9410276Ssam 	|	cmd_list cmd
9530945Scsvsj 		= {
9630945Scsvsj 			fromname = (char *) 0;
9736552Sbostic 			restart_point = (off_t) 0;
9830945Scsvsj 		}
9930945Scsvsj 	|	cmd_list rcmd
10010276Ssam 	;
10110276Ssam 
10210276Ssam cmd:		USER SP username CRLF
10310276Ssam 		= {
10436304Skarels 			user((char *) $3);
10526494Sminshall 			free((char *) $3);
10610276Ssam 		}
10710276Ssam 	|	PASS SP password CRLF
10810276Ssam 		= {
10926494Sminshall 			pass((char *) $3);
11026494Sminshall 			free((char *) $3);
11110276Ssam 		}
11210276Ssam 	|	PORT SP host_port CRLF
11310276Ssam 		= {
11410320Ssam 			usedefault = 0;
11536304Skarels 			if (pdata >= 0) {
11626045Sminshall 				(void) close(pdata);
11736304Skarels 				pdata = -1;
11826045Sminshall 			}
11927107Smckusick 			reply(200, "PORT command successful.");
12010276Ssam 		}
12126045Sminshall 	|	PASV CRLF
12226045Sminshall 		= {
12326045Sminshall 			passive();
12426045Sminshall 		}
12510276Ssam 	|	TYPE SP type_code CRLF
12610276Ssam 		= {
12710276Ssam 			switch (cmd_type) {
12810276Ssam 
12910276Ssam 			case TYPE_A:
13010276Ssam 				if (cmd_form == FORM_N) {
13110276Ssam 					reply(200, "Type set to A.");
13210276Ssam 					type = cmd_type;
13310276Ssam 					form = cmd_form;
13410276Ssam 				} else
13510276Ssam 					reply(504, "Form must be N.");
13610276Ssam 				break;
13710276Ssam 
13810276Ssam 			case TYPE_E:
13910276Ssam 				reply(504, "Type E not implemented.");
14010276Ssam 				break;
14110276Ssam 
14210276Ssam 			case TYPE_I:
14310276Ssam 				reply(200, "Type set to I.");
14410276Ssam 				type = cmd_type;
14510276Ssam 				break;
14610276Ssam 
14710276Ssam 			case TYPE_L:
14836552Sbostic 				if (cmd_bytesz == NBBY) {
14910276Ssam 					reply(200,
15036552Sbostic 					    "Type set to L (byte size %d).",
15136552Sbostic 					    NBBY);
15210276Ssam 					type = cmd_type;
15310276Ssam 				} else
15436552Sbostic 					reply(504, "Byte size must be %d.",
15536552Sbostic 					    NBBY);
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:
16727107Smckusick 				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 		= {
18427107Smckusick 			reply(202, "ALLO command ignored.");
18510276Ssam 		}
18610276Ssam 	|	RETR check_login SP pathname CRLF
18710276Ssam 		= {
18810302Ssam 			if ($2 && $4 != NULL)
189*36620Srick 				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)
19636304Skarels 				store((char *) $4, "w", 0);
19710302Ssam 			if ($4 != NULL)
19826494Sminshall 				free((char *) $4);
19910276Ssam 		}
20010276Ssam 	|	APPE check_login SP pathname CRLF
20110276Ssam 		= {
20210302Ssam 			if ($2 && $4 != NULL)
20336304Skarels 				store((char *) $4, "a", 0);
20410302Ssam 			if ($4 != NULL)
20526494Sminshall 				free((char *) $4);
20610276Ssam 		}
20710276Ssam 	|	NLST check_login CRLF
20810276Ssam 		= {
20910276Ssam 			if ($2)
210*36620Srick 				send_file_list(".");
21110276Ssam 		}
212*36620Srick 	|	NLST check_login SP STRING CRLF
21310276Ssam 		= {
214*36620Srick 			if ($2 && $4 != NULL)
215*36620Srick 				send_file_list((char *) $4);
21610302Ssam 			if ($4 != NULL)
21726494Sminshall 				free((char *) $4);
21810276Ssam 		}
21910276Ssam 	|	LIST check_login CRLF
22010276Ssam 		= {
22110276Ssam 			if ($2)
222*36620Srick 				retrieve("/bin/ls -lgA", "");
22310276Ssam 		}
22410276Ssam 	|	LIST check_login SP pathname CRLF
22510276Ssam 		= {
22610302Ssam 			if ($2 && $4 != NULL)
227*36620Srick 				retrieve("/bin/ls -lgA %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 		}
23830945Scsvsj 	|	RNTO SP pathname CRLF
23930945Scsvsj 		= {
24030945Scsvsj 			if (fromname) {
24130945Scsvsj 				renamecmd(fromname, (char *) $3);
24230945Scsvsj 				free(fromname);
24330945Scsvsj 				fromname = (char *) 0;
24430945Scsvsj 			} else {
24530945Scsvsj 				reply(503, "Bad sequence of commands.");
24630945Scsvsj 			}
24730945Scsvsj 			free((char *) $3);
24830945Scsvsj 		}
24926045Sminshall 	|	ABOR CRLF
25026045Sminshall 		= {
25127107Smckusick 			reply(225, "ABOR command successful.");
25226045Sminshall 		}
25310276Ssam 	|	CWD check_login CRLF
25410276Ssam 		= {
25510276Ssam 			if ($2)
25610276Ssam 				cwd(pw->pw_dir);
25710276Ssam 		}
25810276Ssam 	|	CWD check_login SP pathname CRLF
25910276Ssam 		= {
26010302Ssam 			if ($2 && $4 != NULL)
26126494Sminshall 				cwd((char *) $4);
26210302Ssam 			if ($4 != NULL)
26326494Sminshall 				free((char *) $4);
26410276Ssam 		}
26510276Ssam 	|	HELP CRLF
26610276Ssam 		= {
26726494Sminshall 			help((char *) 0);
26810276Ssam 		}
26910276Ssam 	|	HELP SP STRING CRLF
27010276Ssam 		= {
27126494Sminshall 			help((char *) $3);
27210276Ssam 		}
27310276Ssam 	|	NOOP CRLF
27410276Ssam 		= {
27527107Smckusick 			reply(200, "NOOP command successful.");
27610276Ssam 		}
277*36620Srick 	|	MKD check_login SP pathname CRLF
27810276Ssam 		= {
27910302Ssam 			if ($2 && $4 != NULL)
28026494Sminshall 				makedir((char *) $4);
28110302Ssam 			if ($4 != NULL)
28226494Sminshall 				free((char *) $4);
28310276Ssam 		}
284*36620Srick 	|	RMD check_login SP pathname CRLF
28510276Ssam 		= {
28610302Ssam 			if ($2 && $4 != NULL)
28726494Sminshall 				removedir((char *) $4);
28810302Ssam 			if ($4 != NULL)
28926494Sminshall 				free((char *) $4);
29010276Ssam 		}
291*36620Srick 	|	PWD check_login CRLF
29210276Ssam 		= {
29310276Ssam 			if ($2)
29410302Ssam 				pwd();
29510276Ssam 		}
296*36620Srick 	|	CDUP check_login CRLF
29710276Ssam 		= {
29810276Ssam 			if ($2)
29910276Ssam 				cwd("..");
30010276Ssam 		}
30126045Sminshall 	|	STOU check_login SP pathname CRLF
30226045Sminshall 		= {
30336304Skarels 			if ($2 && $4 != NULL)
30436304Skarels 				store((char *) $4, "w", 1);
30526045Sminshall 			if ($4 != NULL)
30626494Sminshall 				free((char *) $4);
30726045Sminshall 		}
30836552Sbostic 	|	SYST CRLF
30936552Sbostic 		= {
31036552Sbostic 			reply(215, "UNIX Type: L%d Version: BSD-%d",
31136552Sbostic 				NBBY, BSD);
31236552Sbostic 		}
31310276Ssam 	|	QUIT CRLF
31410276Ssam 		= {
31510276Ssam 			reply(221, "Goodbye.");
31613246Ssam 			dologout(0);
31710276Ssam 		}
31810276Ssam 	|	error CRLF
31910276Ssam 		= {
32010276Ssam 			yyerrok;
32110276Ssam 		}
32210276Ssam 	;
32330945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
32430945Scsvsj 		= {
32530945Scsvsj 			char *renamefrom();
32630945Scsvsj 
32736552Sbostic 			restart_point = (off_t) 0;
32830945Scsvsj 			if ($2 && $4) {
32930945Scsvsj 				fromname = renamefrom((char *) $4);
33030945Scsvsj 				if (fromname == (char *) 0 && $4) {
33130945Scsvsj 					free((char *) $4);
33230945Scsvsj 				}
33330945Scsvsj 			}
33430945Scsvsj 		}
33536552Sbostic 	|	REST SP byte_size CRLF
33636552Sbostic 		= {
33736552Sbostic 			long atol();
33836552Sbostic 
33936552Sbostic 			fromname = (char *) 0;
34036552Sbostic 			restart_point = $3;
34136552Sbostic 			reply(350, "Restarting at %ld. Send STORE or RETRIEVE to initiate transfer.", restart_point);
34236552Sbostic 		}
34330945Scsvsj 	;
34430945Scsvsj 
34510276Ssam username:	STRING
34610276Ssam 	;
34710276Ssam 
34836304Skarels password:	/* empty */
34936304Skarels 		= {
35036304Skarels 			$$ = (int) "";
35136304Skarels 		}
35236304Skarels 	|	STRING
35310276Ssam 	;
35410276Ssam 
35510276Ssam byte_size:	NUMBER
35610276Ssam 	;
35710276Ssam 
35810276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
35910276Ssam 		NUMBER COMMA NUMBER
36010276Ssam 		= {
36110276Ssam 			register char *a, *p;
36210276Ssam 
36310276Ssam 			a = (char *)&data_dest.sin_addr;
36410276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
36510276Ssam 			p = (char *)&data_dest.sin_port;
36610276Ssam 			p[0] = $9; p[1] = $11;
36710324Ssam 			data_dest.sin_family = AF_INET;
36810276Ssam 		}
36910276Ssam 	;
37010276Ssam 
37110276Ssam form_code:	N
37210276Ssam 	= {
37310276Ssam 		$$ = FORM_N;
37410276Ssam 	}
37510276Ssam 	|	T
37610276Ssam 	= {
37710276Ssam 		$$ = FORM_T;
37810276Ssam 	}
37910276Ssam 	|	C
38010276Ssam 	= {
38110276Ssam 		$$ = FORM_C;
38210276Ssam 	}
38310276Ssam 	;
38410276Ssam 
38510276Ssam type_code:	A
38610276Ssam 	= {
38710276Ssam 		cmd_type = TYPE_A;
38810276Ssam 		cmd_form = FORM_N;
38910276Ssam 	}
39010276Ssam 	|	A SP form_code
39110276Ssam 	= {
39210276Ssam 		cmd_type = TYPE_A;
39310276Ssam 		cmd_form = $3;
39410276Ssam 	}
39510276Ssam 	|	E
39610276Ssam 	= {
39710276Ssam 		cmd_type = TYPE_E;
39810276Ssam 		cmd_form = FORM_N;
39910276Ssam 	}
40010276Ssam 	|	E SP form_code
40110276Ssam 	= {
40210276Ssam 		cmd_type = TYPE_E;
40310276Ssam 		cmd_form = $3;
40410276Ssam 	}
40510276Ssam 	|	I
40610276Ssam 	= {
40710276Ssam 		cmd_type = TYPE_I;
40810276Ssam 	}
40910276Ssam 	|	L
41010276Ssam 	= {
41110276Ssam 		cmd_type = TYPE_L;
41236552Sbostic 		cmd_bytesz = NBBY;
41310276Ssam 	}
41410276Ssam 	|	L SP byte_size
41510276Ssam 	= {
41610276Ssam 		cmd_type = TYPE_L;
41710276Ssam 		cmd_bytesz = $3;
41810276Ssam 	}
41910276Ssam 	/* this is for a bug in the BBN ftp */
42010276Ssam 	|	L byte_size
42110276Ssam 	= {
42210276Ssam 		cmd_type = TYPE_L;
42310276Ssam 		cmd_bytesz = $2;
42410276Ssam 	}
42510276Ssam 	;
42610276Ssam 
42710276Ssam struct_code:	F
42810276Ssam 	= {
42910276Ssam 		$$ = STRU_F;
43010276Ssam 	}
43110276Ssam 	|	R
43210276Ssam 	= {
43310276Ssam 		$$ = STRU_R;
43410276Ssam 	}
43510276Ssam 	|	P
43610276Ssam 	= {
43710276Ssam 		$$ = STRU_P;
43810276Ssam 	}
43910276Ssam 	;
44010276Ssam 
44110276Ssam mode_code:	S
44210276Ssam 	= {
44310276Ssam 		$$ = MODE_S;
44410276Ssam 	}
44510276Ssam 	|	B
44610276Ssam 	= {
44710276Ssam 		$$ = MODE_B;
44810276Ssam 	}
44910276Ssam 	|	C
45010276Ssam 	= {
45110276Ssam 		$$ = MODE_C;
45210276Ssam 	}
45310276Ssam 	;
45410276Ssam 
45510276Ssam pathname:	pathstring
45610276Ssam 	= {
45727107Smckusick 		/*
45827107Smckusick 		 * Problem: this production is used for all pathname
45927107Smckusick 		 * processing, but only gives a 550 error reply.
46027107Smckusick 		 * This is a valid reply in some cases but not in others.
46127107Smckusick 		 */
46236304Skarels 		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
46326494Sminshall 			$$ = (int)*glob((char *) $1);
46410302Ssam 			if (globerr != NULL) {
46510276Ssam 				reply(550, globerr);
46610302Ssam 				$$ = NULL;
46710302Ssam 			}
46826494Sminshall 			free((char *) $1);
46910276Ssam 		} else
47010276Ssam 			$$ = $1;
47110276Ssam 	}
47210276Ssam 	;
47310276Ssam 
47410276Ssam pathstring:	STRING
47510276Ssam 	;
47610276Ssam 
47710276Ssam check_login:	/* empty */
47810276Ssam 	= {
47910276Ssam 		if (logged_in)
48010276Ssam 			$$ = 1;
48110276Ssam 		else {
48210276Ssam 			reply(530, "Please login with USER and PASS.");
48310276Ssam 			$$ = 0;
48410276Ssam 		}
48510276Ssam 	}
48610276Ssam 	;
48710276Ssam 
48810276Ssam %%
48910276Ssam 
49010276Ssam extern jmp_buf errcatch;
49110276Ssam 
49210276Ssam #define	CMD	0	/* beginning of command */
49310276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
49410276Ssam #define	STR1	2	/* expect SP followed by STRING */
49510276Ssam #define	STR2	3	/* expect STRING */
49636304Skarels #define	OSTR	4	/* optional SP then STRING */
49736304Skarels #define	ZSTR1	5	/* SP then optional STRING */
49836304Skarels #define	ZSTR2	6	/* optional STRING after SP */
49910276Ssam 
50010276Ssam struct tab {
50110276Ssam 	char	*name;
50210276Ssam 	short	token;
50310276Ssam 	short	state;
50410276Ssam 	short	implemented;	/* 1 if command is implemented */
50510276Ssam 	char	*help;
50610276Ssam };
50710276Ssam 
50810276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
50910276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
51036304Skarels 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
51110276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
51210276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
51310276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
51410276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
51526045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
51610276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
51710276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
51810276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
51910276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
52010276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
52110276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
52210276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
52310276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
52410276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
52510276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
52610276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
52710276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
52810276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
52910276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
53036552Sbostic 	{ "REST", REST, ARGS, 1,	"(restart command)" },
53110276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
53210276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
53326045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
53410276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
53536304Skarels 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
53610276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
53710276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
53810276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
53910276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
54036552Sbostic 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
54110276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
54210276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
54310276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
544*36620Srick 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
545*36620Srick 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
546*36620Srick 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
547*36620Srick 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
548*36620Srick 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
549*36620Srick 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
550*36620Srick 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
551*36620Srick 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
55226045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
55310276Ssam 	{ NULL,   0,    0,    0,	0 }
55410276Ssam };
55510276Ssam 
55610276Ssam struct tab *
55710276Ssam lookup(cmd)
55810276Ssam 	char *cmd;
55910276Ssam {
56010276Ssam 	register struct tab *p;
56110276Ssam 
56210276Ssam 	for (p = cmdtab; p->name != NULL; p++)
56310276Ssam 		if (strcmp(cmd, p->name) == 0)
56410276Ssam 			return (p);
56510276Ssam 	return (0);
56610276Ssam }
56710276Ssam 
56813033Ssam #include <arpa/telnet.h>
56910276Ssam 
57010276Ssam /*
57110276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
57210276Ssam  */
57310276Ssam char *
57410276Ssam getline(s, n, iop)
57510276Ssam 	char *s;
57610276Ssam 	register FILE *iop;
57710276Ssam {
57810276Ssam 	register c;
57926494Sminshall 	register char *cs;
58010276Ssam 
58110276Ssam 	cs = s;
58227751Sminshall /* tmpline may contain saved command from urgent mode interruption */
58326045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
58426045Sminshall 		*cs++ = tmpline[c];
58526045Sminshall 		if (tmpline[c] == '\n') {
58626045Sminshall 			*cs++ = '\0';
58736304Skarels 			if (debug)
58836304Skarels 				syslog(LOG_DEBUG, "command: %s", s);
58926045Sminshall 			tmpline[0] = '\0';
59026045Sminshall 			return(s);
59126045Sminshall 		}
59236304Skarels 		if (c == 0)
59326045Sminshall 			tmpline[0] = '\0';
59426045Sminshall 	}
59536304Skarels 	while ((c = getc(iop)) != EOF) {
59636304Skarels 		c &= 0377;
59736304Skarels 		if (c == IAC) {
59836304Skarels 		    if ((c = getc(iop)) != EOF) {
59936304Skarels 			c &= 0377;
60036304Skarels 			switch (c) {
60127751Sminshall 			case WILL:
60227751Sminshall 			case WONT:
60336277Sbostic 				c = getc(iop);
60436304Skarels 				printf("%c%c%c", IAC, DONT, 0377&c);
60527751Sminshall 				(void) fflush(stdout);
60636304Skarels 				continue;
60727751Sminshall 			case DO:
60827751Sminshall 			case DONT:
60936277Sbostic 				c = getc(iop);
61036304Skarels 				printf("%c%c%c", IAC, WONT, 0377&c);
61127751Sminshall 				(void) fflush(stdout);
61236304Skarels 				continue;
61336304Skarels 			case IAC:
61427751Sminshall 				break;
61527751Sminshall 			default:
61636304Skarels 				continue;	/* ignore command */
61727751Sminshall 			}
61836304Skarels 		    }
61910276Ssam 		}
62036304Skarels 		*cs++ = c;
62136304Skarels 		if (--n <= 0 || c == '\n')
62210276Ssam 			break;
62310276Ssam 	}
62427751Sminshall 	if (c == EOF && cs == s)
62518303Sralph 		return (NULL);
62610276Ssam 	*cs++ = '\0';
62736304Skarels 	if (debug)
62836304Skarels 		syslog(LOG_DEBUG, "command: %s", s);
62910276Ssam 	return (s);
63010276Ssam }
63110276Ssam 
63211652Ssam static int
63311652Ssam toolong()
63411652Ssam {
63526494Sminshall 	time_t now;
63611652Ssam 	extern char *ctime();
63726494Sminshall 	extern time_t time();
63811652Ssam 
63911652Ssam 	reply(421,
64011652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
64126494Sminshall 	(void) time(&now);
64211652Ssam 	if (logging) {
64326494Sminshall 		syslog(LOG_INFO,
64436304Skarels 			"User %s timed out after %d seconds at %s",
64511652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
64611652Ssam 	}
64713246Ssam 	dologout(1);
64811652Ssam }
64911652Ssam 
65010276Ssam yylex()
65110276Ssam {
65210276Ssam 	static int cpos, state;
65310276Ssam 	register char *cp;
65410276Ssam 	register struct tab *p;
65510276Ssam 	int n;
65636277Sbostic 	char c, *strpbrk();
65710276Ssam 
65810276Ssam 	for (;;) {
65910276Ssam 		switch (state) {
66010276Ssam 
66110276Ssam 		case CMD:
66226494Sminshall 			(void) signal(SIGALRM, toolong);
66326494Sminshall 			(void) alarm((unsigned) timeout);
66410276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
66510276Ssam 				reply(221, "You could at least say goodbye.");
66613246Ssam 				dologout(0);
66710276Ssam 			}
66826494Sminshall 			(void) alarm(0);
669*36620Srick #ifdef SETPROCTITLE
670*36620Srick 			if (strncasecmp(cbuf, "PASS", 4) != NULL) {
671*36620Srick 				setproctitle("%s: %s", remotehost, cbuf);
672*36620Srick 			}
673*36620Srick #endif /* SETPROCTITLE */
67436324Sbostic 			if ((cp = index(cbuf, '\r'))) {
67536324Sbostic 				*cp++ = '\n';
67636324Sbostic 				*cp = '\0';
67736324Sbostic 			}
67836277Sbostic 			if ((cp = strpbrk(cbuf, " \n")))
67936277Sbostic 				cpos = cp - cbuf;
68036304Skarels 			if (cpos == 0)
68110276Ssam 				cpos = 4;
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 			}
70436317Sbostic 			/* FALLTHROUGH */
70510276Ssam 
70610276Ssam 		case STR1:
70736304Skarels 		case ZSTR1:
70810276Ssam 			if (cbuf[cpos] == ' ') {
70910276Ssam 				cpos++;
71036317Sbostic 				state = state == OSTR ? STR2 : ++state;
71110276Ssam 				return (SP);
71210276Ssam 			}
71310276Ssam 			break;
71410276Ssam 
71536304Skarels 		case ZSTR2:
71636304Skarels 			if (cbuf[cpos] == '\n') {
71736304Skarels 				state = CMD;
71836304Skarels 				return (CRLF);
71936304Skarels 			}
72036304Skarels 			/* FALL THRU */
72136304Skarels 
72210276Ssam 		case STR2:
72310276Ssam 			cp = &cbuf[cpos];
72410276Ssam 			n = strlen(cp);
72510276Ssam 			cpos += n - 1;
72610276Ssam 			/*
72710276Ssam 			 * Make sure the string is nonempty and \n terminated.
72810276Ssam 			 */
72910276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
73010276Ssam 				cbuf[cpos] = '\0';
73110276Ssam 				yylval = copy(cp);
73210276Ssam 				cbuf[cpos] = '\n';
73310276Ssam 				state = ARGS;
73410276Ssam 				return (STRING);
73510276Ssam 			}
73610276Ssam 			break;
73710276Ssam 
73810276Ssam 		case ARGS:
73910276Ssam 			if (isdigit(cbuf[cpos])) {
74010276Ssam 				cp = &cbuf[cpos];
74110276Ssam 				while (isdigit(cbuf[++cpos]))
74210276Ssam 					;
74310276Ssam 				c = cbuf[cpos];
74410276Ssam 				cbuf[cpos] = '\0';
74510276Ssam 				yylval = atoi(cp);
74610276Ssam 				cbuf[cpos] = c;
74710276Ssam 				return (NUMBER);
74810276Ssam 			}
74910276Ssam 			switch (cbuf[cpos++]) {
75010276Ssam 
75110276Ssam 			case '\n':
75210276Ssam 				state = CMD;
75310276Ssam 				return (CRLF);
75410276Ssam 
75510276Ssam 			case ' ':
75610276Ssam 				return (SP);
75710276Ssam 
75810276Ssam 			case ',':
75910276Ssam 				return (COMMA);
76010276Ssam 
76110276Ssam 			case 'A':
76210276Ssam 			case 'a':
76310276Ssam 				return (A);
76410276Ssam 
76510276Ssam 			case 'B':
76610276Ssam 			case 'b':
76710276Ssam 				return (B);
76810276Ssam 
76910276Ssam 			case 'C':
77010276Ssam 			case 'c':
77110276Ssam 				return (C);
77210276Ssam 
77310276Ssam 			case 'E':
77410276Ssam 			case 'e':
77510276Ssam 				return (E);
77610276Ssam 
77710276Ssam 			case 'F':
77810276Ssam 			case 'f':
77910276Ssam 				return (F);
78010276Ssam 
78110276Ssam 			case 'I':
78210276Ssam 			case 'i':
78310276Ssam 				return (I);
78410276Ssam 
78510276Ssam 			case 'L':
78610276Ssam 			case 'l':
78710276Ssam 				return (L);
78810276Ssam 
78910276Ssam 			case 'N':
79010276Ssam 			case 'n':
79110276Ssam 				return (N);
79210276Ssam 
79310276Ssam 			case 'P':
79410276Ssam 			case 'p':
79510276Ssam 				return (P);
79610276Ssam 
79710276Ssam 			case 'R':
79810276Ssam 			case 'r':
79910276Ssam 				return (R);
80010276Ssam 
80110276Ssam 			case 'S':
80210276Ssam 			case 's':
80310276Ssam 				return (S);
80410276Ssam 
80510276Ssam 			case 'T':
80610276Ssam 			case 't':
80710276Ssam 				return (T);
80810276Ssam 
80910276Ssam 			}
81010276Ssam 			break;
81110276Ssam 
81210276Ssam 		default:
81310276Ssam 			fatal("Unknown state in scanner.");
81410276Ssam 		}
81526494Sminshall 		yyerror((char *) 0);
81610276Ssam 		state = CMD;
81726494Sminshall 		longjmp(errcatch,0);
81810276Ssam 	}
81910276Ssam }
82010276Ssam 
82110276Ssam upper(s)
82236277Sbostic 	register char *s;
82310276Ssam {
82410276Ssam 	while (*s != '\0') {
82510276Ssam 		if (islower(*s))
82610276Ssam 			*s = toupper(*s);
82710276Ssam 		s++;
82810276Ssam 	}
82910276Ssam }
83010276Ssam 
83110276Ssam copy(s)
83210276Ssam 	char *s;
83310276Ssam {
83410276Ssam 	char *p;
83526494Sminshall 	extern char *malloc(), *strcpy();
83610276Ssam 
83726494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
83810276Ssam 	if (p == NULL)
83910276Ssam 		fatal("Ran out of memory.");
84026494Sminshall 	(void) strcpy(p, s);
84110276Ssam 	return ((int)p);
84210276Ssam }
84310276Ssam 
84410276Ssam help(s)
84510276Ssam 	char *s;
84610276Ssam {
84710276Ssam 	register struct tab *c;
84810276Ssam 	register int width, NCMDS;
84910276Ssam 
85010276Ssam 	width = 0, NCMDS = 0;
85110276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
85231132Smckusick 		int len = strlen(c->name) + 1;
85310276Ssam 
85410276Ssam 		if (len > width)
85510276Ssam 			width = len;
85610276Ssam 		NCMDS++;
85710276Ssam 	}
85810276Ssam 	width = (width + 8) &~ 7;
85910276Ssam 	if (s == 0) {
86010276Ssam 		register int i, j, w;
86110276Ssam 		int columns, lines;
86210276Ssam 
86310276Ssam 		lreply(214,
86410276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
86510276Ssam 		columns = 76 / width;
86610276Ssam 		if (columns == 0)
86710276Ssam 			columns = 1;
86810276Ssam 		lines = (NCMDS + columns - 1) / columns;
86910276Ssam 		for (i = 0; i < lines; i++) {
87027107Smckusick 			printf("   ");
87110276Ssam 			for (j = 0; j < columns; j++) {
87210276Ssam 				c = cmdtab + j * lines + i;
87310276Ssam 				printf("%s%c", c->name,
87410276Ssam 					c->implemented ? ' ' : '*');
87510302Ssam 				if (c + lines >= &cmdtab[NCMDS])
87610276Ssam 					break;
87731132Smckusick 				w = strlen(c->name) + 1;
87810276Ssam 				while (w < width) {
87910276Ssam 					putchar(' ');
88010276Ssam 					w++;
88110276Ssam 				}
88210276Ssam 			}
88310276Ssam 			printf("\r\n");
88410276Ssam 		}
88526494Sminshall 		(void) fflush(stdout);
88610276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
88710276Ssam 		return;
88810276Ssam 	}
88910276Ssam 	upper(s);
89010276Ssam 	c = lookup(s);
89110276Ssam 	if (c == (struct tab *)0) {
89227107Smckusick 		reply(502, "Unknown command %s.", s);
89310276Ssam 		return;
89410276Ssam 	}
89510276Ssam 	if (c->implemented)
89610276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
89710276Ssam 	else
89810276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
89910276Ssam }
900