ftpcmd.y revision 71278
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 71278 2001-01-20 01:34:22Z jedgar $";
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>
721592Srgrimes
731592Srgrimes#include "extern.h"
741592Srgrimes
7556668Sshinextern	union sockunion data_dest, his_addr;
761592Srgrimesextern	int logged_in;
771592Srgrimesextern	struct passwd *pw;
781592Srgrimesextern	int guest;
7917435Spstextern 	int paranoid;
801592Srgrimesextern	int logging;
811592Srgrimesextern	int type;
821592Srgrimesextern	int form;
831592Srgrimesextern	int debug;
841592Srgrimesextern	int timeout;
851592Srgrimesextern	int maxtimeout;
861592Srgrimesextern  int pdata;
8727650Sdavidnextern	char *hostname;
8827650Sdavidnextern	char remotehost[];
891592Srgrimesextern	char proctitle[];
901592Srgrimesextern	int usedefault;
911592Srgrimesextern  int transflag;
921592Srgrimesextern  char tmpline[];
9370102Sphkextern	int readonly;
9470102Sphkextern	int noepsv;
951592Srgrimes
961592Srgrimesoff_t	restart_point;
971592Srgrimes
981592Srgrimesstatic	int cmd_type;
991592Srgrimesstatic	int cmd_form;
1001592Srgrimesstatic	int cmd_bytesz;
1011592Srgrimeschar	cbuf[512];
1021592Srgrimeschar	*fromname;
1031592Srgrimes
10456668Sshinextern int epsvall;
10556668Sshin
1061592Srgrimes%}
1071592Srgrimes
1081592Srgrimes%union {
1091592Srgrimes	int	i;
1101592Srgrimes	char   *s;
1111592Srgrimes}
1121592Srgrimes
1131592Srgrimes%token
1141592Srgrimes	A	B	C	E	F	I
1151592Srgrimes	L	N	P	R	S	T
11656668Sshin	ALL
1171592Srgrimes
1181592Srgrimes	SP	CRLF	COMMA
1191592Srgrimes
1201592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1211592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1221592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1231592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1241592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1251592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1261592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
12756668Sshin	LPRT	LPSV	EPRT	EPSV
1281592Srgrimes
1291592Srgrimes	UMASK	IDLE	CHMOD
1301592Srgrimes
1311592Srgrimes	LEXERR
1321592Srgrimes
1331592Srgrimes%token	<s> STRING
1341592Srgrimes%token	<i> NUMBER
1351592Srgrimes
1361592Srgrimes%type	<i> check_login octal_number byte_size
13770102Sphk%type	<i> check_login_ro octal_number byte_size
13870102Sphk%type	<i> check_login_epsv octal_number byte_size
1391592Srgrimes%type	<i> struct_code mode_code type_code form_code
14056668Sshin%type	<s> pathstring pathname password username ext_arg
14156668Sshin%type	<s> ALL
1421592Srgrimes
1431592Srgrimes%start	cmd_list
1441592Srgrimes
1451592Srgrimes%%
1461592Srgrimes
1471592Srgrimescmd_list
1481592Srgrimes	: /* empty */
1491592Srgrimes	| cmd_list cmd
1501592Srgrimes		{
1511592Srgrimes			fromname = (char *) 0;
1521592Srgrimes			restart_point = (off_t) 0;
1531592Srgrimes		}
1541592Srgrimes	| cmd_list rcmd
1551592Srgrimes	;
1561592Srgrimes
1571592Srgrimescmd
1581592Srgrimes	: USER SP username CRLF
1591592Srgrimes		{
1601592Srgrimes			user($3);
1611592Srgrimes			free($3);
1621592Srgrimes		}
1631592Srgrimes	| PASS SP password CRLF
1641592Srgrimes		{
1651592Srgrimes			pass($3);
1661592Srgrimes			free($3);
1671592Srgrimes		}
16817433Spst	| PORT check_login SP host_port CRLF
1691592Srgrimes		{
17056668Sshin			if (epsvall) {
17156668Sshin				reply(501, "no PORT allowed after EPSV ALL");
17256668Sshin				goto port_done;
17356668Sshin			}
17456668Sshin			if (!$2)
17556668Sshin				goto port_done;
17656668Sshin			if (port_check("PORT") == 1)
17756668Sshin				goto port_done;
17856668Sshin#ifdef INET6
17956668Sshin			if ((his_addr.su_family != AF_INET6 ||
18056668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
18156668Sshin				/* shoud never happen */
18256668Sshin				usedefault = 1;
18356668Sshin				reply(500, "Invalid address rejected.");
18456668Sshin				goto port_done;
18556668Sshin			}
18656668Sshin			port_check_v6("pcmd");
18756668Sshin#endif
18856668Sshin		port_done:
18956668Sshin		}
19056668Sshin	| LPRT check_login SP host_long_port CRLF
19156668Sshin		{
19256668Sshin			if (epsvall) {
19356668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
19456668Sshin				goto lprt_done;
19556668Sshin			}
19656668Sshin			if (!$2)
19756668Sshin				goto lprt_done;
19856668Sshin			if (port_check("LPRT") == 1)
19956668Sshin				goto lprt_done;
20056668Sshin#ifdef INET6
20156668Sshin			if (his_addr.su_family != AF_INET6) {
20256668Sshin				usedefault = 1;
20356668Sshin				reply(500, "Invalid address rejected.");
20456668Sshin				goto lprt_done;
20556668Sshin			}
20656668Sshin			if (port_check_v6("LPRT") == 1)
20756668Sshin				goto lprt_done;
20856668Sshin#endif
20956668Sshin		lprt_done:
21056668Sshin		}
21156668Sshin	| EPRT check_login SP STRING CRLF
21256668Sshin		{
21356668Sshin			char delim;
21456668Sshin			char *tmp = NULL;
21556668Sshin			char *p, *q;
21656668Sshin			char *result[3];
21756668Sshin			struct addrinfo hints;
21856668Sshin			struct addrinfo *res;
21956668Sshin			int i;
22056668Sshin
22156668Sshin			if (epsvall) {
22256668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
22356668Sshin				goto eprt_done;
22456668Sshin			}
22556668Sshin			if (!$2)
22656668Sshin				goto eprt_done;
22756668Sshin
22856668Sshin			memset(&data_dest, 0, sizeof(data_dest));
22956668Sshin			tmp = strdup($4);
23056668Sshin			if (debug)
23156668Sshin				syslog(LOG_DEBUG, "%s", tmp);
23256668Sshin			if (!tmp) {
23356668Sshin				fatal("not enough core");
23456668Sshin				/*NOTREACHED*/
23556668Sshin			}
23656668Sshin			p = tmp;
23756668Sshin			delim = p[0];
23856668Sshin			p++;
23956668Sshin			memset(result, 0, sizeof(result));
24056668Sshin			for (i = 0; i < 3; i++) {
24156668Sshin				q = strchr(p, delim);
24256668Sshin				if (!q || *q != delim) {
24356668Sshin		parsefail:
24456668Sshin					reply(500,
24556668Sshin						"Invalid argument, rejected.");
24656668Sshin					if (tmp)
24756668Sshin						free(tmp);
24817433Spst					usedefault = 1;
24956668Sshin					goto eprt_done;
25017433Spst				}
25156668Sshin				*q++ = '\0';
25256668Sshin				result[i] = p;
25356668Sshin				if (debug)
25456668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
25556668Sshin				p = q;
2561592Srgrimes			}
25756668Sshin
25856668Sshin			/* some more sanity check */
25956668Sshin			p = result[0];
26056668Sshin			while (*p) {
26156668Sshin				if (!isdigit(*p))
26256668Sshin					goto parsefail;
26356668Sshin				p++;
26456668Sshin			}
26556668Sshin			p = result[2];
26656668Sshin			while (*p) {
26756668Sshin				if (!isdigit(*p))
26856668Sshin					goto parsefail;
26956668Sshin				p++;
27056668Sshin			}
27156668Sshin
27256668Sshin			/* grab address */
27356668Sshin			memset(&hints, 0, sizeof(hints));
27456668Sshin			if (atoi(result[0]) == 1)
27556668Sshin				hints.ai_family = PF_INET;
27656668Sshin#ifdef INET6
27756668Sshin			else if (atoi(result[0]) == 2)
27856668Sshin				hints.ai_family = PF_INET6;
27956668Sshin#endif
28056668Sshin			else
28156668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
28256668Sshin			hints.ai_socktype = SOCK_STREAM;
28356668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
28456668Sshin			if (i)
28556668Sshin				goto parsefail;
28656668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
28756668Sshin#ifdef INET6
28856668Sshin			if (his_addr.su_family == AF_INET6
28956668Sshin			    && data_dest.su_family == AF_INET6) {
29056668Sshin				/* XXX more sanity checks! */
29156668Sshin				data_dest.su_sin6.sin6_scope_id =
29256668Sshin					his_addr.su_sin6.sin6_scope_id;
29356668Sshin			}
29456668Sshin#endif
29556668Sshin			free(tmp);
29656668Sshin			tmp = NULL;
29756668Sshin
29856668Sshin			if (port_check("EPRT") == 1)
29956668Sshin				goto eprt_done;
30056668Sshin#ifdef INET6
30156668Sshin			if (his_addr.su_family != AF_INET6) {
30256668Sshin				usedefault = 1;
30356668Sshin				reply(500, "Invalid address rejected.");
30456668Sshin				goto eprt_done;
30556668Sshin			}
30656668Sshin			if (port_check_v6("EPRT") == 1)
30756668Sshin				goto eprt_done;
30856668Sshin#endif
30956668Sshin		eprt_done:;
3101592Srgrimes		}
31117433Spst	| PASV check_login CRLF
3121592Srgrimes		{
31356668Sshin			if (epsvall)
31456668Sshin				reply(501, "no PASV allowed after EPSV ALL");
31556668Sshin			else if ($2)
31617433Spst				passive();
3171592Srgrimes		}
31856668Sshin	| LPSV check_login CRLF
31956668Sshin		{
32056668Sshin			if (epsvall)
32156668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
32256668Sshin			else if ($2)
32356668Sshin				long_passive("LPSV", PF_UNSPEC);
32456668Sshin		}
32570102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
32656668Sshin		{
32756668Sshin			if ($2) {
32856668Sshin				int pf;
32956668Sshin				switch ($4) {
33056668Sshin				case 1:
33156668Sshin					pf = PF_INET;
33256668Sshin					break;
33356668Sshin#ifdef INET6
33456668Sshin				case 2:
33556668Sshin					pf = PF_INET6;
33656668Sshin					break;
33756668Sshin#endif
33856668Sshin				default:
33956668Sshin					pf = -1;	/*junk value*/
34056668Sshin					break;
34156668Sshin				}
34256668Sshin				long_passive("EPSV", pf);
34356668Sshin			}
34456668Sshin		}
34570102Sphk	| EPSV check_login_epsv SP ALL CRLF
34656668Sshin		{
34756668Sshin			if ($2) {
34856668Sshin				reply(200,
34956668Sshin				      "EPSV ALL command successful.");
35056668Sshin				epsvall++;
35156668Sshin			}
35256668Sshin		}
35370102Sphk	| EPSV check_login_epsv CRLF
35456668Sshin		{
35556668Sshin			if ($2)
35656668Sshin				long_passive("EPSV", PF_UNSPEC);
35756668Sshin		}
35871278Sjedgar	| TYPE check_login SP type_code CRLF
3591592Srgrimes		{
36071278Sjedgar			if ($2) {
36171278Sjedgar				switch (cmd_type) {
3621592Srgrimes
36371278Sjedgar				case TYPE_A:
36471278Sjedgar					if (cmd_form == FORM_N) {
36571278Sjedgar						reply(200, "Type set to A.");
36671278Sjedgar						type = cmd_type;
36771278Sjedgar						form = cmd_form;
36871278Sjedgar					} else
36971278Sjedgar						reply(504, "Form must be N.");
37071278Sjedgar					break;
3711592Srgrimes
37271278Sjedgar				case TYPE_E:
37371278Sjedgar					reply(504, "Type E not implemented.");
37471278Sjedgar					break;
3751592Srgrimes
37671278Sjedgar				case TYPE_I:
37771278Sjedgar					reply(200, "Type set to I.");
37871278Sjedgar					type = cmd_type;
37971278Sjedgar					break;
3801592Srgrimes
38171278Sjedgar				case TYPE_L:
3821592Srgrimes#if NBBY == 8
38371278Sjedgar					if (cmd_bytesz == 8) {
38471278Sjedgar						reply(200,
38571278Sjedgar						    "Type set to L (byte size 8).");
38671278Sjedgar						type = cmd_type;
38771278Sjedgar					} else
38871278Sjedgar						reply(504, "Byte size must be 8.");
3891592Srgrimes#else /* NBBY == 8 */
39071278Sjedgar					UNIMPLEMENTED for NBBY != 8
3911592Srgrimes#endif /* NBBY == 8 */
39271278Sjedgar				}
3931592Srgrimes			}
3941592Srgrimes		}
39571278Sjedgar	| STRU check_login SP struct_code CRLF
3961592Srgrimes		{
39771278Sjedgar			if ($2) {
39871278Sjedgar				switch ($4) {
3991592Srgrimes
40071278Sjedgar				case STRU_F:
40171278Sjedgar					reply(200, "STRU F ok.");
40271278Sjedgar					break;
4031592Srgrimes
40471278Sjedgar				default:
40571278Sjedgar					reply(504, "Unimplemented STRU type.");
40671278Sjedgar				}
4071592Srgrimes			}
4081592Srgrimes		}
40971278Sjedgar	| MODE check_login SP mode_code CRLF
4101592Srgrimes		{
41171278Sjedgar			if ($2) {
41271278Sjedgar				switch ($4) {
4131592Srgrimes
41471278Sjedgar				case MODE_S:
41571278Sjedgar					reply(200, "MODE S ok.");
41671278Sjedgar					break;
41771278Sjedgar
41871278Sjedgar				default:
41971278Sjedgar					reply(502, "Unimplemented MODE type.");
42071278Sjedgar				}
4211592Srgrimes			}
4221592Srgrimes		}
42371278Sjedgar	| ALLO check_login SP NUMBER CRLF
4241592Srgrimes		{
42571278Sjedgar			if ($2) {
42671278Sjedgar				reply(202, "ALLO command ignored.");
42771278Sjedgar			}
4281592Srgrimes		}
42971278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4301592Srgrimes		{
43171278Sjedgar			if ($2) {
43271278Sjedgar				reply(202, "ALLO command ignored.");
43371278Sjedgar			}
4341592Srgrimes		}
4351592Srgrimes	| RETR check_login SP pathname CRLF
4361592Srgrimes		{
4371592Srgrimes			if ($2 && $4 != NULL)
4381592Srgrimes				retrieve((char *) 0, $4);
4391592Srgrimes			if ($4 != NULL)
4401592Srgrimes				free($4);
4411592Srgrimes		}
44270102Sphk	| STOR check_login_ro SP pathname CRLF
4431592Srgrimes		{
4441592Srgrimes			if ($2 && $4 != NULL)
4451592Srgrimes				store($4, "w", 0);
4461592Srgrimes			if ($4 != NULL)
4471592Srgrimes				free($4);
4481592Srgrimes		}
44970102Sphk	| APPE check_login_ro SP pathname CRLF
4501592Srgrimes		{
4511592Srgrimes			if ($2 && $4 != NULL)
4521592Srgrimes				store($4, "a", 0);
4531592Srgrimes			if ($4 != NULL)
4541592Srgrimes				free($4);
4551592Srgrimes		}
4561592Srgrimes	| NLST check_login CRLF
4571592Srgrimes		{
4581592Srgrimes			if ($2)
4591592Srgrimes				send_file_list(".");
4601592Srgrimes		}
4611592Srgrimes	| NLST check_login SP STRING CRLF
4621592Srgrimes		{
4631592Srgrimes			if ($2 && $4 != NULL)
4641592Srgrimes				send_file_list($4);
4651592Srgrimes			if ($4 != NULL)
4661592Srgrimes				free($4);
4671592Srgrimes		}
4681592Srgrimes	| LIST check_login CRLF
4691592Srgrimes		{
4701592Srgrimes			if ($2)
4711592Srgrimes				retrieve("/bin/ls -lgA", "");
4721592Srgrimes		}
4731592Srgrimes	| LIST check_login SP pathname CRLF
4741592Srgrimes		{
4751592Srgrimes			if ($2 && $4 != NULL)
4761592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4771592Srgrimes			if ($4 != NULL)
4781592Srgrimes				free($4);
4791592Srgrimes		}
4801592Srgrimes	| STAT check_login SP pathname CRLF
4811592Srgrimes		{
4821592Srgrimes			if ($2 && $4 != NULL)
4831592Srgrimes				statfilecmd($4);
4841592Srgrimes			if ($4 != NULL)
4851592Srgrimes				free($4);
4861592Srgrimes		}
48771278Sjedgar	| STAT check_login CRLF
4881592Srgrimes		{
48971278Sjedgar			if ($2) {
49071278Sjedgar				statcmd();
49171278Sjedgar			}
4921592Srgrimes		}
49370102Sphk	| DELE check_login_ro SP pathname CRLF
4941592Srgrimes		{
4951592Srgrimes			if ($2 && $4 != NULL)
4961592Srgrimes				delete($4);
4971592Srgrimes			if ($4 != NULL)
4981592Srgrimes				free($4);
4991592Srgrimes		}
50070102Sphk	| RNTO check_login_ro SP pathname CRLF
5011592Srgrimes		{
50217433Spst			if ($2) {
50317433Spst				if (fromname) {
50417433Spst					renamecmd(fromname, $4);
50517433Spst					free(fromname);
50617433Spst					fromname = (char *) 0;
50717433Spst				} else {
50817433Spst					reply(503, "Bad sequence of commands.");
50917433Spst				}
5101592Srgrimes			}
51117433Spst			free($4);
5121592Srgrimes		}
51371278Sjedgar	| ABOR check_login CRLF
5141592Srgrimes		{
51571278Sjedgar			if ($2)
51671278Sjedgar				reply(225, "ABOR command successful.");
5171592Srgrimes		}
5181592Srgrimes	| CWD check_login CRLF
5191592Srgrimes		{
52069234Sdanny			if ($2) {
52169234Sdanny				if (guest)
52269234Sdanny					cwd("/");
52369234Sdanny				else
52469234Sdanny					cwd(pw->pw_dir);
52569234Sdanny			}
5261592Srgrimes		}
5271592Srgrimes	| CWD check_login SP pathname CRLF
5281592Srgrimes		{
5291592Srgrimes			if ($2 && $4 != NULL)
5301592Srgrimes				cwd($4);
5311592Srgrimes			if ($4 != NULL)
5321592Srgrimes				free($4);
5331592Srgrimes		}
5341592Srgrimes	| HELP CRLF
5351592Srgrimes		{
5361592Srgrimes			help(cmdtab, (char *) 0);
5371592Srgrimes		}
5381592Srgrimes	| HELP SP STRING CRLF
5391592Srgrimes		{
5401592Srgrimes			char *cp = $3;
5411592Srgrimes
5421592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5431592Srgrimes				cp = $3 + 4;
5441592Srgrimes				if (*cp == ' ')
5451592Srgrimes					cp++;
5461592Srgrimes				if (*cp)
5471592Srgrimes					help(sitetab, cp);
5481592Srgrimes				else
5491592Srgrimes					help(sitetab, (char *) 0);
5501592Srgrimes			} else
5511592Srgrimes				help(cmdtab, $3);
5521592Srgrimes		}
5531592Srgrimes	| NOOP CRLF
5541592Srgrimes		{
5551592Srgrimes			reply(200, "NOOP command successful.");
5561592Srgrimes		}
55770102Sphk	| MKD check_login_ro SP pathname CRLF
5581592Srgrimes		{
5591592Srgrimes			if ($2 && $4 != NULL)
5601592Srgrimes				makedir($4);
5611592Srgrimes			if ($4 != NULL)
5621592Srgrimes				free($4);
5631592Srgrimes		}
56470102Sphk	| RMD check_login_ro SP pathname CRLF
5651592Srgrimes		{
5661592Srgrimes			if ($2 && $4 != NULL)
5671592Srgrimes				removedir($4);
5681592Srgrimes			if ($4 != NULL)
5691592Srgrimes				free($4);
5701592Srgrimes		}
5711592Srgrimes	| PWD check_login CRLF
5721592Srgrimes		{
5731592Srgrimes			if ($2)
5741592Srgrimes				pwd();
5751592Srgrimes		}
5761592Srgrimes	| CDUP check_login CRLF
5771592Srgrimes		{
5781592Srgrimes			if ($2)
5791592Srgrimes				cwd("..");
5801592Srgrimes		}
5811592Srgrimes	| SITE SP HELP CRLF
5821592Srgrimes		{
5831592Srgrimes			help(sitetab, (char *) 0);
5841592Srgrimes		}
5851592Srgrimes	| SITE SP HELP SP STRING CRLF
5861592Srgrimes		{
5871592Srgrimes			help(sitetab, $5);
5881592Srgrimes		}
5891592Srgrimes	| SITE SP UMASK check_login CRLF
5901592Srgrimes		{
5911592Srgrimes			int oldmask;
5921592Srgrimes
5931592Srgrimes			if ($4) {
5941592Srgrimes				oldmask = umask(0);
5951592Srgrimes				(void) umask(oldmask);
5961592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
5971592Srgrimes			}
5981592Srgrimes		}
5991592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6001592Srgrimes		{
6011592Srgrimes			int oldmask;
6021592Srgrimes
6031592Srgrimes			if ($4) {
6041592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6051592Srgrimes					reply(501, "Bad UMASK value");
6061592Srgrimes				} else {
6071592Srgrimes					oldmask = umask($6);
6081592Srgrimes					reply(200,
6091592Srgrimes					    "UMASK set to %03o (was %03o)",
6101592Srgrimes					    $6, oldmask);
6111592Srgrimes				}
6121592Srgrimes			}
6131592Srgrimes		}
61470102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6151592Srgrimes		{
6161592Srgrimes			if ($4 && ($8 != NULL)) {
6171592Srgrimes				if ($6 > 0777)
6181592Srgrimes					reply(501,
6191592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6201592Srgrimes				else if (chmod($8, $6) < 0)
6211592Srgrimes					perror_reply(550, $8);
6221592Srgrimes				else
6231592Srgrimes					reply(200, "CHMOD command successful.");
6241592Srgrimes			}
6251592Srgrimes			if ($8 != NULL)
6261592Srgrimes				free($8);
6271592Srgrimes		}
62871278Sjedgar	| SITE SP check_login IDLE CRLF
6291592Srgrimes		{
63071278Sjedgar			if ($3)
63171278Sjedgar				reply(200,
63271278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
63371278Sjedgar				    timeout, maxtimeout);
6341592Srgrimes		}
63571278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6361592Srgrimes		{
63771278Sjedgar			if ($3) {
63871278Sjedgar				if ($6 < 30 || $6 > maxtimeout) {
63971278Sjedgar					reply(501,
64071278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
64171278Sjedgar					    maxtimeout);
64271278Sjedgar				} else {
64371278Sjedgar					timeout = $6;
64471278Sjedgar					(void) alarm((unsigned) timeout);
64571278Sjedgar					reply(200,
64671278Sjedgar					    "Maximum IDLE time set to %d seconds",
64771278Sjedgar					    timeout);
64871278Sjedgar				}
6491592Srgrimes			}
6501592Srgrimes		}
65170102Sphk	| STOU check_login_ro SP pathname CRLF
6521592Srgrimes		{
6531592Srgrimes			if ($2 && $4 != NULL)
6541592Srgrimes				store($4, "w", 1);
6551592Srgrimes			if ($4 != NULL)
6561592Srgrimes				free($4);
6571592Srgrimes		}
65871278Sjedgar	| SYST check_login CRLF
6591592Srgrimes		{
66071278Sjedgar			if ($2)
6611592Srgrimes#ifdef unix
6621592Srgrimes#ifdef BSD
6631592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6641592Srgrimes				NBBY, BSD);
6651592Srgrimes#else /* BSD */
6661592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6671592Srgrimes#endif /* BSD */
6681592Srgrimes#else /* unix */
6691592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6701592Srgrimes#endif /* unix */
6711592Srgrimes		}
6721592Srgrimes
6731592Srgrimes		/*
6741592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6751592Srgrimes		 * it will be in the updated RFC.
6761592Srgrimes		 *
6771592Srgrimes		 * Return size of file in a format suitable for
6781592Srgrimes		 * using with RESTART (we just count bytes).
6791592Srgrimes		 */
6801592Srgrimes	| SIZE check_login SP pathname CRLF
6811592Srgrimes		{
6821592Srgrimes			if ($2 && $4 != NULL)
6831592Srgrimes				sizecmd($4);
6841592Srgrimes			if ($4 != NULL)
6851592Srgrimes				free($4);
6861592Srgrimes		}
6871592Srgrimes
6881592Srgrimes		/*
6891592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
6901592Srgrimes		 * it will be in the updated RFC.
6911592Srgrimes		 *
6921592Srgrimes		 * Return modification time of file as an ISO 3307
6931592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
6941592Srgrimes		 * where xxx is the fractional second (of any precision,
6951592Srgrimes		 * not necessarily 3 digits)
6961592Srgrimes		 */
6971592Srgrimes	| MDTM check_login SP pathname CRLF
6981592Srgrimes		{
6991592Srgrimes			if ($2 && $4 != NULL) {
7001592Srgrimes				struct stat stbuf;
7011592Srgrimes				if (stat($4, &stbuf) < 0)
7021592Srgrimes					reply(550, "%s: %s",
7031592Srgrimes					    $4, strerror(errno));
7041592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7051592Srgrimes					reply(550, "%s: not a plain file.", $4);
7061592Srgrimes				} else {
7071592Srgrimes					struct tm *t;
7081592Srgrimes					t = gmtime(&stbuf.st_mtime);
7091592Srgrimes					reply(213,
71017435Spst					    "%04d%02d%02d%02d%02d%02d",
71117435Spst					    1900 + t->tm_year,
71217435Spst					    t->tm_mon+1, t->tm_mday,
7131592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7141592Srgrimes				}
7151592Srgrimes			}
7161592Srgrimes			if ($4 != NULL)
7171592Srgrimes				free($4);
7181592Srgrimes		}
7191592Srgrimes	| QUIT CRLF
7201592Srgrimes		{
7211592Srgrimes			reply(221, "Goodbye.");
7221592Srgrimes			dologout(0);
7231592Srgrimes		}
7241592Srgrimes	| error CRLF
7251592Srgrimes		{
7261592Srgrimes			yyerrok;
7271592Srgrimes		}
7281592Srgrimes	;
7291592Srgrimesrcmd
73070102Sphk	: RNFR check_login_ro SP pathname CRLF
7311592Srgrimes		{
7321592Srgrimes			char *renamefrom();
7331592Srgrimes
7341592Srgrimes			restart_point = (off_t) 0;
7351592Srgrimes			if ($2 && $4) {
7361592Srgrimes				fromname = renamefrom($4);
7371592Srgrimes				if (fromname == (char *) 0 && $4) {
7381592Srgrimes					free($4);
7391592Srgrimes				}
7401592Srgrimes			}
7411592Srgrimes		}
74271278Sjedgar	| REST check_login SP byte_size CRLF
7431592Srgrimes		{
74471278Sjedgar			if ($2) {
74571278Sjedgar				fromname = (char *) 0;
74671278Sjedgar				restart_point = $4;  /* XXX $4 is only "int" */
74771278Sjedgar				reply(350, "Restarting at %qd. %s",
74871278Sjedgar				    restart_point,
74971278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
75071278Sjedgar			}
7511592Srgrimes		}
7521592Srgrimes	;
7531592Srgrimes
7541592Srgrimesusername
7551592Srgrimes	: STRING
7561592Srgrimes	;
7571592Srgrimes
7581592Srgrimespassword
7591592Srgrimes	: /* empty */
7601592Srgrimes		{
7611592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7621592Srgrimes		}
7631592Srgrimes	| STRING
7641592Srgrimes	;
7651592Srgrimes
7661592Srgrimesbyte_size
7671592Srgrimes	: NUMBER
7681592Srgrimes	;
7691592Srgrimes
7701592Srgrimeshost_port
7711592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7721592Srgrimes		NUMBER COMMA NUMBER
7731592Srgrimes		{
7741592Srgrimes			char *a, *p;
7751592Srgrimes
77656668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
77756668Sshin			data_dest.su_family = AF_INET;
77856668Sshin			p = (char *)&data_dest.su_sin.sin_port;
77917435Spst			p[0] = $9; p[1] = $11;
78056668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
7811592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
7821592Srgrimes		}
7831592Srgrimes	;
7841592Srgrimes
78556668Sshinhost_long_port
78656668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
78756668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
78856668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
78956668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
79056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
79156668Sshin		NUMBER
79256668Sshin		{
79356668Sshin			char *a, *p;
79456668Sshin
79556668Sshin			memset(&data_dest, 0, sizeof(data_dest));
79656668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
79756668Sshin			data_dest.su_family = AF_INET6;
79856668Sshin			p = (char *)&data_dest.su_port;
79956668Sshin			p[0] = $39; p[1] = $41;
80056668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
80156668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
80256668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
80356668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
80456668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
80556668Sshin			if (his_addr.su_family == AF_INET6) {
80656668Sshin				/* XXX more sanity checks! */
80756668Sshin				data_dest.su_sin6.sin6_scope_id =
80856668Sshin					his_addr.su_sin6.sin6_scope_id;
80956668Sshin			}
81056668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
81156668Sshin				memset(&data_dest, 0, sizeof(data_dest));
81256668Sshin		}
81356668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
81456668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
81556668Sshin		NUMBER
81656668Sshin		{
81756668Sshin			char *a, *p;
81856668Sshin
81956668Sshin			memset(&data_dest, 0, sizeof(data_dest));
82056668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
82156668Sshin			data_dest.su_family = AF_INET;
82256668Sshin			p = (char *)&data_dest.su_port;
82356668Sshin			p[0] = $15; p[1] = $17;
82456668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
82556668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
82656668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
82756668Sshin				memset(&data_dest, 0, sizeof(data_dest));
82856668Sshin		}
82956668Sshin	;
83056668Sshin
8311592Srgrimesform_code
8321592Srgrimes	: N
8331592Srgrimes		{
8341592Srgrimes			$$ = FORM_N;
8351592Srgrimes		}
8361592Srgrimes	| T
8371592Srgrimes		{
8381592Srgrimes			$$ = FORM_T;
8391592Srgrimes		}
8401592Srgrimes	| C
8411592Srgrimes		{
8421592Srgrimes			$$ = FORM_C;
8431592Srgrimes		}
8441592Srgrimes	;
8451592Srgrimes
8461592Srgrimestype_code
8471592Srgrimes	: A
8481592Srgrimes		{
8491592Srgrimes			cmd_type = TYPE_A;
8501592Srgrimes			cmd_form = FORM_N;
8511592Srgrimes		}
8521592Srgrimes	| A SP form_code
8531592Srgrimes		{
8541592Srgrimes			cmd_type = TYPE_A;
8551592Srgrimes			cmd_form = $3;
8561592Srgrimes		}
8571592Srgrimes	| E
8581592Srgrimes		{
8591592Srgrimes			cmd_type = TYPE_E;
8601592Srgrimes			cmd_form = FORM_N;
8611592Srgrimes		}
8621592Srgrimes	| E SP form_code
8631592Srgrimes		{
8641592Srgrimes			cmd_type = TYPE_E;
8651592Srgrimes			cmd_form = $3;
8661592Srgrimes		}
8671592Srgrimes	| I
8681592Srgrimes		{
8691592Srgrimes			cmd_type = TYPE_I;
8701592Srgrimes		}
8711592Srgrimes	| L
8721592Srgrimes		{
8731592Srgrimes			cmd_type = TYPE_L;
8741592Srgrimes			cmd_bytesz = NBBY;
8751592Srgrimes		}
8761592Srgrimes	| L SP byte_size
8771592Srgrimes		{
8781592Srgrimes			cmd_type = TYPE_L;
8791592Srgrimes			cmd_bytesz = $3;
8801592Srgrimes		}
8811592Srgrimes		/* this is for a bug in the BBN ftp */
8821592Srgrimes	| L byte_size
8831592Srgrimes		{
8841592Srgrimes			cmd_type = TYPE_L;
8851592Srgrimes			cmd_bytesz = $2;
8861592Srgrimes		}
8871592Srgrimes	;
8881592Srgrimes
8891592Srgrimesstruct_code
8901592Srgrimes	: F
8911592Srgrimes		{
8921592Srgrimes			$$ = STRU_F;
8931592Srgrimes		}
8941592Srgrimes	| R
8951592Srgrimes		{
8961592Srgrimes			$$ = STRU_R;
8971592Srgrimes		}
8981592Srgrimes	| P
8991592Srgrimes		{
9001592Srgrimes			$$ = STRU_P;
9011592Srgrimes		}
9021592Srgrimes	;
9031592Srgrimes
9041592Srgrimesmode_code
9051592Srgrimes	: S
9061592Srgrimes		{
9071592Srgrimes			$$ = MODE_S;
9081592Srgrimes		}
9091592Srgrimes	| B
9101592Srgrimes		{
9111592Srgrimes			$$ = MODE_B;
9121592Srgrimes		}
9131592Srgrimes	| C
9141592Srgrimes		{
9151592Srgrimes			$$ = MODE_C;
9161592Srgrimes		}
9171592Srgrimes	;
9181592Srgrimes
9191592Srgrimespathname
9201592Srgrimes	: pathstring
9211592Srgrimes		{
9221592Srgrimes			/*
9231592Srgrimes			 * Problem: this production is used for all pathname
9241592Srgrimes			 * processing, but only gives a 550 error reply.
9251592Srgrimes			 * This is a valid reply in some cases but not in others.
9261592Srgrimes			 */
9271592Srgrimes			if (logged_in && $1 && *$1 == '~') {
9281592Srgrimes				glob_t gl;
9291592Srgrimes				int flags =
9301592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9311592Srgrimes
9321592Srgrimes				memset(&gl, 0, sizeof(gl));
9331592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9341592Srgrimes				    gl.gl_pathc == 0) {
9351592Srgrimes					reply(550, "not found");
9361592Srgrimes					$$ = NULL;
9371592Srgrimes				} else {
9381592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9391592Srgrimes				}
9401592Srgrimes				globfree(&gl);
9411592Srgrimes				free($1);
9421592Srgrimes			} else
9431592Srgrimes				$$ = $1;
9441592Srgrimes		}
9451592Srgrimes	;
9461592Srgrimes
9471592Srgrimespathstring
9481592Srgrimes	: STRING
9491592Srgrimes	;
9501592Srgrimes
9511592Srgrimesoctal_number
9521592Srgrimes	: NUMBER
9531592Srgrimes		{
9541592Srgrimes			int ret, dec, multby, digit;
9551592Srgrimes
9561592Srgrimes			/*
9571592Srgrimes			 * Convert a number that was read as decimal number
9581592Srgrimes			 * to what it would be if it had been read as octal.
9591592Srgrimes			 */
9601592Srgrimes			dec = $1;
9611592Srgrimes			multby = 1;
9621592Srgrimes			ret = 0;
9631592Srgrimes			while (dec) {
9641592Srgrimes				digit = dec%10;
9651592Srgrimes				if (digit > 7) {
9661592Srgrimes					ret = -1;
9671592Srgrimes					break;
9681592Srgrimes				}
9691592Srgrimes				ret += digit * multby;
9701592Srgrimes				multby *= 8;
9711592Srgrimes				dec /= 10;
9721592Srgrimes			}
9731592Srgrimes			$$ = ret;
9741592Srgrimes		}
9751592Srgrimes	;
9761592Srgrimes
9771592Srgrimes
9781592Srgrimescheck_login
9791592Srgrimes	: /* empty */
9801592Srgrimes		{
98170102Sphk		$$ = check_login1();
9821592Srgrimes		}
9831592Srgrimes	;
9841592Srgrimes
98570102Sphkcheck_login_epsv
98670102Sphk	: /* empty */
98770102Sphk		{
98870102Sphk		if (noepsv) {
98970102Sphk			reply(500, "EPSV command disabled");
99070102Sphk			$$ = 0;
99170102Sphk		}
99270102Sphk		else
99370102Sphk			$$ = check_login1();
99470102Sphk		}
99570102Sphk	;
99670102Sphk
99770102Sphkcheck_login_ro
99870102Sphk	: /* empty */
99970102Sphk		{
100070102Sphk		if (readonly) {
100170102Sphk			reply(202, "Command ignored. Server is in readonly mode.");
100270102Sphk			$$ = 0;
100370102Sphk		}
100470102Sphk		else
100570102Sphk			$$ = check_login1();
100670102Sphk		}
100770102Sphk	;
100870102Sphk
10091592Srgrimes%%
10101592Srgrimes
10111592Srgrimesextern jmp_buf errcatch;
10121592Srgrimes
10131592Srgrimes#define	CMD	0	/* beginning of command */
10141592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10151592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10161592Srgrimes#define	STR2	3	/* expect STRING */
10171592Srgrimes#define	OSTR	4	/* optional SP then STRING */
10181592Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
10191592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10201592Srgrimes#define	SITECMD	7	/* SITE command */
10211592Srgrimes#define	NSTR	8	/* Number followed by a string */
10221592Srgrimes
10231592Srgrimesstruct tab {
10241592Srgrimes	char	*name;
10251592Srgrimes	short	token;
10261592Srgrimes	short	state;
10271592Srgrimes	short	implemented;	/* 1 if command is implemented */
10281592Srgrimes	char	*help;
10291592Srgrimes};
10301592Srgrimes
10311592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10321592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
10331592Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
10341592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10351592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10361592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10371592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10381592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
103956668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
104056668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10411592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
104256668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
104356668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10441592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10451592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10461592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10471592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10481592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10491592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10501592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10511592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10521592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10531592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10541592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10551592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10561592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10571592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10581592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10591592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10601592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10611592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10621592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10631592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10641592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10651592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10661592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10671592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10681592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10691592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10701592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10711592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
10721592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
10731592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
10741592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
10751592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
10761592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
10771592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
10781592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10791592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10801592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
10811592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
10821592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
10831592Srgrimes	{ NULL,   0,    0,    0,	0 }
10841592Srgrimes};
10851592Srgrimes
10861592Srgrimesstruct tab sitetab[] = {
10871592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
10881592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
10891592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
10901592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10911592Srgrimes	{ NULL,   0,    0,    0,	0 }
10921592Srgrimes};
10931592Srgrimes
10941592Srgrimesstatic char	*copy __P((char *));
10951592Srgrimesstatic void	 help __P((struct tab *, char *));
10961592Srgrimesstatic struct tab *
10971592Srgrimes		 lookup __P((struct tab *, char *));
109856668Sshinstatic int	 port_check __P((const char *));
109956668Sshinstatic int	 port_check_v6 __P((const char *));
11001592Srgrimesstatic void	 sizecmd __P((char *));
11011592Srgrimesstatic void	 toolong __P((int));
110256668Sshinstatic void	 v4map_data_dest __P((void));
11031592Srgrimesstatic int	 yylex __P((void));
11041592Srgrimes
11051592Srgrimesstatic struct tab *
11061592Srgrimeslookup(p, cmd)
11071592Srgrimes	struct tab *p;
11081592Srgrimes	char *cmd;
11091592Srgrimes{
11101592Srgrimes
11111592Srgrimes	for (; p->name != NULL; p++)
11121592Srgrimes		if (strcmp(cmd, p->name) == 0)
11131592Srgrimes			return (p);
11141592Srgrimes	return (0);
11151592Srgrimes}
11161592Srgrimes
11171592Srgrimes#include <arpa/telnet.h>
11181592Srgrimes
11191592Srgrimes/*
11201592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11211592Srgrimes */
11221592Srgrimeschar *
11231592Srgrimesgetline(s, n, iop)
11241592Srgrimes	char *s;
11251592Srgrimes	int n;
11261592Srgrimes	FILE *iop;
11271592Srgrimes{
11281592Srgrimes	int c;
11291592Srgrimes	register char *cs;
11301592Srgrimes
11311592Srgrimes	cs = s;
11321592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11331592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11341592Srgrimes		*cs++ = tmpline[c];
11351592Srgrimes		if (tmpline[c] == '\n') {
11361592Srgrimes			*cs++ = '\0';
11371592Srgrimes			if (debug)
11381592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11391592Srgrimes			tmpline[0] = '\0';
11401592Srgrimes			return(s);
11411592Srgrimes		}
11421592Srgrimes		if (c == 0)
11431592Srgrimes			tmpline[0] = '\0';
11441592Srgrimes	}
11451592Srgrimes	while ((c = getc(iop)) != EOF) {
11461592Srgrimes		c &= 0377;
11471592Srgrimes		if (c == IAC) {
11481592Srgrimes		    if ((c = getc(iop)) != EOF) {
11491592Srgrimes			c &= 0377;
11501592Srgrimes			switch (c) {
11511592Srgrimes			case WILL:
11521592Srgrimes			case WONT:
11531592Srgrimes				c = getc(iop);
11541592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11551592Srgrimes				(void) fflush(stdout);
11561592Srgrimes				continue;
11571592Srgrimes			case DO:
11581592Srgrimes			case DONT:
11591592Srgrimes				c = getc(iop);
11601592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11611592Srgrimes				(void) fflush(stdout);
11621592Srgrimes				continue;
11631592Srgrimes			case IAC:
11641592Srgrimes				break;
11651592Srgrimes			default:
11661592Srgrimes				continue;	/* ignore command */
11671592Srgrimes			}
11681592Srgrimes		    }
11691592Srgrimes		}
11701592Srgrimes		*cs++ = c;
11711592Srgrimes		if (--n <= 0 || c == '\n')
11721592Srgrimes			break;
11731592Srgrimes	}
11741592Srgrimes	if (c == EOF && cs == s)
11751592Srgrimes		return (NULL);
11761592Srgrimes	*cs++ = '\0';
11771592Srgrimes	if (debug) {
11781592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
11791592Srgrimes			/* Don't syslog passwords */
11801592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
11811592Srgrimes		} else {
11821592Srgrimes			register char *cp;
11831592Srgrimes			register int len;
11841592Srgrimes
11851592Srgrimes			/* Don't syslog trailing CR-LF */
11861592Srgrimes			len = strlen(s);
11871592Srgrimes			cp = s + len - 1;
11881592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
11891592Srgrimes				--cp;
11901592Srgrimes				--len;
11911592Srgrimes			}
11921592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
11931592Srgrimes		}
11941592Srgrimes	}
11951592Srgrimes	return (s);
11961592Srgrimes}
11971592Srgrimes
11981592Srgrimesstatic void
11991592Srgrimestoolong(signo)
12001592Srgrimes	int signo;
12011592Srgrimes{
12021592Srgrimes
12031592Srgrimes	reply(421,
12041592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12051592Srgrimes	if (logging)
12061592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12071592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12081592Srgrimes	dologout(1);
12091592Srgrimes}
12101592Srgrimes
12111592Srgrimesstatic int
12121592Srgrimesyylex()
12131592Srgrimes{
12141592Srgrimes	static int cpos, state;
12151592Srgrimes	char *cp, *cp2;
12161592Srgrimes	struct tab *p;
12171592Srgrimes	int n;
12181592Srgrimes	char c;
12191592Srgrimes
12201592Srgrimes	for (;;) {
12211592Srgrimes		switch (state) {
12221592Srgrimes
12231592Srgrimes		case CMD:
12241592Srgrimes			(void) signal(SIGALRM, toolong);
12251592Srgrimes			(void) alarm((unsigned) timeout);
12261592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12271592Srgrimes				reply(221, "You could at least say goodbye.");
12281592Srgrimes				dologout(0);
12291592Srgrimes			}
12301592Srgrimes			(void) alarm(0);
12311592Srgrimes#ifdef SETPROCTITLE
123229574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12331592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12341592Srgrimes#endif /* SETPROCTITLE */
12351592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12361592Srgrimes				*cp++ = '\n';
12371592Srgrimes				*cp = '\0';
12381592Srgrimes			}
12391592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12401592Srgrimes				cpos = cp - cbuf;
12411592Srgrimes			if (cpos == 0)
12421592Srgrimes				cpos = 4;
12431592Srgrimes			c = cbuf[cpos];
12441592Srgrimes			cbuf[cpos] = '\0';
12451592Srgrimes			upper(cbuf);
12461592Srgrimes			p = lookup(cmdtab, cbuf);
12471592Srgrimes			cbuf[cpos] = c;
12483776Spst			if (p != 0) {
12491592Srgrimes				if (p->implemented == 0) {
12501592Srgrimes					nack(p->name);
12511592Srgrimes					longjmp(errcatch,0);
12521592Srgrimes					/* NOTREACHED */
12531592Srgrimes				}
12541592Srgrimes				state = p->state;
12551592Srgrimes				yylval.s = p->name;
12561592Srgrimes				return (p->token);
12571592Srgrimes			}
12581592Srgrimes			break;
12591592Srgrimes
12601592Srgrimes		case SITECMD:
12611592Srgrimes			if (cbuf[cpos] == ' ') {
12621592Srgrimes				cpos++;
12631592Srgrimes				return (SP);
12641592Srgrimes			}
12651592Srgrimes			cp = &cbuf[cpos];
12661592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12671592Srgrimes				cpos = cp2 - cbuf;
12681592Srgrimes			c = cbuf[cpos];
12691592Srgrimes			cbuf[cpos] = '\0';
12701592Srgrimes			upper(cp);
12711592Srgrimes			p = lookup(sitetab, cp);
12721592Srgrimes			cbuf[cpos] = c;
12733777Spst			if (guest == 0 && p != 0) {
12741592Srgrimes				if (p->implemented == 0) {
12751592Srgrimes					state = CMD;
12761592Srgrimes					nack(p->name);
12771592Srgrimes					longjmp(errcatch,0);
12781592Srgrimes					/* NOTREACHED */
12791592Srgrimes				}
12801592Srgrimes				state = p->state;
12811592Srgrimes				yylval.s = p->name;
12821592Srgrimes				return (p->token);
12831592Srgrimes			}
12841592Srgrimes			state = CMD;
12851592Srgrimes			break;
12861592Srgrimes
12871592Srgrimes		case OSTR:
12881592Srgrimes			if (cbuf[cpos] == '\n') {
12891592Srgrimes				state = CMD;
12901592Srgrimes				return (CRLF);
12911592Srgrimes			}
12921592Srgrimes			/* FALLTHROUGH */
12931592Srgrimes
12941592Srgrimes		case STR1:
12951592Srgrimes		case ZSTR1:
12961592Srgrimes		dostr1:
12971592Srgrimes			if (cbuf[cpos] == ' ') {
12981592Srgrimes				cpos++;
129951979Salfred				state = state == OSTR ? STR2 : state+1;
13001592Srgrimes				return (SP);
13011592Srgrimes			}
13021592Srgrimes			break;
13031592Srgrimes
13041592Srgrimes		case ZSTR2:
13051592Srgrimes			if (cbuf[cpos] == '\n') {
13061592Srgrimes				state = CMD;
13071592Srgrimes				return (CRLF);
13081592Srgrimes			}
13091592Srgrimes			/* FALLTHROUGH */
13101592Srgrimes
13111592Srgrimes		case STR2:
13121592Srgrimes			cp = &cbuf[cpos];
13131592Srgrimes			n = strlen(cp);
13141592Srgrimes			cpos += n - 1;
13151592Srgrimes			/*
13161592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13171592Srgrimes			 */
13181592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13191592Srgrimes				cbuf[cpos] = '\0';
13201592Srgrimes				yylval.s = copy(cp);
13211592Srgrimes				cbuf[cpos] = '\n';
13221592Srgrimes				state = ARGS;
13231592Srgrimes				return (STRING);
13241592Srgrimes			}
13251592Srgrimes			break;
13261592Srgrimes
13271592Srgrimes		case NSTR:
13281592Srgrimes			if (cbuf[cpos] == ' ') {
13291592Srgrimes				cpos++;
13301592Srgrimes				return (SP);
13311592Srgrimes			}
13321592Srgrimes			if (isdigit(cbuf[cpos])) {
13331592Srgrimes				cp = &cbuf[cpos];
13341592Srgrimes				while (isdigit(cbuf[++cpos]))
13351592Srgrimes					;
13361592Srgrimes				c = cbuf[cpos];
13371592Srgrimes				cbuf[cpos] = '\0';
13381592Srgrimes				yylval.i = atoi(cp);
13391592Srgrimes				cbuf[cpos] = c;
13401592Srgrimes				state = STR1;
13411592Srgrimes				return (NUMBER);
13421592Srgrimes			}
13431592Srgrimes			state = STR1;
13441592Srgrimes			goto dostr1;
13451592Srgrimes
13461592Srgrimes		case ARGS:
13471592Srgrimes			if (isdigit(cbuf[cpos])) {
13481592Srgrimes				cp = &cbuf[cpos];
13491592Srgrimes				while (isdigit(cbuf[++cpos]))
13501592Srgrimes					;
13511592Srgrimes				c = cbuf[cpos];
13521592Srgrimes				cbuf[cpos] = '\0';
13531592Srgrimes				yylval.i = atoi(cp);
13541592Srgrimes				cbuf[cpos] = c;
13551592Srgrimes				return (NUMBER);
13561592Srgrimes			}
135756668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
135856668Sshin			 && !isalnum(cbuf[cpos + 3])) {
135956668Sshin				cpos += 3;
136056668Sshin				return ALL;
136156668Sshin			}
13621592Srgrimes			switch (cbuf[cpos++]) {
13631592Srgrimes
13641592Srgrimes			case '\n':
13651592Srgrimes				state = CMD;
13661592Srgrimes				return (CRLF);
13671592Srgrimes
13681592Srgrimes			case ' ':
13691592Srgrimes				return (SP);
13701592Srgrimes
13711592Srgrimes			case ',':
13721592Srgrimes				return (COMMA);
13731592Srgrimes
13741592Srgrimes			case 'A':
13751592Srgrimes			case 'a':
13761592Srgrimes				return (A);
13771592Srgrimes
13781592Srgrimes			case 'B':
13791592Srgrimes			case 'b':
13801592Srgrimes				return (B);
13811592Srgrimes
13821592Srgrimes			case 'C':
13831592Srgrimes			case 'c':
13841592Srgrimes				return (C);
13851592Srgrimes
13861592Srgrimes			case 'E':
13871592Srgrimes			case 'e':
13881592Srgrimes				return (E);
13891592Srgrimes
13901592Srgrimes			case 'F':
13911592Srgrimes			case 'f':
13921592Srgrimes				return (F);
13931592Srgrimes
13941592Srgrimes			case 'I':
13951592Srgrimes			case 'i':
13961592Srgrimes				return (I);
13971592Srgrimes
13981592Srgrimes			case 'L':
13991592Srgrimes			case 'l':
14001592Srgrimes				return (L);
14011592Srgrimes
14021592Srgrimes			case 'N':
14031592Srgrimes			case 'n':
14041592Srgrimes				return (N);
14051592Srgrimes
14061592Srgrimes			case 'P':
14071592Srgrimes			case 'p':
14081592Srgrimes				return (P);
14091592Srgrimes
14101592Srgrimes			case 'R':
14111592Srgrimes			case 'r':
14121592Srgrimes				return (R);
14131592Srgrimes
14141592Srgrimes			case 'S':
14151592Srgrimes			case 's':
14161592Srgrimes				return (S);
14171592Srgrimes
14181592Srgrimes			case 'T':
14191592Srgrimes			case 't':
14201592Srgrimes				return (T);
14211592Srgrimes
14221592Srgrimes			}
14231592Srgrimes			break;
14241592Srgrimes
14251592Srgrimes		default:
14261592Srgrimes			fatal("Unknown state in scanner.");
14271592Srgrimes		}
14281592Srgrimes		yyerror((char *) 0);
14291592Srgrimes		state = CMD;
14301592Srgrimes		longjmp(errcatch,0);
14311592Srgrimes	}
14321592Srgrimes}
14331592Srgrimes
14341592Srgrimesvoid
14351592Srgrimesupper(s)
14361592Srgrimes	char *s;
14371592Srgrimes{
14381592Srgrimes	while (*s != '\0') {
14391592Srgrimes		if (islower(*s))
14401592Srgrimes			*s = toupper(*s);
14411592Srgrimes		s++;
14421592Srgrimes	}
14431592Srgrimes}
14441592Srgrimes
14451592Srgrimesstatic char *
14461592Srgrimescopy(s)
14471592Srgrimes	char *s;
14481592Srgrimes{
14491592Srgrimes	char *p;
14501592Srgrimes
14511592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14521592Srgrimes	if (p == NULL)
14531592Srgrimes		fatal("Ran out of memory.");
14541592Srgrimes	(void) strcpy(p, s);
14551592Srgrimes	return (p);
14561592Srgrimes}
14571592Srgrimes
14581592Srgrimesstatic void
14591592Srgrimeshelp(ctab, s)
14601592Srgrimes	struct tab *ctab;
14611592Srgrimes	char *s;
14621592Srgrimes{
14631592Srgrimes	struct tab *c;
14641592Srgrimes	int width, NCMDS;
14651592Srgrimes	char *type;
14661592Srgrimes
14671592Srgrimes	if (ctab == sitetab)
14681592Srgrimes		type = "SITE ";
14691592Srgrimes	else
14701592Srgrimes		type = "";
14711592Srgrimes	width = 0, NCMDS = 0;
14721592Srgrimes	for (c = ctab; c->name != NULL; c++) {
14731592Srgrimes		int len = strlen(c->name);
14741592Srgrimes
14751592Srgrimes		if (len > width)
14761592Srgrimes			width = len;
14771592Srgrimes		NCMDS++;
14781592Srgrimes	}
14791592Srgrimes	width = (width + 8) &~ 7;
14801592Srgrimes	if (s == 0) {
14811592Srgrimes		int i, j, w;
14821592Srgrimes		int columns, lines;
14831592Srgrimes
14841592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
14851592Srgrimes		    type, "(* =>'s unimplemented)");
14861592Srgrimes		columns = 76 / width;
14871592Srgrimes		if (columns == 0)
14881592Srgrimes			columns = 1;
14891592Srgrimes		lines = (NCMDS + columns - 1) / columns;
14901592Srgrimes		for (i = 0; i < lines; i++) {
14911592Srgrimes			printf("   ");
14921592Srgrimes			for (j = 0; j < columns; j++) {
14931592Srgrimes				c = ctab + j * lines + i;
14941592Srgrimes				printf("%s%c", c->name,
14951592Srgrimes					c->implemented ? ' ' : '*');
14961592Srgrimes				if (c + lines >= &ctab[NCMDS])
14971592Srgrimes					break;
14981592Srgrimes				w = strlen(c->name) + 1;
14991592Srgrimes				while (w < width) {
15001592Srgrimes					putchar(' ');
15011592Srgrimes					w++;
15021592Srgrimes				}
15031592Srgrimes			}
15041592Srgrimes			printf("\r\n");
15051592Srgrimes		}
15061592Srgrimes		(void) fflush(stdout);
15071592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15081592Srgrimes		return;
15091592Srgrimes	}
15101592Srgrimes	upper(s);
15111592Srgrimes	c = lookup(ctab, s);
15121592Srgrimes	if (c == (struct tab *)0) {
15131592Srgrimes		reply(502, "Unknown command %s.", s);
15141592Srgrimes		return;
15151592Srgrimes	}
15161592Srgrimes	if (c->implemented)
15171592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15181592Srgrimes	else
15191592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15201592Srgrimes		    c->name, c->help);
15211592Srgrimes}
15221592Srgrimes
15231592Srgrimesstatic void
15241592Srgrimessizecmd(filename)
15251592Srgrimes	char *filename;
15261592Srgrimes{
15271592Srgrimes	switch (type) {
15281592Srgrimes	case TYPE_L:
15291592Srgrimes	case TYPE_I: {
15301592Srgrimes		struct stat stbuf;
153163350Sdes		if (stat(filename, &stbuf) < 0)
153263350Sdes			perror_reply(550, filename);
153363350Sdes		else if (!S_ISREG(stbuf.st_mode))
15341592Srgrimes			reply(550, "%s: not a plain file.", filename);
15351592Srgrimes		else
15361592Srgrimes			reply(213, "%qu", stbuf.st_size);
15371592Srgrimes		break; }
15381592Srgrimes	case TYPE_A: {
15391592Srgrimes		FILE *fin;
15401592Srgrimes		int c;
15411592Srgrimes		off_t count;
15421592Srgrimes		struct stat stbuf;
15431592Srgrimes		fin = fopen(filename, "r");
15441592Srgrimes		if (fin == NULL) {
15451592Srgrimes			perror_reply(550, filename);
15461592Srgrimes			return;
15471592Srgrimes		}
154863350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
154963350Sdes			perror_reply(550, filename);
155063350Sdes			(void) fclose(fin);
155163350Sdes			return;
155263350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15531592Srgrimes			reply(550, "%s: not a plain file.", filename);
15541592Srgrimes			(void) fclose(fin);
15551592Srgrimes			return;
15561592Srgrimes		}
15571592Srgrimes
15581592Srgrimes		count = 0;
15591592Srgrimes		while((c=getc(fin)) != EOF) {
15601592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15611592Srgrimes				count++;
15621592Srgrimes			count++;
15631592Srgrimes		}
15641592Srgrimes		(void) fclose(fin);
15651592Srgrimes
15661592Srgrimes		reply(213, "%qd", count);
15671592Srgrimes		break; }
15681592Srgrimes	default:
15691592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15701592Srgrimes	}
15711592Srgrimes}
157256668Sshin
157356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
157456668Sshinstatic int
157556668Sshinport_check(pcmd)
157656668Sshin	const char *pcmd;
157756668Sshin{
157856668Sshin	if (his_addr.su_family == AF_INET) {
157956668Sshin		if (data_dest.su_family != AF_INET) {
158056668Sshin			usedefault = 1;
158156668Sshin			reply(500, "Invalid address rejected.");
158256668Sshin			return 1;
158356668Sshin		}
158456668Sshin		if (paranoid &&
158556668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
158656668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
158756668Sshin			    &his_addr.su_sin.sin_addr,
158856668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
158956668Sshin			usedefault = 1;
159056668Sshin			reply(500, "Illegal PORT range rejected.");
159156668Sshin		} else {
159256668Sshin			usedefault = 0;
159356668Sshin			if (pdata >= 0) {
159456668Sshin				(void) close(pdata);
159556668Sshin				pdata = -1;
159656668Sshin			}
159756668Sshin			reply(200, "%s command successful.", pcmd);
159856668Sshin		}
159956668Sshin		return 1;
160056668Sshin	}
160156668Sshin	return 0;
160256668Sshin}
160356668Sshin
160470102Sphkstatic int
160570102Sphkcheck_login1()
160670102Sphk{
160770102Sphk	if (logged_in)
160870102Sphk		return 1;
160970102Sphk	else {
161070102Sphk		reply(530, "Please login with USER and PASS.");
161170102Sphk		return 0;
161270102Sphk	}
161370102Sphk}
161470102Sphk
161556668Sshin#ifdef INET6
161656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
161756668Sshinstatic int
161856668Sshinport_check_v6(pcmd)
161956668Sshin	const char *pcmd;
162056668Sshin{
162156668Sshin	if (his_addr.su_family == AF_INET6) {
162256668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
162356668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
162456668Sshin			v4map_data_dest();
162556668Sshin		if (data_dest.su_family != AF_INET6) {
162656668Sshin			usedefault = 1;
162756668Sshin			reply(500, "Invalid address rejected.");
162856668Sshin			return 1;
162956668Sshin		}
163056668Sshin		if (paranoid &&
163156668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
163256668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
163356668Sshin			    &his_addr.su_sin6.sin6_addr,
163456668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
163556668Sshin			usedefault = 1;
163656668Sshin			reply(500, "Illegal PORT range rejected.");
163756668Sshin		} else {
163856668Sshin			usedefault = 0;
163956668Sshin			if (pdata >= 0) {
164056668Sshin				(void) close(pdata);
164156668Sshin				pdata = -1;
164256668Sshin			}
164356668Sshin			reply(200, "%s command successful.", pcmd);
164456668Sshin		}
164556668Sshin		return 1;
164656668Sshin	}
164756668Sshin	return 0;
164856668Sshin}
164956668Sshin
165056668Sshinstatic void
165156668Sshinv4map_data_dest()
165256668Sshin{
165356668Sshin	struct in_addr savedaddr;
165456668Sshin	int savedport;
165556668Sshin
165656668Sshin	if (data_dest.su_family != AF_INET) {
165756668Sshin		usedefault = 1;
165856668Sshin		reply(500, "Invalid address rejected.");
165956668Sshin		return;
166056668Sshin	}
166156668Sshin
166256668Sshin	savedaddr = data_dest.su_sin.sin_addr;
166356668Sshin	savedport = data_dest.su_port;
166456668Sshin
166556668Sshin	memset(&data_dest, 0, sizeof(data_dest));
166656668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
166756668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
166856668Sshin	data_dest.su_sin6.sin6_port = savedport;
166956668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
167056668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
167156668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
167256668Sshin}
167356668Sshin#endif
1674