xref: /csrg-svn/libexec/ftpd/ftpcmd.y (revision 36324)
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*36324Sbostic  *	@(#)ftpcmd.y	5.16 (Berkeley) 12/08/88
1822501Sdist  */
1922501Sdist 
2022501Sdist /*
2110276Ssam  * Grammar for FTP commands.
2210276Ssam  * See RFC 765.
2310276Ssam  */
2410276Ssam 
2510276Ssam %{
2610276Ssam 
2710276Ssam #ifndef lint
28*36324Sbostic static char sccsid[] = "@(#)ftpcmd.y	5.16 (Berkeley) 12/08/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 transflag;
5926045Sminshall extern  char tmpline[];
6010276Ssam char	**glob();
6110276Ssam 
6210276Ssam static	int cmd_type;
6310276Ssam static	int cmd_form;
6410276Ssam static	int cmd_bytesz;
6536304Skarels char	cbuf[512];
6636304Skarels char	*fromname;
6710276Ssam 
6810276Ssam char	*index();
6910276Ssam %}
7010276Ssam 
7110276Ssam %token
7210276Ssam 	A	B	C	E	F	I
7310276Ssam 	L	N	P	R	S	T
7410276Ssam 
7510276Ssam 	SP	CRLF	COMMA	STRING	NUMBER
7610276Ssam 
7710276Ssam 	USER	PASS	ACCT	REIN	QUIT	PORT
7810276Ssam 	PASV	TYPE	STRU	MODE	RETR	STOR
7910276Ssam 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
8010276Ssam 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
8110276Ssam 	ABOR	DELE	CWD	LIST	NLST	SITE
8210276Ssam 	STAT	HELP	NOOP	XMKD	XRMD	XPWD
8326045Sminshall 	XCUP	STOU
8410276Ssam 
8510276Ssam 	LEXERR
8610276Ssam 
8710276Ssam %start	cmd_list
8810276Ssam 
8910276Ssam %%
9010276Ssam 
9110276Ssam cmd_list:	/* empty */
9210276Ssam 	|	cmd_list cmd
9330945Scsvsj 		= {
9430945Scsvsj 			fromname = (char *) 0;
9530945Scsvsj 		}
9630945Scsvsj 	|	cmd_list rcmd
9710276Ssam 	;
9810276Ssam 
9910276Ssam cmd:		USER SP username CRLF
10010276Ssam 		= {
10136304Skarels 			user((char *) $3);
10226494Sminshall 			free((char *) $3);
10310276Ssam 		}
10410276Ssam 	|	PASS SP password CRLF
10510276Ssam 		= {
10626494Sminshall 			pass((char *) $3);
10726494Sminshall 			free((char *) $3);
10810276Ssam 		}
10910276Ssam 	|	PORT SP host_port CRLF
11010276Ssam 		= {
11110320Ssam 			usedefault = 0;
11236304Skarels 			if (pdata >= 0) {
11326045Sminshall 				(void) close(pdata);
11436304Skarels 				pdata = -1;
11526045Sminshall 			}
11627107Smckusick 			reply(200, "PORT command successful.");
11710276Ssam 		}
11826045Sminshall 	|	PASV CRLF
11926045Sminshall 		= {
12026045Sminshall 			passive();
12126045Sminshall 		}
12210276Ssam 	|	TYPE SP type_code CRLF
12310276Ssam 		= {
12410276Ssam 			switch (cmd_type) {
12510276Ssam 
12610276Ssam 			case TYPE_A:
12710276Ssam 				if (cmd_form == FORM_N) {
12810276Ssam 					reply(200, "Type set to A.");
12910276Ssam 					type = cmd_type;
13010276Ssam 					form = cmd_form;
13110276Ssam 				} else
13210276Ssam 					reply(504, "Form must be N.");
13310276Ssam 				break;
13410276Ssam 
13510276Ssam 			case TYPE_E:
13610276Ssam 				reply(504, "Type E not implemented.");
13710276Ssam 				break;
13810276Ssam 
13910276Ssam 			case TYPE_I:
14010276Ssam 				reply(200, "Type set to I.");
14110276Ssam 				type = cmd_type;
14210276Ssam 				break;
14310276Ssam 
14410276Ssam 			case TYPE_L:
14510276Ssam 				if (cmd_bytesz == 8) {
14610276Ssam 					reply(200,
14710276Ssam 					    "Type set to L (byte size 8).");
14810276Ssam 					type = cmd_type;
14910276Ssam 				} else
15010276Ssam 					reply(504, "Byte size must be 8.");
15110276Ssam 			}
15210276Ssam 		}
15310276Ssam 	|	STRU SP struct_code CRLF
15410276Ssam 		= {
15510276Ssam 			switch ($3) {
15610276Ssam 
15710276Ssam 			case STRU_F:
15810276Ssam 				reply(200, "STRU F ok.");
15910276Ssam 				break;
16010276Ssam 
16110276Ssam 			default:
16227107Smckusick 				reply(504, "Unimplemented STRU type.");
16310276Ssam 			}
16410276Ssam 		}
16510276Ssam 	|	MODE SP mode_code CRLF
16610276Ssam 		= {
16710276Ssam 			switch ($3) {
16810276Ssam 
16910276Ssam 			case MODE_S:
17010276Ssam 				reply(200, "MODE S ok.");
17110276Ssam 				break;
17210276Ssam 
17310276Ssam 			default:
17410276Ssam 				reply(502, "Unimplemented MODE type.");
17510276Ssam 			}
17610276Ssam 		}
17710276Ssam 	|	ALLO SP NUMBER CRLF
17810276Ssam 		= {
17927107Smckusick 			reply(202, "ALLO command ignored.");
18010276Ssam 		}
18110276Ssam 	|	RETR check_login SP pathname CRLF
18210276Ssam 		= {
18310302Ssam 			if ($2 && $4 != NULL)
18426494Sminshall 				retrieve((char *) 0, (char *) $4);
18510302Ssam 			if ($4 != NULL)
18626494Sminshall 				free((char *) $4);
18710276Ssam 		}
18810276Ssam 	|	STOR check_login SP pathname CRLF
18910276Ssam 		= {
19010302Ssam 			if ($2 && $4 != NULL)
19136304Skarels 				store((char *) $4, "w", 0);
19210302Ssam 			if ($4 != NULL)
19326494Sminshall 				free((char *) $4);
19410276Ssam 		}
19510276Ssam 	|	APPE check_login SP pathname CRLF
19610276Ssam 		= {
19710302Ssam 			if ($2 && $4 != NULL)
19836304Skarels 				store((char *) $4, "a", 0);
19910302Ssam 			if ($4 != NULL)
20026494Sminshall 				free((char *) $4);
20110276Ssam 		}
20210276Ssam 	|	NLST check_login CRLF
20310276Ssam 		= {
20410276Ssam 			if ($2)
20511217Ssam 				retrieve("/bin/ls", "");
20610276Ssam 		}
20710276Ssam 	|	NLST check_login SP pathname CRLF
20810276Ssam 		= {
20910302Ssam 			if ($2 && $4 != NULL)
21026494Sminshall 				retrieve("/bin/ls %s", (char *) $4);
21110302Ssam 			if ($4 != NULL)
21226494Sminshall 				free((char *) $4);
21310276Ssam 		}
21410276Ssam 	|	LIST check_login CRLF
21510276Ssam 		= {
21610276Ssam 			if ($2)
21710318Ssam 				retrieve("/bin/ls -lg", "");
21810276Ssam 		}
21910276Ssam 	|	LIST check_login SP pathname CRLF
22010276Ssam 		= {
22110302Ssam 			if ($2 && $4 != NULL)
22226494Sminshall 				retrieve("/bin/ls -lg %s", (char *) $4);
22310302Ssam 			if ($4 != NULL)
22426494Sminshall 				free((char *) $4);
22510276Ssam 		}
22610276Ssam 	|	DELE check_login SP pathname CRLF
22710276Ssam 		= {
22810302Ssam 			if ($2 && $4 != NULL)
22926494Sminshall 				delete((char *) $4);
23010302Ssam 			if ($4 != NULL)
23126494Sminshall 				free((char *) $4);
23210276Ssam 		}
23330945Scsvsj 	|	RNTO SP pathname CRLF
23430945Scsvsj 		= {
23530945Scsvsj 			if (fromname) {
23630945Scsvsj 				renamecmd(fromname, (char *) $3);
23730945Scsvsj 				free(fromname);
23830945Scsvsj 				fromname = (char *) 0;
23930945Scsvsj 			} else {
24030945Scsvsj 				reply(503, "Bad sequence of commands.");
24130945Scsvsj 			}
24230945Scsvsj 			free((char *) $3);
24330945Scsvsj 		}
24426045Sminshall 	|	ABOR CRLF
24526045Sminshall 		= {
24627107Smckusick 			reply(225, "ABOR command successful.");
24726045Sminshall 		}
24810276Ssam 	|	CWD check_login CRLF
24910276Ssam 		= {
25010276Ssam 			if ($2)
25110276Ssam 				cwd(pw->pw_dir);
25210276Ssam 		}
25310276Ssam 	|	CWD check_login SP pathname CRLF
25410276Ssam 		= {
25510302Ssam 			if ($2 && $4 != NULL)
25626494Sminshall 				cwd((char *) $4);
25710302Ssam 			if ($4 != NULL)
25826494Sminshall 				free((char *) $4);
25910276Ssam 		}
26010276Ssam 	|	HELP CRLF
26110276Ssam 		= {
26226494Sminshall 			help((char *) 0);
26310276Ssam 		}
26410276Ssam 	|	HELP SP STRING CRLF
26510276Ssam 		= {
26626494Sminshall 			help((char *) $3);
26710276Ssam 		}
26810276Ssam 	|	NOOP CRLF
26910276Ssam 		= {
27027107Smckusick 			reply(200, "NOOP command successful.");
27110276Ssam 		}
27210276Ssam 	|	XMKD check_login SP pathname CRLF
27310276Ssam 		= {
27410302Ssam 			if ($2 && $4 != NULL)
27526494Sminshall 				makedir((char *) $4);
27610302Ssam 			if ($4 != NULL)
27726494Sminshall 				free((char *) $4);
27810276Ssam 		}
27910276Ssam 	|	XRMD check_login SP pathname CRLF
28010276Ssam 		= {
28110302Ssam 			if ($2 && $4 != NULL)
28226494Sminshall 				removedir((char *) $4);
28310302Ssam 			if ($4 != NULL)
28426494Sminshall 				free((char *) $4);
28510276Ssam 		}
28610276Ssam 	|	XPWD check_login CRLF
28710276Ssam 		= {
28810276Ssam 			if ($2)
28910302Ssam 				pwd();
29010276Ssam 		}
29110276Ssam 	|	XCUP check_login CRLF
29210276Ssam 		= {
29310276Ssam 			if ($2)
29410276Ssam 				cwd("..");
29510276Ssam 		}
29626045Sminshall 	|	STOU check_login SP pathname CRLF
29726045Sminshall 		= {
29836304Skarels 			if ($2 && $4 != NULL)
29936304Skarels 				store((char *) $4, "w", 1);
30026045Sminshall 			if ($4 != NULL)
30126494Sminshall 				free((char *) $4);
30226045Sminshall 		}
30310276Ssam 	|	QUIT CRLF
30410276Ssam 		= {
30510276Ssam 			reply(221, "Goodbye.");
30613246Ssam 			dologout(0);
30710276Ssam 		}
30810276Ssam 	|	error CRLF
30910276Ssam 		= {
31010276Ssam 			yyerrok;
31110276Ssam 		}
31210276Ssam 	;
31310276Ssam 
31430945Scsvsj rcmd:		RNFR check_login SP pathname CRLF
31530945Scsvsj 		= {
31630945Scsvsj 			char *renamefrom();
31730945Scsvsj 
31830945Scsvsj 			if ($2 && $4) {
31930945Scsvsj 				fromname = renamefrom((char *) $4);
32030945Scsvsj 				if (fromname == (char *) 0 && $4) {
32130945Scsvsj 					free((char *) $4);
32230945Scsvsj 				}
32330945Scsvsj 			}
32430945Scsvsj 		}
32530945Scsvsj 	;
32630945Scsvsj 
32710276Ssam username:	STRING
32810276Ssam 	;
32910276Ssam 
33036304Skarels password:	/* empty */
33136304Skarels 		= {
33236304Skarels 			$$ = (int) "";
33336304Skarels 		}
33436304Skarels 	|	STRING
33510276Ssam 	;
33610276Ssam 
33710276Ssam byte_size:	NUMBER
33810276Ssam 	;
33910276Ssam 
34010276Ssam host_port:	NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
34110276Ssam 		NUMBER COMMA NUMBER
34210276Ssam 		= {
34310276Ssam 			register char *a, *p;
34410276Ssam 
34510276Ssam 			a = (char *)&data_dest.sin_addr;
34610276Ssam 			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
34710276Ssam 			p = (char *)&data_dest.sin_port;
34810276Ssam 			p[0] = $9; p[1] = $11;
34910324Ssam 			data_dest.sin_family = AF_INET;
35010276Ssam 		}
35110276Ssam 	;
35210276Ssam 
35310276Ssam form_code:	N
35410276Ssam 	= {
35510276Ssam 		$$ = FORM_N;
35610276Ssam 	}
35710276Ssam 	|	T
35810276Ssam 	= {
35910276Ssam 		$$ = FORM_T;
36010276Ssam 	}
36110276Ssam 	|	C
36210276Ssam 	= {
36310276Ssam 		$$ = FORM_C;
36410276Ssam 	}
36510276Ssam 	;
36610276Ssam 
36710276Ssam type_code:	A
36810276Ssam 	= {
36910276Ssam 		cmd_type = TYPE_A;
37010276Ssam 		cmd_form = FORM_N;
37110276Ssam 	}
37210276Ssam 	|	A SP form_code
37310276Ssam 	= {
37410276Ssam 		cmd_type = TYPE_A;
37510276Ssam 		cmd_form = $3;
37610276Ssam 	}
37710276Ssam 	|	E
37810276Ssam 	= {
37910276Ssam 		cmd_type = TYPE_E;
38010276Ssam 		cmd_form = FORM_N;
38110276Ssam 	}
38210276Ssam 	|	E SP form_code
38310276Ssam 	= {
38410276Ssam 		cmd_type = TYPE_E;
38510276Ssam 		cmd_form = $3;
38610276Ssam 	}
38710276Ssam 	|	I
38810276Ssam 	= {
38910276Ssam 		cmd_type = TYPE_I;
39010276Ssam 	}
39110276Ssam 	|	L
39210276Ssam 	= {
39310276Ssam 		cmd_type = TYPE_L;
39410276Ssam 		cmd_bytesz = 8;
39510276Ssam 	}
39610276Ssam 	|	L SP byte_size
39710276Ssam 	= {
39810276Ssam 		cmd_type = TYPE_L;
39910276Ssam 		cmd_bytesz = $3;
40010276Ssam 	}
40110276Ssam 	/* this is for a bug in the BBN ftp */
40210276Ssam 	|	L byte_size
40310276Ssam 	= {
40410276Ssam 		cmd_type = TYPE_L;
40510276Ssam 		cmd_bytesz = $2;
40610276Ssam 	}
40710276Ssam 	;
40810276Ssam 
40910276Ssam struct_code:	F
41010276Ssam 	= {
41110276Ssam 		$$ = STRU_F;
41210276Ssam 	}
41310276Ssam 	|	R
41410276Ssam 	= {
41510276Ssam 		$$ = STRU_R;
41610276Ssam 	}
41710276Ssam 	|	P
41810276Ssam 	= {
41910276Ssam 		$$ = STRU_P;
42010276Ssam 	}
42110276Ssam 	;
42210276Ssam 
42310276Ssam mode_code:	S
42410276Ssam 	= {
42510276Ssam 		$$ = MODE_S;
42610276Ssam 	}
42710276Ssam 	|	B
42810276Ssam 	= {
42910276Ssam 		$$ = MODE_B;
43010276Ssam 	}
43110276Ssam 	|	C
43210276Ssam 	= {
43310276Ssam 		$$ = MODE_C;
43410276Ssam 	}
43510276Ssam 	;
43610276Ssam 
43710276Ssam pathname:	pathstring
43810276Ssam 	= {
43927107Smckusick 		/*
44027107Smckusick 		 * Problem: this production is used for all pathname
44127107Smckusick 		 * processing, but only gives a 550 error reply.
44227107Smckusick 		 * This is a valid reply in some cases but not in others.
44327107Smckusick 		 */
44436304Skarels 		if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
44526494Sminshall 			$$ = (int)*glob((char *) $1);
44610302Ssam 			if (globerr != NULL) {
44710276Ssam 				reply(550, globerr);
44810302Ssam 				$$ = NULL;
44910302Ssam 			}
45026494Sminshall 			free((char *) $1);
45110276Ssam 		} else
45210276Ssam 			$$ = $1;
45310276Ssam 	}
45410276Ssam 	;
45510276Ssam 
45610276Ssam pathstring:	STRING
45710276Ssam 	;
45810276Ssam 
45910276Ssam check_login:	/* empty */
46010276Ssam 	= {
46110276Ssam 		if (logged_in)
46210276Ssam 			$$ = 1;
46310276Ssam 		else {
46410276Ssam 			reply(530, "Please login with USER and PASS.");
46510276Ssam 			$$ = 0;
46610276Ssam 		}
46710276Ssam 	}
46810276Ssam 	;
46910276Ssam 
47010276Ssam %%
47110276Ssam 
47210276Ssam extern jmp_buf errcatch;
47310276Ssam 
47410276Ssam #define	CMD	0	/* beginning of command */
47510276Ssam #define	ARGS	1	/* expect miscellaneous arguments */
47610276Ssam #define	STR1	2	/* expect SP followed by STRING */
47710276Ssam #define	STR2	3	/* expect STRING */
47836304Skarels #define	OSTR	4	/* optional SP then STRING */
47936304Skarels #define	ZSTR1	5	/* SP then optional STRING */
48036304Skarels #define	ZSTR2	6	/* optional STRING after SP */
48110276Ssam 
48210276Ssam struct tab {
48310276Ssam 	char	*name;
48410276Ssam 	short	token;
48510276Ssam 	short	state;
48610276Ssam 	short	implemented;	/* 1 if command is implemented */
48710276Ssam 	char	*help;
48810276Ssam };
48910276Ssam 
49010276Ssam struct tab cmdtab[] = {		/* In order defined in RFC 765 */
49110276Ssam 	{ "USER", USER, STR1, 1,	"<sp> username" },
49236304Skarels 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
49310276Ssam 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
49410276Ssam 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
49510276Ssam 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
49610276Ssam 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
49726045Sminshall 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
49810276Ssam 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
49910276Ssam 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
50010276Ssam 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
50110276Ssam 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
50210276Ssam 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
50310276Ssam 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
50410276Ssam 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
50510276Ssam 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
50610276Ssam 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
50710276Ssam 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
50810276Ssam 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
50910276Ssam 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
51010276Ssam 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
51110276Ssam 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
51210276Ssam 	{ "REST", REST, STR1, 0,	"(restart command)" },
51310276Ssam 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
51410276Ssam 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
51526045Sminshall 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
51610276Ssam 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
51736304Skarels 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
51810276Ssam 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
51910276Ssam 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
52010276Ssam 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
52110276Ssam 	{ "SITE", SITE, STR1, 0,	"(get site parameters)" },
52210276Ssam 	{ "STAT", STAT, OSTR, 0,	"(get server status)" },
52310276Ssam 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
52410276Ssam 	{ "NOOP", NOOP, ARGS, 1,	"" },
52526045Sminshall 	{ "MKD",  XMKD, STR1, 1,	"<sp> path-name" },
52610276Ssam 	{ "XMKD", XMKD, STR1, 1,	"<sp> path-name" },
52726045Sminshall 	{ "RMD",  XRMD, STR1, 1,	"<sp> path-name" },
52810276Ssam 	{ "XRMD", XRMD, STR1, 1,	"<sp> path-name" },
52926045Sminshall 	{ "PWD",  XPWD, ARGS, 1,	"(return current directory)" },
53010276Ssam 	{ "XPWD", XPWD, ARGS, 1,	"(return current directory)" },
53126045Sminshall 	{ "CDUP", XCUP, ARGS, 1,	"(change to parent directory)" },
53210276Ssam 	{ "XCUP", XCUP, ARGS, 1,	"(change to parent directory)" },
53326045Sminshall 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
53410276Ssam 	{ NULL,   0,    0,    0,	0 }
53510276Ssam };
53610276Ssam 
53710276Ssam struct tab *
53810276Ssam lookup(cmd)
53910276Ssam 	char *cmd;
54010276Ssam {
54110276Ssam 	register struct tab *p;
54210276Ssam 
54310276Ssam 	for (p = cmdtab; p->name != NULL; p++)
54410276Ssam 		if (strcmp(cmd, p->name) == 0)
54510276Ssam 			return (p);
54610276Ssam 	return (0);
54710276Ssam }
54810276Ssam 
54913033Ssam #include <arpa/telnet.h>
55010276Ssam 
55110276Ssam /*
55210276Ssam  * getline - a hacked up version of fgets to ignore TELNET escape codes.
55310276Ssam  */
55410276Ssam char *
55510276Ssam getline(s, n, iop)
55610276Ssam 	char *s;
55710276Ssam 	register FILE *iop;
55810276Ssam {
55910276Ssam 	register c;
56026494Sminshall 	register char *cs;
56110276Ssam 
56210276Ssam 	cs = s;
56327751Sminshall /* tmpline may contain saved command from urgent mode interruption */
56426045Sminshall 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
56526045Sminshall 		*cs++ = tmpline[c];
56626045Sminshall 		if (tmpline[c] == '\n') {
56726045Sminshall 			*cs++ = '\0';
56836304Skarels 			if (debug)
56936304Skarels 				syslog(LOG_DEBUG, "command: %s", s);
57026045Sminshall 			tmpline[0] = '\0';
57126045Sminshall 			return(s);
57226045Sminshall 		}
57336304Skarels 		if (c == 0)
57426045Sminshall 			tmpline[0] = '\0';
57526045Sminshall 	}
57636304Skarels 	while ((c = getc(iop)) != EOF) {
57736304Skarels 		c &= 0377;
57836304Skarels 		if (c == IAC) {
57936304Skarels 		    if ((c = getc(iop)) != EOF) {
58036304Skarels 			c &= 0377;
58136304Skarels 			switch (c) {
58227751Sminshall 			case WILL:
58327751Sminshall 			case WONT:
58436277Sbostic 				c = getc(iop);
58536304Skarels 				printf("%c%c%c", IAC, DONT, 0377&c);
58627751Sminshall 				(void) fflush(stdout);
58736304Skarels 				continue;
58827751Sminshall 			case DO:
58927751Sminshall 			case DONT:
59036277Sbostic 				c = getc(iop);
59136304Skarels 				printf("%c%c%c", IAC, WONT, 0377&c);
59227751Sminshall 				(void) fflush(stdout);
59336304Skarels 				continue;
59436304Skarels 			case IAC:
59527751Sminshall 				break;
59627751Sminshall 			default:
59736304Skarels 				continue;	/* ignore command */
59827751Sminshall 			}
59936304Skarels 		    }
60010276Ssam 		}
60136304Skarels 		*cs++ = c;
60236304Skarels 		if (--n <= 0 || c == '\n')
60310276Ssam 			break;
60410276Ssam 	}
60527751Sminshall 	if (c == EOF && cs == s)
60618303Sralph 		return (NULL);
60710276Ssam 	*cs++ = '\0';
60836304Skarels 	if (debug)
60936304Skarels 		syslog(LOG_DEBUG, "command: %s", s);
61010276Ssam 	return (s);
61110276Ssam }
61210276Ssam 
61311652Ssam static int
61411652Ssam toolong()
61511652Ssam {
61626494Sminshall 	time_t now;
61711652Ssam 	extern char *ctime();
61826494Sminshall 	extern time_t time();
61911652Ssam 
62011652Ssam 	reply(421,
62111652Ssam 	  "Timeout (%d seconds): closing control connection.", timeout);
62226494Sminshall 	(void) time(&now);
62311652Ssam 	if (logging) {
62426494Sminshall 		syslog(LOG_INFO,
62536304Skarels 			"User %s timed out after %d seconds at %s",
62611652Ssam 			(pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
62711652Ssam 	}
62813246Ssam 	dologout(1);
62911652Ssam }
63011652Ssam 
63110276Ssam yylex()
63210276Ssam {
63310276Ssam 	static int cpos, state;
63410276Ssam 	register char *cp;
63510276Ssam 	register struct tab *p;
63610276Ssam 	int n;
63736277Sbostic 	char c, *strpbrk();
63810276Ssam 
63910276Ssam 	for (;;) {
64010276Ssam 		switch (state) {
64110276Ssam 
64210276Ssam 		case CMD:
64326494Sminshall 			(void) signal(SIGALRM, toolong);
64426494Sminshall 			(void) alarm((unsigned) timeout);
64510276Ssam 			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
64610276Ssam 				reply(221, "You could at least say goodbye.");
64713246Ssam 				dologout(0);
64810276Ssam 			}
64926494Sminshall 			(void) alarm(0);
650*36324Sbostic 			if ((cp = index(cbuf, '\r'))) {
651*36324Sbostic 				*cp++ = '\n';
652*36324Sbostic 				*cp = '\0';
653*36324Sbostic 			}
65436277Sbostic 			if ((cp = strpbrk(cbuf, " \n")))
65536277Sbostic 				cpos = cp - cbuf;
65636304Skarels 			if (cpos == 0)
65710276Ssam 				cpos = 4;
65810276Ssam 			c = cbuf[cpos];
65910276Ssam 			cbuf[cpos] = '\0';
66010276Ssam 			upper(cbuf);
66110276Ssam 			p = lookup(cbuf);
66210276Ssam 			cbuf[cpos] = c;
66310276Ssam 			if (p != 0) {
66410276Ssam 				if (p->implemented == 0) {
66510276Ssam 					nack(p->name);
66626494Sminshall 					longjmp(errcatch,0);
66710276Ssam 					/* NOTREACHED */
66810276Ssam 				}
66910276Ssam 				state = p->state;
67010276Ssam 				yylval = (int) p->name;
67110276Ssam 				return (p->token);
67210276Ssam 			}
67310276Ssam 			break;
67410276Ssam 
67510276Ssam 		case OSTR:
67610276Ssam 			if (cbuf[cpos] == '\n') {
67710276Ssam 				state = CMD;
67810276Ssam 				return (CRLF);
67910276Ssam 			}
68036317Sbostic 			/* FALLTHROUGH */
68110276Ssam 
68210276Ssam 		case STR1:
68336304Skarels 		case ZSTR1:
68410276Ssam 			if (cbuf[cpos] == ' ') {
68510276Ssam 				cpos++;
68636317Sbostic 				state = state == OSTR ? STR2 : ++state;
68710276Ssam 				return (SP);
68810276Ssam 			}
68910276Ssam 			break;
69010276Ssam 
69136304Skarels 		case ZSTR2:
69236304Skarels 			if (cbuf[cpos] == '\n') {
69336304Skarels 				state = CMD;
69436304Skarels 				return (CRLF);
69536304Skarels 			}
69636304Skarels 			/* FALL THRU */
69736304Skarels 
69810276Ssam 		case STR2:
69910276Ssam 			cp = &cbuf[cpos];
70010276Ssam 			n = strlen(cp);
70110276Ssam 			cpos += n - 1;
70210276Ssam 			/*
70310276Ssam 			 * Make sure the string is nonempty and \n terminated.
70410276Ssam 			 */
70510276Ssam 			if (n > 1 && cbuf[cpos] == '\n') {
70610276Ssam 				cbuf[cpos] = '\0';
70710276Ssam 				yylval = copy(cp);
70810276Ssam 				cbuf[cpos] = '\n';
70910276Ssam 				state = ARGS;
71010276Ssam 				return (STRING);
71110276Ssam 			}
71210276Ssam 			break;
71310276Ssam 
71410276Ssam 		case ARGS:
71510276Ssam 			if (isdigit(cbuf[cpos])) {
71610276Ssam 				cp = &cbuf[cpos];
71710276Ssam 				while (isdigit(cbuf[++cpos]))
71810276Ssam 					;
71910276Ssam 				c = cbuf[cpos];
72010276Ssam 				cbuf[cpos] = '\0';
72110276Ssam 				yylval = atoi(cp);
72210276Ssam 				cbuf[cpos] = c;
72310276Ssam 				return (NUMBER);
72410276Ssam 			}
72510276Ssam 			switch (cbuf[cpos++]) {
72610276Ssam 
72710276Ssam 			case '\n':
72810276Ssam 				state = CMD;
72910276Ssam 				return (CRLF);
73010276Ssam 
73110276Ssam 			case ' ':
73210276Ssam 				return (SP);
73310276Ssam 
73410276Ssam 			case ',':
73510276Ssam 				return (COMMA);
73610276Ssam 
73710276Ssam 			case 'A':
73810276Ssam 			case 'a':
73910276Ssam 				return (A);
74010276Ssam 
74110276Ssam 			case 'B':
74210276Ssam 			case 'b':
74310276Ssam 				return (B);
74410276Ssam 
74510276Ssam 			case 'C':
74610276Ssam 			case 'c':
74710276Ssam 				return (C);
74810276Ssam 
74910276Ssam 			case 'E':
75010276Ssam 			case 'e':
75110276Ssam 				return (E);
75210276Ssam 
75310276Ssam 			case 'F':
75410276Ssam 			case 'f':
75510276Ssam 				return (F);
75610276Ssam 
75710276Ssam 			case 'I':
75810276Ssam 			case 'i':
75910276Ssam 				return (I);
76010276Ssam 
76110276Ssam 			case 'L':
76210276Ssam 			case 'l':
76310276Ssam 				return (L);
76410276Ssam 
76510276Ssam 			case 'N':
76610276Ssam 			case 'n':
76710276Ssam 				return (N);
76810276Ssam 
76910276Ssam 			case 'P':
77010276Ssam 			case 'p':
77110276Ssam 				return (P);
77210276Ssam 
77310276Ssam 			case 'R':
77410276Ssam 			case 'r':
77510276Ssam 				return (R);
77610276Ssam 
77710276Ssam 			case 'S':
77810276Ssam 			case 's':
77910276Ssam 				return (S);
78010276Ssam 
78110276Ssam 			case 'T':
78210276Ssam 			case 't':
78310276Ssam 				return (T);
78410276Ssam 
78510276Ssam 			}
78610276Ssam 			break;
78710276Ssam 
78810276Ssam 		default:
78910276Ssam 			fatal("Unknown state in scanner.");
79010276Ssam 		}
79126494Sminshall 		yyerror((char *) 0);
79210276Ssam 		state = CMD;
79326494Sminshall 		longjmp(errcatch,0);
79410276Ssam 	}
79510276Ssam }
79610276Ssam 
79710276Ssam upper(s)
79836277Sbostic 	register char *s;
79910276Ssam {
80010276Ssam 	while (*s != '\0') {
80110276Ssam 		if (islower(*s))
80210276Ssam 			*s = toupper(*s);
80310276Ssam 		s++;
80410276Ssam 	}
80510276Ssam }
80610276Ssam 
80710276Ssam copy(s)
80810276Ssam 	char *s;
80910276Ssam {
81010276Ssam 	char *p;
81126494Sminshall 	extern char *malloc(), *strcpy();
81210276Ssam 
81326494Sminshall 	p = malloc((unsigned) strlen(s) + 1);
81410276Ssam 	if (p == NULL)
81510276Ssam 		fatal("Ran out of memory.");
81626494Sminshall 	(void) strcpy(p, s);
81710276Ssam 	return ((int)p);
81810276Ssam }
81910276Ssam 
82010276Ssam help(s)
82110276Ssam 	char *s;
82210276Ssam {
82310276Ssam 	register struct tab *c;
82410276Ssam 	register int width, NCMDS;
82510276Ssam 
82610276Ssam 	width = 0, NCMDS = 0;
82710276Ssam 	for (c = cmdtab; c->name != NULL; c++) {
82831132Smckusick 		int len = strlen(c->name) + 1;
82910276Ssam 
83010276Ssam 		if (len > width)
83110276Ssam 			width = len;
83210276Ssam 		NCMDS++;
83310276Ssam 	}
83410276Ssam 	width = (width + 8) &~ 7;
83510276Ssam 	if (s == 0) {
83610276Ssam 		register int i, j, w;
83710276Ssam 		int columns, lines;
83810276Ssam 
83910276Ssam 		lreply(214,
84010276Ssam 	  "The following commands are recognized (* =>'s unimplemented).");
84110276Ssam 		columns = 76 / width;
84210276Ssam 		if (columns == 0)
84310276Ssam 			columns = 1;
84410276Ssam 		lines = (NCMDS + columns - 1) / columns;
84510276Ssam 		for (i = 0; i < lines; i++) {
84627107Smckusick 			printf("   ");
84710276Ssam 			for (j = 0; j < columns; j++) {
84810276Ssam 				c = cmdtab + j * lines + i;
84910276Ssam 				printf("%s%c", c->name,
85010276Ssam 					c->implemented ? ' ' : '*');
85110302Ssam 				if (c + lines >= &cmdtab[NCMDS])
85210276Ssam 					break;
85331132Smckusick 				w = strlen(c->name) + 1;
85410276Ssam 				while (w < width) {
85510276Ssam 					putchar(' ');
85610276Ssam 					w++;
85710276Ssam 				}
85810276Ssam 			}
85910276Ssam 			printf("\r\n");
86010276Ssam 		}
86126494Sminshall 		(void) fflush(stdout);
86210276Ssam 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
86310276Ssam 		return;
86410276Ssam 	}
86510276Ssam 	upper(s);
86610276Ssam 	c = lookup(s);
86710276Ssam 	if (c == (struct tab *)0) {
86827107Smckusick 		reply(502, "Unknown command %s.", s);
86910276Ssam 		return;
87010276Ssam 	}
87110276Ssam 	if (c->implemented)
87210276Ssam 		reply(214, "Syntax: %s %s", c->name, c->help);
87310276Ssam 	else
87410276Ssam 		reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
87510276Ssam }
876