xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 36640)
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*36640Srick  *	@(#)ftpcmd.y	5.19 (Berkeley) 01/26/89
1822501Sdist  */
1922501Sdist 
2022501Sdist /*
2110276Ssam  * Grammar for FTP commands.
2210276Ssam  * See RFC 765.
2310276Ssam  */
2410276Ssam 
2510276Ssam %{
2610276Ssam 
2710276Ssam #ifndef lint
28*36640Srick static char sccsid[] = "@(#)ftpcmd.y	5.19	(Berkeley) 01/26/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;
5536620Srick 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
8436620Srick 	STAT	HELP	NOOP	MKD	RMD	PWD
8536620Srick 	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)
18936620Srick 				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)
21036620Srick 				send_file_list(".");
21110276Ssam 		}
21236620Srick 	|	NLST check_login SP STRING CRLF
21310276Ssam 		= {
21436620Srick 			if ($2 && $4 != NULL)
21536620Srick 				send_file_list((char *) $4);
21610302Ssam 			if ($4 != NULL)
21726494Sminshall 				free((char *) $4);
21810276Ssam 		}
21910276Ssam 	|	LIST check_login CRLF
22010276Ssam 		= {
22110276Ssam 			if ($2)
22236620Srick 				retrieve("/bin/ls -lgA", "");
22310276Ssam 		}
22410276Ssam 	|	LIST check_login SP pathname CRLF
22510276Ssam 		= {
22610302Ssam 			if ($2 && $4 != NULL)
22736620Srick 				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 		}
27736620Srick 	|	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 		}
28436620Srick 	|	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 		}
29136620Srick 	|	PWD check_login CRLF
29210276Ssam 		= {
29310276Ssam 			if ($2)
29410302Ssam 				pwd();
29510276Ssam 		}
29636620Srick 	|	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 		= {
310*36640Srick #ifdef unix
31136552Sbostic 			reply(215, "UNIX Type: L%d Version: BSD-%d",
312*36640Srick #else
313*36640Srick 			reply(215, "UNKNOWN Type: L%d Version: BSD-%d",
314*36640Srick #endif
31536552Sbostic 				NBBY, BSD);
31636552Sbostic 		}
31710276Ssam 	|	QUIT CRLF
31810276Ssam 		= {
31910276Ssam 			reply(221, "Goodbye.");
32013246Ssam 			dologout(0);
32110276Ssam 		}
32210276Ssam 	|	error CRLF
32310276Ssam 		= {
32410276Ssam 			yyerrok;
32510276Ssam 		}
32610276Ssam 	;
32730945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
32830945Scsvsj 		= {
32930945Scsvsj 			char *renamefrom();
33030945Scsvsj 
33136552Sbostic 			restart_point = (off_t) 0;
33230945Scsvsj 			if ($2 && $4) {
33330945Scsvsj 				fromname = renamefrom((char *) $4);
33430945Scsvsj 				if (fromname == (char *) 0 && $4) {
33530945Scsvsj 					free((char *) $4);
33630945Scsvsj 				}
33730945Scsvsj 			}
33830945Scsvsj 		}
33936552Sbostic 	|	REST SP byte_size CRLF
34036552Sbostic 		= {
34136552Sbostic 			long atol();
34236552Sbostic 
34336552Sbostic 			fromname = (char *) 0;
34436552Sbostic 			restart_point = $3;
34536552Sbostic 			reply(350, "Restarting at %ld. Send STORE or RETRIEVE to initiate transfer.", restart_point);
34636552Sbostic 		}
34730945Scsvsj 	;
34830945Scsvsj 
34910276Ssam username:	STRING
35010276Ssam 	;
35110276Ssam 
35236304Skarels password:	/* empty */
35336304Skarels 		= {
35436304Skarels 			$$ = (int) "";
35536304Skarels 		}
35636304Skarels 	|	STRING
35710276Ssam 	;
35810276Ssam 
35910276Ssam byte_size:	NUMBER
36010276Ssam 	;
36110276Ssam 
36210276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
36310276Ssam 		NUMBER COMMA NUMBER
36410276Ssam 		= {
36510276Ssam 			register char *a, *p;
36610276Ssam 
36710276Ssam 			a = (char *)&data_dest.sin_addr;
36810276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
36910276Ssam 			p = (char *)&data_dest.sin_port;
37010276Ssam 			p[0] = $9; p[1] = $11;
37110324Ssam 			data_dest.sin_family = AF_INET;
37210276Ssam 		}
37310276Ssam 	;
37410276Ssam 
37510276Ssam form_code:	N
37610276Ssam 	= {
37710276Ssam 		$$ = FORM_N;
37810276Ssam 	}
37910276Ssam 	|	T
38010276Ssam 	= {
38110276Ssam 		$$ = FORM_T;
38210276Ssam 	}
38310276Ssam 	|	C
38410276Ssam 	= {
38510276Ssam 		$$ = FORM_C;
38610276Ssam 	}
38710276Ssam 	;
38810276Ssam 
38910276Ssam type_code:	A
39010276Ssam 	= {
39110276Ssam 		cmd_type = TYPE_A;
39210276Ssam 		cmd_form = FORM_N;
39310276Ssam 	}
39410276Ssam 	|	A SP form_code
39510276Ssam 	= {
39610276Ssam 		cmd_type = TYPE_A;
39710276Ssam 		cmd_form = $3;
39810276Ssam 	}
39910276Ssam 	|	E
40010276Ssam 	= {
40110276Ssam 		cmd_type = TYPE_E;
40210276Ssam 		cmd_form = FORM_N;
40310276Ssam 	}
40410276Ssam 	|	E SP form_code
40510276Ssam 	= {
40610276Ssam 		cmd_type = TYPE_E;
40710276Ssam 		cmd_form = $3;
40810276Ssam 	}
40910276Ssam 	|	I
41010276Ssam 	= {
41110276Ssam 		cmd_type = TYPE_I;
41210276Ssam 	}
41310276Ssam 	|	L
41410276Ssam 	= {
41510276Ssam 		cmd_type = TYPE_L;
41636552Sbostic 		cmd_bytesz = NBBY;
41710276Ssam 	}
41810276Ssam 	|	L SP byte_size
41910276Ssam 	= {
42010276Ssam 		cmd_type = TYPE_L;
42110276Ssam 		cmd_bytesz = $3;
42210276Ssam 	}
42310276Ssam 	/* this is for a bug in the BBN ftp */
42410276Ssam 	|	L byte_size
42510276Ssam 	= {
42610276Ssam 		cmd_type = TYPE_L;
42710276Ssam 		cmd_bytesz = $2;
42810276Ssam 	}
42910276Ssam 	;
43010276Ssam 
43110276Ssam struct_code:	F
43210276Ssam 	= {
43310276Ssam 		$$ = STRU_F;
43410276Ssam 	}
43510276Ssam 	|	R
43610276Ssam 	= {
43710276Ssam 		$$ = STRU_R;
43810276Ssam 	}
43910276Ssam 	|	P
44010276Ssam 	= {
44110276Ssam 		$$ = STRU_P;
44210276Ssam 	}
44310276Ssam 	;
44410276Ssam 
44510276Ssam mode_code:	S
44610276Ssam 	= {
44710276Ssam 		$$ = MODE_S;
44810276Ssam 	}
44910276Ssam 	|	B
45010276Ssam 	= {
45110276Ssam 		$$ = MODE_B;
45210276Ssam 	}
45310276Ssam 	|	C
45410276Ssam 	= {
45510276Ssam 		$$ = MODE_C;
45610276Ssam 	}
45710276Ssam 	;
45810276Ssam 
45910276Ssam pathname:	pathstring
46010276Ssam 	= {
46127107Smckusick 		/*
46227107Smckusick 		 * Problem: this production is used for all pathname
46327107Smckusick 		 * processing, but only gives a 550 error reply.
46427107Smckusick 		 * This is a valid reply in some cases but not in others.
46527107Smckusick 		 */
46636304Skarels 		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
46726494Sminshall 			$$ = (int)*glob((char *) $1);
46810302Ssam 			if (globerr != NULL) {
46910276Ssam 				reply(550, globerr);
47010302Ssam 				$$ = NULL;
47110302Ssam 			}
47226494Sminshall 			free((char *) $1);
47310276Ssam 		} else
47410276Ssam 			$$ = $1;
47510276Ssam 	}
47610276Ssam 	;
47710276Ssam 
47810276Ssam pathstring:	STRING
47910276Ssam 	;
48010276Ssam 
48110276Ssam check_login:	/* empty */
48210276Ssam 	= {
48310276Ssam 		if (logged_in)
48410276Ssam 			$$ = 1;
48510276Ssam 		else {
48610276Ssam 			reply(530, "Please login with USER and PASS.");
48710276Ssam 			$$ = 0;
48810276Ssam 		}
48910276Ssam 	}
49010276Ssam 	;
49110276Ssam 
49210276Ssam %%
49310276Ssam 
49410276Ssam extern jmp_buf errcatch;
49510276Ssam 
49610276Ssam #define	CMD	0	/* beginning of command */
49710276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
49810276Ssam #define	STR1	2	/* expect SP followed by STRING */
49910276Ssam #define	STR2	3	/* expect STRING */
50036304Skarels #define	OSTR	4	/* optional SP then STRING */
50136304Skarels #define	ZSTR1	5	/* SP then optional STRING */
50236304Skarels #define	ZSTR2	6	/* optional STRING after SP */
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" },
51436304Skarels 	{ "PASS", PASS, ZSTR1, 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)" },
53436552Sbostic 	{ "REST", REST, ARGS, 1,	"(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" },
53936304Skarels 	{ "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)" },
54436552Sbostic 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
54510276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
54610276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
54710276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
54836620Srick 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
54936620Srick 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
55036620Srick 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
55136620Srick 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
55236620Srick 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
55336620Srick 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
55436620Srick 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
55536620Srick 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
55626045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
55710276Ssam 	{ NULL,   0,    0,    0,	0 }
55810276Ssam };
55910276Ssam 
56010276Ssam struct tab *
56110276Ssam lookup(cmd)
56210276Ssam 	char *cmd;
56310276Ssam {
56410276Ssam 	register struct tab *p;
56510276Ssam 
56610276Ssam 	for (p = cmdtab; p->name != NULL; p++)
56710276Ssam 		if (strcmp(cmd, p->name) == 0)
56810276Ssam 			return (p);
56910276Ssam 	return (0);
57010276Ssam }
57110276Ssam 
57213033Ssam #include <arpa/telnet.h>
57310276Ssam 
57410276Ssam /*
57510276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
57610276Ssam  */
57710276Ssam char *
57810276Ssam getline(s, n, iop)
57910276Ssam 	char *s;
58010276Ssam 	register FILE *iop;
58110276Ssam {
58210276Ssam 	register c;
58326494Sminshall 	register char *cs;
58410276Ssam 
58510276Ssam 	cs = s;
58627751Sminshall /* tmpline may contain saved command from urgent mode interruption */
58726045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
58826045Sminshall 		*cs++ = tmpline[c];
58926045Sminshall 		if (tmpline[c] == '\n') {
59026045Sminshall 			*cs++ = '\0';
59136304Skarels 			if (debug)
59236304Skarels 				syslog(LOG_DEBUG, "command: %s", s);
59326045Sminshall 			tmpline[0] = '\0';
59426045Sminshall 			return(s);
59526045Sminshall 		}
59636304Skarels 		if (c == 0)
59726045Sminshall 			tmpline[0] = '\0';
59826045Sminshall 	}
59936304Skarels 	while ((c = getc(iop)) != EOF) {
60036304Skarels 		c &= 0377;
60136304Skarels 		if (c == IAC) {
60236304Skarels 		    if ((c = getc(iop)) != EOF) {
60336304Skarels 			c &= 0377;
60436304Skarels 			switch (c) {
60527751Sminshall 			case WILL:
60627751Sminshall 			case WONT:
60736277Sbostic 				c = getc(iop);
60836304Skarels 				printf("%c%c%c", IAC, DONT, 0377&c);
60927751Sminshall 				(void) fflush(stdout);
61036304Skarels 				continue;
61127751Sminshall 			case DO:
61227751Sminshall 			case DONT:
61336277Sbostic 				c = getc(iop);
61436304Skarels 				printf("%c%c%c", IAC, WONT, 0377&c);
61527751Sminshall 				(void) fflush(stdout);
61636304Skarels 				continue;
61736304Skarels 			case IAC:
61827751Sminshall 				break;
61927751Sminshall 			default:
62036304Skarels 				continue;	/* ignore command */
62127751Sminshall 			}
62236304Skarels 		    }
62310276Ssam 		}
62436304Skarels 		*cs++ = c;
62536304Skarels 		if (--n <= 0 || c == '\n')
62610276Ssam 			break;
62710276Ssam 	}
62827751Sminshall 	if (c == EOF && cs == s)
62918303Sralph 		return (NULL);
63010276Ssam 	*cs++ = '\0';
63136304Skarels 	if (debug)
63236304Skarels 		syslog(LOG_DEBUG, "command: %s", s);
63310276Ssam 	return (s);
63410276Ssam }
63510276Ssam 
63611652Ssam static int
63711652Ssam toolong()
63811652Ssam {
63926494Sminshall 	time_t now;
64011652Ssam 	extern char *ctime();
64126494Sminshall 	extern time_t time();
64211652Ssam 
64311652Ssam 	reply(421,
64411652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
64526494Sminshall 	(void) time(&now);
64611652Ssam 	if (logging) {
64726494Sminshall 		syslog(LOG_INFO,
64836304Skarels 			"User %s timed out after %d seconds at %s",
64911652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
65011652Ssam 	}
65113246Ssam 	dologout(1);
65211652Ssam }
65311652Ssam 
65410276Ssam yylex()
65510276Ssam {
65610276Ssam 	static int cpos, state;
65710276Ssam 	register char *cp;
65810276Ssam 	register struct tab *p;
65910276Ssam 	int n;
66036277Sbostic 	char c, *strpbrk();
66110276Ssam 
66210276Ssam 	for (;;) {
66310276Ssam 		switch (state) {
66410276Ssam 
66510276Ssam 		case CMD:
66626494Sminshall 			(void) signal(SIGALRM, toolong);
66726494Sminshall 			(void) alarm((unsigned) timeout);
66810276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
66910276Ssam 				reply(221, "You could at least say goodbye.");
67013246Ssam 				dologout(0);
67110276Ssam 			}
67226494Sminshall 			(void) alarm(0);
67336620Srick #ifdef SETPROCTITLE
67436620Srick 			if (strncasecmp(cbuf, "PASS", 4) != NULL) {
67536620Srick 				setproctitle("%s: %s", remotehost, cbuf);
67636620Srick 			}
67736620Srick #endif /* SETPROCTITLE */
67836324Sbostic 			if ((cp = index(cbuf, '\r'))) {
67936324Sbostic 				*cp++ = '\n';
68036324Sbostic 				*cp = '\0';
68136324Sbostic 			}
68236277Sbostic 			if ((cp = strpbrk(cbuf, " \n")))
68336277Sbostic 				cpos = cp - cbuf;
68436304Skarels 			if (cpos == 0)
68510276Ssam 				cpos = 4;
68610276Ssam 			c = cbuf[cpos];
68710276Ssam 			cbuf[cpos] = '\0';
68810276Ssam 			upper(cbuf);
68910276Ssam 			p = lookup(cbuf);
69010276Ssam 			cbuf[cpos] = c;
69110276Ssam 			if (p != 0) {
69210276Ssam 				if (p->implemented == 0) {
69310276Ssam 					nack(p->name);
69426494Sminshall 					longjmp(errcatch,0);
69510276Ssam 					/* NOTREACHED */
69610276Ssam 				}
69710276Ssam 				state = p->state;
69810276Ssam 				yylval = (int) p->name;
69910276Ssam 				return (p->token);
70010276Ssam 			}
70110276Ssam 			break;
70210276Ssam 
70310276Ssam 		case OSTR:
70410276Ssam 			if (cbuf[cpos] == '\n') {
70510276Ssam 				state = CMD;
70610276Ssam 				return (CRLF);
70710276Ssam 			}
70836317Sbostic 			/* FALLTHROUGH */
70910276Ssam 
71010276Ssam 		case STR1:
71136304Skarels 		case ZSTR1:
71210276Ssam 			if (cbuf[cpos] == ' ') {
71310276Ssam 				cpos++;
71436317Sbostic 				state = state == OSTR ? STR2 : ++state;
71510276Ssam 				return (SP);
71610276Ssam 			}
71710276Ssam 			break;
71810276Ssam 
71936304Skarels 		case ZSTR2:
72036304Skarels 			if (cbuf[cpos] == '\n') {
72136304Skarels 				state = CMD;
72236304Skarels 				return (CRLF);
72336304Skarels 			}
72436304Skarels 			/* FALL THRU */
72536304Skarels 
72610276Ssam 		case STR2:
72710276Ssam 			cp = &cbuf[cpos];
72810276Ssam 			n = strlen(cp);
72910276Ssam 			cpos += n - 1;
73010276Ssam 			/*
73110276Ssam 			 * Make sure the string is nonempty and \n terminated.
73210276Ssam 			 */
73310276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
73410276Ssam 				cbuf[cpos] = '\0';
73510276Ssam 				yylval = copy(cp);
73610276Ssam 				cbuf[cpos] = '\n';
73710276Ssam 				state = ARGS;
73810276Ssam 				return (STRING);
73910276Ssam 			}
74010276Ssam 			break;
74110276Ssam 
74210276Ssam 		case ARGS:
74310276Ssam 			if (isdigit(cbuf[cpos])) {
74410276Ssam 				cp = &cbuf[cpos];
74510276Ssam 				while (isdigit(cbuf[++cpos]))
74610276Ssam 					;
74710276Ssam 				c = cbuf[cpos];
74810276Ssam 				cbuf[cpos] = '\0';
74910276Ssam 				yylval = atoi(cp);
75010276Ssam 				cbuf[cpos] = c;
75110276Ssam 				return (NUMBER);
75210276Ssam 			}
75310276Ssam 			switch (cbuf[cpos++]) {
75410276Ssam 
75510276Ssam 			case '\n':
75610276Ssam 				state = CMD;
75710276Ssam 				return (CRLF);
75810276Ssam 
75910276Ssam 			case ' ':
76010276Ssam 				return (SP);
76110276Ssam 
76210276Ssam 			case ',':
76310276Ssam 				return (COMMA);
76410276Ssam 
76510276Ssam 			case 'A':
76610276Ssam 			case 'a':
76710276Ssam 				return (A);
76810276Ssam 
76910276Ssam 			case 'B':
77010276Ssam 			case 'b':
77110276Ssam 				return (B);
77210276Ssam 
77310276Ssam 			case 'C':
77410276Ssam 			case 'c':
77510276Ssam 				return (C);
77610276Ssam 
77710276Ssam 			case 'E':
77810276Ssam 			case 'e':
77910276Ssam 				return (E);
78010276Ssam 
78110276Ssam 			case 'F':
78210276Ssam 			case 'f':
78310276Ssam 				return (F);
78410276Ssam 
78510276Ssam 			case 'I':
78610276Ssam 			case 'i':
78710276Ssam 				return (I);
78810276Ssam 
78910276Ssam 			case 'L':
79010276Ssam 			case 'l':
79110276Ssam 				return (L);
79210276Ssam 
79310276Ssam 			case 'N':
79410276Ssam 			case 'n':
79510276Ssam 				return (N);
79610276Ssam 
79710276Ssam 			case 'P':
79810276Ssam 			case 'p':
79910276Ssam 				return (P);
80010276Ssam 
80110276Ssam 			case 'R':
80210276Ssam 			case 'r':
80310276Ssam 				return (R);
80410276Ssam 
80510276Ssam 			case 'S':
80610276Ssam 			case 's':
80710276Ssam 				return (S);
80810276Ssam 
80910276Ssam 			case 'T':
81010276Ssam 			case 't':
81110276Ssam 				return (T);
81210276Ssam 
81310276Ssam 			}
81410276Ssam 			break;
81510276Ssam 
81610276Ssam 		default:
81710276Ssam 			fatal("Unknown state in scanner.");
81810276Ssam 		}
81926494Sminshall 		yyerror((char *) 0);
82010276Ssam 		state = CMD;
82126494Sminshall 		longjmp(errcatch,0);
82210276Ssam 	}
82310276Ssam }
82410276Ssam 
82510276Ssam upper(s)
82636277Sbostic 	register char *s;
82710276Ssam {
82810276Ssam 	while (*s != '\0') {
82910276Ssam 		if (islower(*s))
83010276Ssam 			*s = toupper(*s);
83110276Ssam 		s++;
83210276Ssam 	}
83310276Ssam }
83410276Ssam 
83510276Ssam copy(s)
83610276Ssam 	char *s;
83710276Ssam {
83810276Ssam 	char *p;
83926494Sminshall 	extern char *malloc(), *strcpy();
84010276Ssam 
84126494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
84210276Ssam 	if (p == NULL)
84310276Ssam 		fatal("Ran out of memory.");
84426494Sminshall 	(void) strcpy(p, s);
84510276Ssam 	return ((int)p);
84610276Ssam }
84710276Ssam 
84810276Ssam help(s)
84910276Ssam 	char *s;
85010276Ssam {
85110276Ssam 	register struct tab *c;
85210276Ssam 	register int width, NCMDS;
85310276Ssam 
85410276Ssam 	width = 0, NCMDS = 0;
85510276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
85631132Smckusick 		int len = strlen(c->name) + 1;
85710276Ssam 
85810276Ssam 		if (len > width)
85910276Ssam 			width = len;
86010276Ssam 		NCMDS++;
86110276Ssam 	}
86210276Ssam 	width = (width + 8) &~ 7;
86310276Ssam 	if (s == 0) {
86410276Ssam 		register int i, j, w;
86510276Ssam 		int columns, lines;
86610276Ssam 
86710276Ssam 		lreply(214,
86810276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
86910276Ssam 		columns = 76 / width;
87010276Ssam 		if (columns == 0)
87110276Ssam 			columns = 1;
87210276Ssam 		lines = (NCMDS + columns - 1) / columns;
87310276Ssam 		for (i = 0; i < lines; i++) {
87427107Smckusick 			printf("   ");
87510276Ssam 			for (j = 0; j < columns; j++) {
87610276Ssam 				c = cmdtab + j * lines + i;
87710276Ssam 				printf("%s%c", c->name,
87810276Ssam 					c->implemented ? ' ' : '*');
87910302Ssam 				if (c + lines >= &cmdtab[NCMDS])
88010276Ssam 					break;
88131132Smckusick 				w = strlen(c->name) + 1;
88210276Ssam 				while (w < width) {
88310276Ssam 					putchar(' ');
88410276Ssam 					w++;
88510276Ssam 				}
88610276Ssam 			}
88710276Ssam 			printf("\r\n");
88810276Ssam 		}
88926494Sminshall 		(void) fflush(stdout);
89010276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
89110276Ssam 		return;
89210276Ssam 	}
89310276Ssam 	upper(s);
89410276Ssam 	c = lookup(s);
89510276Ssam 	if (c == (struct tab *)0) {
89627107Smckusick 		reply(502, "Unknown command %s.", s);
89710276Ssam 		return;
89810276Ssam 	}
89910276Ssam 	if (c->implemented)
90010276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
90110276Ssam 	else
90210276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
90310276Ssam }
904