ftpcmd.y revision 75535
11592Srgrimes/*
21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
131592Srgrimes * 3. All advertising materials mentioning features or use of this software
141592Srgrimes *    must display the following acknowledgement:
151592Srgrimes *	This product includes software developed by the University of
161592Srgrimes *	California, Berkeley and its contributors.
171592Srgrimes * 4. Neither the name of the University nor the names of its contributors
181592Srgrimes *    may be used to endorse or promote products derived from this software
191592Srgrimes *    without specific prior written permission.
201592Srgrimes *
211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311592Srgrimes * SUCH DAMAGE.
321592Srgrimes *
331592Srgrimes *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
341592Srgrimes */
351592Srgrimes
361592Srgrimes/*
371592Srgrimes * Grammar for FTP commands.
381592Srgrimes * See RFC 959.
391592Srgrimes */
401592Srgrimes
411592Srgrimes%{
421592Srgrimes
431592Srgrimes#ifndef lint
4431329Scharnier#if 0
451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
4631329Scharnier#endif
4731329Scharnierstatic const char rcsid[] =
4850476Speter  "$FreeBSD: head/libexec/ftpd/ftpcmd.y 75535 2001-04-15 20:59:29Z phk $";
491592Srgrimes#endif /* not lint */
501592Srgrimes
511592Srgrimes#include <sys/param.h>
521592Srgrimes#include <sys/socket.h>
531592Srgrimes#include <sys/stat.h>
541592Srgrimes
551592Srgrimes#include <netinet/in.h>
561592Srgrimes#include <arpa/ftp.h>
571592Srgrimes
581592Srgrimes#include <ctype.h>
591592Srgrimes#include <errno.h>
601592Srgrimes#include <glob.h>
6156668Sshin#include <netdb.h>
621592Srgrimes#include <pwd.h>
631592Srgrimes#include <setjmp.h>
641592Srgrimes#include <signal.h>
651592Srgrimes#include <stdio.h>
661592Srgrimes#include <stdlib.h>
671592Srgrimes#include <string.h>
681592Srgrimes#include <syslog.h>
691592Srgrimes#include <time.h>
701592Srgrimes#include <unistd.h>
7113139Speter#include <libutil.h>
7275535Sphk#include <md5.h>
731592Srgrimes
741592Srgrimes#include "extern.h"
751592Srgrimes
7656668Sshinextern	union sockunion data_dest, his_addr;
771592Srgrimesextern	int logged_in;
781592Srgrimesextern	struct passwd *pw;
791592Srgrimesextern	int guest;
8017435Spstextern 	int paranoid;
811592Srgrimesextern	int logging;
821592Srgrimesextern	int type;
831592Srgrimesextern	int form;
841592Srgrimesextern	int debug;
851592Srgrimesextern	int timeout;
861592Srgrimesextern	int maxtimeout;
871592Srgrimesextern  int pdata;
8827650Sdavidnextern	char *hostname;
8927650Sdavidnextern	char remotehost[];
901592Srgrimesextern	char proctitle[];
911592Srgrimesextern	int usedefault;
921592Srgrimesextern  int transflag;
931592Srgrimesextern  char tmpline[];
9470102Sphkextern	int readonly;
9570102Sphkextern	int noepsv;
961592Srgrimes
971592Srgrimesoff_t	restart_point;
981592Srgrimes
991592Srgrimesstatic	int cmd_type;
1001592Srgrimesstatic	int cmd_form;
1011592Srgrimesstatic	int cmd_bytesz;
1021592Srgrimeschar	cbuf[512];
1031592Srgrimeschar	*fromname;
1041592Srgrimes
10556668Sshinextern int epsvall;
10656668Sshin
1071592Srgrimes%}
1081592Srgrimes
1091592Srgrimes%union {
1101592Srgrimes	int	i;
1111592Srgrimes	char   *s;
1121592Srgrimes}
1131592Srgrimes
1141592Srgrimes%token
1151592Srgrimes	A	B	C	E	F	I
1161592Srgrimes	L	N	P	R	S	T
11756668Sshin	ALL
1181592Srgrimes
1191592Srgrimes	SP	CRLF	COMMA
1201592Srgrimes
1211592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1221592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1231592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1241592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1251592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1261592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1271592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
12856668Sshin	LPRT	LPSV	EPRT	EPSV
1291592Srgrimes
13075535Sphk	UMASK	IDLE	CHMOD	MDFIVE
1311592Srgrimes
1321592Srgrimes	LEXERR
1331592Srgrimes
1341592Srgrimes%token	<s> STRING
1351592Srgrimes%token	<i> NUMBER
1361592Srgrimes
1371592Srgrimes%type	<i> check_login octal_number byte_size
13870102Sphk%type	<i> check_login_ro octal_number byte_size
13970102Sphk%type	<i> check_login_epsv octal_number byte_size
1401592Srgrimes%type	<i> struct_code mode_code type_code form_code
14156668Sshin%type	<s> pathstring pathname password username ext_arg
14256668Sshin%type	<s> ALL
1431592Srgrimes
1441592Srgrimes%start	cmd_list
1451592Srgrimes
1461592Srgrimes%%
1471592Srgrimes
1481592Srgrimescmd_list
1491592Srgrimes	: /* empty */
1501592Srgrimes	| cmd_list cmd
1511592Srgrimes		{
1521592Srgrimes			fromname = (char *) 0;
1531592Srgrimes			restart_point = (off_t) 0;
1541592Srgrimes		}
1551592Srgrimes	| cmd_list rcmd
1561592Srgrimes	;
1571592Srgrimes
1581592Srgrimescmd
1591592Srgrimes	: USER SP username CRLF
1601592Srgrimes		{
1611592Srgrimes			user($3);
1621592Srgrimes			free($3);
1631592Srgrimes		}
1641592Srgrimes	| PASS SP password CRLF
1651592Srgrimes		{
1661592Srgrimes			pass($3);
1671592Srgrimes			free($3);
1681592Srgrimes		}
16917433Spst	| PORT check_login SP host_port CRLF
1701592Srgrimes		{
17156668Sshin			if (epsvall) {
17256668Sshin				reply(501, "no PORT allowed after EPSV ALL");
17356668Sshin				goto port_done;
17456668Sshin			}
17556668Sshin			if (!$2)
17656668Sshin				goto port_done;
17756668Sshin			if (port_check("PORT") == 1)
17856668Sshin				goto port_done;
17956668Sshin#ifdef INET6
18056668Sshin			if ((his_addr.su_family != AF_INET6 ||
18156668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
18256668Sshin				/* shoud never happen */
18356668Sshin				usedefault = 1;
18456668Sshin				reply(500, "Invalid address rejected.");
18556668Sshin				goto port_done;
18656668Sshin			}
18756668Sshin			port_check_v6("pcmd");
18856668Sshin#endif
18956668Sshin		port_done:
19056668Sshin		}
19156668Sshin	| LPRT check_login SP host_long_port CRLF
19256668Sshin		{
19356668Sshin			if (epsvall) {
19456668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
19556668Sshin				goto lprt_done;
19656668Sshin			}
19756668Sshin			if (!$2)
19856668Sshin				goto lprt_done;
19956668Sshin			if (port_check("LPRT") == 1)
20056668Sshin				goto lprt_done;
20156668Sshin#ifdef INET6
20256668Sshin			if (his_addr.su_family != AF_INET6) {
20356668Sshin				usedefault = 1;
20456668Sshin				reply(500, "Invalid address rejected.");
20556668Sshin				goto lprt_done;
20656668Sshin			}
20756668Sshin			if (port_check_v6("LPRT") == 1)
20856668Sshin				goto lprt_done;
20956668Sshin#endif
21056668Sshin		lprt_done:
21156668Sshin		}
21256668Sshin	| EPRT check_login SP STRING CRLF
21356668Sshin		{
21456668Sshin			char delim;
21556668Sshin			char *tmp = NULL;
21656668Sshin			char *p, *q;
21756668Sshin			char *result[3];
21856668Sshin			struct addrinfo hints;
21956668Sshin			struct addrinfo *res;
22056668Sshin			int i;
22156668Sshin
22256668Sshin			if (epsvall) {
22356668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
22456668Sshin				goto eprt_done;
22556668Sshin			}
22656668Sshin			if (!$2)
22756668Sshin				goto eprt_done;
22856668Sshin
22956668Sshin			memset(&data_dest, 0, sizeof(data_dest));
23056668Sshin			tmp = strdup($4);
23156668Sshin			if (debug)
23256668Sshin				syslog(LOG_DEBUG, "%s", tmp);
23356668Sshin			if (!tmp) {
23456668Sshin				fatal("not enough core");
23556668Sshin				/*NOTREACHED*/
23656668Sshin			}
23756668Sshin			p = tmp;
23856668Sshin			delim = p[0];
23956668Sshin			p++;
24056668Sshin			memset(result, 0, sizeof(result));
24156668Sshin			for (i = 0; i < 3; i++) {
24256668Sshin				q = strchr(p, delim);
24356668Sshin				if (!q || *q != delim) {
24456668Sshin		parsefail:
24556668Sshin					reply(500,
24656668Sshin						"Invalid argument, rejected.");
24756668Sshin					if (tmp)
24856668Sshin						free(tmp);
24917433Spst					usedefault = 1;
25056668Sshin					goto eprt_done;
25117433Spst				}
25256668Sshin				*q++ = '\0';
25356668Sshin				result[i] = p;
25456668Sshin				if (debug)
25556668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
25656668Sshin				p = q;
2571592Srgrimes			}
25856668Sshin
25956668Sshin			/* some more sanity check */
26056668Sshin			p = result[0];
26156668Sshin			while (*p) {
26256668Sshin				if (!isdigit(*p))
26356668Sshin					goto parsefail;
26456668Sshin				p++;
26556668Sshin			}
26656668Sshin			p = result[2];
26756668Sshin			while (*p) {
26856668Sshin				if (!isdigit(*p))
26956668Sshin					goto parsefail;
27056668Sshin				p++;
27156668Sshin			}
27256668Sshin
27356668Sshin			/* grab address */
27456668Sshin			memset(&hints, 0, sizeof(hints));
27556668Sshin			if (atoi(result[0]) == 1)
27656668Sshin				hints.ai_family = PF_INET;
27756668Sshin#ifdef INET6
27856668Sshin			else if (atoi(result[0]) == 2)
27956668Sshin				hints.ai_family = PF_INET6;
28056668Sshin#endif
28156668Sshin			else
28256668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
28356668Sshin			hints.ai_socktype = SOCK_STREAM;
28456668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
28556668Sshin			if (i)
28656668Sshin				goto parsefail;
28756668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
28856668Sshin#ifdef INET6
28956668Sshin			if (his_addr.su_family == AF_INET6
29056668Sshin			    && data_dest.su_family == AF_INET6) {
29156668Sshin				/* XXX more sanity checks! */
29256668Sshin				data_dest.su_sin6.sin6_scope_id =
29356668Sshin					his_addr.su_sin6.sin6_scope_id;
29456668Sshin			}
29556668Sshin#endif
29656668Sshin			free(tmp);
29756668Sshin			tmp = NULL;
29856668Sshin
29956668Sshin			if (port_check("EPRT") == 1)
30056668Sshin				goto eprt_done;
30156668Sshin#ifdef INET6
30256668Sshin			if (his_addr.su_family != AF_INET6) {
30356668Sshin				usedefault = 1;
30456668Sshin				reply(500, "Invalid address rejected.");
30556668Sshin				goto eprt_done;
30656668Sshin			}
30756668Sshin			if (port_check_v6("EPRT") == 1)
30856668Sshin				goto eprt_done;
30956668Sshin#endif
31056668Sshin		eprt_done:;
3111592Srgrimes		}
31217433Spst	| PASV check_login CRLF
3131592Srgrimes		{
31456668Sshin			if (epsvall)
31556668Sshin				reply(501, "no PASV allowed after EPSV ALL");
31656668Sshin			else if ($2)
31717433Spst				passive();
3181592Srgrimes		}
31956668Sshin	| LPSV check_login CRLF
32056668Sshin		{
32156668Sshin			if (epsvall)
32256668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
32356668Sshin			else if ($2)
32456668Sshin				long_passive("LPSV", PF_UNSPEC);
32556668Sshin		}
32670102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
32756668Sshin		{
32856668Sshin			if ($2) {
32956668Sshin				int pf;
33056668Sshin				switch ($4) {
33156668Sshin				case 1:
33256668Sshin					pf = PF_INET;
33356668Sshin					break;
33456668Sshin#ifdef INET6
33556668Sshin				case 2:
33656668Sshin					pf = PF_INET6;
33756668Sshin					break;
33856668Sshin#endif
33956668Sshin				default:
34056668Sshin					pf = -1;	/*junk value*/
34156668Sshin					break;
34256668Sshin				}
34356668Sshin				long_passive("EPSV", pf);
34456668Sshin			}
34556668Sshin		}
34670102Sphk	| EPSV check_login_epsv SP ALL CRLF
34756668Sshin		{
34856668Sshin			if ($2) {
34956668Sshin				reply(200,
35056668Sshin				      "EPSV ALL command successful.");
35156668Sshin				epsvall++;
35256668Sshin			}
35356668Sshin		}
35470102Sphk	| EPSV check_login_epsv CRLF
35556668Sshin		{
35656668Sshin			if ($2)
35756668Sshin				long_passive("EPSV", PF_UNSPEC);
35856668Sshin		}
35971278Sjedgar	| TYPE check_login SP type_code CRLF
3601592Srgrimes		{
36171278Sjedgar			if ($2) {
36271278Sjedgar				switch (cmd_type) {
3631592Srgrimes
36471278Sjedgar				case TYPE_A:
36571278Sjedgar					if (cmd_form == FORM_N) {
36671278Sjedgar						reply(200, "Type set to A.");
36771278Sjedgar						type = cmd_type;
36871278Sjedgar						form = cmd_form;
36971278Sjedgar					} else
37071278Sjedgar						reply(504, "Form must be N.");
37171278Sjedgar					break;
3721592Srgrimes
37371278Sjedgar				case TYPE_E:
37471278Sjedgar					reply(504, "Type E not implemented.");
37571278Sjedgar					break;
3761592Srgrimes
37771278Sjedgar				case TYPE_I:
37871278Sjedgar					reply(200, "Type set to I.");
37971278Sjedgar					type = cmd_type;
38071278Sjedgar					break;
3811592Srgrimes
38271278Sjedgar				case TYPE_L:
3831592Srgrimes#if NBBY == 8
38471278Sjedgar					if (cmd_bytesz == 8) {
38571278Sjedgar						reply(200,
38671278Sjedgar						    "Type set to L (byte size 8).");
38771278Sjedgar						type = cmd_type;
38871278Sjedgar					} else
38971278Sjedgar						reply(504, "Byte size must be 8.");
3901592Srgrimes#else /* NBBY == 8 */
39171278Sjedgar					UNIMPLEMENTED for NBBY != 8
3921592Srgrimes#endif /* NBBY == 8 */
39371278Sjedgar				}
3941592Srgrimes			}
3951592Srgrimes		}
39671278Sjedgar	| STRU check_login SP struct_code CRLF
3971592Srgrimes		{
39871278Sjedgar			if ($2) {
39971278Sjedgar				switch ($4) {
4001592Srgrimes
40171278Sjedgar				case STRU_F:
40271278Sjedgar					reply(200, "STRU F ok.");
40371278Sjedgar					break;
4041592Srgrimes
40571278Sjedgar				default:
40671278Sjedgar					reply(504, "Unimplemented STRU type.");
40771278Sjedgar				}
4081592Srgrimes			}
4091592Srgrimes		}
41071278Sjedgar	| MODE check_login SP mode_code CRLF
4111592Srgrimes		{
41271278Sjedgar			if ($2) {
41371278Sjedgar				switch ($4) {
4141592Srgrimes
41571278Sjedgar				case MODE_S:
41671278Sjedgar					reply(200, "MODE S ok.");
41771278Sjedgar					break;
41871278Sjedgar
41971278Sjedgar				default:
42071278Sjedgar					reply(502, "Unimplemented MODE type.");
42171278Sjedgar				}
4221592Srgrimes			}
4231592Srgrimes		}
42471278Sjedgar	| ALLO check_login SP NUMBER CRLF
4251592Srgrimes		{
42671278Sjedgar			if ($2) {
42771278Sjedgar				reply(202, "ALLO command ignored.");
42871278Sjedgar			}
4291592Srgrimes		}
43071278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4311592Srgrimes		{
43271278Sjedgar			if ($2) {
43371278Sjedgar				reply(202, "ALLO command ignored.");
43471278Sjedgar			}
4351592Srgrimes		}
4361592Srgrimes	| RETR check_login SP pathname CRLF
4371592Srgrimes		{
4381592Srgrimes			if ($2 && $4 != NULL)
4391592Srgrimes				retrieve((char *) 0, $4);
4401592Srgrimes			if ($4 != NULL)
4411592Srgrimes				free($4);
4421592Srgrimes		}
44370102Sphk	| STOR check_login_ro SP pathname CRLF
4441592Srgrimes		{
4451592Srgrimes			if ($2 && $4 != NULL)
4461592Srgrimes				store($4, "w", 0);
4471592Srgrimes			if ($4 != NULL)
4481592Srgrimes				free($4);
4491592Srgrimes		}
45070102Sphk	| APPE check_login_ro SP pathname CRLF
4511592Srgrimes		{
4521592Srgrimes			if ($2 && $4 != NULL)
4531592Srgrimes				store($4, "a", 0);
4541592Srgrimes			if ($4 != NULL)
4551592Srgrimes				free($4);
4561592Srgrimes		}
4571592Srgrimes	| NLST check_login CRLF
4581592Srgrimes		{
4591592Srgrimes			if ($2)
4601592Srgrimes				send_file_list(".");
4611592Srgrimes		}
4621592Srgrimes	| NLST check_login SP STRING CRLF
4631592Srgrimes		{
4641592Srgrimes			if ($2 && $4 != NULL)
4651592Srgrimes				send_file_list($4);
4661592Srgrimes			if ($4 != NULL)
4671592Srgrimes				free($4);
4681592Srgrimes		}
4691592Srgrimes	| LIST check_login CRLF
4701592Srgrimes		{
4711592Srgrimes			if ($2)
4721592Srgrimes				retrieve("/bin/ls -lgA", "");
4731592Srgrimes		}
4741592Srgrimes	| LIST check_login SP pathname CRLF
4751592Srgrimes		{
4761592Srgrimes			if ($2 && $4 != NULL)
4771592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4781592Srgrimes			if ($4 != NULL)
4791592Srgrimes				free($4);
4801592Srgrimes		}
4811592Srgrimes	| STAT check_login SP pathname CRLF
4821592Srgrimes		{
4831592Srgrimes			if ($2 && $4 != NULL)
4841592Srgrimes				statfilecmd($4);
4851592Srgrimes			if ($4 != NULL)
4861592Srgrimes				free($4);
4871592Srgrimes		}
48871278Sjedgar	| STAT check_login CRLF
4891592Srgrimes		{
49071278Sjedgar			if ($2) {
49171278Sjedgar				statcmd();
49271278Sjedgar			}
4931592Srgrimes		}
49470102Sphk	| DELE check_login_ro SP pathname CRLF
4951592Srgrimes		{
4961592Srgrimes			if ($2 && $4 != NULL)
4971592Srgrimes				delete($4);
4981592Srgrimes			if ($4 != NULL)
4991592Srgrimes				free($4);
5001592Srgrimes		}
50170102Sphk	| RNTO check_login_ro SP pathname CRLF
5021592Srgrimes		{
50317433Spst			if ($2) {
50417433Spst				if (fromname) {
50517433Spst					renamecmd(fromname, $4);
50617433Spst					free(fromname);
50717433Spst					fromname = (char *) 0;
50817433Spst				} else {
50917433Spst					reply(503, "Bad sequence of commands.");
51017433Spst				}
5111592Srgrimes			}
51217433Spst			free($4);
5131592Srgrimes		}
51471278Sjedgar	| ABOR check_login CRLF
5151592Srgrimes		{
51671278Sjedgar			if ($2)
51771278Sjedgar				reply(225, "ABOR command successful.");
5181592Srgrimes		}
5191592Srgrimes	| CWD check_login CRLF
5201592Srgrimes		{
52169234Sdanny			if ($2) {
52269234Sdanny				if (guest)
52369234Sdanny					cwd("/");
52469234Sdanny				else
52569234Sdanny					cwd(pw->pw_dir);
52669234Sdanny			}
5271592Srgrimes		}
5281592Srgrimes	| CWD check_login SP pathname CRLF
5291592Srgrimes		{
5301592Srgrimes			if ($2 && $4 != NULL)
5311592Srgrimes				cwd($4);
5321592Srgrimes			if ($4 != NULL)
5331592Srgrimes				free($4);
5341592Srgrimes		}
5351592Srgrimes	| HELP CRLF
5361592Srgrimes		{
5371592Srgrimes			help(cmdtab, (char *) 0);
5381592Srgrimes		}
5391592Srgrimes	| HELP SP STRING CRLF
5401592Srgrimes		{
5411592Srgrimes			char *cp = $3;
5421592Srgrimes
5431592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5441592Srgrimes				cp = $3 + 4;
5451592Srgrimes				if (*cp == ' ')
5461592Srgrimes					cp++;
5471592Srgrimes				if (*cp)
5481592Srgrimes					help(sitetab, cp);
5491592Srgrimes				else
5501592Srgrimes					help(sitetab, (char *) 0);
5511592Srgrimes			} else
5521592Srgrimes				help(cmdtab, $3);
5531592Srgrimes		}
5541592Srgrimes	| NOOP CRLF
5551592Srgrimes		{
5561592Srgrimes			reply(200, "NOOP command successful.");
5571592Srgrimes		}
55870102Sphk	| MKD check_login_ro SP pathname CRLF
5591592Srgrimes		{
5601592Srgrimes			if ($2 && $4 != NULL)
5611592Srgrimes				makedir($4);
5621592Srgrimes			if ($4 != NULL)
5631592Srgrimes				free($4);
5641592Srgrimes		}
56570102Sphk	| RMD check_login_ro SP pathname CRLF
5661592Srgrimes		{
5671592Srgrimes			if ($2 && $4 != NULL)
5681592Srgrimes				removedir($4);
5691592Srgrimes			if ($4 != NULL)
5701592Srgrimes				free($4);
5711592Srgrimes		}
5721592Srgrimes	| PWD check_login CRLF
5731592Srgrimes		{
5741592Srgrimes			if ($2)
5751592Srgrimes				pwd();
5761592Srgrimes		}
5771592Srgrimes	| CDUP check_login CRLF
5781592Srgrimes		{
5791592Srgrimes			if ($2)
5801592Srgrimes				cwd("..");
5811592Srgrimes		}
5821592Srgrimes	| SITE SP HELP CRLF
5831592Srgrimes		{
5841592Srgrimes			help(sitetab, (char *) 0);
5851592Srgrimes		}
5861592Srgrimes	| SITE SP HELP SP STRING CRLF
5871592Srgrimes		{
5881592Srgrimes			help(sitetab, $5);
5891592Srgrimes		}
59075535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
59175535Sphk		{
59275535Sphk			char p[64], *q;
59375535Sphk
59475535Sphk			if ($4) {
59575535Sphk				q = MD5File($6, p);
59675535Sphk				if (q != NULL)
59775535Sphk					reply(200, "MD5(%s) = %s", $6, p);
59875535Sphk				else
59975535Sphk					perror_reply(550, $6);
60075535Sphk			}
60175535Sphk		}
6021592Srgrimes	| SITE SP UMASK check_login CRLF
6031592Srgrimes		{
6041592Srgrimes			int oldmask;
6051592Srgrimes
6061592Srgrimes			if ($4) {
6071592Srgrimes				oldmask = umask(0);
6081592Srgrimes				(void) umask(oldmask);
6091592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6101592Srgrimes			}
6111592Srgrimes		}
6121592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6131592Srgrimes		{
6141592Srgrimes			int oldmask;
6151592Srgrimes
6161592Srgrimes			if ($4) {
6171592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6181592Srgrimes					reply(501, "Bad UMASK value");
6191592Srgrimes				} else {
6201592Srgrimes					oldmask = umask($6);
6211592Srgrimes					reply(200,
6221592Srgrimes					    "UMASK set to %03o (was %03o)",
6231592Srgrimes					    $6, oldmask);
6241592Srgrimes				}
6251592Srgrimes			}
6261592Srgrimes		}
62770102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6281592Srgrimes		{
6291592Srgrimes			if ($4 && ($8 != NULL)) {
6301592Srgrimes				if ($6 > 0777)
6311592Srgrimes					reply(501,
6321592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6331592Srgrimes				else if (chmod($8, $6) < 0)
6341592Srgrimes					perror_reply(550, $8);
6351592Srgrimes				else
6361592Srgrimes					reply(200, "CHMOD command successful.");
6371592Srgrimes			}
6381592Srgrimes			if ($8 != NULL)
6391592Srgrimes				free($8);
6401592Srgrimes		}
64171278Sjedgar	| SITE SP check_login IDLE CRLF
6421592Srgrimes		{
64371278Sjedgar			if ($3)
64471278Sjedgar				reply(200,
64571278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
64671278Sjedgar				    timeout, maxtimeout);
6471592Srgrimes		}
64871278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6491592Srgrimes		{
65071278Sjedgar			if ($3) {
65171278Sjedgar				if ($6 < 30 || $6 > maxtimeout) {
65271278Sjedgar					reply(501,
65371278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
65471278Sjedgar					    maxtimeout);
65571278Sjedgar				} else {
65671278Sjedgar					timeout = $6;
65771278Sjedgar					(void) alarm((unsigned) timeout);
65871278Sjedgar					reply(200,
65971278Sjedgar					    "Maximum IDLE time set to %d seconds",
66071278Sjedgar					    timeout);
66171278Sjedgar				}
6621592Srgrimes			}
6631592Srgrimes		}
66470102Sphk	| STOU check_login_ro SP pathname CRLF
6651592Srgrimes		{
6661592Srgrimes			if ($2 && $4 != NULL)
6671592Srgrimes				store($4, "w", 1);
6681592Srgrimes			if ($4 != NULL)
6691592Srgrimes				free($4);
6701592Srgrimes		}
67171278Sjedgar	| SYST check_login CRLF
6721592Srgrimes		{
67371278Sjedgar			if ($2)
6741592Srgrimes#ifdef unix
6751592Srgrimes#ifdef BSD
6761592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6771592Srgrimes				NBBY, BSD);
6781592Srgrimes#else /* BSD */
6791592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6801592Srgrimes#endif /* BSD */
6811592Srgrimes#else /* unix */
6821592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6831592Srgrimes#endif /* unix */
6841592Srgrimes		}
6851592Srgrimes
6861592Srgrimes		/*
6871592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6881592Srgrimes		 * it will be in the updated RFC.
6891592Srgrimes		 *
6901592Srgrimes		 * Return size of file in a format suitable for
6911592Srgrimes		 * using with RESTART (we just count bytes).
6921592Srgrimes		 */
6931592Srgrimes	| SIZE check_login SP pathname CRLF
6941592Srgrimes		{
6951592Srgrimes			if ($2 && $4 != NULL)
6961592Srgrimes				sizecmd($4);
6971592Srgrimes			if ($4 != NULL)
6981592Srgrimes				free($4);
6991592Srgrimes		}
7001592Srgrimes
7011592Srgrimes		/*
7021592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7031592Srgrimes		 * it will be in the updated RFC.
7041592Srgrimes		 *
7051592Srgrimes		 * Return modification time of file as an ISO 3307
7061592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7071592Srgrimes		 * where xxx is the fractional second (of any precision,
7081592Srgrimes		 * not necessarily 3 digits)
7091592Srgrimes		 */
7101592Srgrimes	| MDTM check_login SP pathname CRLF
7111592Srgrimes		{
7121592Srgrimes			if ($2 && $4 != NULL) {
7131592Srgrimes				struct stat stbuf;
7141592Srgrimes				if (stat($4, &stbuf) < 0)
7151592Srgrimes					reply(550, "%s: %s",
7161592Srgrimes					    $4, strerror(errno));
7171592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7181592Srgrimes					reply(550, "%s: not a plain file.", $4);
7191592Srgrimes				} else {
7201592Srgrimes					struct tm *t;
7211592Srgrimes					t = gmtime(&stbuf.st_mtime);
7221592Srgrimes					reply(213,
72317435Spst					    "%04d%02d%02d%02d%02d%02d",
72417435Spst					    1900 + t->tm_year,
72517435Spst					    t->tm_mon+1, t->tm_mday,
7261592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7271592Srgrimes				}
7281592Srgrimes			}
7291592Srgrimes			if ($4 != NULL)
7301592Srgrimes				free($4);
7311592Srgrimes		}
7321592Srgrimes	| QUIT CRLF
7331592Srgrimes		{
7341592Srgrimes			reply(221, "Goodbye.");
7351592Srgrimes			dologout(0);
7361592Srgrimes		}
7371592Srgrimes	| error CRLF
7381592Srgrimes		{
7391592Srgrimes			yyerrok;
7401592Srgrimes		}
7411592Srgrimes	;
7421592Srgrimesrcmd
74370102Sphk	: RNFR check_login_ro SP pathname CRLF
7441592Srgrimes		{
7451592Srgrimes			char *renamefrom();
7461592Srgrimes
7471592Srgrimes			restart_point = (off_t) 0;
7481592Srgrimes			if ($2 && $4) {
7491592Srgrimes				fromname = renamefrom($4);
7501592Srgrimes				if (fromname == (char *) 0 && $4) {
7511592Srgrimes					free($4);
7521592Srgrimes				}
7531592Srgrimes			}
7541592Srgrimes		}
75571278Sjedgar	| REST check_login SP byte_size CRLF
7561592Srgrimes		{
75771278Sjedgar			if ($2) {
75871278Sjedgar				fromname = (char *) 0;
75971278Sjedgar				restart_point = $4;  /* XXX $4 is only "int" */
76071278Sjedgar				reply(350, "Restarting at %qd. %s",
76171278Sjedgar				    restart_point,
76271278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
76371278Sjedgar			}
7641592Srgrimes		}
7651592Srgrimes	;
7661592Srgrimes
7671592Srgrimesusername
7681592Srgrimes	: STRING
7691592Srgrimes	;
7701592Srgrimes
7711592Srgrimespassword
7721592Srgrimes	: /* empty */
7731592Srgrimes		{
7741592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7751592Srgrimes		}
7761592Srgrimes	| STRING
7771592Srgrimes	;
7781592Srgrimes
7791592Srgrimesbyte_size
7801592Srgrimes	: NUMBER
7811592Srgrimes	;
7821592Srgrimes
7831592Srgrimeshost_port
7841592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7851592Srgrimes		NUMBER COMMA NUMBER
7861592Srgrimes		{
7871592Srgrimes			char *a, *p;
7881592Srgrimes
78956668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
79056668Sshin			data_dest.su_family = AF_INET;
79156668Sshin			p = (char *)&data_dest.su_sin.sin_port;
79217435Spst			p[0] = $9; p[1] = $11;
79356668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
7941592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
7951592Srgrimes		}
7961592Srgrimes	;
7971592Srgrimes
79856668Sshinhost_long_port
79956668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80156668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80256668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80356668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80456668Sshin		NUMBER
80556668Sshin		{
80656668Sshin			char *a, *p;
80756668Sshin
80856668Sshin			memset(&data_dest, 0, sizeof(data_dest));
80956668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
81056668Sshin			data_dest.su_family = AF_INET6;
81156668Sshin			p = (char *)&data_dest.su_port;
81256668Sshin			p[0] = $39; p[1] = $41;
81356668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
81456668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
81556668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
81656668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
81756668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
81856668Sshin			if (his_addr.su_family == AF_INET6) {
81956668Sshin				/* XXX more sanity checks! */
82056668Sshin				data_dest.su_sin6.sin6_scope_id =
82156668Sshin					his_addr.su_sin6.sin6_scope_id;
82256668Sshin			}
82356668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
82456668Sshin				memset(&data_dest, 0, sizeof(data_dest));
82556668Sshin		}
82656668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82756668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82856668Sshin		NUMBER
82956668Sshin		{
83056668Sshin			char *a, *p;
83156668Sshin
83256668Sshin			memset(&data_dest, 0, sizeof(data_dest));
83356668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
83456668Sshin			data_dest.su_family = AF_INET;
83556668Sshin			p = (char *)&data_dest.su_port;
83656668Sshin			p[0] = $15; p[1] = $17;
83756668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
83856668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
83956668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
84056668Sshin				memset(&data_dest, 0, sizeof(data_dest));
84156668Sshin		}
84256668Sshin	;
84356668Sshin
8441592Srgrimesform_code
8451592Srgrimes	: N
8461592Srgrimes		{
8471592Srgrimes			$$ = FORM_N;
8481592Srgrimes		}
8491592Srgrimes	| T
8501592Srgrimes		{
8511592Srgrimes			$$ = FORM_T;
8521592Srgrimes		}
8531592Srgrimes	| C
8541592Srgrimes		{
8551592Srgrimes			$$ = FORM_C;
8561592Srgrimes		}
8571592Srgrimes	;
8581592Srgrimes
8591592Srgrimestype_code
8601592Srgrimes	: A
8611592Srgrimes		{
8621592Srgrimes			cmd_type = TYPE_A;
8631592Srgrimes			cmd_form = FORM_N;
8641592Srgrimes		}
8651592Srgrimes	| A SP form_code
8661592Srgrimes		{
8671592Srgrimes			cmd_type = TYPE_A;
8681592Srgrimes			cmd_form = $3;
8691592Srgrimes		}
8701592Srgrimes	| E
8711592Srgrimes		{
8721592Srgrimes			cmd_type = TYPE_E;
8731592Srgrimes			cmd_form = FORM_N;
8741592Srgrimes		}
8751592Srgrimes	| E SP form_code
8761592Srgrimes		{
8771592Srgrimes			cmd_type = TYPE_E;
8781592Srgrimes			cmd_form = $3;
8791592Srgrimes		}
8801592Srgrimes	| I
8811592Srgrimes		{
8821592Srgrimes			cmd_type = TYPE_I;
8831592Srgrimes		}
8841592Srgrimes	| L
8851592Srgrimes		{
8861592Srgrimes			cmd_type = TYPE_L;
8871592Srgrimes			cmd_bytesz = NBBY;
8881592Srgrimes		}
8891592Srgrimes	| L SP byte_size
8901592Srgrimes		{
8911592Srgrimes			cmd_type = TYPE_L;
8921592Srgrimes			cmd_bytesz = $3;
8931592Srgrimes		}
8941592Srgrimes		/* this is for a bug in the BBN ftp */
8951592Srgrimes	| L byte_size
8961592Srgrimes		{
8971592Srgrimes			cmd_type = TYPE_L;
8981592Srgrimes			cmd_bytesz = $2;
8991592Srgrimes		}
9001592Srgrimes	;
9011592Srgrimes
9021592Srgrimesstruct_code
9031592Srgrimes	: F
9041592Srgrimes		{
9051592Srgrimes			$$ = STRU_F;
9061592Srgrimes		}
9071592Srgrimes	| R
9081592Srgrimes		{
9091592Srgrimes			$$ = STRU_R;
9101592Srgrimes		}
9111592Srgrimes	| P
9121592Srgrimes		{
9131592Srgrimes			$$ = STRU_P;
9141592Srgrimes		}
9151592Srgrimes	;
9161592Srgrimes
9171592Srgrimesmode_code
9181592Srgrimes	: S
9191592Srgrimes		{
9201592Srgrimes			$$ = MODE_S;
9211592Srgrimes		}
9221592Srgrimes	| B
9231592Srgrimes		{
9241592Srgrimes			$$ = MODE_B;
9251592Srgrimes		}
9261592Srgrimes	| C
9271592Srgrimes		{
9281592Srgrimes			$$ = MODE_C;
9291592Srgrimes		}
9301592Srgrimes	;
9311592Srgrimes
9321592Srgrimespathname
9331592Srgrimes	: pathstring
9341592Srgrimes		{
9351592Srgrimes			/*
9361592Srgrimes			 * Problem: this production is used for all pathname
9371592Srgrimes			 * processing, but only gives a 550 error reply.
9381592Srgrimes			 * This is a valid reply in some cases but not in others.
9391592Srgrimes			 */
9401592Srgrimes			if (logged_in && $1 && *$1 == '~') {
9411592Srgrimes				glob_t gl;
9421592Srgrimes				int flags =
9431592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9441592Srgrimes
9451592Srgrimes				memset(&gl, 0, sizeof(gl));
9461592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9471592Srgrimes				    gl.gl_pathc == 0) {
9481592Srgrimes					reply(550, "not found");
9491592Srgrimes					$$ = NULL;
9501592Srgrimes				} else {
9511592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9521592Srgrimes				}
9531592Srgrimes				globfree(&gl);
9541592Srgrimes				free($1);
9551592Srgrimes			} else
9561592Srgrimes				$$ = $1;
9571592Srgrimes		}
9581592Srgrimes	;
9591592Srgrimes
9601592Srgrimespathstring
9611592Srgrimes	: STRING
9621592Srgrimes	;
9631592Srgrimes
9641592Srgrimesoctal_number
9651592Srgrimes	: NUMBER
9661592Srgrimes		{
9671592Srgrimes			int ret, dec, multby, digit;
9681592Srgrimes
9691592Srgrimes			/*
9701592Srgrimes			 * Convert a number that was read as decimal number
9711592Srgrimes			 * to what it would be if it had been read as octal.
9721592Srgrimes			 */
9731592Srgrimes			dec = $1;
9741592Srgrimes			multby = 1;
9751592Srgrimes			ret = 0;
9761592Srgrimes			while (dec) {
9771592Srgrimes				digit = dec%10;
9781592Srgrimes				if (digit > 7) {
9791592Srgrimes					ret = -1;
9801592Srgrimes					break;
9811592Srgrimes				}
9821592Srgrimes				ret += digit * multby;
9831592Srgrimes				multby *= 8;
9841592Srgrimes				dec /= 10;
9851592Srgrimes			}
9861592Srgrimes			$$ = ret;
9871592Srgrimes		}
9881592Srgrimes	;
9891592Srgrimes
9901592Srgrimes
9911592Srgrimescheck_login
9921592Srgrimes	: /* empty */
9931592Srgrimes		{
99470102Sphk		$$ = check_login1();
9951592Srgrimes		}
9961592Srgrimes	;
9971592Srgrimes
99870102Sphkcheck_login_epsv
99970102Sphk	: /* empty */
100070102Sphk		{
100170102Sphk		if (noepsv) {
100270102Sphk			reply(500, "EPSV command disabled");
100370102Sphk			$$ = 0;
100470102Sphk		}
100570102Sphk		else
100670102Sphk			$$ = check_login1();
100770102Sphk		}
100870102Sphk	;
100970102Sphk
101070102Sphkcheck_login_ro
101170102Sphk	: /* empty */
101270102Sphk		{
101370102Sphk		if (readonly) {
101472710Sdes			reply(550, "Permission denied.");
101570102Sphk			$$ = 0;
101670102Sphk		}
101770102Sphk		else
101870102Sphk			$$ = check_login1();
101970102Sphk		}
102070102Sphk	;
102170102Sphk
10221592Srgrimes%%
10231592Srgrimes
10241592Srgrimesextern jmp_buf errcatch;
10251592Srgrimes
10261592Srgrimes#define	CMD	0	/* beginning of command */
10271592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10281592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10291592Srgrimes#define	STR2	3	/* expect STRING */
10301592Srgrimes#define	OSTR	4	/* optional SP then STRING */
10311592Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
10321592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10331592Srgrimes#define	SITECMD	7	/* SITE command */
10341592Srgrimes#define	NSTR	8	/* Number followed by a string */
10351592Srgrimes
10361592Srgrimesstruct tab {
10371592Srgrimes	char	*name;
10381592Srgrimes	short	token;
10391592Srgrimes	short	state;
10401592Srgrimes	short	implemented;	/* 1 if command is implemented */
10411592Srgrimes	char	*help;
10421592Srgrimes};
10431592Srgrimes
10441592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10451592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
10461592Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
10471592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10481592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10491592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10501592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10511592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
105256668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
105356668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10541592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
105556668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
105656668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10571592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10581592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10591592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10601592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10611592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10621592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10631592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10641592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10651592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10661592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10671592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10681592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10691592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10701592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10711592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10721592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10731592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10741592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10751592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10761592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10771592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10781592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10791592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10801592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10811592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10821592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10831592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10841592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
10851592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
10861592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
10871592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
10881592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
10891592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
10901592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
10911592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10921592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10931592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
10941592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
10951592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
10961592Srgrimes	{ NULL,   0,    0,    0,	0 }
10971592Srgrimes};
10981592Srgrimes
10991592Srgrimesstruct tab sitetab[] = {
110075535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11011592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11021592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11031592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11041592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11051592Srgrimes	{ NULL,   0,    0,    0,	0 }
11061592Srgrimes};
11071592Srgrimes
11081592Srgrimesstatic char	*copy __P((char *));
11091592Srgrimesstatic void	 help __P((struct tab *, char *));
11101592Srgrimesstatic struct tab *
11111592Srgrimes		 lookup __P((struct tab *, char *));
111256668Sshinstatic int	 port_check __P((const char *));
111356668Sshinstatic int	 port_check_v6 __P((const char *));
11141592Srgrimesstatic void	 sizecmd __P((char *));
11151592Srgrimesstatic void	 toolong __P((int));
111656668Sshinstatic void	 v4map_data_dest __P((void));
11171592Srgrimesstatic int	 yylex __P((void));
11181592Srgrimes
11191592Srgrimesstatic struct tab *
11201592Srgrimeslookup(p, cmd)
11211592Srgrimes	struct tab *p;
11221592Srgrimes	char *cmd;
11231592Srgrimes{
11241592Srgrimes
11251592Srgrimes	for (; p->name != NULL; p++)
11261592Srgrimes		if (strcmp(cmd, p->name) == 0)
11271592Srgrimes			return (p);
11281592Srgrimes	return (0);
11291592Srgrimes}
11301592Srgrimes
11311592Srgrimes#include <arpa/telnet.h>
11321592Srgrimes
11331592Srgrimes/*
11341592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11351592Srgrimes */
11361592Srgrimeschar *
11371592Srgrimesgetline(s, n, iop)
11381592Srgrimes	char *s;
11391592Srgrimes	int n;
11401592Srgrimes	FILE *iop;
11411592Srgrimes{
11421592Srgrimes	int c;
11431592Srgrimes	register char *cs;
11441592Srgrimes
11451592Srgrimes	cs = s;
11461592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11471592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11481592Srgrimes		*cs++ = tmpline[c];
11491592Srgrimes		if (tmpline[c] == '\n') {
11501592Srgrimes			*cs++ = '\0';
11511592Srgrimes			if (debug)
11521592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11531592Srgrimes			tmpline[0] = '\0';
11541592Srgrimes			return(s);
11551592Srgrimes		}
11561592Srgrimes		if (c == 0)
11571592Srgrimes			tmpline[0] = '\0';
11581592Srgrimes	}
11591592Srgrimes	while ((c = getc(iop)) != EOF) {
11601592Srgrimes		c &= 0377;
11611592Srgrimes		if (c == IAC) {
11621592Srgrimes		    if ((c = getc(iop)) != EOF) {
11631592Srgrimes			c &= 0377;
11641592Srgrimes			switch (c) {
11651592Srgrimes			case WILL:
11661592Srgrimes			case WONT:
11671592Srgrimes				c = getc(iop);
11681592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11691592Srgrimes				(void) fflush(stdout);
11701592Srgrimes				continue;
11711592Srgrimes			case DO:
11721592Srgrimes			case DONT:
11731592Srgrimes				c = getc(iop);
11741592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11751592Srgrimes				(void) fflush(stdout);
11761592Srgrimes				continue;
11771592Srgrimes			case IAC:
11781592Srgrimes				break;
11791592Srgrimes			default:
11801592Srgrimes				continue;	/* ignore command */
11811592Srgrimes			}
11821592Srgrimes		    }
11831592Srgrimes		}
11841592Srgrimes		*cs++ = c;
11851592Srgrimes		if (--n <= 0 || c == '\n')
11861592Srgrimes			break;
11871592Srgrimes	}
11881592Srgrimes	if (c == EOF && cs == s)
11891592Srgrimes		return (NULL);
11901592Srgrimes	*cs++ = '\0';
11911592Srgrimes	if (debug) {
11921592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
11931592Srgrimes			/* Don't syslog passwords */
11941592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
11951592Srgrimes		} else {
11961592Srgrimes			register char *cp;
11971592Srgrimes			register int len;
11981592Srgrimes
11991592Srgrimes			/* Don't syslog trailing CR-LF */
12001592Srgrimes			len = strlen(s);
12011592Srgrimes			cp = s + len - 1;
12021592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12031592Srgrimes				--cp;
12041592Srgrimes				--len;
12051592Srgrimes			}
12061592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12071592Srgrimes		}
12081592Srgrimes	}
12091592Srgrimes	return (s);
12101592Srgrimes}
12111592Srgrimes
12121592Srgrimesstatic void
12131592Srgrimestoolong(signo)
12141592Srgrimes	int signo;
12151592Srgrimes{
12161592Srgrimes
12171592Srgrimes	reply(421,
12181592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12191592Srgrimes	if (logging)
12201592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12211592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12221592Srgrimes	dologout(1);
12231592Srgrimes}
12241592Srgrimes
12251592Srgrimesstatic int
12261592Srgrimesyylex()
12271592Srgrimes{
12281592Srgrimes	static int cpos, state;
12291592Srgrimes	char *cp, *cp2;
12301592Srgrimes	struct tab *p;
12311592Srgrimes	int n;
12321592Srgrimes	char c;
12331592Srgrimes
12341592Srgrimes	for (;;) {
12351592Srgrimes		switch (state) {
12361592Srgrimes
12371592Srgrimes		case CMD:
12381592Srgrimes			(void) signal(SIGALRM, toolong);
12391592Srgrimes			(void) alarm((unsigned) timeout);
12401592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12411592Srgrimes				reply(221, "You could at least say goodbye.");
12421592Srgrimes				dologout(0);
12431592Srgrimes			}
12441592Srgrimes			(void) alarm(0);
12451592Srgrimes#ifdef SETPROCTITLE
124629574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12471592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12481592Srgrimes#endif /* SETPROCTITLE */
12491592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12501592Srgrimes				*cp++ = '\n';
12511592Srgrimes				*cp = '\0';
12521592Srgrimes			}
12531592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12541592Srgrimes				cpos = cp - cbuf;
12551592Srgrimes			if (cpos == 0)
12561592Srgrimes				cpos = 4;
12571592Srgrimes			c = cbuf[cpos];
12581592Srgrimes			cbuf[cpos] = '\0';
12591592Srgrimes			upper(cbuf);
12601592Srgrimes			p = lookup(cmdtab, cbuf);
12611592Srgrimes			cbuf[cpos] = c;
12623776Spst			if (p != 0) {
12631592Srgrimes				if (p->implemented == 0) {
12641592Srgrimes					nack(p->name);
12651592Srgrimes					longjmp(errcatch,0);
12661592Srgrimes					/* NOTREACHED */
12671592Srgrimes				}
12681592Srgrimes				state = p->state;
12691592Srgrimes				yylval.s = p->name;
12701592Srgrimes				return (p->token);
12711592Srgrimes			}
12721592Srgrimes			break;
12731592Srgrimes
12741592Srgrimes		case SITECMD:
12751592Srgrimes			if (cbuf[cpos] == ' ') {
12761592Srgrimes				cpos++;
12771592Srgrimes				return (SP);
12781592Srgrimes			}
12791592Srgrimes			cp = &cbuf[cpos];
12801592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12811592Srgrimes				cpos = cp2 - cbuf;
12821592Srgrimes			c = cbuf[cpos];
12831592Srgrimes			cbuf[cpos] = '\0';
12841592Srgrimes			upper(cp);
12851592Srgrimes			p = lookup(sitetab, cp);
12861592Srgrimes			cbuf[cpos] = c;
12873777Spst			if (guest == 0 && p != 0) {
12881592Srgrimes				if (p->implemented == 0) {
12891592Srgrimes					state = CMD;
12901592Srgrimes					nack(p->name);
12911592Srgrimes					longjmp(errcatch,0);
12921592Srgrimes					/* NOTREACHED */
12931592Srgrimes				}
12941592Srgrimes				state = p->state;
12951592Srgrimes				yylval.s = p->name;
12961592Srgrimes				return (p->token);
12971592Srgrimes			}
12981592Srgrimes			state = CMD;
12991592Srgrimes			break;
13001592Srgrimes
13011592Srgrimes		case OSTR:
13021592Srgrimes			if (cbuf[cpos] == '\n') {
13031592Srgrimes				state = CMD;
13041592Srgrimes				return (CRLF);
13051592Srgrimes			}
13061592Srgrimes			/* FALLTHROUGH */
13071592Srgrimes
13081592Srgrimes		case STR1:
13091592Srgrimes		case ZSTR1:
13101592Srgrimes		dostr1:
13111592Srgrimes			if (cbuf[cpos] == ' ') {
13121592Srgrimes				cpos++;
131351979Salfred				state = state == OSTR ? STR2 : state+1;
13141592Srgrimes				return (SP);
13151592Srgrimes			}
13161592Srgrimes			break;
13171592Srgrimes
13181592Srgrimes		case ZSTR2:
13191592Srgrimes			if (cbuf[cpos] == '\n') {
13201592Srgrimes				state = CMD;
13211592Srgrimes				return (CRLF);
13221592Srgrimes			}
13231592Srgrimes			/* FALLTHROUGH */
13241592Srgrimes
13251592Srgrimes		case STR2:
13261592Srgrimes			cp = &cbuf[cpos];
13271592Srgrimes			n = strlen(cp);
13281592Srgrimes			cpos += n - 1;
13291592Srgrimes			/*
13301592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13311592Srgrimes			 */
13321592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13331592Srgrimes				cbuf[cpos] = '\0';
13341592Srgrimes				yylval.s = copy(cp);
13351592Srgrimes				cbuf[cpos] = '\n';
13361592Srgrimes				state = ARGS;
13371592Srgrimes				return (STRING);
13381592Srgrimes			}
13391592Srgrimes			break;
13401592Srgrimes
13411592Srgrimes		case NSTR:
13421592Srgrimes			if (cbuf[cpos] == ' ') {
13431592Srgrimes				cpos++;
13441592Srgrimes				return (SP);
13451592Srgrimes			}
13461592Srgrimes			if (isdigit(cbuf[cpos])) {
13471592Srgrimes				cp = &cbuf[cpos];
13481592Srgrimes				while (isdigit(cbuf[++cpos]))
13491592Srgrimes					;
13501592Srgrimes				c = cbuf[cpos];
13511592Srgrimes				cbuf[cpos] = '\0';
13521592Srgrimes				yylval.i = atoi(cp);
13531592Srgrimes				cbuf[cpos] = c;
13541592Srgrimes				state = STR1;
13551592Srgrimes				return (NUMBER);
13561592Srgrimes			}
13571592Srgrimes			state = STR1;
13581592Srgrimes			goto dostr1;
13591592Srgrimes
13601592Srgrimes		case ARGS:
13611592Srgrimes			if (isdigit(cbuf[cpos])) {
13621592Srgrimes				cp = &cbuf[cpos];
13631592Srgrimes				while (isdigit(cbuf[++cpos]))
13641592Srgrimes					;
13651592Srgrimes				c = cbuf[cpos];
13661592Srgrimes				cbuf[cpos] = '\0';
13671592Srgrimes				yylval.i = atoi(cp);
13681592Srgrimes				cbuf[cpos] = c;
13691592Srgrimes				return (NUMBER);
13701592Srgrimes			}
137156668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
137256668Sshin			 && !isalnum(cbuf[cpos + 3])) {
137356668Sshin				cpos += 3;
137456668Sshin				return ALL;
137556668Sshin			}
13761592Srgrimes			switch (cbuf[cpos++]) {
13771592Srgrimes
13781592Srgrimes			case '\n':
13791592Srgrimes				state = CMD;
13801592Srgrimes				return (CRLF);
13811592Srgrimes
13821592Srgrimes			case ' ':
13831592Srgrimes				return (SP);
13841592Srgrimes
13851592Srgrimes			case ',':
13861592Srgrimes				return (COMMA);
13871592Srgrimes
13881592Srgrimes			case 'A':
13891592Srgrimes			case 'a':
13901592Srgrimes				return (A);
13911592Srgrimes
13921592Srgrimes			case 'B':
13931592Srgrimes			case 'b':
13941592Srgrimes				return (B);
13951592Srgrimes
13961592Srgrimes			case 'C':
13971592Srgrimes			case 'c':
13981592Srgrimes				return (C);
13991592Srgrimes
14001592Srgrimes			case 'E':
14011592Srgrimes			case 'e':
14021592Srgrimes				return (E);
14031592Srgrimes
14041592Srgrimes			case 'F':
14051592Srgrimes			case 'f':
14061592Srgrimes				return (F);
14071592Srgrimes
14081592Srgrimes			case 'I':
14091592Srgrimes			case 'i':
14101592Srgrimes				return (I);
14111592Srgrimes
14121592Srgrimes			case 'L':
14131592Srgrimes			case 'l':
14141592Srgrimes				return (L);
14151592Srgrimes
14161592Srgrimes			case 'N':
14171592Srgrimes			case 'n':
14181592Srgrimes				return (N);
14191592Srgrimes
14201592Srgrimes			case 'P':
14211592Srgrimes			case 'p':
14221592Srgrimes				return (P);
14231592Srgrimes
14241592Srgrimes			case 'R':
14251592Srgrimes			case 'r':
14261592Srgrimes				return (R);
14271592Srgrimes
14281592Srgrimes			case 'S':
14291592Srgrimes			case 's':
14301592Srgrimes				return (S);
14311592Srgrimes
14321592Srgrimes			case 'T':
14331592Srgrimes			case 't':
14341592Srgrimes				return (T);
14351592Srgrimes
14361592Srgrimes			}
14371592Srgrimes			break;
14381592Srgrimes
14391592Srgrimes		default:
14401592Srgrimes			fatal("Unknown state in scanner.");
14411592Srgrimes		}
14421592Srgrimes		yyerror((char *) 0);
14431592Srgrimes		state = CMD;
14441592Srgrimes		longjmp(errcatch,0);
14451592Srgrimes	}
14461592Srgrimes}
14471592Srgrimes
14481592Srgrimesvoid
14491592Srgrimesupper(s)
14501592Srgrimes	char *s;
14511592Srgrimes{
14521592Srgrimes	while (*s != '\0') {
14531592Srgrimes		if (islower(*s))
14541592Srgrimes			*s = toupper(*s);
14551592Srgrimes		s++;
14561592Srgrimes	}
14571592Srgrimes}
14581592Srgrimes
14591592Srgrimesstatic char *
14601592Srgrimescopy(s)
14611592Srgrimes	char *s;
14621592Srgrimes{
14631592Srgrimes	char *p;
14641592Srgrimes
14651592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14661592Srgrimes	if (p == NULL)
14671592Srgrimes		fatal("Ran out of memory.");
14681592Srgrimes	(void) strcpy(p, s);
14691592Srgrimes	return (p);
14701592Srgrimes}
14711592Srgrimes
14721592Srgrimesstatic void
14731592Srgrimeshelp(ctab, s)
14741592Srgrimes	struct tab *ctab;
14751592Srgrimes	char *s;
14761592Srgrimes{
14771592Srgrimes	struct tab *c;
14781592Srgrimes	int width, NCMDS;
14791592Srgrimes	char *type;
14801592Srgrimes
14811592Srgrimes	if (ctab == sitetab)
14821592Srgrimes		type = "SITE ";
14831592Srgrimes	else
14841592Srgrimes		type = "";
14851592Srgrimes	width = 0, NCMDS = 0;
14861592Srgrimes	for (c = ctab; c->name != NULL; c++) {
14871592Srgrimes		int len = strlen(c->name);
14881592Srgrimes
14891592Srgrimes		if (len > width)
14901592Srgrimes			width = len;
14911592Srgrimes		NCMDS++;
14921592Srgrimes	}
14931592Srgrimes	width = (width + 8) &~ 7;
14941592Srgrimes	if (s == 0) {
14951592Srgrimes		int i, j, w;
14961592Srgrimes		int columns, lines;
14971592Srgrimes
14981592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
14991592Srgrimes		    type, "(* =>'s unimplemented)");
15001592Srgrimes		columns = 76 / width;
15011592Srgrimes		if (columns == 0)
15021592Srgrimes			columns = 1;
15031592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15041592Srgrimes		for (i = 0; i < lines; i++) {
15051592Srgrimes			printf("   ");
15061592Srgrimes			for (j = 0; j < columns; j++) {
15071592Srgrimes				c = ctab + j * lines + i;
15081592Srgrimes				printf("%s%c", c->name,
15091592Srgrimes					c->implemented ? ' ' : '*');
15101592Srgrimes				if (c + lines >= &ctab[NCMDS])
15111592Srgrimes					break;
15121592Srgrimes				w = strlen(c->name) + 1;
15131592Srgrimes				while (w < width) {
15141592Srgrimes					putchar(' ');
15151592Srgrimes					w++;
15161592Srgrimes				}
15171592Srgrimes			}
15181592Srgrimes			printf("\r\n");
15191592Srgrimes		}
15201592Srgrimes		(void) fflush(stdout);
15211592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15221592Srgrimes		return;
15231592Srgrimes	}
15241592Srgrimes	upper(s);
15251592Srgrimes	c = lookup(ctab, s);
15261592Srgrimes	if (c == (struct tab *)0) {
15271592Srgrimes		reply(502, "Unknown command %s.", s);
15281592Srgrimes		return;
15291592Srgrimes	}
15301592Srgrimes	if (c->implemented)
15311592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15321592Srgrimes	else
15331592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15341592Srgrimes		    c->name, c->help);
15351592Srgrimes}
15361592Srgrimes
15371592Srgrimesstatic void
15381592Srgrimessizecmd(filename)
15391592Srgrimes	char *filename;
15401592Srgrimes{
15411592Srgrimes	switch (type) {
15421592Srgrimes	case TYPE_L:
15431592Srgrimes	case TYPE_I: {
15441592Srgrimes		struct stat stbuf;
154563350Sdes		if (stat(filename, &stbuf) < 0)
154663350Sdes			perror_reply(550, filename);
154763350Sdes		else if (!S_ISREG(stbuf.st_mode))
15481592Srgrimes			reply(550, "%s: not a plain file.", filename);
15491592Srgrimes		else
15501592Srgrimes			reply(213, "%qu", stbuf.st_size);
15511592Srgrimes		break; }
15521592Srgrimes	case TYPE_A: {
15531592Srgrimes		FILE *fin;
15541592Srgrimes		int c;
15551592Srgrimes		off_t count;
15561592Srgrimes		struct stat stbuf;
15571592Srgrimes		fin = fopen(filename, "r");
15581592Srgrimes		if (fin == NULL) {
15591592Srgrimes			perror_reply(550, filename);
15601592Srgrimes			return;
15611592Srgrimes		}
156263350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
156363350Sdes			perror_reply(550, filename);
156463350Sdes			(void) fclose(fin);
156563350Sdes			return;
156663350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15671592Srgrimes			reply(550, "%s: not a plain file.", filename);
15681592Srgrimes			(void) fclose(fin);
15691592Srgrimes			return;
15701592Srgrimes		}
15711592Srgrimes
15721592Srgrimes		count = 0;
15731592Srgrimes		while((c=getc(fin)) != EOF) {
15741592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15751592Srgrimes				count++;
15761592Srgrimes			count++;
15771592Srgrimes		}
15781592Srgrimes		(void) fclose(fin);
15791592Srgrimes
15801592Srgrimes		reply(213, "%qd", count);
15811592Srgrimes		break; }
15821592Srgrimes	default:
15831592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15841592Srgrimes	}
15851592Srgrimes}
158656668Sshin
158756668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
158856668Sshinstatic int
158956668Sshinport_check(pcmd)
159056668Sshin	const char *pcmd;
159156668Sshin{
159256668Sshin	if (his_addr.su_family == AF_INET) {
159356668Sshin		if (data_dest.su_family != AF_INET) {
159456668Sshin			usedefault = 1;
159556668Sshin			reply(500, "Invalid address rejected.");
159656668Sshin			return 1;
159756668Sshin		}
159856668Sshin		if (paranoid &&
159956668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
160056668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
160156668Sshin			    &his_addr.su_sin.sin_addr,
160256668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
160356668Sshin			usedefault = 1;
160456668Sshin			reply(500, "Illegal PORT range rejected.");
160556668Sshin		} else {
160656668Sshin			usedefault = 0;
160756668Sshin			if (pdata >= 0) {
160856668Sshin				(void) close(pdata);
160956668Sshin				pdata = -1;
161056668Sshin			}
161156668Sshin			reply(200, "%s command successful.", pcmd);
161256668Sshin		}
161356668Sshin		return 1;
161456668Sshin	}
161556668Sshin	return 0;
161656668Sshin}
161756668Sshin
161870102Sphkstatic int
161970102Sphkcheck_login1()
162070102Sphk{
162170102Sphk	if (logged_in)
162270102Sphk		return 1;
162370102Sphk	else {
162470102Sphk		reply(530, "Please login with USER and PASS.");
162570102Sphk		return 0;
162670102Sphk	}
162770102Sphk}
162870102Sphk
162956668Sshin#ifdef INET6
163056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
163156668Sshinstatic int
163256668Sshinport_check_v6(pcmd)
163356668Sshin	const char *pcmd;
163456668Sshin{
163556668Sshin	if (his_addr.su_family == AF_INET6) {
163656668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
163756668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
163856668Sshin			v4map_data_dest();
163956668Sshin		if (data_dest.su_family != AF_INET6) {
164056668Sshin			usedefault = 1;
164156668Sshin			reply(500, "Invalid address rejected.");
164256668Sshin			return 1;
164356668Sshin		}
164456668Sshin		if (paranoid &&
164556668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
164656668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
164756668Sshin			    &his_addr.su_sin6.sin6_addr,
164856668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
164956668Sshin			usedefault = 1;
165056668Sshin			reply(500, "Illegal PORT range rejected.");
165156668Sshin		} else {
165256668Sshin			usedefault = 0;
165356668Sshin			if (pdata >= 0) {
165456668Sshin				(void) close(pdata);
165556668Sshin				pdata = -1;
165656668Sshin			}
165756668Sshin			reply(200, "%s command successful.", pcmd);
165856668Sshin		}
165956668Sshin		return 1;
166056668Sshin	}
166156668Sshin	return 0;
166256668Sshin}
166356668Sshin
166456668Sshinstatic void
166556668Sshinv4map_data_dest()
166656668Sshin{
166756668Sshin	struct in_addr savedaddr;
166856668Sshin	int savedport;
166956668Sshin
167056668Sshin	if (data_dest.su_family != AF_INET) {
167156668Sshin		usedefault = 1;
167256668Sshin		reply(500, "Invalid address rejected.");
167356668Sshin		return;
167456668Sshin	}
167556668Sshin
167656668Sshin	savedaddr = data_dest.su_sin.sin_addr;
167756668Sshin	savedport = data_dest.su_port;
167856668Sshin
167956668Sshin	memset(&data_dest, 0, sizeof(data_dest));
168056668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
168156668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
168256668Sshin	data_dest.su_sin6.sin6_port = savedport;
168356668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
168456668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
168556668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
168656668Sshin}
168756668Sshin#endif
1688