ftpcmd.y revision 76096
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 76096 2001-04-28 07:55:19Z markm $";
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;
8476096Smarkmextern	int ftpdebug;
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
14175567Speter%type	<s> pathstring pathname password username
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		}
16975556Sgreen	| PASS CRLF
17075556Sgreen		{
17175556Sgreen			pass("");
17275556Sgreen		}
17317433Spst	| PORT check_login SP host_port CRLF
1741592Srgrimes		{
17556668Sshin			if (epsvall) {
17656668Sshin				reply(501, "no PORT allowed after EPSV ALL");
17756668Sshin				goto port_done;
17856668Sshin			}
17956668Sshin			if (!$2)
18056668Sshin				goto port_done;
18156668Sshin			if (port_check("PORT") == 1)
18256668Sshin				goto port_done;
18356668Sshin#ifdef INET6
18456668Sshin			if ((his_addr.su_family != AF_INET6 ||
18556668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
18656668Sshin				/* shoud never happen */
18756668Sshin				usedefault = 1;
18856668Sshin				reply(500, "Invalid address rejected.");
18956668Sshin				goto port_done;
19056668Sshin			}
19156668Sshin			port_check_v6("pcmd");
19256668Sshin#endif
19356668Sshin		port_done:
19456668Sshin		}
19556668Sshin	| LPRT check_login SP host_long_port CRLF
19656668Sshin		{
19756668Sshin			if (epsvall) {
19856668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
19956668Sshin				goto lprt_done;
20056668Sshin			}
20156668Sshin			if (!$2)
20256668Sshin				goto lprt_done;
20356668Sshin			if (port_check("LPRT") == 1)
20456668Sshin				goto lprt_done;
20556668Sshin#ifdef INET6
20656668Sshin			if (his_addr.su_family != AF_INET6) {
20756668Sshin				usedefault = 1;
20856668Sshin				reply(500, "Invalid address rejected.");
20956668Sshin				goto lprt_done;
21056668Sshin			}
21156668Sshin			if (port_check_v6("LPRT") == 1)
21256668Sshin				goto lprt_done;
21356668Sshin#endif
21456668Sshin		lprt_done:
21556668Sshin		}
21656668Sshin	| EPRT check_login SP STRING CRLF
21756668Sshin		{
21856668Sshin			char delim;
21956668Sshin			char *tmp = NULL;
22056668Sshin			char *p, *q;
22156668Sshin			char *result[3];
22256668Sshin			struct addrinfo hints;
22356668Sshin			struct addrinfo *res;
22456668Sshin			int i;
22556668Sshin
22656668Sshin			if (epsvall) {
22756668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
22856668Sshin				goto eprt_done;
22956668Sshin			}
23056668Sshin			if (!$2)
23156668Sshin				goto eprt_done;
23256668Sshin
23356668Sshin			memset(&data_dest, 0, sizeof(data_dest));
23456668Sshin			tmp = strdup($4);
23576096Smarkm			if (ftpdebug)
23656668Sshin				syslog(LOG_DEBUG, "%s", tmp);
23756668Sshin			if (!tmp) {
23876096Smarkm				fatalerror("not enough core");
23956668Sshin				/*NOTREACHED*/
24056668Sshin			}
24156668Sshin			p = tmp;
24256668Sshin			delim = p[0];
24356668Sshin			p++;
24456668Sshin			memset(result, 0, sizeof(result));
24556668Sshin			for (i = 0; i < 3; i++) {
24656668Sshin				q = strchr(p, delim);
24756668Sshin				if (!q || *q != delim) {
24856668Sshin		parsefail:
24956668Sshin					reply(500,
25056668Sshin						"Invalid argument, rejected.");
25156668Sshin					if (tmp)
25256668Sshin						free(tmp);
25317433Spst					usedefault = 1;
25456668Sshin					goto eprt_done;
25517433Spst				}
25656668Sshin				*q++ = '\0';
25756668Sshin				result[i] = p;
25876096Smarkm				if (ftpdebug)
25956668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
26056668Sshin				p = q;
2611592Srgrimes			}
26256668Sshin
26356668Sshin			/* some more sanity check */
26456668Sshin			p = result[0];
26556668Sshin			while (*p) {
26656668Sshin				if (!isdigit(*p))
26756668Sshin					goto parsefail;
26856668Sshin				p++;
26956668Sshin			}
27056668Sshin			p = result[2];
27156668Sshin			while (*p) {
27256668Sshin				if (!isdigit(*p))
27356668Sshin					goto parsefail;
27456668Sshin				p++;
27556668Sshin			}
27656668Sshin
27756668Sshin			/* grab address */
27856668Sshin			memset(&hints, 0, sizeof(hints));
27956668Sshin			if (atoi(result[0]) == 1)
28056668Sshin				hints.ai_family = PF_INET;
28156668Sshin#ifdef INET6
28256668Sshin			else if (atoi(result[0]) == 2)
28356668Sshin				hints.ai_family = PF_INET6;
28456668Sshin#endif
28556668Sshin			else
28656668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
28756668Sshin			hints.ai_socktype = SOCK_STREAM;
28856668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
28956668Sshin			if (i)
29056668Sshin				goto parsefail;
29156668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
29256668Sshin#ifdef INET6
29356668Sshin			if (his_addr.su_family == AF_INET6
29456668Sshin			    && data_dest.su_family == AF_INET6) {
29556668Sshin				/* XXX more sanity checks! */
29656668Sshin				data_dest.su_sin6.sin6_scope_id =
29756668Sshin					his_addr.su_sin6.sin6_scope_id;
29856668Sshin			}
29956668Sshin#endif
30056668Sshin			free(tmp);
30156668Sshin			tmp = NULL;
30256668Sshin
30356668Sshin			if (port_check("EPRT") == 1)
30456668Sshin				goto eprt_done;
30556668Sshin#ifdef INET6
30656668Sshin			if (his_addr.su_family != AF_INET6) {
30756668Sshin				usedefault = 1;
30856668Sshin				reply(500, "Invalid address rejected.");
30956668Sshin				goto eprt_done;
31056668Sshin			}
31156668Sshin			if (port_check_v6("EPRT") == 1)
31256668Sshin				goto eprt_done;
31356668Sshin#endif
31456668Sshin		eprt_done:;
3151592Srgrimes		}
31617433Spst	| PASV check_login CRLF
3171592Srgrimes		{
31856668Sshin			if (epsvall)
31956668Sshin				reply(501, "no PASV allowed after EPSV ALL");
32056668Sshin			else if ($2)
32117433Spst				passive();
3221592Srgrimes		}
32356668Sshin	| LPSV check_login CRLF
32456668Sshin		{
32556668Sshin			if (epsvall)
32656668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
32756668Sshin			else if ($2)
32856668Sshin				long_passive("LPSV", PF_UNSPEC);
32956668Sshin		}
33070102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
33156668Sshin		{
33256668Sshin			if ($2) {
33356668Sshin				int pf;
33456668Sshin				switch ($4) {
33556668Sshin				case 1:
33656668Sshin					pf = PF_INET;
33756668Sshin					break;
33856668Sshin#ifdef INET6
33956668Sshin				case 2:
34056668Sshin					pf = PF_INET6;
34156668Sshin					break;
34256668Sshin#endif
34356668Sshin				default:
34456668Sshin					pf = -1;	/*junk value*/
34556668Sshin					break;
34656668Sshin				}
34756668Sshin				long_passive("EPSV", pf);
34856668Sshin			}
34956668Sshin		}
35070102Sphk	| EPSV check_login_epsv SP ALL CRLF
35156668Sshin		{
35256668Sshin			if ($2) {
35356668Sshin				reply(200,
35456668Sshin				      "EPSV ALL command successful.");
35556668Sshin				epsvall++;
35656668Sshin			}
35756668Sshin		}
35870102Sphk	| EPSV check_login_epsv CRLF
35956668Sshin		{
36056668Sshin			if ($2)
36156668Sshin				long_passive("EPSV", PF_UNSPEC);
36256668Sshin		}
36371278Sjedgar	| TYPE check_login SP type_code CRLF
3641592Srgrimes		{
36571278Sjedgar			if ($2) {
36671278Sjedgar				switch (cmd_type) {
3671592Srgrimes
36871278Sjedgar				case TYPE_A:
36971278Sjedgar					if (cmd_form == FORM_N) {
37071278Sjedgar						reply(200, "Type set to A.");
37171278Sjedgar						type = cmd_type;
37271278Sjedgar						form = cmd_form;
37371278Sjedgar					} else
37471278Sjedgar						reply(504, "Form must be N.");
37571278Sjedgar					break;
3761592Srgrimes
37771278Sjedgar				case TYPE_E:
37871278Sjedgar					reply(504, "Type E not implemented.");
37971278Sjedgar					break;
3801592Srgrimes
38171278Sjedgar				case TYPE_I:
38271278Sjedgar					reply(200, "Type set to I.");
38371278Sjedgar					type = cmd_type;
38471278Sjedgar					break;
3851592Srgrimes
38671278Sjedgar				case TYPE_L:
3871592Srgrimes#if NBBY == 8
38871278Sjedgar					if (cmd_bytesz == 8) {
38971278Sjedgar						reply(200,
39071278Sjedgar						    "Type set to L (byte size 8).");
39171278Sjedgar						type = cmd_type;
39271278Sjedgar					} else
39371278Sjedgar						reply(504, "Byte size must be 8.");
3941592Srgrimes#else /* NBBY == 8 */
39571278Sjedgar					UNIMPLEMENTED for NBBY != 8
3961592Srgrimes#endif /* NBBY == 8 */
39771278Sjedgar				}
3981592Srgrimes			}
3991592Srgrimes		}
40071278Sjedgar	| STRU check_login SP struct_code CRLF
4011592Srgrimes		{
40271278Sjedgar			if ($2) {
40371278Sjedgar				switch ($4) {
4041592Srgrimes
40571278Sjedgar				case STRU_F:
40671278Sjedgar					reply(200, "STRU F ok.");
40771278Sjedgar					break;
4081592Srgrimes
40971278Sjedgar				default:
41071278Sjedgar					reply(504, "Unimplemented STRU type.");
41171278Sjedgar				}
4121592Srgrimes			}
4131592Srgrimes		}
41471278Sjedgar	| MODE check_login SP mode_code CRLF
4151592Srgrimes		{
41671278Sjedgar			if ($2) {
41771278Sjedgar				switch ($4) {
4181592Srgrimes
41971278Sjedgar				case MODE_S:
42071278Sjedgar					reply(200, "MODE S ok.");
42171278Sjedgar					break;
42271278Sjedgar
42371278Sjedgar				default:
42471278Sjedgar					reply(502, "Unimplemented MODE type.");
42571278Sjedgar				}
4261592Srgrimes			}
4271592Srgrimes		}
42871278Sjedgar	| ALLO check_login SP NUMBER CRLF
4291592Srgrimes		{
43071278Sjedgar			if ($2) {
43171278Sjedgar				reply(202, "ALLO command ignored.");
43271278Sjedgar			}
4331592Srgrimes		}
43471278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4351592Srgrimes		{
43671278Sjedgar			if ($2) {
43771278Sjedgar				reply(202, "ALLO command ignored.");
43871278Sjedgar			}
4391592Srgrimes		}
4401592Srgrimes	| RETR check_login SP pathname CRLF
4411592Srgrimes		{
4421592Srgrimes			if ($2 && $4 != NULL)
4431592Srgrimes				retrieve((char *) 0, $4);
4441592Srgrimes			if ($4 != NULL)
4451592Srgrimes				free($4);
4461592Srgrimes		}
44770102Sphk	| STOR check_login_ro SP pathname CRLF
4481592Srgrimes		{
4491592Srgrimes			if ($2 && $4 != NULL)
4501592Srgrimes				store($4, "w", 0);
4511592Srgrimes			if ($4 != NULL)
4521592Srgrimes				free($4);
4531592Srgrimes		}
45470102Sphk	| APPE check_login_ro SP pathname CRLF
4551592Srgrimes		{
4561592Srgrimes			if ($2 && $4 != NULL)
4571592Srgrimes				store($4, "a", 0);
4581592Srgrimes			if ($4 != NULL)
4591592Srgrimes				free($4);
4601592Srgrimes		}
4611592Srgrimes	| NLST check_login CRLF
4621592Srgrimes		{
4631592Srgrimes			if ($2)
4641592Srgrimes				send_file_list(".");
4651592Srgrimes		}
4661592Srgrimes	| NLST check_login SP STRING CRLF
4671592Srgrimes		{
4681592Srgrimes			if ($2 && $4 != NULL)
4691592Srgrimes				send_file_list($4);
4701592Srgrimes			if ($4 != NULL)
4711592Srgrimes				free($4);
4721592Srgrimes		}
4731592Srgrimes	| LIST check_login CRLF
4741592Srgrimes		{
4751592Srgrimes			if ($2)
4761592Srgrimes				retrieve("/bin/ls -lgA", "");
4771592Srgrimes		}
47875567Speter	| LIST check_login SP pathstring CRLF
4791592Srgrimes		{
4801592Srgrimes			if ($2 && $4 != NULL)
4811592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4821592Srgrimes			if ($4 != NULL)
4831592Srgrimes				free($4);
4841592Srgrimes		}
4851592Srgrimes	| STAT check_login SP pathname CRLF
4861592Srgrimes		{
4871592Srgrimes			if ($2 && $4 != NULL)
4881592Srgrimes				statfilecmd($4);
4891592Srgrimes			if ($4 != NULL)
4901592Srgrimes				free($4);
4911592Srgrimes		}
49271278Sjedgar	| STAT check_login CRLF
4931592Srgrimes		{
49471278Sjedgar			if ($2) {
49571278Sjedgar				statcmd();
49671278Sjedgar			}
4971592Srgrimes		}
49870102Sphk	| DELE check_login_ro SP pathname CRLF
4991592Srgrimes		{
5001592Srgrimes			if ($2 && $4 != NULL)
5011592Srgrimes				delete($4);
5021592Srgrimes			if ($4 != NULL)
5031592Srgrimes				free($4);
5041592Srgrimes		}
50570102Sphk	| RNTO check_login_ro SP pathname CRLF
5061592Srgrimes		{
50717433Spst			if ($2) {
50817433Spst				if (fromname) {
50917433Spst					renamecmd(fromname, $4);
51017433Spst					free(fromname);
51117433Spst					fromname = (char *) 0;
51217433Spst				} else {
51317433Spst					reply(503, "Bad sequence of commands.");
51417433Spst				}
5151592Srgrimes			}
51617433Spst			free($4);
5171592Srgrimes		}
51871278Sjedgar	| ABOR check_login CRLF
5191592Srgrimes		{
52071278Sjedgar			if ($2)
52171278Sjedgar				reply(225, "ABOR command successful.");
5221592Srgrimes		}
5231592Srgrimes	| CWD check_login CRLF
5241592Srgrimes		{
52569234Sdanny			if ($2) {
52669234Sdanny				if (guest)
52769234Sdanny					cwd("/");
52869234Sdanny				else
52969234Sdanny					cwd(pw->pw_dir);
53069234Sdanny			}
5311592Srgrimes		}
5321592Srgrimes	| CWD check_login SP pathname CRLF
5331592Srgrimes		{
5341592Srgrimes			if ($2 && $4 != NULL)
5351592Srgrimes				cwd($4);
5361592Srgrimes			if ($4 != NULL)
5371592Srgrimes				free($4);
5381592Srgrimes		}
5391592Srgrimes	| HELP CRLF
5401592Srgrimes		{
5411592Srgrimes			help(cmdtab, (char *) 0);
5421592Srgrimes		}
5431592Srgrimes	| HELP SP STRING CRLF
5441592Srgrimes		{
5451592Srgrimes			char *cp = $3;
5461592Srgrimes
5471592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5481592Srgrimes				cp = $3 + 4;
5491592Srgrimes				if (*cp == ' ')
5501592Srgrimes					cp++;
5511592Srgrimes				if (*cp)
5521592Srgrimes					help(sitetab, cp);
5531592Srgrimes				else
5541592Srgrimes					help(sitetab, (char *) 0);
5551592Srgrimes			} else
5561592Srgrimes				help(cmdtab, $3);
5571592Srgrimes		}
5581592Srgrimes	| NOOP CRLF
5591592Srgrimes		{
5601592Srgrimes			reply(200, "NOOP command successful.");
5611592Srgrimes		}
56270102Sphk	| MKD check_login_ro SP pathname CRLF
5631592Srgrimes		{
5641592Srgrimes			if ($2 && $4 != NULL)
5651592Srgrimes				makedir($4);
5661592Srgrimes			if ($4 != NULL)
5671592Srgrimes				free($4);
5681592Srgrimes		}
56970102Sphk	| RMD check_login_ro SP pathname CRLF
5701592Srgrimes		{
5711592Srgrimes			if ($2 && $4 != NULL)
5721592Srgrimes				removedir($4);
5731592Srgrimes			if ($4 != NULL)
5741592Srgrimes				free($4);
5751592Srgrimes		}
5761592Srgrimes	| PWD check_login CRLF
5771592Srgrimes		{
5781592Srgrimes			if ($2)
5791592Srgrimes				pwd();
5801592Srgrimes		}
5811592Srgrimes	| CDUP check_login CRLF
5821592Srgrimes		{
5831592Srgrimes			if ($2)
5841592Srgrimes				cwd("..");
5851592Srgrimes		}
5861592Srgrimes	| SITE SP HELP CRLF
5871592Srgrimes		{
5881592Srgrimes			help(sitetab, (char *) 0);
5891592Srgrimes		}
5901592Srgrimes	| SITE SP HELP SP STRING CRLF
5911592Srgrimes		{
5921592Srgrimes			help(sitetab, $5);
5931592Srgrimes		}
59475535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
59575535Sphk		{
59675535Sphk			char p[64], *q;
59775535Sphk
59875535Sphk			if ($4) {
59975535Sphk				q = MD5File($6, p);
60075535Sphk				if (q != NULL)
60175535Sphk					reply(200, "MD5(%s) = %s", $6, p);
60275535Sphk				else
60375535Sphk					perror_reply(550, $6);
60475535Sphk			}
60575535Sphk		}
6061592Srgrimes	| SITE SP UMASK check_login CRLF
6071592Srgrimes		{
6081592Srgrimes			int oldmask;
6091592Srgrimes
6101592Srgrimes			if ($4) {
6111592Srgrimes				oldmask = umask(0);
6121592Srgrimes				(void) umask(oldmask);
6131592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6141592Srgrimes			}
6151592Srgrimes		}
6161592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6171592Srgrimes		{
6181592Srgrimes			int oldmask;
6191592Srgrimes
6201592Srgrimes			if ($4) {
6211592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6221592Srgrimes					reply(501, "Bad UMASK value");
6231592Srgrimes				} else {
6241592Srgrimes					oldmask = umask($6);
6251592Srgrimes					reply(200,
6261592Srgrimes					    "UMASK set to %03o (was %03o)",
6271592Srgrimes					    $6, oldmask);
6281592Srgrimes				}
6291592Srgrimes			}
6301592Srgrimes		}
63170102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6321592Srgrimes		{
6331592Srgrimes			if ($4 && ($8 != NULL)) {
6341592Srgrimes				if ($6 > 0777)
6351592Srgrimes					reply(501,
6361592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6371592Srgrimes				else if (chmod($8, $6) < 0)
6381592Srgrimes					perror_reply(550, $8);
6391592Srgrimes				else
6401592Srgrimes					reply(200, "CHMOD command successful.");
6411592Srgrimes			}
6421592Srgrimes			if ($8 != NULL)
6431592Srgrimes				free($8);
6441592Srgrimes		}
64571278Sjedgar	| SITE SP check_login IDLE CRLF
6461592Srgrimes		{
64771278Sjedgar			if ($3)
64871278Sjedgar				reply(200,
64971278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
65071278Sjedgar				    timeout, maxtimeout);
6511592Srgrimes		}
65271278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6531592Srgrimes		{
65471278Sjedgar			if ($3) {
65571278Sjedgar				if ($6 < 30 || $6 > maxtimeout) {
65671278Sjedgar					reply(501,
65771278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
65871278Sjedgar					    maxtimeout);
65971278Sjedgar				} else {
66071278Sjedgar					timeout = $6;
66171278Sjedgar					(void) alarm((unsigned) timeout);
66271278Sjedgar					reply(200,
66371278Sjedgar					    "Maximum IDLE time set to %d seconds",
66471278Sjedgar					    timeout);
66571278Sjedgar				}
6661592Srgrimes			}
6671592Srgrimes		}
66870102Sphk	| STOU check_login_ro SP pathname CRLF
6691592Srgrimes		{
6701592Srgrimes			if ($2 && $4 != NULL)
6711592Srgrimes				store($4, "w", 1);
6721592Srgrimes			if ($4 != NULL)
6731592Srgrimes				free($4);
6741592Srgrimes		}
67571278Sjedgar	| SYST check_login CRLF
6761592Srgrimes		{
67771278Sjedgar			if ($2)
6781592Srgrimes#ifdef unix
6791592Srgrimes#ifdef BSD
6801592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6811592Srgrimes				NBBY, BSD);
6821592Srgrimes#else /* BSD */
6831592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6841592Srgrimes#endif /* BSD */
6851592Srgrimes#else /* unix */
6861592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6871592Srgrimes#endif /* unix */
6881592Srgrimes		}
6891592Srgrimes
6901592Srgrimes		/*
6911592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6921592Srgrimes		 * it will be in the updated RFC.
6931592Srgrimes		 *
6941592Srgrimes		 * Return size of file in a format suitable for
6951592Srgrimes		 * using with RESTART (we just count bytes).
6961592Srgrimes		 */
6971592Srgrimes	| SIZE check_login SP pathname CRLF
6981592Srgrimes		{
6991592Srgrimes			if ($2 && $4 != NULL)
7001592Srgrimes				sizecmd($4);
7011592Srgrimes			if ($4 != NULL)
7021592Srgrimes				free($4);
7031592Srgrimes		}
7041592Srgrimes
7051592Srgrimes		/*
7061592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7071592Srgrimes		 * it will be in the updated RFC.
7081592Srgrimes		 *
7091592Srgrimes		 * Return modification time of file as an ISO 3307
7101592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7111592Srgrimes		 * where xxx is the fractional second (of any precision,
7121592Srgrimes		 * not necessarily 3 digits)
7131592Srgrimes		 */
7141592Srgrimes	| MDTM check_login SP pathname CRLF
7151592Srgrimes		{
7161592Srgrimes			if ($2 && $4 != NULL) {
7171592Srgrimes				struct stat stbuf;
7181592Srgrimes				if (stat($4, &stbuf) < 0)
7191592Srgrimes					reply(550, "%s: %s",
7201592Srgrimes					    $4, strerror(errno));
7211592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7221592Srgrimes					reply(550, "%s: not a plain file.", $4);
7231592Srgrimes				} else {
7241592Srgrimes					struct tm *t;
7251592Srgrimes					t = gmtime(&stbuf.st_mtime);
7261592Srgrimes					reply(213,
72717435Spst					    "%04d%02d%02d%02d%02d%02d",
72817435Spst					    1900 + t->tm_year,
72917435Spst					    t->tm_mon+1, t->tm_mday,
7301592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7311592Srgrimes				}
7321592Srgrimes			}
7331592Srgrimes			if ($4 != NULL)
7341592Srgrimes				free($4);
7351592Srgrimes		}
7361592Srgrimes	| QUIT CRLF
7371592Srgrimes		{
7381592Srgrimes			reply(221, "Goodbye.");
7391592Srgrimes			dologout(0);
7401592Srgrimes		}
7411592Srgrimes	| error CRLF
7421592Srgrimes		{
7431592Srgrimes			yyerrok;
7441592Srgrimes		}
7451592Srgrimes	;
7461592Srgrimesrcmd
74770102Sphk	: RNFR check_login_ro SP pathname CRLF
7481592Srgrimes		{
7491592Srgrimes			char *renamefrom();
7501592Srgrimes
7511592Srgrimes			restart_point = (off_t) 0;
7521592Srgrimes			if ($2 && $4) {
7531592Srgrimes				fromname = renamefrom($4);
7541592Srgrimes				if (fromname == (char *) 0 && $4) {
7551592Srgrimes					free($4);
7561592Srgrimes				}
7571592Srgrimes			}
7581592Srgrimes		}
75971278Sjedgar	| REST check_login SP byte_size CRLF
7601592Srgrimes		{
76171278Sjedgar			if ($2) {
76271278Sjedgar				fromname = (char *) 0;
76371278Sjedgar				restart_point = $4;  /* XXX $4 is only "int" */
76471278Sjedgar				reply(350, "Restarting at %qd. %s",
76571278Sjedgar				    restart_point,
76671278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
76771278Sjedgar			}
7681592Srgrimes		}
7691592Srgrimes	;
7701592Srgrimes
7711592Srgrimesusername
7721592Srgrimes	: STRING
7731592Srgrimes	;
7741592Srgrimes
7751592Srgrimespassword
7761592Srgrimes	: /* empty */
7771592Srgrimes		{
7781592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7791592Srgrimes		}
7801592Srgrimes	| STRING
7811592Srgrimes	;
7821592Srgrimes
7831592Srgrimesbyte_size
7841592Srgrimes	: NUMBER
7851592Srgrimes	;
7861592Srgrimes
7871592Srgrimeshost_port
7881592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7891592Srgrimes		NUMBER COMMA NUMBER
7901592Srgrimes		{
7911592Srgrimes			char *a, *p;
7921592Srgrimes
79356668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
79456668Sshin			data_dest.su_family = AF_INET;
79556668Sshin			p = (char *)&data_dest.su_sin.sin_port;
79617435Spst			p[0] = $9; p[1] = $11;
79756668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
7981592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
7991592Srgrimes		}
8001592Srgrimes	;
8011592Srgrimes
80256668Sshinhost_long_port
80356668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80456668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80556668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80656668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80756668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80856668Sshin		NUMBER
80956668Sshin		{
81056668Sshin			char *a, *p;
81156668Sshin
81256668Sshin			memset(&data_dest, 0, sizeof(data_dest));
81356668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
81456668Sshin			data_dest.su_family = AF_INET6;
81556668Sshin			p = (char *)&data_dest.su_port;
81656668Sshin			p[0] = $39; p[1] = $41;
81756668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
81856668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
81956668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
82056668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
82156668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
82256668Sshin			if (his_addr.su_family == AF_INET6) {
82356668Sshin				/* XXX more sanity checks! */
82456668Sshin				data_dest.su_sin6.sin6_scope_id =
82556668Sshin					his_addr.su_sin6.sin6_scope_id;
82656668Sshin			}
82756668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
82856668Sshin				memset(&data_dest, 0, sizeof(data_dest));
82956668Sshin		}
83056668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83156668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83256668Sshin		NUMBER
83356668Sshin		{
83456668Sshin			char *a, *p;
83556668Sshin
83656668Sshin			memset(&data_dest, 0, sizeof(data_dest));
83756668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
83856668Sshin			data_dest.su_family = AF_INET;
83956668Sshin			p = (char *)&data_dest.su_port;
84056668Sshin			p[0] = $15; p[1] = $17;
84156668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
84256668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
84356668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
84456668Sshin				memset(&data_dest, 0, sizeof(data_dest));
84556668Sshin		}
84656668Sshin	;
84756668Sshin
8481592Srgrimesform_code
8491592Srgrimes	: N
8501592Srgrimes		{
8511592Srgrimes			$$ = FORM_N;
8521592Srgrimes		}
8531592Srgrimes	| T
8541592Srgrimes		{
8551592Srgrimes			$$ = FORM_T;
8561592Srgrimes		}
8571592Srgrimes	| C
8581592Srgrimes		{
8591592Srgrimes			$$ = FORM_C;
8601592Srgrimes		}
8611592Srgrimes	;
8621592Srgrimes
8631592Srgrimestype_code
8641592Srgrimes	: A
8651592Srgrimes		{
8661592Srgrimes			cmd_type = TYPE_A;
8671592Srgrimes			cmd_form = FORM_N;
8681592Srgrimes		}
8691592Srgrimes	| A SP form_code
8701592Srgrimes		{
8711592Srgrimes			cmd_type = TYPE_A;
8721592Srgrimes			cmd_form = $3;
8731592Srgrimes		}
8741592Srgrimes	| E
8751592Srgrimes		{
8761592Srgrimes			cmd_type = TYPE_E;
8771592Srgrimes			cmd_form = FORM_N;
8781592Srgrimes		}
8791592Srgrimes	| E SP form_code
8801592Srgrimes		{
8811592Srgrimes			cmd_type = TYPE_E;
8821592Srgrimes			cmd_form = $3;
8831592Srgrimes		}
8841592Srgrimes	| I
8851592Srgrimes		{
8861592Srgrimes			cmd_type = TYPE_I;
8871592Srgrimes		}
8881592Srgrimes	| L
8891592Srgrimes		{
8901592Srgrimes			cmd_type = TYPE_L;
8911592Srgrimes			cmd_bytesz = NBBY;
8921592Srgrimes		}
8931592Srgrimes	| L SP byte_size
8941592Srgrimes		{
8951592Srgrimes			cmd_type = TYPE_L;
8961592Srgrimes			cmd_bytesz = $3;
8971592Srgrimes		}
8981592Srgrimes		/* this is for a bug in the BBN ftp */
8991592Srgrimes	| L byte_size
9001592Srgrimes		{
9011592Srgrimes			cmd_type = TYPE_L;
9021592Srgrimes			cmd_bytesz = $2;
9031592Srgrimes		}
9041592Srgrimes	;
9051592Srgrimes
9061592Srgrimesstruct_code
9071592Srgrimes	: F
9081592Srgrimes		{
9091592Srgrimes			$$ = STRU_F;
9101592Srgrimes		}
9111592Srgrimes	| R
9121592Srgrimes		{
9131592Srgrimes			$$ = STRU_R;
9141592Srgrimes		}
9151592Srgrimes	| P
9161592Srgrimes		{
9171592Srgrimes			$$ = STRU_P;
9181592Srgrimes		}
9191592Srgrimes	;
9201592Srgrimes
9211592Srgrimesmode_code
9221592Srgrimes	: S
9231592Srgrimes		{
9241592Srgrimes			$$ = MODE_S;
9251592Srgrimes		}
9261592Srgrimes	| B
9271592Srgrimes		{
9281592Srgrimes			$$ = MODE_B;
9291592Srgrimes		}
9301592Srgrimes	| C
9311592Srgrimes		{
9321592Srgrimes			$$ = MODE_C;
9331592Srgrimes		}
9341592Srgrimes	;
9351592Srgrimes
9361592Srgrimespathname
9371592Srgrimes	: pathstring
9381592Srgrimes		{
9391592Srgrimes			/*
9401592Srgrimes			 * Problem: this production is used for all pathname
9411592Srgrimes			 * processing, but only gives a 550 error reply.
9421592Srgrimes			 * This is a valid reply in some cases but not in others.
9431592Srgrimes			 */
94475567Speter			if (logged_in && $1) {
9451592Srgrimes				glob_t gl;
9461592Srgrimes				int flags =
9471592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9481592Srgrimes
9491592Srgrimes				memset(&gl, 0, sizeof(gl));
95075560Sjedgar				flags |= GLOB_MAXPATH;
95175560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9521592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9531592Srgrimes				    gl.gl_pathc == 0) {
9541592Srgrimes					reply(550, "not found");
9551592Srgrimes					$$ = NULL;
95675567Speter				} else if (gl.gl_pathc > 1) {
95775567Speter					reply(550, "ambiguous");
95875567Speter					$$ = NULL;
9591592Srgrimes				} else {
9601592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9611592Srgrimes				}
9621592Srgrimes				globfree(&gl);
9631592Srgrimes				free($1);
9641592Srgrimes			} else
9651592Srgrimes				$$ = $1;
9661592Srgrimes		}
9671592Srgrimes	;
9681592Srgrimes
9691592Srgrimespathstring
9701592Srgrimes	: STRING
9711592Srgrimes	;
9721592Srgrimes
9731592Srgrimesoctal_number
9741592Srgrimes	: NUMBER
9751592Srgrimes		{
9761592Srgrimes			int ret, dec, multby, digit;
9771592Srgrimes
9781592Srgrimes			/*
9791592Srgrimes			 * Convert a number that was read as decimal number
9801592Srgrimes			 * to what it would be if it had been read as octal.
9811592Srgrimes			 */
9821592Srgrimes			dec = $1;
9831592Srgrimes			multby = 1;
9841592Srgrimes			ret = 0;
9851592Srgrimes			while (dec) {
9861592Srgrimes				digit = dec%10;
9871592Srgrimes				if (digit > 7) {
9881592Srgrimes					ret = -1;
9891592Srgrimes					break;
9901592Srgrimes				}
9911592Srgrimes				ret += digit * multby;
9921592Srgrimes				multby *= 8;
9931592Srgrimes				dec /= 10;
9941592Srgrimes			}
9951592Srgrimes			$$ = ret;
9961592Srgrimes		}
9971592Srgrimes	;
9981592Srgrimes
9991592Srgrimes
10001592Srgrimescheck_login
10011592Srgrimes	: /* empty */
10021592Srgrimes		{
100370102Sphk		$$ = check_login1();
10041592Srgrimes		}
10051592Srgrimes	;
10061592Srgrimes
100770102Sphkcheck_login_epsv
100870102Sphk	: /* empty */
100970102Sphk		{
101070102Sphk		if (noepsv) {
101170102Sphk			reply(500, "EPSV command disabled");
101270102Sphk			$$ = 0;
101370102Sphk		}
101470102Sphk		else
101570102Sphk			$$ = check_login1();
101670102Sphk		}
101770102Sphk	;
101870102Sphk
101970102Sphkcheck_login_ro
102070102Sphk	: /* empty */
102170102Sphk		{
102270102Sphk		if (readonly) {
102372710Sdes			reply(550, "Permission denied.");
102470102Sphk			$$ = 0;
102570102Sphk		}
102670102Sphk		else
102770102Sphk			$$ = check_login1();
102870102Sphk		}
102970102Sphk	;
103070102Sphk
10311592Srgrimes%%
10321592Srgrimes
10331592Srgrimesextern jmp_buf errcatch;
10341592Srgrimes
10351592Srgrimes#define	CMD	0	/* beginning of command */
10361592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10371592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10381592Srgrimes#define	STR2	3	/* expect STRING */
10391592Srgrimes#define	OSTR	4	/* optional SP then STRING */
104075556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10411592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10421592Srgrimes#define	SITECMD	7	/* SITE command */
10431592Srgrimes#define	NSTR	8	/* Number followed by a string */
10441592Srgrimes
104575560Sjedgar#define	MAXGLOBARGS	1000
104675560Sjedgar
10471592Srgrimesstruct tab {
10481592Srgrimes	char	*name;
10491592Srgrimes	short	token;
10501592Srgrimes	short	state;
10511592Srgrimes	short	implemented;	/* 1 if command is implemented */
10521592Srgrimes	char	*help;
10531592Srgrimes};
10541592Srgrimes
10551592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10561592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
105775556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10581592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10591592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10601592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10611592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10621592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
106356668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
106456668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10651592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
106656668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
106756668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10681592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10691592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10701592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10711592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10721592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10731592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10741592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10751592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10761592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10771592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10781592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10791592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10801592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10811592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10821592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10831592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10841592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10851592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10861592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10871592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10881592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10891592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10901592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10911592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10921592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10931592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10941592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10951592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
10961592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
10971592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
10981592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
10991592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11001592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11011592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11021592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11031592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11041592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11051592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11061592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11071592Srgrimes	{ NULL,   0,    0,    0,	0 }
11081592Srgrimes};
11091592Srgrimes
11101592Srgrimesstruct tab sitetab[] = {
111175535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11121592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11131592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11141592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11151592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11161592Srgrimes	{ NULL,   0,    0,    0,	0 }
11171592Srgrimes};
11181592Srgrimes
11191592Srgrimesstatic char	*copy __P((char *));
11201592Srgrimesstatic void	 help __P((struct tab *, char *));
11211592Srgrimesstatic struct tab *
11221592Srgrimes		 lookup __P((struct tab *, char *));
112356668Sshinstatic int	 port_check __P((const char *));
112456668Sshinstatic int	 port_check_v6 __P((const char *));
11251592Srgrimesstatic void	 sizecmd __P((char *));
11261592Srgrimesstatic void	 toolong __P((int));
112756668Sshinstatic void	 v4map_data_dest __P((void));
11281592Srgrimesstatic int	 yylex __P((void));
11291592Srgrimes
11301592Srgrimesstatic struct tab *
11311592Srgrimeslookup(p, cmd)
11321592Srgrimes	struct tab *p;
11331592Srgrimes	char *cmd;
11341592Srgrimes{
11351592Srgrimes
11361592Srgrimes	for (; p->name != NULL; p++)
11371592Srgrimes		if (strcmp(cmd, p->name) == 0)
11381592Srgrimes			return (p);
11391592Srgrimes	return (0);
11401592Srgrimes}
11411592Srgrimes
11421592Srgrimes#include <arpa/telnet.h>
11431592Srgrimes
11441592Srgrimes/*
11451592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11461592Srgrimes */
11471592Srgrimeschar *
11481592Srgrimesgetline(s, n, iop)
11491592Srgrimes	char *s;
11501592Srgrimes	int n;
11511592Srgrimes	FILE *iop;
11521592Srgrimes{
11531592Srgrimes	int c;
11541592Srgrimes	register char *cs;
11551592Srgrimes
11561592Srgrimes	cs = s;
11571592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11581592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11591592Srgrimes		*cs++ = tmpline[c];
11601592Srgrimes		if (tmpline[c] == '\n') {
11611592Srgrimes			*cs++ = '\0';
116276096Smarkm			if (ftpdebug)
11631592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11641592Srgrimes			tmpline[0] = '\0';
11651592Srgrimes			return(s);
11661592Srgrimes		}
11671592Srgrimes		if (c == 0)
11681592Srgrimes			tmpline[0] = '\0';
11691592Srgrimes	}
11701592Srgrimes	while ((c = getc(iop)) != EOF) {
11711592Srgrimes		c &= 0377;
11721592Srgrimes		if (c == IAC) {
11731592Srgrimes		    if ((c = getc(iop)) != EOF) {
11741592Srgrimes			c &= 0377;
11751592Srgrimes			switch (c) {
11761592Srgrimes			case WILL:
11771592Srgrimes			case WONT:
11781592Srgrimes				c = getc(iop);
11791592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11801592Srgrimes				(void) fflush(stdout);
11811592Srgrimes				continue;
11821592Srgrimes			case DO:
11831592Srgrimes			case DONT:
11841592Srgrimes				c = getc(iop);
11851592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11861592Srgrimes				(void) fflush(stdout);
11871592Srgrimes				continue;
11881592Srgrimes			case IAC:
11891592Srgrimes				break;
11901592Srgrimes			default:
11911592Srgrimes				continue;	/* ignore command */
11921592Srgrimes			}
11931592Srgrimes		    }
11941592Srgrimes		}
11951592Srgrimes		*cs++ = c;
11961592Srgrimes		if (--n <= 0 || c == '\n')
11971592Srgrimes			break;
11981592Srgrimes	}
11991592Srgrimes	if (c == EOF && cs == s)
12001592Srgrimes		return (NULL);
12011592Srgrimes	*cs++ = '\0';
120276096Smarkm	if (ftpdebug) {
12031592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12041592Srgrimes			/* Don't syslog passwords */
12051592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12061592Srgrimes		} else {
12071592Srgrimes			register char *cp;
12081592Srgrimes			register int len;
12091592Srgrimes
12101592Srgrimes			/* Don't syslog trailing CR-LF */
12111592Srgrimes			len = strlen(s);
12121592Srgrimes			cp = s + len - 1;
12131592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12141592Srgrimes				--cp;
12151592Srgrimes				--len;
12161592Srgrimes			}
12171592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12181592Srgrimes		}
12191592Srgrimes	}
12201592Srgrimes	return (s);
12211592Srgrimes}
12221592Srgrimes
12231592Srgrimesstatic void
12241592Srgrimestoolong(signo)
12251592Srgrimes	int signo;
12261592Srgrimes{
12271592Srgrimes
12281592Srgrimes	reply(421,
12291592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12301592Srgrimes	if (logging)
12311592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12321592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12331592Srgrimes	dologout(1);
12341592Srgrimes}
12351592Srgrimes
12361592Srgrimesstatic int
12371592Srgrimesyylex()
12381592Srgrimes{
12391592Srgrimes	static int cpos, state;
12401592Srgrimes	char *cp, *cp2;
12411592Srgrimes	struct tab *p;
12421592Srgrimes	int n;
12431592Srgrimes	char c;
12441592Srgrimes
12451592Srgrimes	for (;;) {
12461592Srgrimes		switch (state) {
12471592Srgrimes
12481592Srgrimes		case CMD:
12491592Srgrimes			(void) signal(SIGALRM, toolong);
12501592Srgrimes			(void) alarm((unsigned) timeout);
12511592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12521592Srgrimes				reply(221, "You could at least say goodbye.");
12531592Srgrimes				dologout(0);
12541592Srgrimes			}
12551592Srgrimes			(void) alarm(0);
12561592Srgrimes#ifdef SETPROCTITLE
125729574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12581592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12591592Srgrimes#endif /* SETPROCTITLE */
12601592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12611592Srgrimes				*cp++ = '\n';
12621592Srgrimes				*cp = '\0';
12631592Srgrimes			}
12641592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12651592Srgrimes				cpos = cp - cbuf;
12661592Srgrimes			if (cpos == 0)
12671592Srgrimes				cpos = 4;
12681592Srgrimes			c = cbuf[cpos];
12691592Srgrimes			cbuf[cpos] = '\0';
12701592Srgrimes			upper(cbuf);
12711592Srgrimes			p = lookup(cmdtab, cbuf);
12721592Srgrimes			cbuf[cpos] = c;
12733776Spst			if (p != 0) {
12741592Srgrimes				if (p->implemented == 0) {
12751592Srgrimes					nack(p->name);
12761592Srgrimes					longjmp(errcatch,0);
12771592Srgrimes					/* NOTREACHED */
12781592Srgrimes				}
12791592Srgrimes				state = p->state;
12801592Srgrimes				yylval.s = p->name;
12811592Srgrimes				return (p->token);
12821592Srgrimes			}
12831592Srgrimes			break;
12841592Srgrimes
12851592Srgrimes		case SITECMD:
12861592Srgrimes			if (cbuf[cpos] == ' ') {
12871592Srgrimes				cpos++;
12881592Srgrimes				return (SP);
12891592Srgrimes			}
12901592Srgrimes			cp = &cbuf[cpos];
12911592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12921592Srgrimes				cpos = cp2 - cbuf;
12931592Srgrimes			c = cbuf[cpos];
12941592Srgrimes			cbuf[cpos] = '\0';
12951592Srgrimes			upper(cp);
12961592Srgrimes			p = lookup(sitetab, cp);
12971592Srgrimes			cbuf[cpos] = c;
12983777Spst			if (guest == 0 && p != 0) {
12991592Srgrimes				if (p->implemented == 0) {
13001592Srgrimes					state = CMD;
13011592Srgrimes					nack(p->name);
13021592Srgrimes					longjmp(errcatch,0);
13031592Srgrimes					/* NOTREACHED */
13041592Srgrimes				}
13051592Srgrimes				state = p->state;
13061592Srgrimes				yylval.s = p->name;
13071592Srgrimes				return (p->token);
13081592Srgrimes			}
13091592Srgrimes			state = CMD;
13101592Srgrimes			break;
13111592Srgrimes
131275556Sgreen		case ZSTR1:
13131592Srgrimes		case OSTR:
13141592Srgrimes			if (cbuf[cpos] == '\n') {
13151592Srgrimes				state = CMD;
13161592Srgrimes				return (CRLF);
13171592Srgrimes			}
13181592Srgrimes			/* FALLTHROUGH */
13191592Srgrimes
13201592Srgrimes		case STR1:
13211592Srgrimes		dostr1:
13221592Srgrimes			if (cbuf[cpos] == ' ') {
13231592Srgrimes				cpos++;
132451979Salfred				state = state == OSTR ? STR2 : state+1;
13251592Srgrimes				return (SP);
13261592Srgrimes			}
13271592Srgrimes			break;
13281592Srgrimes
13291592Srgrimes		case ZSTR2:
13301592Srgrimes			if (cbuf[cpos] == '\n') {
13311592Srgrimes				state = CMD;
13321592Srgrimes				return (CRLF);
13331592Srgrimes			}
13341592Srgrimes			/* FALLTHROUGH */
13351592Srgrimes
13361592Srgrimes		case STR2:
13371592Srgrimes			cp = &cbuf[cpos];
13381592Srgrimes			n = strlen(cp);
13391592Srgrimes			cpos += n - 1;
13401592Srgrimes			/*
13411592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13421592Srgrimes			 */
13431592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13441592Srgrimes				cbuf[cpos] = '\0';
13451592Srgrimes				yylval.s = copy(cp);
13461592Srgrimes				cbuf[cpos] = '\n';
13471592Srgrimes				state = ARGS;
13481592Srgrimes				return (STRING);
13491592Srgrimes			}
13501592Srgrimes			break;
13511592Srgrimes
13521592Srgrimes		case NSTR:
13531592Srgrimes			if (cbuf[cpos] == ' ') {
13541592Srgrimes				cpos++;
13551592Srgrimes				return (SP);
13561592Srgrimes			}
13571592Srgrimes			if (isdigit(cbuf[cpos])) {
13581592Srgrimes				cp = &cbuf[cpos];
13591592Srgrimes				while (isdigit(cbuf[++cpos]))
13601592Srgrimes					;
13611592Srgrimes				c = cbuf[cpos];
13621592Srgrimes				cbuf[cpos] = '\0';
13631592Srgrimes				yylval.i = atoi(cp);
13641592Srgrimes				cbuf[cpos] = c;
13651592Srgrimes				state = STR1;
13661592Srgrimes				return (NUMBER);
13671592Srgrimes			}
13681592Srgrimes			state = STR1;
13691592Srgrimes			goto dostr1;
13701592Srgrimes
13711592Srgrimes		case ARGS:
13721592Srgrimes			if (isdigit(cbuf[cpos])) {
13731592Srgrimes				cp = &cbuf[cpos];
13741592Srgrimes				while (isdigit(cbuf[++cpos]))
13751592Srgrimes					;
13761592Srgrimes				c = cbuf[cpos];
13771592Srgrimes				cbuf[cpos] = '\0';
13781592Srgrimes				yylval.i = atoi(cp);
13791592Srgrimes				cbuf[cpos] = c;
13801592Srgrimes				return (NUMBER);
13811592Srgrimes			}
138256668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
138356668Sshin			 && !isalnum(cbuf[cpos + 3])) {
138456668Sshin				cpos += 3;
138556668Sshin				return ALL;
138656668Sshin			}
13871592Srgrimes			switch (cbuf[cpos++]) {
13881592Srgrimes
13891592Srgrimes			case '\n':
13901592Srgrimes				state = CMD;
13911592Srgrimes				return (CRLF);
13921592Srgrimes
13931592Srgrimes			case ' ':
13941592Srgrimes				return (SP);
13951592Srgrimes
13961592Srgrimes			case ',':
13971592Srgrimes				return (COMMA);
13981592Srgrimes
13991592Srgrimes			case 'A':
14001592Srgrimes			case 'a':
14011592Srgrimes				return (A);
14021592Srgrimes
14031592Srgrimes			case 'B':
14041592Srgrimes			case 'b':
14051592Srgrimes				return (B);
14061592Srgrimes
14071592Srgrimes			case 'C':
14081592Srgrimes			case 'c':
14091592Srgrimes				return (C);
14101592Srgrimes
14111592Srgrimes			case 'E':
14121592Srgrimes			case 'e':
14131592Srgrimes				return (E);
14141592Srgrimes
14151592Srgrimes			case 'F':
14161592Srgrimes			case 'f':
14171592Srgrimes				return (F);
14181592Srgrimes
14191592Srgrimes			case 'I':
14201592Srgrimes			case 'i':
14211592Srgrimes				return (I);
14221592Srgrimes
14231592Srgrimes			case 'L':
14241592Srgrimes			case 'l':
14251592Srgrimes				return (L);
14261592Srgrimes
14271592Srgrimes			case 'N':
14281592Srgrimes			case 'n':
14291592Srgrimes				return (N);
14301592Srgrimes
14311592Srgrimes			case 'P':
14321592Srgrimes			case 'p':
14331592Srgrimes				return (P);
14341592Srgrimes
14351592Srgrimes			case 'R':
14361592Srgrimes			case 'r':
14371592Srgrimes				return (R);
14381592Srgrimes
14391592Srgrimes			case 'S':
14401592Srgrimes			case 's':
14411592Srgrimes				return (S);
14421592Srgrimes
14431592Srgrimes			case 'T':
14441592Srgrimes			case 't':
14451592Srgrimes				return (T);
14461592Srgrimes
14471592Srgrimes			}
14481592Srgrimes			break;
14491592Srgrimes
14501592Srgrimes		default:
145176096Smarkm			fatalerror("Unknown state in scanner.");
14521592Srgrimes		}
14531592Srgrimes		yyerror((char *) 0);
14541592Srgrimes		state = CMD;
14551592Srgrimes		longjmp(errcatch,0);
14561592Srgrimes	}
14571592Srgrimes}
14581592Srgrimes
14591592Srgrimesvoid
14601592Srgrimesupper(s)
14611592Srgrimes	char *s;
14621592Srgrimes{
14631592Srgrimes	while (*s != '\0') {
14641592Srgrimes		if (islower(*s))
14651592Srgrimes			*s = toupper(*s);
14661592Srgrimes		s++;
14671592Srgrimes	}
14681592Srgrimes}
14691592Srgrimes
14701592Srgrimesstatic char *
14711592Srgrimescopy(s)
14721592Srgrimes	char *s;
14731592Srgrimes{
14741592Srgrimes	char *p;
14751592Srgrimes
14761592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14771592Srgrimes	if (p == NULL)
147876096Smarkm		fatalerror("Ran out of memory.");
14791592Srgrimes	(void) strcpy(p, s);
14801592Srgrimes	return (p);
14811592Srgrimes}
14821592Srgrimes
14831592Srgrimesstatic void
14841592Srgrimeshelp(ctab, s)
14851592Srgrimes	struct tab *ctab;
14861592Srgrimes	char *s;
14871592Srgrimes{
14881592Srgrimes	struct tab *c;
14891592Srgrimes	int width, NCMDS;
14901592Srgrimes	char *type;
14911592Srgrimes
14921592Srgrimes	if (ctab == sitetab)
14931592Srgrimes		type = "SITE ";
14941592Srgrimes	else
14951592Srgrimes		type = "";
14961592Srgrimes	width = 0, NCMDS = 0;
14971592Srgrimes	for (c = ctab; c->name != NULL; c++) {
14981592Srgrimes		int len = strlen(c->name);
14991592Srgrimes
15001592Srgrimes		if (len > width)
15011592Srgrimes			width = len;
15021592Srgrimes		NCMDS++;
15031592Srgrimes	}
15041592Srgrimes	width = (width + 8) &~ 7;
15051592Srgrimes	if (s == 0) {
15061592Srgrimes		int i, j, w;
15071592Srgrimes		int columns, lines;
15081592Srgrimes
15091592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15101592Srgrimes		    type, "(* =>'s unimplemented)");
15111592Srgrimes		columns = 76 / width;
15121592Srgrimes		if (columns == 0)
15131592Srgrimes			columns = 1;
15141592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15151592Srgrimes		for (i = 0; i < lines; i++) {
15161592Srgrimes			printf("   ");
15171592Srgrimes			for (j = 0; j < columns; j++) {
15181592Srgrimes				c = ctab + j * lines + i;
15191592Srgrimes				printf("%s%c", c->name,
15201592Srgrimes					c->implemented ? ' ' : '*');
15211592Srgrimes				if (c + lines >= &ctab[NCMDS])
15221592Srgrimes					break;
15231592Srgrimes				w = strlen(c->name) + 1;
15241592Srgrimes				while (w < width) {
15251592Srgrimes					putchar(' ');
15261592Srgrimes					w++;
15271592Srgrimes				}
15281592Srgrimes			}
15291592Srgrimes			printf("\r\n");
15301592Srgrimes		}
15311592Srgrimes		(void) fflush(stdout);
15321592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15331592Srgrimes		return;
15341592Srgrimes	}
15351592Srgrimes	upper(s);
15361592Srgrimes	c = lookup(ctab, s);
15371592Srgrimes	if (c == (struct tab *)0) {
15381592Srgrimes		reply(502, "Unknown command %s.", s);
15391592Srgrimes		return;
15401592Srgrimes	}
15411592Srgrimes	if (c->implemented)
15421592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15431592Srgrimes	else
15441592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15451592Srgrimes		    c->name, c->help);
15461592Srgrimes}
15471592Srgrimes
15481592Srgrimesstatic void
15491592Srgrimessizecmd(filename)
15501592Srgrimes	char *filename;
15511592Srgrimes{
15521592Srgrimes	switch (type) {
15531592Srgrimes	case TYPE_L:
15541592Srgrimes	case TYPE_I: {
15551592Srgrimes		struct stat stbuf;
155663350Sdes		if (stat(filename, &stbuf) < 0)
155763350Sdes			perror_reply(550, filename);
155863350Sdes		else if (!S_ISREG(stbuf.st_mode))
15591592Srgrimes			reply(550, "%s: not a plain file.", filename);
15601592Srgrimes		else
15611592Srgrimes			reply(213, "%qu", stbuf.st_size);
15621592Srgrimes		break; }
15631592Srgrimes	case TYPE_A: {
15641592Srgrimes		FILE *fin;
15651592Srgrimes		int c;
15661592Srgrimes		off_t count;
15671592Srgrimes		struct stat stbuf;
15681592Srgrimes		fin = fopen(filename, "r");
15691592Srgrimes		if (fin == NULL) {
15701592Srgrimes			perror_reply(550, filename);
15711592Srgrimes			return;
15721592Srgrimes		}
157363350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
157463350Sdes			perror_reply(550, filename);
157563350Sdes			(void) fclose(fin);
157663350Sdes			return;
157763350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15781592Srgrimes			reply(550, "%s: not a plain file.", filename);
15791592Srgrimes			(void) fclose(fin);
15801592Srgrimes			return;
15811592Srgrimes		}
15821592Srgrimes
15831592Srgrimes		count = 0;
15841592Srgrimes		while((c=getc(fin)) != EOF) {
15851592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15861592Srgrimes				count++;
15871592Srgrimes			count++;
15881592Srgrimes		}
15891592Srgrimes		(void) fclose(fin);
15901592Srgrimes
15911592Srgrimes		reply(213, "%qd", count);
15921592Srgrimes		break; }
15931592Srgrimes	default:
15941592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15951592Srgrimes	}
15961592Srgrimes}
159756668Sshin
159856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
159956668Sshinstatic int
160056668Sshinport_check(pcmd)
160156668Sshin	const char *pcmd;
160256668Sshin{
160356668Sshin	if (his_addr.su_family == AF_INET) {
160456668Sshin		if (data_dest.su_family != AF_INET) {
160556668Sshin			usedefault = 1;
160656668Sshin			reply(500, "Invalid address rejected.");
160756668Sshin			return 1;
160856668Sshin		}
160956668Sshin		if (paranoid &&
161056668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
161156668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
161256668Sshin			    &his_addr.su_sin.sin_addr,
161356668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
161456668Sshin			usedefault = 1;
161556668Sshin			reply(500, "Illegal PORT range rejected.");
161656668Sshin		} else {
161756668Sshin			usedefault = 0;
161856668Sshin			if (pdata >= 0) {
161956668Sshin				(void) close(pdata);
162056668Sshin				pdata = -1;
162156668Sshin			}
162256668Sshin			reply(200, "%s command successful.", pcmd);
162356668Sshin		}
162456668Sshin		return 1;
162556668Sshin	}
162656668Sshin	return 0;
162756668Sshin}
162856668Sshin
162970102Sphkstatic int
163070102Sphkcheck_login1()
163170102Sphk{
163270102Sphk	if (logged_in)
163370102Sphk		return 1;
163470102Sphk	else {
163570102Sphk		reply(530, "Please login with USER and PASS.");
163670102Sphk		return 0;
163770102Sphk	}
163870102Sphk}
163970102Sphk
164056668Sshin#ifdef INET6
164156668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
164256668Sshinstatic int
164356668Sshinport_check_v6(pcmd)
164456668Sshin	const char *pcmd;
164556668Sshin{
164656668Sshin	if (his_addr.su_family == AF_INET6) {
164756668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
164856668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
164956668Sshin			v4map_data_dest();
165056668Sshin		if (data_dest.su_family != AF_INET6) {
165156668Sshin			usedefault = 1;
165256668Sshin			reply(500, "Invalid address rejected.");
165356668Sshin			return 1;
165456668Sshin		}
165556668Sshin		if (paranoid &&
165656668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
165756668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
165856668Sshin			    &his_addr.su_sin6.sin6_addr,
165956668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
166056668Sshin			usedefault = 1;
166156668Sshin			reply(500, "Illegal PORT range rejected.");
166256668Sshin		} else {
166356668Sshin			usedefault = 0;
166456668Sshin			if (pdata >= 0) {
166556668Sshin				(void) close(pdata);
166656668Sshin				pdata = -1;
166756668Sshin			}
166856668Sshin			reply(200, "%s command successful.", pcmd);
166956668Sshin		}
167056668Sshin		return 1;
167156668Sshin	}
167256668Sshin	return 0;
167356668Sshin}
167456668Sshin
167556668Sshinstatic void
167656668Sshinv4map_data_dest()
167756668Sshin{
167856668Sshin	struct in_addr savedaddr;
167956668Sshin	int savedport;
168056668Sshin
168156668Sshin	if (data_dest.su_family != AF_INET) {
168256668Sshin		usedefault = 1;
168356668Sshin		reply(500, "Invalid address rejected.");
168456668Sshin		return;
168556668Sshin	}
168656668Sshin
168756668Sshin	savedaddr = data_dest.su_sin.sin_addr;
168856668Sshin	savedport = data_dest.su_port;
168956668Sshin
169056668Sshin	memset(&data_dest, 0, sizeof(data_dest));
169156668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
169256668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
169356668Sshin	data_dest.su_sin6.sin6_port = savedport;
169456668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
169556668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
169656668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
169756668Sshin}
169856668Sshin#endif
1699