xref: /minix3/libexec/ftpd/ftpcmd.y (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ftpcmd.y,v 1.94 2015/08/10 07:45:50 shm Exp $	*/
262da0113SBen Gras 
362da0113SBen Gras /*-
462da0113SBen Gras  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
562da0113SBen Gras  * All rights reserved.
662da0113SBen Gras  *
762da0113SBen Gras  * This code is derived from software contributed to The NetBSD Foundation
862da0113SBen Gras  * by Luke Mewburn.
962da0113SBen Gras  *
1062da0113SBen Gras  * Redistribution and use in source and binary forms, with or without
1162da0113SBen Gras  * modification, are permitted provided that the following conditions
1262da0113SBen Gras  * are met:
1362da0113SBen Gras  * 1. Redistributions of source code must retain the above copyright
1462da0113SBen Gras  *    notice, this list of conditions and the following disclaimer.
1562da0113SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
1662da0113SBen Gras  *    notice, this list of conditions and the following disclaimer in the
1762da0113SBen Gras  *    documentation and/or other materials provided with the distribution.
1862da0113SBen Gras  *
1962da0113SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2062da0113SBen Gras  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2162da0113SBen Gras  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2262da0113SBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2362da0113SBen Gras  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2462da0113SBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2562da0113SBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2662da0113SBen Gras  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2762da0113SBen Gras  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2862da0113SBen Gras  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2962da0113SBen Gras  * POSSIBILITY OF SUCH DAMAGE.
3062da0113SBen Gras  */
3162da0113SBen Gras 
3262da0113SBen Gras /*
3362da0113SBen Gras  * Copyright (c) 1985, 1988, 1993, 1994
3462da0113SBen Gras  *	The Regents of the University of California.  All rights reserved.
3562da0113SBen Gras  *
3662da0113SBen Gras  * Redistribution and use in source and binary forms, with or without
3762da0113SBen Gras  * modification, are permitted provided that the following conditions
3862da0113SBen Gras  * are met:
3962da0113SBen Gras  * 1. Redistributions of source code must retain the above copyright
4062da0113SBen Gras  *    notice, this list of conditions and the following disclaimer.
4162da0113SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
4262da0113SBen Gras  *    notice, this list of conditions and the following disclaimer in the
4362da0113SBen Gras  *    documentation and/or other materials provided with the distribution.
4462da0113SBen Gras  * 3. Neither the name of the University nor the names of its contributors
4562da0113SBen Gras  *    may be used to endorse or promote products derived from this software
4662da0113SBen Gras  *    without specific prior written permission.
4762da0113SBen Gras  *
4862da0113SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4962da0113SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5062da0113SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5162da0113SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5262da0113SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5362da0113SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5462da0113SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5562da0113SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5662da0113SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5762da0113SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5862da0113SBen Gras  * SUCH DAMAGE.
5962da0113SBen Gras  *
6062da0113SBen Gras  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
6162da0113SBen Gras  */
6262da0113SBen Gras 
6362da0113SBen Gras /*
6462da0113SBen Gras  * Grammar for FTP commands.
6562da0113SBen Gras  * See RFC 959.
6662da0113SBen Gras  */
6762da0113SBen Gras 
6862da0113SBen Gras %{
6962da0113SBen Gras #include <sys/cdefs.h>
7062da0113SBen Gras 
7162da0113SBen Gras #ifndef lint
7262da0113SBen Gras #if 0
7362da0113SBen Gras static char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
7462da0113SBen Gras #else
75*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ftpcmd.y,v 1.94 2015/08/10 07:45:50 shm Exp $");
7662da0113SBen Gras #endif
7762da0113SBen Gras #endif /* not lint */
7862da0113SBen Gras 
7962da0113SBen Gras #include <sys/param.h>
8062da0113SBen Gras #include <sys/socket.h>
8162da0113SBen Gras #include <sys/stat.h>
8262da0113SBen Gras 
8362da0113SBen Gras #include <netinet/in.h>
8462da0113SBen Gras #include <arpa/ftp.h>
8562da0113SBen Gras #include <arpa/inet.h>
8662da0113SBen Gras 
8762da0113SBen Gras #include <ctype.h>
8862da0113SBen Gras #include <errno.h>
8962da0113SBen Gras #include <pwd.h>
9062da0113SBen Gras #include <stdio.h>
9162da0113SBen Gras #include <stdlib.h>
9262da0113SBen Gras #include <string.h>
9362da0113SBen Gras #include <syslog.h>
9462da0113SBen Gras #include <time.h>
9562da0113SBen Gras #include <tzfile.h>
9662da0113SBen Gras #include <unistd.h>
9762da0113SBen Gras #include <netdb.h>
9862da0113SBen Gras 
9962da0113SBen Gras #ifdef KERBEROS5
10062da0113SBen Gras #include <krb5/krb5.h>
10162da0113SBen Gras #endif
10262da0113SBen Gras 
10362da0113SBen Gras #include "extern.h"
10462da0113SBen Gras #include "version.h"
10562da0113SBen Gras 
10662da0113SBen Gras static	int cmd_type;
10762da0113SBen Gras static	int cmd_form;
10862da0113SBen Gras static	int cmd_bytesz;
10962da0113SBen Gras 
11062da0113SBen Gras char	cbuf[FTP_BUFLEN];
11162da0113SBen Gras char	*cmdp;
11262da0113SBen Gras char	*fromname;
11362da0113SBen Gras 
11462da0113SBen Gras extern int	epsvall;
11562da0113SBen Gras struct tab	sitetab[];
11662da0113SBen Gras 
11762da0113SBen Gras static	int	check_write(const char *, int);
11862da0113SBen Gras static	void	help(struct tab *, const char *);
11962da0113SBen Gras static	void	port_check(const char *, int);
12062da0113SBen Gras 	int	yylex(void);
12162da0113SBen Gras 
12262da0113SBen Gras %}
12362da0113SBen Gras 
12462da0113SBen Gras %union {
12562da0113SBen Gras 	struct {
12662da0113SBen Gras 		LLT	ll;
12762da0113SBen Gras 		int	i;
12862da0113SBen Gras 	} u;
12962da0113SBen Gras 	char *s;
13062da0113SBen Gras 	const char *cs;
13162da0113SBen Gras }
13262da0113SBen Gras 
13362da0113SBen Gras %token
13462da0113SBen Gras 	A	B	C	E	F	I
13562da0113SBen Gras 	L	N	P	R	S	T
13662da0113SBen Gras 
13762da0113SBen Gras 	SP	CRLF	COMMA	ALL
13862da0113SBen Gras 
13962da0113SBen Gras 	USER	PASS	ACCT	CWD	CDUP	SMNT
14062da0113SBen Gras 	QUIT	REIN	PORT	PASV	TYPE	STRU
14162da0113SBen Gras 	MODE	RETR	STOR	STOU	APPE	ALLO
14262da0113SBen Gras 	REST	RNFR	RNTO	ABOR	DELE	RMD
14362da0113SBen Gras 	MKD	PWD	LIST	NLST	SITE	SYST
14462da0113SBen Gras 	STAT	HELP	NOOP
14562da0113SBen Gras 
14662da0113SBen Gras 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
14762da0113SBen Gras 	CONF	ENC
14862da0113SBen Gras 
14962da0113SBen Gras 	FEAT	OPTS
15062da0113SBen Gras 
15162da0113SBen Gras 	SIZE	MDTM	MLST	MLSD
15262da0113SBen Gras 
15362da0113SBen Gras 	LPRT	LPSV	EPRT	EPSV
15462da0113SBen Gras 
15562da0113SBen Gras 	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
15662da0113SBen Gras 	MSOM
15762da0113SBen Gras 
15862da0113SBen Gras 	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
15962da0113SBen Gras 
16062da0113SBen Gras 	LEXERR
16162da0113SBen Gras 
16262da0113SBen Gras %token	<s> STRING
16362da0113SBen Gras %token	<u> NUMBER
16462da0113SBen Gras 
16562da0113SBen Gras %type	<u.i> check_login octal_number byte_size
16662da0113SBen Gras %type	<u.i> struct_code mode_code type_code form_code decimal_integer
16762da0113SBen Gras %type	<s> pathstring pathname password username
16862da0113SBen Gras %type	<s> mechanism_name base64data prot_code
16962da0113SBen Gras 
17062da0113SBen Gras %start	cmd_sel
17162da0113SBen Gras 
17262da0113SBen Gras %%
17362da0113SBen Gras 
17462da0113SBen Gras cmd_sel
17562da0113SBen Gras 	: cmd
17662da0113SBen Gras 		{
17762da0113SBen Gras 			REASSIGN(fromname, NULL);
17862da0113SBen Gras 			restart_point = (off_t) 0;
17962da0113SBen Gras 		}
18062da0113SBen Gras 
18162da0113SBen Gras 	| rcmd
18262da0113SBen Gras 
18362da0113SBen Gras 	;
18462da0113SBen Gras 
18562da0113SBen Gras cmd
18662da0113SBen Gras 						/* RFC 959 */
18762da0113SBen Gras 	: USER SP username CRLF
18862da0113SBen Gras 		{
18962da0113SBen Gras 			user($3);
19062da0113SBen Gras 			free($3);
19162da0113SBen Gras 		}
19262da0113SBen Gras 
19362da0113SBen Gras 	| PASS SP password CRLF
19462da0113SBen Gras 		{
19562da0113SBen Gras 			pass($3);
196*0a6a1f1dSLionel Sambuc 			explicit_memset($3, 0, strlen($3));
19762da0113SBen Gras 			free($3);
19862da0113SBen Gras 		}
19962da0113SBen Gras 
20062da0113SBen Gras 	| CWD check_login CRLF
20162da0113SBen Gras 		{
20262da0113SBen Gras 			if ($2)
20362da0113SBen Gras 				cwd(homedir);
20462da0113SBen Gras 		}
20562da0113SBen Gras 
20662da0113SBen Gras 	| CWD check_login SP pathname CRLF
20762da0113SBen Gras 		{
20862da0113SBen Gras 			if ($2 && $4 != NULL)
20962da0113SBen Gras 				cwd($4);
21062da0113SBen Gras 			if ($4 != NULL)
21162da0113SBen Gras 				free($4);
21262da0113SBen Gras 		}
21362da0113SBen Gras 
21462da0113SBen Gras 	| CDUP check_login CRLF
21562da0113SBen Gras 		{
21662da0113SBen Gras 			if ($2)
21762da0113SBen Gras 				cwd("..");
21862da0113SBen Gras 		}
21962da0113SBen Gras 
22062da0113SBen Gras 	| QUIT CRLF
22162da0113SBen Gras 		{
22262da0113SBen Gras 			if (logged_in) {
22362da0113SBen Gras 				reply(-221, "%s", "");
22462da0113SBen Gras 				reply(0,
22562da0113SBen Gras  "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
22662da0113SBen Gras 				    (LLT)total_data, PLURAL(total_data),
22762da0113SBen Gras 				    (LLT)total_files, PLURAL(total_files));
22862da0113SBen Gras 				reply(0,
22962da0113SBen Gras  "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
23062da0113SBen Gras 				    (LLT)total_bytes, PLURAL(total_bytes),
23162da0113SBen Gras 				    (LLT)total_xfers, PLURAL(total_xfers));
23262da0113SBen Gras 			}
23362da0113SBen Gras 			reply(221,
23462da0113SBen Gras 			    "Thank you for using the FTP service on %s.",
23562da0113SBen Gras 			    hostname);
23662da0113SBen Gras 			if (logged_in && logging) {
23762da0113SBen Gras 				syslog(LOG_INFO,
23862da0113SBen Gras 		"Data traffic: " LLF " byte%s in " LLF " file%s",
23962da0113SBen Gras 				    (LLT)total_data, PLURAL(total_data),
24062da0113SBen Gras 				    (LLT)total_files, PLURAL(total_files));
24162da0113SBen Gras 				syslog(LOG_INFO,
24262da0113SBen Gras 		"Total traffic: " LLF " byte%s in " LLF " transfer%s",
24362da0113SBen Gras 				    (LLT)total_bytes, PLURAL(total_bytes),
24462da0113SBen Gras 				    (LLT)total_xfers, PLURAL(total_xfers));
24562da0113SBen Gras 			}
24662da0113SBen Gras 
24762da0113SBen Gras 			dologout(0);
24862da0113SBen Gras 		}
24962da0113SBen Gras 
25062da0113SBen Gras 	| PORT check_login SP host_port CRLF
25162da0113SBen Gras 		{
25262da0113SBen Gras 			if ($2)
25362da0113SBen Gras 				port_check("PORT", AF_INET);
25462da0113SBen Gras 		}
25562da0113SBen Gras 
25662da0113SBen Gras 	| LPRT check_login SP host_long_port4 CRLF
25762da0113SBen Gras 		{
25862da0113SBen Gras 			if ($2)
25962da0113SBen Gras 				port_check("LPRT", AF_INET);
26062da0113SBen Gras 		}
26162da0113SBen Gras 
26262da0113SBen Gras 	| LPRT check_login SP host_long_port6 CRLF
26362da0113SBen Gras 		{
26462da0113SBen Gras #ifdef INET6
26562da0113SBen Gras 			if ($2)
26662da0113SBen Gras 				port_check("LPRT", AF_INET6);
26762da0113SBen Gras #else
26862da0113SBen Gras 			reply(500, "IPv6 support not available.");
26962da0113SBen Gras #endif
27062da0113SBen Gras 		}
27162da0113SBen Gras 
27262da0113SBen Gras 	| EPRT check_login SP STRING CRLF
27362da0113SBen Gras 		{
27462da0113SBen Gras 			if ($2) {
27562da0113SBen Gras 				if (extended_port($4) == 0)
27662da0113SBen Gras 					port_check("EPRT", -1);
27762da0113SBen Gras 			}
27862da0113SBen Gras 			free($4);
27962da0113SBen Gras 		}
28062da0113SBen Gras 
28162da0113SBen Gras 	| PASV check_login CRLF
28262da0113SBen Gras 		{
28362da0113SBen Gras 			if ($2) {
28462da0113SBen Gras 				if (CURCLASS_FLAGS_ISSET(passive))
28562da0113SBen Gras 					passive();
28662da0113SBen Gras 				else
28762da0113SBen Gras 					reply(500, "PASV mode not available.");
28862da0113SBen Gras 			}
28962da0113SBen Gras 		}
29062da0113SBen Gras 
29162da0113SBen Gras 	| LPSV check_login CRLF
29262da0113SBen Gras 		{
29362da0113SBen Gras 			if ($2) {
29462da0113SBen Gras 				if (CURCLASS_FLAGS_ISSET(passive)) {
29562da0113SBen Gras 					if (epsvall)
29662da0113SBen Gras 						reply(501,
29762da0113SBen Gras 						    "LPSV disallowed after EPSV ALL");
29862da0113SBen Gras 					else
29962da0113SBen Gras 						long_passive("LPSV", PF_UNSPEC);
30062da0113SBen Gras 				} else
30162da0113SBen Gras 					reply(500, "LPSV mode not available.");
30262da0113SBen Gras 			}
30362da0113SBen Gras 		}
30462da0113SBen Gras 
30562da0113SBen Gras 	| EPSV check_login SP NUMBER CRLF
30662da0113SBen Gras 		{
30762da0113SBen Gras 			if ($2) {
30862da0113SBen Gras 				if (CURCLASS_FLAGS_ISSET(passive))
30962da0113SBen Gras 					long_passive("EPSV",
31062da0113SBen Gras 					    epsvproto2af($4.i));
31162da0113SBen Gras 				else
31262da0113SBen Gras 					reply(500, "EPSV mode not available.");
31362da0113SBen Gras 			}
31462da0113SBen Gras 		}
31562da0113SBen Gras 
31662da0113SBen Gras 	| EPSV check_login SP ALL CRLF
31762da0113SBen Gras 		{
31862da0113SBen Gras 			if ($2) {
31962da0113SBen Gras 				if (CURCLASS_FLAGS_ISSET(passive)) {
32062da0113SBen Gras 					reply(200,
32162da0113SBen Gras 					    "EPSV ALL command successful.");
32262da0113SBen Gras 					epsvall++;
32362da0113SBen Gras 				} else
32462da0113SBen Gras 					reply(500, "EPSV mode not available.");
32562da0113SBen Gras 			}
32662da0113SBen Gras 		}
32762da0113SBen Gras 
32862da0113SBen Gras 	| EPSV check_login CRLF
32962da0113SBen Gras 		{
33062da0113SBen Gras 			if ($2) {
33162da0113SBen Gras 				if (CURCLASS_FLAGS_ISSET(passive))
33262da0113SBen Gras 					long_passive("EPSV", PF_UNSPEC);
33362da0113SBen Gras 				else
33462da0113SBen Gras 					reply(500, "EPSV mode not available.");
33562da0113SBen Gras 			}
33662da0113SBen Gras 		}
33762da0113SBen Gras 
33862da0113SBen Gras 	| TYPE check_login SP type_code CRLF
33962da0113SBen Gras 		{
34062da0113SBen Gras 			if ($2) {
34162da0113SBen Gras 
34262da0113SBen Gras 			switch (cmd_type) {
34362da0113SBen Gras 
34462da0113SBen Gras 			case TYPE_A:
34562da0113SBen Gras 				if (cmd_form == FORM_N) {
34662da0113SBen Gras 					reply(200, "Type set to A.");
34762da0113SBen Gras 					type = cmd_type;
34862da0113SBen Gras 					form = cmd_form;
34962da0113SBen Gras 				} else
35062da0113SBen Gras 					reply(504, "Form must be N.");
35162da0113SBen Gras 				break;
35262da0113SBen Gras 
35362da0113SBen Gras 			case TYPE_E:
35462da0113SBen Gras 				reply(504, "Type E not implemented.");
35562da0113SBen Gras 				break;
35662da0113SBen Gras 
35762da0113SBen Gras 			case TYPE_I:
35862da0113SBen Gras 				reply(200, "Type set to I.");
35962da0113SBen Gras 				type = cmd_type;
36062da0113SBen Gras 				break;
36162da0113SBen Gras 
36262da0113SBen Gras 			case TYPE_L:
36362da0113SBen Gras #if NBBY == 8
36462da0113SBen Gras 				if (cmd_bytesz == 8) {
36562da0113SBen Gras 					reply(200,
36662da0113SBen Gras 					    "Type set to L (byte size 8).");
36762da0113SBen Gras 					type = cmd_type;
36862da0113SBen Gras 				} else
36962da0113SBen Gras 					reply(504, "Byte size must be 8.");
37062da0113SBen Gras #else /* NBBY == 8 */
37162da0113SBen Gras 				UNIMPLEMENTED for NBBY != 8
37262da0113SBen Gras #endif /* NBBY == 8 */
37362da0113SBen Gras 			}
37462da0113SBen Gras 
37562da0113SBen Gras 			}
37662da0113SBen Gras 		}
37762da0113SBen Gras 
37862da0113SBen Gras 	| STRU check_login SP struct_code CRLF
37962da0113SBen Gras 		{
38062da0113SBen Gras 			if ($2) {
38162da0113SBen Gras 				switch ($4) {
38262da0113SBen Gras 
38362da0113SBen Gras 				case STRU_F:
38462da0113SBen Gras 					reply(200, "STRU F ok.");
38562da0113SBen Gras 					break;
38662da0113SBen Gras 
38762da0113SBen Gras 				default:
38862da0113SBen Gras 					reply(504, "Unimplemented STRU type.");
38962da0113SBen Gras 				}
39062da0113SBen Gras 			}
39162da0113SBen Gras 		}
39262da0113SBen Gras 
39362da0113SBen Gras 	| MODE check_login SP mode_code CRLF
39462da0113SBen Gras 		{
39562da0113SBen Gras 			if ($2) {
39662da0113SBen Gras 				switch ($4) {
39762da0113SBen Gras 
39862da0113SBen Gras 				case MODE_S:
39962da0113SBen Gras 					reply(200, "MODE S ok.");
40062da0113SBen Gras 					break;
40162da0113SBen Gras 
40262da0113SBen Gras 				default:
40362da0113SBen Gras 					reply(502, "Unimplemented MODE type.");
40462da0113SBen Gras 				}
40562da0113SBen Gras 			}
40662da0113SBen Gras 		}
40762da0113SBen Gras 
40862da0113SBen Gras 	| RETR check_login SP pathname CRLF
40962da0113SBen Gras 		{
41062da0113SBen Gras 			if ($2 && $4 != NULL)
41162da0113SBen Gras 				retrieve(NULL, $4);
41262da0113SBen Gras 			if ($4 != NULL)
41362da0113SBen Gras 				free($4);
41462da0113SBen Gras 		}
41562da0113SBen Gras 
41662da0113SBen Gras 	| STOR SP pathname CRLF
41762da0113SBen Gras 		{
41862da0113SBen Gras 			if (check_write($3, 1))
41962da0113SBen Gras 				store($3, "w", 0);
42062da0113SBen Gras 			if ($3 != NULL)
42162da0113SBen Gras 				free($3);
42262da0113SBen Gras 		}
42362da0113SBen Gras 
42462da0113SBen Gras 	| STOU SP pathname CRLF
42562da0113SBen Gras 		{
42662da0113SBen Gras 			if (check_write($3, 1))
42762da0113SBen Gras 				store($3, "w", 1);
42862da0113SBen Gras 			if ($3 != NULL)
42962da0113SBen Gras 				free($3);
43062da0113SBen Gras 		}
43162da0113SBen Gras 
43262da0113SBen Gras 	| APPE SP pathname CRLF
43362da0113SBen Gras 		{
43462da0113SBen Gras 			if (check_write($3, 1))
43562da0113SBen Gras 				store($3, "a", 0);
43662da0113SBen Gras 			if ($3 != NULL)
43762da0113SBen Gras 				free($3);
43862da0113SBen Gras 		}
43962da0113SBen Gras 
44062da0113SBen Gras 	| ALLO check_login SP NUMBER CRLF
44162da0113SBen Gras 		{
44262da0113SBen Gras 			if ($2)
44362da0113SBen Gras 				reply(202, "ALLO command ignored.");
44462da0113SBen Gras 		}
44562da0113SBen Gras 
44662da0113SBen Gras 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
44762da0113SBen Gras 		{
44862da0113SBen Gras 			if ($2)
44962da0113SBen Gras 				reply(202, "ALLO command ignored.");
45062da0113SBen Gras 		}
45162da0113SBen Gras 
45262da0113SBen Gras 	| RNTO SP pathname CRLF
45362da0113SBen Gras 		{
45462da0113SBen Gras 			if (check_write($3, 0)) {
45562da0113SBen Gras 				if (fromname) {
45662da0113SBen Gras 					renamecmd(fromname, $3);
45762da0113SBen Gras 					REASSIGN(fromname, NULL);
45862da0113SBen Gras 				} else {
45962da0113SBen Gras 					reply(503, "Bad sequence of commands.");
46062da0113SBen Gras 				}
46162da0113SBen Gras 			}
46262da0113SBen Gras 			if ($3 != NULL)
46362da0113SBen Gras 				free($3);
46462da0113SBen Gras 		}
46562da0113SBen Gras 
46662da0113SBen Gras 	| ABOR check_login CRLF
46762da0113SBen Gras 		{
46862da0113SBen Gras 			if (is_oob)
46962da0113SBen Gras 				abor();
47062da0113SBen Gras 			else if ($2)
47162da0113SBen Gras 				reply(225, "ABOR command successful.");
47262da0113SBen Gras 		}
47362da0113SBen Gras 
47462da0113SBen Gras 	| DELE SP pathname CRLF
47562da0113SBen Gras 		{
47662da0113SBen Gras 			if (check_write($3, 0))
47762da0113SBen Gras 				delete($3);
47862da0113SBen Gras 			if ($3 != NULL)
47962da0113SBen Gras 				free($3);
48062da0113SBen Gras 		}
48162da0113SBen Gras 
48262da0113SBen Gras 	| RMD SP pathname CRLF
48362da0113SBen Gras 		{
48462da0113SBen Gras 			if (check_write($3, 0))
48562da0113SBen Gras 				removedir($3);
48662da0113SBen Gras 			if ($3 != NULL)
48762da0113SBen Gras 				free($3);
48862da0113SBen Gras 		}
48962da0113SBen Gras 
49062da0113SBen Gras 	| MKD SP pathname CRLF
49162da0113SBen Gras 		{
49262da0113SBen Gras 			if (check_write($3, 0))
49362da0113SBen Gras 				makedir($3);
49462da0113SBen Gras 			if ($3 != NULL)
49562da0113SBen Gras 				free($3);
49662da0113SBen Gras 		}
49762da0113SBen Gras 
49862da0113SBen Gras 	| PWD check_login CRLF
49962da0113SBen Gras 		{
50062da0113SBen Gras 			if ($2)
50162da0113SBen Gras 				pwd();
50262da0113SBen Gras 		}
50362da0113SBen Gras 
50462da0113SBen Gras 	| LIST check_login CRLF
50562da0113SBen Gras 		{
50662da0113SBen Gras 			const char *argv[] = { INTERNAL_LS, "-lgA", NULL };
50762da0113SBen Gras 
50862da0113SBen Gras 			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
50962da0113SBen Gras 				argv[1] = "-LlgA";
51062da0113SBen Gras 			if ($2)
51162da0113SBen Gras 				retrieve(argv, "");
51262da0113SBen Gras 		}
51362da0113SBen Gras 
51462da0113SBen Gras 	| LIST check_login SP pathname CRLF
51562da0113SBen Gras 		{
51662da0113SBen Gras 			const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
51762da0113SBen Gras 
51862da0113SBen Gras 			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
51962da0113SBen Gras 				argv[1] = "-LlgA";
52062da0113SBen Gras 			if ($2 && $4 != NULL) {
52162da0113SBen Gras 				argv[2] = $4;
52262da0113SBen Gras 				retrieve(argv, $4);
52362da0113SBen Gras 			}
52462da0113SBen Gras 			if ($4 != NULL)
52562da0113SBen Gras 				free($4);
52662da0113SBen Gras 		}
52762da0113SBen Gras 
52862da0113SBen Gras 	| NLST check_login CRLF
52962da0113SBen Gras 		{
53062da0113SBen Gras 			if ($2)
53162da0113SBen Gras 				send_file_list(".");
53262da0113SBen Gras 		}
53362da0113SBen Gras 
53462da0113SBen Gras 	| NLST check_login SP pathname CRLF
53562da0113SBen Gras 		{
53662da0113SBen Gras 			if ($2)
53762da0113SBen Gras 				send_file_list($4);
53862da0113SBen Gras 			free($4);
53962da0113SBen Gras 		}
54062da0113SBen Gras 
54162da0113SBen Gras 	| SITE SP HELP CRLF
54262da0113SBen Gras 		{
54362da0113SBen Gras 			help(sitetab, NULL);
54462da0113SBen Gras 		}
54562da0113SBen Gras 
54662da0113SBen Gras 	| SITE SP CHMOD SP octal_number SP pathname CRLF
54762da0113SBen Gras 		{
54862da0113SBen Gras 			if (check_write($7, 0)) {
54962da0113SBen Gras 				if (($5 == -1) || ($5 > 0777))
55062da0113SBen Gras 					reply(501,
55162da0113SBen Gras 				"CHMOD: Mode value must be between 0 and 0777");
55262da0113SBen Gras 				else if (chmod($7, $5) < 0)
55362da0113SBen Gras 					perror_reply(550, $7);
55462da0113SBen Gras 				else
55562da0113SBen Gras 					reply(200, "CHMOD command successful.");
55662da0113SBen Gras 			}
55762da0113SBen Gras 			if ($7 != NULL)
55862da0113SBen Gras 				free($7);
55962da0113SBen Gras 		}
56062da0113SBen Gras 
56162da0113SBen Gras 	| SITE SP HELP SP STRING CRLF
56262da0113SBen Gras 		{
56362da0113SBen Gras 			help(sitetab, $5);
56462da0113SBen Gras 			free($5);
56562da0113SBen Gras 		}
56662da0113SBen Gras 
56762da0113SBen Gras 	| SITE SP IDLE check_login CRLF
56862da0113SBen Gras 		{
56962da0113SBen Gras 			if ($4) {
57062da0113SBen Gras 				reply(200,
57162da0113SBen Gras 				    "Current IDLE time limit is " LLF
57262da0113SBen Gras 				    " seconds; max " LLF,
57362da0113SBen Gras 				    (LLT)curclass.timeout,
57462da0113SBen Gras 				    (LLT)curclass.maxtimeout);
57562da0113SBen Gras 			}
57662da0113SBen Gras 		}
57762da0113SBen Gras 
57862da0113SBen Gras 	| SITE SP IDLE check_login SP NUMBER CRLF
57962da0113SBen Gras 		{
58062da0113SBen Gras 			if ($4) {
58162da0113SBen Gras 				if ($6.i < 30 || $6.i > curclass.maxtimeout) {
58262da0113SBen Gras 					reply(501,
58362da0113SBen Gras 				"IDLE time limit must be between 30 and "
58462da0113SBen Gras 					    LLF " seconds",
58562da0113SBen Gras 					    (LLT)curclass.maxtimeout);
58662da0113SBen Gras 				} else {
58762da0113SBen Gras 					curclass.timeout = $6.i;
58862da0113SBen Gras 					(void) alarm(curclass.timeout);
58962da0113SBen Gras 					reply(200,
59062da0113SBen Gras 					    "IDLE time limit set to "
59162da0113SBen Gras 					    LLF " seconds",
59262da0113SBen Gras 					    (LLT)curclass.timeout);
59362da0113SBen Gras 				}
59462da0113SBen Gras 			}
59562da0113SBen Gras 		}
59662da0113SBen Gras 
59762da0113SBen Gras 	| SITE SP RATEGET check_login CRLF
59862da0113SBen Gras 		{
59962da0113SBen Gras 			if ($4) {
60062da0113SBen Gras 				reply(200,
60162da0113SBen Gras 				    "Current RATEGET is " LLF " bytes/sec",
60262da0113SBen Gras 				    (LLT)curclass.rateget);
60362da0113SBen Gras 			}
60462da0113SBen Gras 		}
60562da0113SBen Gras 
60662da0113SBen Gras 	| SITE SP RATEGET check_login SP STRING CRLF
60762da0113SBen Gras 		{
60862da0113SBen Gras 			char errbuf[100];
60962da0113SBen Gras 			char *p = $6;
61062da0113SBen Gras 			LLT rate;
61162da0113SBen Gras 
61262da0113SBen Gras 			if ($4) {
61362da0113SBen Gras 				rate = strsuftollx("RATEGET", p, 0,
61462da0113SBen Gras 				    curclass.maxrateget
61562da0113SBen Gras 				    ? curclass.maxrateget
61662da0113SBen Gras 				    : LLTMAX, errbuf, sizeof(errbuf));
61762da0113SBen Gras 				if (errbuf[0])
61862da0113SBen Gras 					reply(501, "%s", errbuf);
61962da0113SBen Gras 				else {
62062da0113SBen Gras 					curclass.rateget = rate;
62162da0113SBen Gras 					reply(200,
62262da0113SBen Gras 					    "RATEGET set to " LLF " bytes/sec",
62362da0113SBen Gras 					    (LLT)curclass.rateget);
62462da0113SBen Gras 				}
62562da0113SBen Gras 			}
62662da0113SBen Gras 			free($6);
62762da0113SBen Gras 		}
62862da0113SBen Gras 
62962da0113SBen Gras 	| SITE SP RATEPUT check_login CRLF
63062da0113SBen Gras 		{
63162da0113SBen Gras 			if ($4) {
63262da0113SBen Gras 				reply(200,
63362da0113SBen Gras 				    "Current RATEPUT is " LLF " bytes/sec",
63462da0113SBen Gras 				    (LLT)curclass.rateput);
63562da0113SBen Gras 			}
63662da0113SBen Gras 		}
63762da0113SBen Gras 
63862da0113SBen Gras 	| SITE SP RATEPUT check_login SP STRING CRLF
63962da0113SBen Gras 		{
64062da0113SBen Gras 			char errbuf[100];
64162da0113SBen Gras 			char *p = $6;
64262da0113SBen Gras 			LLT rate;
64362da0113SBen Gras 
64462da0113SBen Gras 			if ($4) {
64562da0113SBen Gras 				rate = strsuftollx("RATEPUT", p, 0,
64662da0113SBen Gras 				    curclass.maxrateput
64762da0113SBen Gras 				    ? curclass.maxrateput
64862da0113SBen Gras 				    : LLTMAX, errbuf, sizeof(errbuf));
64962da0113SBen Gras 				if (errbuf[0])
65062da0113SBen Gras 					reply(501, "%s", errbuf);
65162da0113SBen Gras 				else {
65262da0113SBen Gras 					curclass.rateput = rate;
65362da0113SBen Gras 					reply(200,
65462da0113SBen Gras 					    "RATEPUT set to " LLF " bytes/sec",
65562da0113SBen Gras 					    (LLT)curclass.rateput);
65662da0113SBen Gras 				}
65762da0113SBen Gras 			}
65862da0113SBen Gras 			free($6);
65962da0113SBen Gras 		}
66062da0113SBen Gras 
66162da0113SBen Gras 	| SITE SP UMASK check_login CRLF
66262da0113SBen Gras 		{
66362da0113SBen Gras 			int oldmask;
66462da0113SBen Gras 
66562da0113SBen Gras 			if ($4) {
66662da0113SBen Gras 				oldmask = umask(0);
66762da0113SBen Gras 				(void) umask(oldmask);
66862da0113SBen Gras 				reply(200, "Current UMASK is %03o", oldmask);
66962da0113SBen Gras 			}
67062da0113SBen Gras 		}
67162da0113SBen Gras 
67262da0113SBen Gras 	| SITE SP UMASK check_login SP octal_number CRLF
67362da0113SBen Gras 		{
67462da0113SBen Gras 			int oldmask;
67562da0113SBen Gras 
67662da0113SBen Gras 			if ($4 && check_write("", 0)) {
67762da0113SBen Gras 				if (($6 == -1) || ($6 > 0777)) {
67862da0113SBen Gras 					reply(501, "Bad UMASK value");
67962da0113SBen Gras 				} else {
68062da0113SBen Gras 					oldmask = umask($6);
68162da0113SBen Gras 					reply(200,
68262da0113SBen Gras 					    "UMASK set to %03o (was %03o)",
68362da0113SBen Gras 					    $6, oldmask);
68462da0113SBen Gras 				}
68562da0113SBen Gras 			}
68662da0113SBen Gras 		}
68762da0113SBen Gras 
68862da0113SBen Gras 	| SYST CRLF
68962da0113SBen Gras 		{
69062da0113SBen Gras 			if (EMPTYSTR(version))
69162da0113SBen Gras 				reply(215, "UNIX Type: L%d", NBBY);
69262da0113SBen Gras 			else
69362da0113SBen Gras 				reply(215, "UNIX Type: L%d Version: %s", NBBY,
69462da0113SBen Gras 				    version);
69562da0113SBen Gras 		}
69662da0113SBen Gras 
69762da0113SBen Gras 	| STAT check_login SP pathname CRLF
69862da0113SBen Gras 		{
69962da0113SBen Gras 			if ($2 && $4 != NULL)
70062da0113SBen Gras 				statfilecmd($4);
70162da0113SBen Gras 			if ($4 != NULL)
70262da0113SBen Gras 				free($4);
70362da0113SBen Gras 		}
70462da0113SBen Gras 
70562da0113SBen Gras 	| STAT CRLF
70662da0113SBen Gras 		{
70762da0113SBen Gras 			if (is_oob)
70862da0113SBen Gras 				statxfer();
70962da0113SBen Gras 			else
71062da0113SBen Gras 				statcmd();
71162da0113SBen Gras 		}
71262da0113SBen Gras 
71362da0113SBen Gras 	| HELP CRLF
71462da0113SBen Gras 		{
71562da0113SBen Gras 			help(cmdtab, NULL);
71662da0113SBen Gras 		}
71762da0113SBen Gras 
71862da0113SBen Gras 	| HELP SP STRING CRLF
71962da0113SBen Gras 		{
72062da0113SBen Gras 			char *cp = $3;
72162da0113SBen Gras 
72262da0113SBen Gras 			if (strncasecmp(cp, "SITE", 4) == 0) {
72362da0113SBen Gras 				cp = $3 + 4;
72462da0113SBen Gras 				if (*cp == ' ')
72562da0113SBen Gras 					cp++;
72662da0113SBen Gras 				if (*cp)
72762da0113SBen Gras 					help(sitetab, cp);
72862da0113SBen Gras 				else
72962da0113SBen Gras 					help(sitetab, NULL);
73062da0113SBen Gras 			} else
73162da0113SBen Gras 				help(cmdtab, $3);
73262da0113SBen Gras 			free($3);
73362da0113SBen Gras 		}
73462da0113SBen Gras 
73562da0113SBen Gras 	| NOOP CRLF
73662da0113SBen Gras 		{
73762da0113SBen Gras 			reply(200, "NOOP command successful.");
73862da0113SBen Gras 		}
73962da0113SBen Gras 
74062da0113SBen Gras 						/* RFC 2228 */
74162da0113SBen Gras 	| AUTH SP mechanism_name CRLF
74262da0113SBen Gras 		{
74362da0113SBen Gras 			reply(502, "RFC 2228 authentication not implemented.");
74462da0113SBen Gras 			free($3);
74562da0113SBen Gras 		}
74662da0113SBen Gras 
74762da0113SBen Gras 	| ADAT SP base64data CRLF
74862da0113SBen Gras 		{
74962da0113SBen Gras 			reply(503,
75062da0113SBen Gras 			    "Please set authentication state with AUTH.");
75162da0113SBen Gras 			free($3);
75262da0113SBen Gras 		}
75362da0113SBen Gras 
75462da0113SBen Gras 	| PROT SP prot_code CRLF
75562da0113SBen Gras 		{
75662da0113SBen Gras 			reply(503,
75762da0113SBen Gras 			    "Please set protection buffer size with PBSZ.");
75862da0113SBen Gras 			free($3);
75962da0113SBen Gras 		}
76062da0113SBen Gras 
76162da0113SBen Gras 	| PBSZ SP decimal_integer CRLF
76262da0113SBen Gras 		{
76362da0113SBen Gras 			reply(503,
76462da0113SBen Gras 			    "Please set authentication state with AUTH.");
76562da0113SBen Gras 		}
76662da0113SBen Gras 
76762da0113SBen Gras 	| CCC CRLF
76862da0113SBen Gras 		{
76962da0113SBen Gras 			reply(533, "No protection enabled.");
77062da0113SBen Gras 		}
77162da0113SBen Gras 
77262da0113SBen Gras 	| MIC SP base64data CRLF
77362da0113SBen Gras 		{
77462da0113SBen Gras 			reply(502, "RFC 2228 authentication not implemented.");
77562da0113SBen Gras 			free($3);
77662da0113SBen Gras 		}
77762da0113SBen Gras 
77862da0113SBen Gras 	| CONF SP base64data CRLF
77962da0113SBen Gras 		{
78062da0113SBen Gras 			reply(502, "RFC 2228 authentication not implemented.");
78162da0113SBen Gras 			free($3);
78262da0113SBen Gras 		}
78362da0113SBen Gras 
78462da0113SBen Gras 	| ENC SP base64data CRLF
78562da0113SBen Gras 		{
78662da0113SBen Gras 			reply(502, "RFC 2228 authentication not implemented.");
78762da0113SBen Gras 			free($3);
78862da0113SBen Gras 		}
78962da0113SBen Gras 
79062da0113SBen Gras 						/* RFC 2389 */
79162da0113SBen Gras 	| FEAT CRLF
79262da0113SBen Gras 		{
79362da0113SBen Gras 
79462da0113SBen Gras 			feat();
79562da0113SBen Gras 		}
79662da0113SBen Gras 
79762da0113SBen Gras 	| OPTS SP STRING CRLF
79862da0113SBen Gras 		{
79962da0113SBen Gras 
80062da0113SBen Gras 			opts($3);
80162da0113SBen Gras 			free($3);
80262da0113SBen Gras 		}
80362da0113SBen Gras 
80462da0113SBen Gras 
80562da0113SBen Gras 						/* RFC 3659 */
80662da0113SBen Gras 
80762da0113SBen Gras 		/*
80862da0113SBen Gras 		 * Return size of file in a format suitable for
80962da0113SBen Gras 		 * using with RESTART (we just count bytes).
81062da0113SBen Gras 		 */
81162da0113SBen Gras 	| SIZE check_login SP pathname CRLF
81262da0113SBen Gras 		{
81362da0113SBen Gras 			if ($2 && $4 != NULL)
81462da0113SBen Gras 				sizecmd($4);
81562da0113SBen Gras 			if ($4 != NULL)
81662da0113SBen Gras 				free($4);
81762da0113SBen Gras 		}
81862da0113SBen Gras 
81962da0113SBen Gras 		/*
82062da0113SBen Gras 		 * Return modification time of file as an ISO 3307
82162da0113SBen Gras 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
82262da0113SBen Gras 		 * where xxx is the fractional second (of any precision,
82362da0113SBen Gras 		 * not necessarily 3 digits)
82462da0113SBen Gras 		 */
82562da0113SBen Gras 	| MDTM check_login SP pathname CRLF
82662da0113SBen Gras 		{
82762da0113SBen Gras 			if ($2 && $4 != NULL) {
82862da0113SBen Gras 				struct stat stbuf;
82962da0113SBen Gras 				if (stat($4, &stbuf) < 0)
83062da0113SBen Gras 					perror_reply(550, $4);
83162da0113SBen Gras 				else if (!S_ISREG(stbuf.st_mode)) {
83262da0113SBen Gras 					reply(550, "%s: not a plain file.", $4);
83362da0113SBen Gras 				} else {
83462da0113SBen Gras 					struct tm *t;
83562da0113SBen Gras 
83662da0113SBen Gras 					t = gmtime(&stbuf.st_mtime);
83762da0113SBen Gras 					reply(213,
83862da0113SBen Gras 					    "%04d%02d%02d%02d%02d%02d",
83962da0113SBen Gras 					    TM_YEAR_BASE + t->tm_year,
84062da0113SBen Gras 					    t->tm_mon+1, t->tm_mday,
84162da0113SBen Gras 					    t->tm_hour, t->tm_min, t->tm_sec);
84262da0113SBen Gras 				}
84362da0113SBen Gras 			}
84462da0113SBen Gras 			if ($4 != NULL)
84562da0113SBen Gras 				free($4);
84662da0113SBen Gras 		}
84762da0113SBen Gras 
84862da0113SBen Gras 	| MLST check_login SP pathname CRLF
84962da0113SBen Gras 		{
85062da0113SBen Gras 			if ($2 && $4 != NULL)
85162da0113SBen Gras 				mlst($4);
85262da0113SBen Gras 			if ($4 != NULL)
85362da0113SBen Gras 				free($4);
85462da0113SBen Gras 		}
85562da0113SBen Gras 
85662da0113SBen Gras 	| MLST check_login CRLF
85762da0113SBen Gras 		{
85862da0113SBen Gras 			mlst(NULL);
85962da0113SBen Gras 		}
86062da0113SBen Gras 
86162da0113SBen Gras 	| MLSD check_login SP pathname CRLF
86262da0113SBen Gras 		{
86362da0113SBen Gras 			if ($2 && $4 != NULL)
86462da0113SBen Gras 				mlsd($4);
86562da0113SBen Gras 			if ($4 != NULL)
86662da0113SBen Gras 				free($4);
86762da0113SBen Gras 		}
86862da0113SBen Gras 
86962da0113SBen Gras 	| MLSD check_login CRLF
87062da0113SBen Gras 		{
87162da0113SBen Gras 			mlsd(NULL);
87262da0113SBen Gras 		}
87362da0113SBen Gras 
87462da0113SBen Gras 	| error CRLF
87562da0113SBen Gras 		{
87662da0113SBen Gras 			yyerrok;
87762da0113SBen Gras 		}
87862da0113SBen Gras 	;
87962da0113SBen Gras 
88062da0113SBen Gras rcmd
88162da0113SBen Gras 	: REST check_login SP NUMBER CRLF
88262da0113SBen Gras 		{
88362da0113SBen Gras 			if ($2) {
88462da0113SBen Gras 				REASSIGN(fromname, NULL);
88562da0113SBen Gras 				restart_point = (off_t)$4.ll;
88662da0113SBen Gras 				reply(350,
88762da0113SBen Gras     "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
88862da0113SBen Gras 				    (LLT)restart_point);
88962da0113SBen Gras 			}
89062da0113SBen Gras 		}
89162da0113SBen Gras 
89262da0113SBen Gras 	| RNFR SP pathname CRLF
89362da0113SBen Gras 		{
89462da0113SBen Gras 			restart_point = (off_t) 0;
89562da0113SBen Gras 			if (check_write($3, 0)) {
89662da0113SBen Gras 				REASSIGN(fromname, NULL);
89762da0113SBen Gras 				fromname = renamefrom($3);
89862da0113SBen Gras 			}
89962da0113SBen Gras 			if ($3 != NULL)
90062da0113SBen Gras 				free($3);
90162da0113SBen Gras 		}
90262da0113SBen Gras 	;
90362da0113SBen Gras 
90462da0113SBen Gras username
90562da0113SBen Gras 	: STRING
90662da0113SBen Gras 	;
90762da0113SBen Gras 
90862da0113SBen Gras password
90962da0113SBen Gras 	: /* empty */
91062da0113SBen Gras 		{
91162da0113SBen Gras 			$$ = (char *)calloc(1, sizeof(char));
91262da0113SBen Gras 		}
91362da0113SBen Gras 
91462da0113SBen Gras 	| STRING
91562da0113SBen Gras 	;
91662da0113SBen Gras 
91762da0113SBen Gras byte_size
91862da0113SBen Gras 	: NUMBER
91962da0113SBen Gras 		{
92062da0113SBen Gras 			$$ = $1.i;
92162da0113SBen Gras 		}
92262da0113SBen Gras 	;
92362da0113SBen Gras 
92462da0113SBen Gras host_port
92562da0113SBen Gras 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
92662da0113SBen Gras 		NUMBER COMMA NUMBER
92762da0113SBen Gras 		{
92862da0113SBen Gras 			char *a, *p;
92962da0113SBen Gras 
93062da0113SBen Gras 			memset(&data_dest, 0, sizeof(data_dest));
93162da0113SBen Gras 			data_dest.su_len = sizeof(struct sockaddr_in);
93262da0113SBen Gras 			data_dest.su_family = AF_INET;
93362da0113SBen Gras 			p = (char *)&data_dest.su_port;
93462da0113SBen Gras 			p[0] = $9.i; p[1] = $11.i;
93562da0113SBen Gras 			a = (char *)&data_dest.su_addr;
93662da0113SBen Gras 			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
93762da0113SBen Gras 		}
93862da0113SBen Gras 	;
93962da0113SBen Gras 
94062da0113SBen Gras host_long_port4
94162da0113SBen Gras 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
94262da0113SBen Gras 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
94362da0113SBen Gras 		NUMBER
94462da0113SBen Gras 		{
94562da0113SBen Gras 			char *a, *p;
94662da0113SBen Gras 
94762da0113SBen Gras 			memset(&data_dest, 0, sizeof(data_dest));
94862da0113SBen Gras 			data_dest.su_len = sizeof(struct sockaddr_in);
94962da0113SBen Gras 			data_dest.su_family = AF_INET;
95062da0113SBen Gras 			p = (char *)&data_dest.su_port;
95162da0113SBen Gras 			p[0] = $15.i; p[1] = $17.i;
95262da0113SBen Gras 			a = (char *)&data_dest.su_addr;
95362da0113SBen Gras 			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
95462da0113SBen Gras 
95562da0113SBen Gras 			/* reject invalid LPRT command */
95662da0113SBen Gras 			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
95762da0113SBen Gras 				memset(&data_dest, 0, sizeof(data_dest));
95862da0113SBen Gras 		}
95962da0113SBen Gras 	;
96062da0113SBen Gras 
96162da0113SBen Gras host_long_port6
96262da0113SBen Gras 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
96362da0113SBen Gras 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
96462da0113SBen Gras 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
96562da0113SBen Gras 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
96662da0113SBen Gras 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
96762da0113SBen Gras 		NUMBER
96862da0113SBen Gras 		{
96962da0113SBen Gras #ifdef INET6
97062da0113SBen Gras 			unsigned char buf[16];
97162da0113SBen Gras 
97262da0113SBen Gras 			(void)memset(&data_dest, 0, sizeof(data_dest));
97362da0113SBen Gras 			data_dest.su_len = sizeof(struct sockaddr_in6);
97462da0113SBen Gras 			data_dest.su_family = AF_INET6;
97562da0113SBen Gras 			buf[0] = $39.i; buf[1] = $41.i;
97662da0113SBen Gras 			(void)memcpy(&data_dest.su_port, buf,
97762da0113SBen Gras 			    sizeof(data_dest.su_port));
97862da0113SBen Gras 			buf[0] = $5.i; buf[1] = $7.i;
97962da0113SBen Gras 			buf[2] = $9.i; buf[3] = $11.i;
98062da0113SBen Gras 			buf[4] = $13.i; buf[5] = $15.i;
98162da0113SBen Gras 			buf[6] = $17.i; buf[7] = $19.i;
98262da0113SBen Gras 			buf[8] = $21.i; buf[9] = $23.i;
98362da0113SBen Gras 			buf[10] = $25.i; buf[11] = $27.i;
98462da0113SBen Gras 			buf[12] = $29.i; buf[13] = $31.i;
98562da0113SBen Gras 			buf[14] = $33.i; buf[15] = $35.i;
98662da0113SBen Gras 			(void)memcpy(&data_dest.si_su.su_sin6.sin6_addr,
98762da0113SBen Gras 			    buf, sizeof(data_dest.si_su.su_sin6.sin6_addr));
98862da0113SBen Gras 			if (his_addr.su_family == AF_INET6) {
98962da0113SBen Gras 				/* XXX: more sanity checks! */
99062da0113SBen Gras 				data_dest.su_scope_id = his_addr.su_scope_id;
99162da0113SBen Gras 			}
99262da0113SBen Gras #else
99362da0113SBen Gras 			memset(&data_dest, 0, sizeof(data_dest));
99462da0113SBen Gras #endif /* INET6 */
99562da0113SBen Gras 			/* reject invalid LPRT command */
99662da0113SBen Gras 			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
99762da0113SBen Gras 				memset(&data_dest, 0, sizeof(data_dest));
99862da0113SBen Gras 		}
99962da0113SBen Gras 	;
100062da0113SBen Gras 
100162da0113SBen Gras form_code
100262da0113SBen Gras 	: N
100362da0113SBen Gras 		{
100462da0113SBen Gras 			$$ = FORM_N;
100562da0113SBen Gras 		}
100662da0113SBen Gras 
100762da0113SBen Gras 	| T
100862da0113SBen Gras 		{
100962da0113SBen Gras 			$$ = FORM_T;
101062da0113SBen Gras 		}
101162da0113SBen Gras 
101262da0113SBen Gras 	| C
101362da0113SBen Gras 		{
101462da0113SBen Gras 			$$ = FORM_C;
101562da0113SBen Gras 		}
101662da0113SBen Gras 	;
101762da0113SBen Gras 
101862da0113SBen Gras type_code
101962da0113SBen Gras 	: A
102062da0113SBen Gras 		{
102162da0113SBen Gras 			cmd_type = TYPE_A;
102262da0113SBen Gras 			cmd_form = FORM_N;
102362da0113SBen Gras 		}
102462da0113SBen Gras 
102562da0113SBen Gras 	| A SP form_code
102662da0113SBen Gras 		{
102762da0113SBen Gras 			cmd_type = TYPE_A;
102862da0113SBen Gras 			cmd_form = $3;
102962da0113SBen Gras 		}
103062da0113SBen Gras 
103162da0113SBen Gras 	| E
103262da0113SBen Gras 		{
103362da0113SBen Gras 			cmd_type = TYPE_E;
103462da0113SBen Gras 			cmd_form = FORM_N;
103562da0113SBen Gras 		}
103662da0113SBen Gras 
103762da0113SBen Gras 	| E SP form_code
103862da0113SBen Gras 		{
103962da0113SBen Gras 			cmd_type = TYPE_E;
104062da0113SBen Gras 			cmd_form = $3;
104162da0113SBen Gras 		}
104262da0113SBen Gras 
104362da0113SBen Gras 	| I
104462da0113SBen Gras 		{
104562da0113SBen Gras 			cmd_type = TYPE_I;
104662da0113SBen Gras 		}
104762da0113SBen Gras 
104862da0113SBen Gras 	| L
104962da0113SBen Gras 		{
105062da0113SBen Gras 			cmd_type = TYPE_L;
105162da0113SBen Gras 			cmd_bytesz = NBBY;
105262da0113SBen Gras 		}
105362da0113SBen Gras 
105462da0113SBen Gras 	| L SP byte_size
105562da0113SBen Gras 		{
105662da0113SBen Gras 			cmd_type = TYPE_L;
105762da0113SBen Gras 			cmd_bytesz = $3;
105862da0113SBen Gras 		}
105962da0113SBen Gras 
106062da0113SBen Gras 		/* this is for a bug in the BBN ftp */
106162da0113SBen Gras 	| L byte_size
106262da0113SBen Gras 		{
106362da0113SBen Gras 			cmd_type = TYPE_L;
106462da0113SBen Gras 			cmd_bytesz = $2;
106562da0113SBen Gras 		}
106662da0113SBen Gras 	;
106762da0113SBen Gras 
106862da0113SBen Gras struct_code
106962da0113SBen Gras 	: F
107062da0113SBen Gras 		{
107162da0113SBen Gras 			$$ = STRU_F;
107262da0113SBen Gras 		}
107362da0113SBen Gras 
107462da0113SBen Gras 	| R
107562da0113SBen Gras 		{
107662da0113SBen Gras 			$$ = STRU_R;
107762da0113SBen Gras 		}
107862da0113SBen Gras 
107962da0113SBen Gras 	| P
108062da0113SBen Gras 		{
108162da0113SBen Gras 			$$ = STRU_P;
108262da0113SBen Gras 		}
108362da0113SBen Gras 	;
108462da0113SBen Gras 
108562da0113SBen Gras mode_code
108662da0113SBen Gras 	: S
108762da0113SBen Gras 		{
108862da0113SBen Gras 			$$ = MODE_S;
108962da0113SBen Gras 		}
109062da0113SBen Gras 
109162da0113SBen Gras 	| B
109262da0113SBen Gras 		{
109362da0113SBen Gras 			$$ = MODE_B;
109462da0113SBen Gras 		}
109562da0113SBen Gras 
109662da0113SBen Gras 	| C
109762da0113SBen Gras 		{
109862da0113SBen Gras 			$$ = MODE_C;
109962da0113SBen Gras 		}
110062da0113SBen Gras 	;
110162da0113SBen Gras 
110262da0113SBen Gras pathname
110362da0113SBen Gras 	: pathstring
110462da0113SBen Gras 		{
110562da0113SBen Gras 			/*
110662da0113SBen Gras 			 * Problem: this production is used for all pathname
110762da0113SBen Gras 			 * processing, but only gives a 550 error reply.
110862da0113SBen Gras 			 * This is a valid reply in some cases but not in
110962da0113SBen Gras 			 * others.
111062da0113SBen Gras 			 */
111162da0113SBen Gras 			if (logged_in && $1 && *$1 == '~') {
111262da0113SBen Gras 				char	*path, *home, *result;
111362da0113SBen Gras 				size_t	len;
111462da0113SBen Gras 
111562da0113SBen Gras 				path = strchr($1 + 1, '/');
111662da0113SBen Gras 				if (path != NULL)
111762da0113SBen Gras 					*path++ = '\0';
111862da0113SBen Gras 				if ($1[1] == '\0')
111962da0113SBen Gras 					home = homedir;
112062da0113SBen Gras 				else {
112162da0113SBen Gras 					struct passwd	*hpw;
112262da0113SBen Gras 
112362da0113SBen Gras 					if ((hpw = getpwnam($1 + 1)) != NULL)
112462da0113SBen Gras 						home = hpw->pw_dir;
112562da0113SBen Gras 					else
112662da0113SBen Gras 						home = $1;
112762da0113SBen Gras 				}
112862da0113SBen Gras 				len = strlen(home) + 1;
112962da0113SBen Gras 				if (path != NULL)
113062da0113SBen Gras 					len += strlen(path) + 1;
113162da0113SBen Gras 				if ((result = malloc(len)) == NULL)
113262da0113SBen Gras 					fatal("Local resource failure: malloc");
113362da0113SBen Gras 				strlcpy(result, home, len);
113462da0113SBen Gras 				if (path != NULL) {
113562da0113SBen Gras 					strlcat(result, "/", len);
113662da0113SBen Gras 					strlcat(result, path, len);
113762da0113SBen Gras 				}
113862da0113SBen Gras 				$$ = result;
113962da0113SBen Gras 				free($1);
114062da0113SBen Gras 			} else
114162da0113SBen Gras 				$$ = $1;
114262da0113SBen Gras 		}
114362da0113SBen Gras 	;
114462da0113SBen Gras 
114562da0113SBen Gras pathstring
114662da0113SBen Gras 	: STRING
114762da0113SBen Gras 	;
114862da0113SBen Gras 
114962da0113SBen Gras octal_number
115062da0113SBen Gras 	: NUMBER
115162da0113SBen Gras 		{
115262da0113SBen Gras 			int ret, dec, multby, digit;
115362da0113SBen Gras 
115462da0113SBen Gras 			/*
115562da0113SBen Gras 			 * Convert a number that was read as decimal number
115662da0113SBen Gras 			 * to what it would be if it had been read as octal.
115762da0113SBen Gras 			 */
115862da0113SBen Gras 			dec = $1.i;
115962da0113SBen Gras 			multby = 1;
116062da0113SBen Gras 			ret = 0;
116162da0113SBen Gras 			while (dec) {
116262da0113SBen Gras 				digit = dec%10;
116362da0113SBen Gras 				if (digit > 7) {
116462da0113SBen Gras 					ret = -1;
116562da0113SBen Gras 					break;
116662da0113SBen Gras 				}
116762da0113SBen Gras 				ret += digit * multby;
116862da0113SBen Gras 				multby *= 8;
116962da0113SBen Gras 				dec /= 10;
117062da0113SBen Gras 			}
117162da0113SBen Gras 			$$ = ret;
117262da0113SBen Gras 		}
117362da0113SBen Gras 	;
117462da0113SBen Gras 
117562da0113SBen Gras mechanism_name
117662da0113SBen Gras 	: STRING
117762da0113SBen Gras 	;
117862da0113SBen Gras 
117962da0113SBen Gras base64data
118062da0113SBen Gras 	: STRING
118162da0113SBen Gras 	;
118262da0113SBen Gras 
118362da0113SBen Gras prot_code
118462da0113SBen Gras 	: STRING
118562da0113SBen Gras 	;
118662da0113SBen Gras 
118762da0113SBen Gras decimal_integer
118862da0113SBen Gras 	: NUMBER
118962da0113SBen Gras 		{
119062da0113SBen Gras 			$$ = $1.i;
119162da0113SBen Gras 		}
119262da0113SBen Gras 	;
119362da0113SBen Gras 
119462da0113SBen Gras check_login
119562da0113SBen Gras 	: /* empty */
119662da0113SBen Gras 		{
119762da0113SBen Gras 			if (logged_in)
119862da0113SBen Gras 				$$ = 1;
119962da0113SBen Gras 			else {
120062da0113SBen Gras 				reply(530, "Please login with USER and PASS.");
120162da0113SBen Gras 				$$ = 0;
120262da0113SBen Gras 				hasyyerrored = 1;
120362da0113SBen Gras 			}
120462da0113SBen Gras 		}
120562da0113SBen Gras 	;
120662da0113SBen Gras 
120762da0113SBen Gras %%
120862da0113SBen Gras 
120962da0113SBen Gras #define	CMD	0	/* beginning of command */
121062da0113SBen Gras #define	ARGS	1	/* expect miscellaneous arguments */
121162da0113SBen Gras #define	STR1	2	/* expect SP followed by STRING */
121262da0113SBen Gras #define	STR2	3	/* expect STRING */
121362da0113SBen Gras #define	OSTR	4	/* optional SP then STRING */
121462da0113SBen Gras #define	ZSTR1	5	/* SP then optional STRING */
121562da0113SBen Gras #define	ZSTR2	6	/* optional STRING after SP */
121662da0113SBen Gras #define	SITECMD	7	/* SITE command */
121762da0113SBen Gras #define	NSTR	8	/* Number followed by a string */
121862da0113SBen Gras #define NOARGS	9	/* No arguments allowed */
121962da0113SBen Gras #define EOLN	10	/* End of line */
122062da0113SBen Gras 
122162da0113SBen Gras struct tab cmdtab[] = {
122262da0113SBen Gras 				/* From RFC 959, in order defined (5.3.1) */
122362da0113SBen Gras 	{ "USER", USER, STR1,	1,	"<sp> username", 0, },
122462da0113SBen Gras 	{ "PASS", PASS, ZSTR1,	1,	"<sp> password", 0, },
122562da0113SBen Gras 	{ "ACCT", ACCT, STR1,	0,	"(specify account)", 0, },
122662da0113SBen Gras 	{ "CWD",  CWD,  OSTR,	1,	"[ <sp> directory-name ]", 0, },
122762da0113SBen Gras 	{ "CDUP", CDUP, NOARGS,	1,	"(change to parent directory)", 0, },
122862da0113SBen Gras 	{ "SMNT", SMNT, ARGS,	0,	"(structure mount)", 0, },
122962da0113SBen Gras 	{ "QUIT", QUIT, NOARGS,	1,	"(terminate service)", 0, },
123062da0113SBen Gras 	{ "REIN", REIN, NOARGS,	0,	"(reinitialize server state)", 0, },
123162da0113SBen Gras 	{ "PORT", PORT, ARGS,	1,	"<sp> b0, b1, b2, b3, b4, b5", 0, },
123262da0113SBen Gras 	{ "LPRT", LPRT, ARGS,	1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, },
123362da0113SBen Gras 	{ "EPRT", EPRT, STR1,	1,	"<sp> |af|addr|port|", 0, },
123462da0113SBen Gras 	{ "PASV", PASV, NOARGS,	1,	"(set server in passive mode)", 0, },
123562da0113SBen Gras 	{ "LPSV", LPSV, ARGS,	1,	"(set server in passive mode)", 0, },
123662da0113SBen Gras 	{ "EPSV", EPSV, ARGS,	1,	"[<sp> af|ALL]", 0, },
123762da0113SBen Gras 	{ "TYPE", TYPE, ARGS,	1,	"<sp> [ A | E | I | L ]", 0, },
123862da0113SBen Gras 	{ "STRU", STRU, ARGS,	1,	"(specify file structure)", 0, },
123962da0113SBen Gras 	{ "MODE", MODE, ARGS,	1,	"(specify transfer mode)", 0, },
124062da0113SBen Gras 	{ "RETR", RETR, STR1,	1,	"<sp> file-name", 0, },
124162da0113SBen Gras 	{ "STOR", STOR, STR1,	1,	"<sp> file-name", 0, },
124262da0113SBen Gras 	{ "STOU", STOU, STR1,	1,	"<sp> file-name", 0, },
124362da0113SBen Gras 	{ "APPE", APPE, STR1,	1,	"<sp> file-name", 0, },
124462da0113SBen Gras 	{ "ALLO", ALLO, ARGS,	1,	"allocate storage (vacuously)", 0, },
124562da0113SBen Gras 	{ "REST", REST, ARGS,	1,	"<sp> offset (restart command)", 0, },
124662da0113SBen Gras 	{ "RNFR", RNFR, STR1,	1,	"<sp> file-name", 0, },
124762da0113SBen Gras 	{ "RNTO", RNTO, STR1,	1,	"<sp> file-name", 0, },
124862da0113SBen Gras 	{ "ABOR", ABOR, NOARGS,	4,	"(abort operation)", 0, },
124962da0113SBen Gras 	{ "DELE", DELE, STR1,	1,	"<sp> file-name", 0, },
125062da0113SBen Gras 	{ "RMD",  RMD,  STR1,	1,	"<sp> path-name", 0, },
125162da0113SBen Gras 	{ "MKD",  MKD,  STR1,	1,	"<sp> path-name", 0, },
125262da0113SBen Gras 	{ "PWD",  PWD,  NOARGS,	1,	"(return current directory)", 0, },
125362da0113SBen Gras 	{ "LIST", LIST, OSTR,	1,	"[ <sp> path-name ]", 0, },
125462da0113SBen Gras 	{ "NLST", NLST, OSTR,	1,	"[ <sp> path-name ]", 0, },
125562da0113SBen Gras 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]", 0, },
125662da0113SBen Gras 	{ "SYST", SYST, NOARGS,	1,	"(get type of operating system)", 0, },
125762da0113SBen Gras 	{ "STAT", STAT, OSTR,	4,	"[ <sp> path-name ]", 0, },
125862da0113SBen Gras 	{ "HELP", HELP, OSTR,	1,	"[ <sp> <string> ]", 0, },
125962da0113SBen Gras 	{ "NOOP", NOOP, NOARGS,	2,	"", 0, },
126062da0113SBen Gras 
126162da0113SBen Gras 				/* From RFC 2228, in order defined */
126262da0113SBen Gras 	{ "AUTH", AUTH, STR1,	1,	"<sp> mechanism-name", 0, },
126362da0113SBen Gras 	{ "ADAT", ADAT, STR1,	1,	"<sp> base-64-data", 0, },
126462da0113SBen Gras 	{ "PROT", PROT, STR1,	1,	"<sp> prot-code", 0, },
126562da0113SBen Gras 	{ "PBSZ", PBSZ, ARGS,	1,	"<sp> decimal-integer", 0, },
126662da0113SBen Gras 	{ "CCC",  CCC,  NOARGS,	1,	"(Disable data protection)", 0, },
126762da0113SBen Gras 	{ "MIC",  MIC,  STR1,	4,	"<sp> base64data", 0, },
126862da0113SBen Gras 	{ "CONF", CONF, STR1,	4,	"<sp> base64data", 0, },
126962da0113SBen Gras 	{ "ENC",  ENC,  STR1,	4,	"<sp> base64data", 0, },
127062da0113SBen Gras 
127162da0113SBen Gras 				/* From RFC 2389, in order defined */
127262da0113SBen Gras 	{ "FEAT", FEAT, NOARGS,	1,	"(display extended features)", 0, },
127362da0113SBen Gras 	{ "OPTS", OPTS, STR1,	1,	"<sp> command [ <sp> options ]", 0, },
127462da0113SBen Gras 
127562da0113SBen Gras 				/* From RFC 3659, in order defined */
127662da0113SBen Gras 	{ "MDTM", MDTM, OSTR,	1,	"<sp> path-name", 0, },
127762da0113SBen Gras 	{ "SIZE", SIZE, OSTR,	1,	"<sp> path-name", 0, },
127862da0113SBen Gras 	{ "MLST", MLST, OSTR,	2,	"[ <sp> path-name ]", 0, },
127962da0113SBen Gras 	{ "MLSD", MLSD, OSTR,	1,	"[ <sp> directory-name ]", 0, },
128062da0113SBen Gras 
128162da0113SBen Gras 				/* obsolete commands */
128262da0113SBen Gras 	{ "MAIL", MAIL, OSTR,	0,	"(mail to user)", 0, },
128362da0113SBen Gras 	{ "MLFL", MLFL, OSTR,	0,	"(mail file)", 0, },
128462da0113SBen Gras 	{ "MRCP", MRCP, STR1,	0,	"(mail recipient)", 0, },
128562da0113SBen Gras 	{ "MRSQ", MRSQ, OSTR,	0,	"(mail recipient scheme question)", 0, },
128662da0113SBen Gras 	{ "MSAM", MSAM, OSTR,	0,	"(mail send to terminal and mailbox)", 0, },
128762da0113SBen Gras 	{ "MSND", MSND, OSTR,	0,	"(mail send to terminal)", 0, },
128862da0113SBen Gras 	{ "MSOM", MSOM, OSTR,	0,	"(mail send to terminal or mailbox)", 0, },
128962da0113SBen Gras 	{ "XCUP", CDUP, NOARGS,	1,	"(change to parent directory)", 0, },
129062da0113SBen Gras 	{ "XCWD", CWD,  OSTR,	1,	"[ <sp> directory-name ]", 0, },
129162da0113SBen Gras 	{ "XMKD", MKD,  STR1,	1,	"<sp> path-name", 0, },
129262da0113SBen Gras 	{ "XPWD", PWD,  NOARGS,	1,	"(return current directory)", 0, },
129362da0113SBen Gras 	{ "XRMD", RMD,  STR1,	1,	"<sp> path-name", 0, },
129462da0113SBen Gras 
129562da0113SBen Gras 	{  NULL,  0,	0,	0,	0, 0, }
129662da0113SBen Gras };
129762da0113SBen Gras 
129862da0113SBen Gras struct tab sitetab[] = {
129962da0113SBen Gras 	{ "CHMOD",	CHMOD,	NSTR,	1,	"<sp> mode <sp> file-name", 0, },
130062da0113SBen Gras 	{ "HELP",	HELP,	OSTR,	1,	"[ <sp> <string> ]", 0, },
130162da0113SBen Gras 	{ "IDLE",	IDLE,	ARGS,	1,	"[ <sp> maximum-idle-time ]", 0, },
130262da0113SBen Gras 	{ "RATEGET",	RATEGET,OSTR,	1,	"[ <sp> get-throttle-rate ]", 0, },
130362da0113SBen Gras 	{ "RATEPUT",	RATEPUT,OSTR,	1,	"[ <sp> put-throttle-rate ]", 0, },
130462da0113SBen Gras 	{ "UMASK",	UMASK,	ARGS,	1,	"[ <sp> umask ]", 0, },
130562da0113SBen Gras 	{ NULL,		0,	0,	0,	0, 0, }
130662da0113SBen Gras };
130762da0113SBen Gras 
130862da0113SBen Gras /*
130962da0113SBen Gras  * Check if a filename is allowed to be modified (isupload == 0) or
131062da0113SBen Gras  * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
131162da0113SBen Gras  * If the filename is NULL, fail.
131262da0113SBen Gras  * If the filename is "", don't do the sane name check.
131362da0113SBen Gras  */
131462da0113SBen Gras static int
131562da0113SBen Gras check_write(const char *file, int isupload)
131662da0113SBen Gras {
131762da0113SBen Gras 	if (file == NULL)
131862da0113SBen Gras 		return (0);
131962da0113SBen Gras 	if (! logged_in) {
132062da0113SBen Gras 		reply(530, "Please login with USER and PASS.");
132162da0113SBen Gras 		return (0);
132262da0113SBen Gras 	}
132362da0113SBen Gras 		/* checking modify */
132462da0113SBen Gras 	if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
132562da0113SBen Gras 		reply(502, "No permission to use this command.");
132662da0113SBen Gras 		return (0);
132762da0113SBen Gras 	}
132862da0113SBen Gras 		/* checking upload */
132962da0113SBen Gras 	if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
133062da0113SBen Gras 		reply(502, "No permission to use this command.");
133162da0113SBen Gras 		return (0);
133262da0113SBen Gras 	}
133362da0113SBen Gras 
133462da0113SBen Gras 		/* checking sanenames */
133562da0113SBen Gras 	if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
133662da0113SBen Gras 		const char *p;
133762da0113SBen Gras 
133862da0113SBen Gras 		if (file[0] == '.')
133962da0113SBen Gras 			goto insane_name;
134062da0113SBen Gras 		for (p = file; *p; p++) {
134162da0113SBen Gras 			if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
134262da0113SBen Gras 			    *p == ',' || *p == '.' || *p == '_')
134362da0113SBen Gras 				continue;
134462da0113SBen Gras  insane_name:
134562da0113SBen Gras 			reply(553, "File name `%s' not allowed.", file);
134662da0113SBen Gras 			return (0);
134762da0113SBen Gras 		}
134862da0113SBen Gras 	}
134962da0113SBen Gras 	return (1);
135062da0113SBen Gras }
135162da0113SBen Gras 
135262da0113SBen Gras struct tab *
135362da0113SBen Gras lookup(struct tab *p, const char *cmd)
135462da0113SBen Gras {
135562da0113SBen Gras 
135662da0113SBen Gras 	for (; p->name != NULL; p++)
135762da0113SBen Gras 		if (strcasecmp(cmd, p->name) == 0)
135862da0113SBen Gras 			return (p);
135962da0113SBen Gras 	return (0);
136062da0113SBen Gras }
136162da0113SBen Gras 
136262da0113SBen Gras #include <arpa/telnet.h>
136362da0113SBen Gras 
136462da0113SBen Gras /*
136562da0113SBen Gras  * get_line - a hacked up version of fgets to ignore TELNET escape codes.
136662da0113SBen Gras  *	`s' is the buffer to read into.
136762da0113SBen Gras  *	`n' is the 1 less than the size of the buffer, to allow trailing NUL
136862da0113SBen Gras  *	`iop' is the FILE to read from.
136962da0113SBen Gras  *	Returns 0 on success, -1 on EOF, -2 if the command was too long.
137062da0113SBen Gras  */
137162da0113SBen Gras int
137262da0113SBen Gras get_line(char *s, int n, FILE *iop)
137362da0113SBen Gras {
137462da0113SBen Gras 	int c;
137562da0113SBen Gras 	char *cs;
137662da0113SBen Gras 
137762da0113SBen Gras 	cs = s;
137862da0113SBen Gras /* tmpline may contain saved command from urgent mode interruption */
137962da0113SBen Gras 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
138062da0113SBen Gras 		*cs++ = tmpline[c];
138162da0113SBen Gras 		if (tmpline[c] == '\n') {
138262da0113SBen Gras 			*cs++ = '\0';
138362da0113SBen Gras 			if (ftpd_debug)
138462da0113SBen Gras 				syslog(LOG_DEBUG, "command: %s", s);
138562da0113SBen Gras 			tmpline[0] = '\0';
138662da0113SBen Gras 			return(0);
138762da0113SBen Gras 		}
138862da0113SBen Gras 		if (c == 0)
138962da0113SBen Gras 			tmpline[0] = '\0';
139062da0113SBen Gras 	}
139162da0113SBen Gras 	while ((c = getc(iop)) != EOF) {
139262da0113SBen Gras 		total_bytes++;
139362da0113SBen Gras 		total_bytes_in++;
139462da0113SBen Gras 		c &= 0377;
139562da0113SBen Gras 		if (c == IAC) {
139662da0113SBen Gras 		    if ((c = getc(iop)) != EOF) {
139762da0113SBen Gras 			total_bytes++;
139862da0113SBen Gras 			total_bytes_in++;
139962da0113SBen Gras 			c &= 0377;
140062da0113SBen Gras 			switch (c) {
140162da0113SBen Gras 			case WILL:
140262da0113SBen Gras 			case WONT:
140362da0113SBen Gras 				c = getc(iop);
140462da0113SBen Gras 				total_bytes++;
140562da0113SBen Gras 				total_bytes_in++;
140662da0113SBen Gras 				cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
140762da0113SBen Gras 				(void) fflush(stdout);
140862da0113SBen Gras 				continue;
140962da0113SBen Gras 			case DO:
141062da0113SBen Gras 			case DONT:
141162da0113SBen Gras 				c = getc(iop);
141262da0113SBen Gras 				total_bytes++;
141362da0113SBen Gras 				total_bytes_in++;
141462da0113SBen Gras 				cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
141562da0113SBen Gras 				(void) fflush(stdout);
141662da0113SBen Gras 				continue;
141762da0113SBen Gras 			case IAC:
141862da0113SBen Gras 				break;
141962da0113SBen Gras 			default:
142062da0113SBen Gras 				continue;	/* ignore command */
142162da0113SBen Gras 			}
142262da0113SBen Gras 		    }
142362da0113SBen Gras 		}
142462da0113SBen Gras 		*cs++ = c;
142562da0113SBen Gras 		if (--n <= 0) {
142662da0113SBen Gras 			/*
142762da0113SBen Gras 			 * If command doesn't fit into buffer, discard the
142862da0113SBen Gras 			 * rest of the command and indicate truncation.
142962da0113SBen Gras 			 * This prevents the command to be split up into
143062da0113SBen Gras 			 * multiple commands.
143162da0113SBen Gras 			 */
143262da0113SBen Gras 			if (ftpd_debug)
143362da0113SBen Gras 				syslog(LOG_DEBUG,
143462da0113SBen Gras 				    "command too long, last char: %d", c);
143562da0113SBen Gras 			while (c != '\n' && (c = getc(iop)) != EOF)
143662da0113SBen Gras 				continue;
143762da0113SBen Gras 			return (-2);
143862da0113SBen Gras 		}
143962da0113SBen Gras 		if (c == '\n')
144062da0113SBen Gras 			break;
144162da0113SBen Gras 	}
144262da0113SBen Gras 	if (c == EOF && cs == s)
144362da0113SBen Gras 		return (-1);
144462da0113SBen Gras 	*cs++ = '\0';
144562da0113SBen Gras 	if (ftpd_debug) {
144662da0113SBen Gras 		if ((curclass.type != CLASS_GUEST &&
144762da0113SBen Gras 		    strncasecmp(s, "PASS ", 5) == 0) ||
144862da0113SBen Gras 		    strncasecmp(s, "ACCT ", 5) == 0) {
144962da0113SBen Gras 			/* Don't syslog passwords */
145062da0113SBen Gras 			syslog(LOG_DEBUG, "command: %.4s ???", s);
145162da0113SBen Gras 		} else {
145262da0113SBen Gras 			char *cp;
145362da0113SBen Gras 			int len;
145462da0113SBen Gras 
145562da0113SBen Gras 			/* Don't syslog trailing CR-LF */
145662da0113SBen Gras 			len = strlen(s);
145762da0113SBen Gras 			cp = s + len - 1;
145862da0113SBen Gras 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
145962da0113SBen Gras 				--cp;
146062da0113SBen Gras 				--len;
146162da0113SBen Gras 			}
146262da0113SBen Gras 			syslog(LOG_DEBUG, "command: %.*s", len, s);
146362da0113SBen Gras 		}
146462da0113SBen Gras 	}
146562da0113SBen Gras 	return (0);
146662da0113SBen Gras }
146762da0113SBen Gras 
146862da0113SBen Gras void
146962da0113SBen Gras ftp_handle_line(char *cp)
147062da0113SBen Gras {
147162da0113SBen Gras 
147262da0113SBen Gras 	cmdp = cp;
147362da0113SBen Gras 	yyparse();
147462da0113SBen Gras }
147562da0113SBen Gras 
147662da0113SBen Gras void
147762da0113SBen Gras ftp_loop(void)
147862da0113SBen Gras {
147962da0113SBen Gras 	int ret;
148062da0113SBen Gras 
148162da0113SBen Gras 	while (1) {
148262da0113SBen Gras 		(void) alarm(curclass.timeout);
148362da0113SBen Gras 		ret = get_line(cbuf, sizeof(cbuf)-1, stdin);
148462da0113SBen Gras 		(void) alarm(0);
148562da0113SBen Gras 		if (ret == -1) {
148662da0113SBen Gras 			reply(221, "You could at least say goodbye.");
148762da0113SBen Gras 			dologout(0);
148862da0113SBen Gras 		} else if (ret == -2) {
148962da0113SBen Gras 			reply(500, "Command too long.");
149062da0113SBen Gras 		} else {
149162da0113SBen Gras 			ftp_handle_line(cbuf);
149262da0113SBen Gras 		}
149362da0113SBen Gras 	}
149462da0113SBen Gras 	/*NOTREACHED*/
149562da0113SBen Gras }
149662da0113SBen Gras 
149762da0113SBen Gras int
149862da0113SBen Gras yylex(void)
149962da0113SBen Gras {
150062da0113SBen Gras 	static int cpos, state;
150162da0113SBen Gras 	char *cp, *cp2;
150262da0113SBen Gras 	struct tab *p;
150362da0113SBen Gras 	int n;
150462da0113SBen Gras 	char c;
150562da0113SBen Gras 
150662da0113SBen Gras 	switch (state) {
150762da0113SBen Gras 
150862da0113SBen Gras 	case CMD:
150962da0113SBen Gras 		hasyyerrored = 0;
151062da0113SBen Gras 		if ((cp = strchr(cmdp, '\r'))) {
151162da0113SBen Gras 			*cp = '\0';
151262da0113SBen Gras #if defined(HAVE_SETPROCTITLE)
151362da0113SBen Gras 			if (strncasecmp(cmdp, "PASS", 4) != 0 &&
151462da0113SBen Gras 			    strncasecmp(cmdp, "ACCT", 4) != 0)
151562da0113SBen Gras 				setproctitle("%s: %s", proctitle, cmdp);
151662da0113SBen Gras #endif /* defined(HAVE_SETPROCTITLE) */
151762da0113SBen Gras 			*cp++ = '\n';
151862da0113SBen Gras 			*cp = '\0';
151962da0113SBen Gras 		}
152062da0113SBen Gras 		if ((cp = strpbrk(cmdp, " \n")))
152162da0113SBen Gras 			cpos = cp - cmdp;
152262da0113SBen Gras 		if (cpos == 0)
152362da0113SBen Gras 			cpos = 4;
152462da0113SBen Gras 		c = cmdp[cpos];
152562da0113SBen Gras 		cmdp[cpos] = '\0';
152662da0113SBen Gras 		p = lookup(cmdtab, cmdp);
152762da0113SBen Gras 		cmdp[cpos] = c;
152862da0113SBen Gras 		if (p != NULL) {
152962da0113SBen Gras 			if (is_oob && ! CMD_OOB(p)) {
153062da0113SBen Gras 				/* command will be handled in-band */
153162da0113SBen Gras 				return (0);
153262da0113SBen Gras 			} else if (! CMD_IMPLEMENTED(p)) {
153362da0113SBen Gras 				reply(502, "%s command not implemented.",
153462da0113SBen Gras 				    p->name);
153562da0113SBen Gras 				hasyyerrored = 1;
153662da0113SBen Gras 				break;
153762da0113SBen Gras 			}
153862da0113SBen Gras 			state = p->state;
153962da0113SBen Gras 			yylval.cs = p->name;
154062da0113SBen Gras 			return (p->token);
154162da0113SBen Gras 		}
154262da0113SBen Gras 		break;
154362da0113SBen Gras 
154462da0113SBen Gras 	case SITECMD:
154562da0113SBen Gras 		if (cmdp[cpos] == ' ') {
154662da0113SBen Gras 			cpos++;
154762da0113SBen Gras 			return (SP);
154862da0113SBen Gras 		}
154962da0113SBen Gras 		cp = &cmdp[cpos];
155062da0113SBen Gras 		if ((cp2 = strpbrk(cp, " \n")))
155162da0113SBen Gras 			cpos = cp2 - cmdp;
155262da0113SBen Gras 		c = cmdp[cpos];
155362da0113SBen Gras 		cmdp[cpos] = '\0';
155462da0113SBen Gras 		p = lookup(sitetab, cp);
155562da0113SBen Gras 		cmdp[cpos] = c;
155662da0113SBen Gras 		if (p != NULL) {
155762da0113SBen Gras 			if (!CMD_IMPLEMENTED(p)) {
155862da0113SBen Gras 				reply(502, "SITE %s command not implemented.",
155962da0113SBen Gras 				    p->name);
156062da0113SBen Gras 				hasyyerrored = 1;
156162da0113SBen Gras 				break;
156262da0113SBen Gras 			}
156362da0113SBen Gras 			state = p->state;
156462da0113SBen Gras 			yylval.cs = p->name;
156562da0113SBen Gras 			return (p->token);
156662da0113SBen Gras 		}
156762da0113SBen Gras 		break;
156862da0113SBen Gras 
156962da0113SBen Gras 	case OSTR:
157062da0113SBen Gras 		if (cmdp[cpos] == '\n') {
157162da0113SBen Gras 			state = EOLN;
157262da0113SBen Gras 			return (CRLF);
157362da0113SBen Gras 		}
157462da0113SBen Gras 		/* FALLTHROUGH */
157562da0113SBen Gras 
157662da0113SBen Gras 	case STR1:
157762da0113SBen Gras 	case ZSTR1:
157862da0113SBen Gras 	dostr1:
157962da0113SBen Gras 		if (cmdp[cpos] == ' ') {
158062da0113SBen Gras 			cpos++;
158162da0113SBen Gras 			state = state == OSTR ? STR2 : state+1;
158262da0113SBen Gras 			return (SP);
158362da0113SBen Gras 		}
158462da0113SBen Gras 		break;
158562da0113SBen Gras 
158662da0113SBen Gras 	case ZSTR2:
158762da0113SBen Gras 		if (cmdp[cpos] == '\n') {
158862da0113SBen Gras 			state = EOLN;
158962da0113SBen Gras 			return (CRLF);
159062da0113SBen Gras 		}
159162da0113SBen Gras 		/* FALLTHROUGH */
159262da0113SBen Gras 
159362da0113SBen Gras 	case STR2:
159462da0113SBen Gras 		cp = &cmdp[cpos];
159562da0113SBen Gras 		n = strlen(cp);
159662da0113SBen Gras 		cpos += n - 1;
159762da0113SBen Gras 		/*
159862da0113SBen Gras 		 * Make sure the string is nonempty and \n terminated.
159962da0113SBen Gras 		 */
160062da0113SBen Gras 		if (n > 1 && cmdp[cpos] == '\n') {
160162da0113SBen Gras 			cmdp[cpos] = '\0';
160262da0113SBen Gras 			yylval.s = ftpd_strdup(cp);
160362da0113SBen Gras 			cmdp[cpos] = '\n';
160462da0113SBen Gras 			state = ARGS;
160562da0113SBen Gras 			return (STRING);
160662da0113SBen Gras 		}
160762da0113SBen Gras 		break;
160862da0113SBen Gras 
160962da0113SBen Gras 	case NSTR:
161062da0113SBen Gras 		if (cmdp[cpos] == ' ') {
161162da0113SBen Gras 			cpos++;
161262da0113SBen Gras 			return (SP);
161362da0113SBen Gras 		}
161462da0113SBen Gras 		if (isdigit((unsigned char)cmdp[cpos])) {
161562da0113SBen Gras 			cp = &cmdp[cpos];
161662da0113SBen Gras 			while (isdigit((unsigned char)cmdp[++cpos]))
161762da0113SBen Gras 				;
161862da0113SBen Gras 			c = cmdp[cpos];
161962da0113SBen Gras 			cmdp[cpos] = '\0';
162062da0113SBen Gras 			yylval.u.i = atoi(cp);
162162da0113SBen Gras 			cmdp[cpos] = c;
162262da0113SBen Gras 			state = STR1;
162362da0113SBen Gras 			return (NUMBER);
162462da0113SBen Gras 		}
162562da0113SBen Gras 		state = STR1;
162662da0113SBen Gras 		goto dostr1;
162762da0113SBen Gras 
162862da0113SBen Gras 	case ARGS:
162962da0113SBen Gras 		if (isdigit((unsigned char)cmdp[cpos])) {
163062da0113SBen Gras 			cp = &cmdp[cpos];
163162da0113SBen Gras 			while (isdigit((unsigned char)cmdp[++cpos]))
163262da0113SBen Gras 				;
163362da0113SBen Gras 			c = cmdp[cpos];
163462da0113SBen Gras 			cmdp[cpos] = '\0';
163562da0113SBen Gras 			yylval.u.i = atoi(cp);
163662da0113SBen Gras 			yylval.u.ll = STRTOLL(cp, NULL, 10);
163762da0113SBen Gras 			cmdp[cpos] = c;
163862da0113SBen Gras 			return (NUMBER);
163962da0113SBen Gras 		}
164062da0113SBen Gras 		if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
164162da0113SBen Gras 		    && !isalnum((unsigned char)cmdp[cpos + 3])) {
164262da0113SBen Gras 			cpos += 3;
164362da0113SBen Gras 			return (ALL);
164462da0113SBen Gras 		}
164562da0113SBen Gras 		switch (cmdp[cpos++]) {
164662da0113SBen Gras 
164762da0113SBen Gras 		case '\n':
164862da0113SBen Gras 			state = EOLN;
164962da0113SBen Gras 			return (CRLF);
165062da0113SBen Gras 
165162da0113SBen Gras 		case ' ':
165262da0113SBen Gras 			return (SP);
165362da0113SBen Gras 
165462da0113SBen Gras 		case ',':
165562da0113SBen Gras 			return (COMMA);
165662da0113SBen Gras 
165762da0113SBen Gras 		case 'A':
165862da0113SBen Gras 		case 'a':
165962da0113SBen Gras 			return (A);
166062da0113SBen Gras 
166162da0113SBen Gras 		case 'B':
166262da0113SBen Gras 		case 'b':
166362da0113SBen Gras 			return (B);
166462da0113SBen Gras 
166562da0113SBen Gras 		case 'C':
166662da0113SBen Gras 		case 'c':
166762da0113SBen Gras 			return (C);
166862da0113SBen Gras 
166962da0113SBen Gras 		case 'E':
167062da0113SBen Gras 		case 'e':
167162da0113SBen Gras 			return (E);
167262da0113SBen Gras 
167362da0113SBen Gras 		case 'F':
167462da0113SBen Gras 		case 'f':
167562da0113SBen Gras 			return (F);
167662da0113SBen Gras 
167762da0113SBen Gras 		case 'I':
167862da0113SBen Gras 		case 'i':
167962da0113SBen Gras 			return (I);
168062da0113SBen Gras 
168162da0113SBen Gras 		case 'L':
168262da0113SBen Gras 		case 'l':
168362da0113SBen Gras 			return (L);
168462da0113SBen Gras 
168562da0113SBen Gras 		case 'N':
168662da0113SBen Gras 		case 'n':
168762da0113SBen Gras 			return (N);
168862da0113SBen Gras 
168962da0113SBen Gras 		case 'P':
169062da0113SBen Gras 		case 'p':
169162da0113SBen Gras 			return (P);
169262da0113SBen Gras 
169362da0113SBen Gras 		case 'R':
169462da0113SBen Gras 		case 'r':
169562da0113SBen Gras 			return (R);
169662da0113SBen Gras 
169762da0113SBen Gras 		case 'S':
169862da0113SBen Gras 		case 's':
169962da0113SBen Gras 			return (S);
170062da0113SBen Gras 
170162da0113SBen Gras 		case 'T':
170262da0113SBen Gras 		case 't':
170362da0113SBen Gras 			return (T);
170462da0113SBen Gras 
170562da0113SBen Gras 		}
170662da0113SBen Gras 		break;
170762da0113SBen Gras 
170862da0113SBen Gras 	case NOARGS:
170962da0113SBen Gras 		if (cmdp[cpos] == '\n') {
171062da0113SBen Gras 			state = EOLN;
171162da0113SBen Gras 			return (CRLF);
171262da0113SBen Gras 		}
171362da0113SBen Gras 		c = cmdp[cpos];
171462da0113SBen Gras 		cmdp[cpos] = '\0';
171562da0113SBen Gras 		reply(501, "'%s' command does not take any arguments.", cmdp);
171662da0113SBen Gras 		hasyyerrored = 1;
171762da0113SBen Gras 		cmdp[cpos] = c;
171862da0113SBen Gras 		break;
171962da0113SBen Gras 
172062da0113SBen Gras 	case EOLN:
172162da0113SBen Gras 		state = CMD;
172262da0113SBen Gras 		return (0);
172362da0113SBen Gras 
172462da0113SBen Gras 	default:
172562da0113SBen Gras 		fatal("Unknown state in scanner.");
172662da0113SBen Gras 	}
172762da0113SBen Gras 	yyerror(NULL);
172862da0113SBen Gras 	state = CMD;
172962da0113SBen Gras 	return (0);
173062da0113SBen Gras }
173162da0113SBen Gras 
173262da0113SBen Gras /* ARGSUSED */
173362da0113SBen Gras void
173462da0113SBen Gras yyerror(const char *s)
173562da0113SBen Gras {
173662da0113SBen Gras 	char *cp;
173762da0113SBen Gras 
173862da0113SBen Gras 	if (hasyyerrored || is_oob)
173962da0113SBen Gras 		return;
174062da0113SBen Gras 	if ((cp = strchr(cmdp,'\n')) != NULL)
174162da0113SBen Gras 		*cp = '\0';
174262da0113SBen Gras 	reply(500, "'%s': command not understood.", cmdp);
174362da0113SBen Gras 	hasyyerrored = 1;
174462da0113SBen Gras }
174562da0113SBen Gras 
174662da0113SBen Gras static void
174762da0113SBen Gras help(struct tab *ctab, const char *s)
174862da0113SBen Gras {
174962da0113SBen Gras 	struct tab *c;
175062da0113SBen Gras 	int width, NCMDS;
175162da0113SBen Gras 	const char *htype;
175262da0113SBen Gras 
175362da0113SBen Gras 	if (ctab == sitetab)
175462da0113SBen Gras 		htype = "SITE ";
175562da0113SBen Gras 	else
175662da0113SBen Gras 		htype = "";
175762da0113SBen Gras 	width = 0, NCMDS = 0;
175862da0113SBen Gras 	for (c = ctab; c->name != NULL; c++) {
175962da0113SBen Gras 		int len = strlen(c->name);
176062da0113SBen Gras 
176162da0113SBen Gras 		if (len > width)
176262da0113SBen Gras 			width = len;
176362da0113SBen Gras 		NCMDS++;
176462da0113SBen Gras 	}
176562da0113SBen Gras 	width = (width + 8) &~ 7;
176662da0113SBen Gras 	if (s == 0) {
176762da0113SBen Gras 		int i, j, w;
176862da0113SBen Gras 		int columns, lines;
176962da0113SBen Gras 
177062da0113SBen Gras 		reply(-214, "%s", "");
177162da0113SBen Gras 		reply(0, "The following %scommands are recognized.", htype);
177262da0113SBen Gras 		reply(0, "(`-' = not implemented, `+' = supports options)");
177362da0113SBen Gras 		columns = 76 / width;
177462da0113SBen Gras 		if (columns == 0)
177562da0113SBen Gras 			columns = 1;
177662da0113SBen Gras 		lines = (NCMDS + columns - 1) / columns;
177762da0113SBen Gras 		for (i = 0; i < lines; i++) {
177862da0113SBen Gras 			cprintf(stdout, "    ");
177962da0113SBen Gras 			for (j = 0; j < columns; j++) {
178062da0113SBen Gras 				c = ctab + j * lines + i;
178162da0113SBen Gras 				cprintf(stdout, "%s", c->name);
178262da0113SBen Gras 				w = strlen(c->name);
178362da0113SBen Gras 				if (! CMD_IMPLEMENTED(c)) {
178462da0113SBen Gras 					CPUTC('-', stdout);
178562da0113SBen Gras 					w++;
178662da0113SBen Gras 				}
178762da0113SBen Gras 				if (CMD_HAS_OPTIONS(c)) {
178862da0113SBen Gras 					CPUTC('+', stdout);
178962da0113SBen Gras 					w++;
179062da0113SBen Gras 				}
179162da0113SBen Gras 				if (c + lines >= &ctab[NCMDS])
179262da0113SBen Gras 					break;
179362da0113SBen Gras 				while (w < width) {
179462da0113SBen Gras 					CPUTC(' ', stdout);
179562da0113SBen Gras 					w++;
179662da0113SBen Gras 				}
179762da0113SBen Gras 			}
179862da0113SBen Gras 			cprintf(stdout, "\r\n");
179962da0113SBen Gras 		}
180062da0113SBen Gras 		(void) fflush(stdout);
180162da0113SBen Gras 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
180262da0113SBen Gras 		return;
180362da0113SBen Gras 	}
180462da0113SBen Gras 	c = lookup(ctab, s);
180562da0113SBen Gras 	if (c == (struct tab *)0) {
180662da0113SBen Gras 		reply(502, "Unknown command '%s'.", s);
180762da0113SBen Gras 		return;
180862da0113SBen Gras 	}
180962da0113SBen Gras 	if (CMD_IMPLEMENTED(c))
181062da0113SBen Gras 		reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
181162da0113SBen Gras 	else
181262da0113SBen Gras 		reply(504, "%s%-*s\t%s; not implemented.", htype, width,
181362da0113SBen Gras 		    c->name, c->help);
181462da0113SBen Gras }
181562da0113SBen Gras 
181662da0113SBen Gras /*
181762da0113SBen Gras  * Check that the structures used for a PORT, LPRT or EPRT command are
181862da0113SBen Gras  * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
181962da0113SBen Gras  * If family != -1 check that his_addr.su_family == family.
182062da0113SBen Gras  */
182162da0113SBen Gras static void
182262da0113SBen Gras port_check(const char *cmd, int family)
182362da0113SBen Gras {
182462da0113SBen Gras 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
182562da0113SBen Gras 	char s1[NI_MAXHOST], s2[NI_MAXHOST];
182662da0113SBen Gras #ifdef NI_WITHSCOPEID
182762da0113SBen Gras 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
182862da0113SBen Gras #else
182962da0113SBen Gras 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
183062da0113SBen Gras #endif
183162da0113SBen Gras 
183262da0113SBen Gras 	if (epsvall) {
183362da0113SBen Gras 		reply(501, "%s disallowed after EPSV ALL", cmd);
183462da0113SBen Gras 		return;
183562da0113SBen Gras 	}
183662da0113SBen Gras 
183762da0113SBen Gras 	if (family != -1 && his_addr.su_family != family) {
183862da0113SBen Gras  port_check_fail:
183962da0113SBen Gras 		reply(500, "Illegal %s command rejected", cmd);
184062da0113SBen Gras 		return;
184162da0113SBen Gras 	}
184262da0113SBen Gras 
184362da0113SBen Gras 	if (data_dest.su_family != his_addr.su_family)
184462da0113SBen Gras 		goto port_check_fail;
184562da0113SBen Gras 
184662da0113SBen Gras 			/* be paranoid, if told so */
184762da0113SBen Gras 	if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
184862da0113SBen Gras #ifdef INET6
184962da0113SBen Gras 		/*
185062da0113SBen Gras 		 * be paranoid, there are getnameinfo implementation that does
185162da0113SBen Gras 		 * not present scopeid portion
185262da0113SBen Gras 		 */
185362da0113SBen Gras 		if (data_dest.su_family == AF_INET6 &&
185462da0113SBen Gras 		    data_dest.su_scope_id != his_addr.su_scope_id)
185562da0113SBen Gras 			goto port_check_fail;
185662da0113SBen Gras #endif
185762da0113SBen Gras 
185862da0113SBen Gras 		if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
185962da0113SBen Gras 		    h1, sizeof(h1), s1, sizeof(s1), niflags))
186062da0113SBen Gras 			goto port_check_fail;
186162da0113SBen Gras 		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
186262da0113SBen Gras 		    h2, sizeof(h2), s2, sizeof(s2), niflags))
186362da0113SBen Gras 			goto port_check_fail;
186462da0113SBen Gras 
186562da0113SBen Gras 		if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
186662da0113SBen Gras 			goto port_check_fail;
186762da0113SBen Gras 	}
186862da0113SBen Gras 
186962da0113SBen Gras 	usedefault = 0;
187062da0113SBen Gras 	if (pdata >= 0) {
187162da0113SBen Gras 		(void) close(pdata);
187262da0113SBen Gras 		pdata = -1;
187362da0113SBen Gras 	}
187462da0113SBen Gras 	reply(200, "%s command successful.", cmd);
187562da0113SBen Gras }
1876