ftpcmd.y revision 101379
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 101379 2002-08-05 14:26:40Z yar $";
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>
6192090Smaxim#include <libutil.h>
6292272Smaxim#include <limits.h>
6392090Smaxim#include <md5.h>
6456668Sshin#include <netdb.h>
651592Srgrimes#include <pwd.h>
661592Srgrimes#include <signal.h>
671592Srgrimes#include <stdio.h>
681592Srgrimes#include <stdlib.h>
691592Srgrimes#include <string.h>
701592Srgrimes#include <syslog.h>
711592Srgrimes#include <time.h>
721592Srgrimes#include <unistd.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;
9682460Snikextern	int noretr;
9782796Ssheldonhextern	int noguestretr;
98100684Syarextern	char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
991592Srgrimes
1001592Srgrimesoff_t	restart_point;
1011592Srgrimes
1021592Srgrimesstatic	int cmd_type;
1031592Srgrimesstatic	int cmd_form;
1041592Srgrimesstatic	int cmd_bytesz;
10589935Syarstatic	int state;
1061592Srgrimeschar	cbuf[512];
10788935Sdwmalonechar	*fromname = (char *) 0;
1081592Srgrimes
10956668Sshinextern int epsvall;
11056668Sshin
1111592Srgrimes%}
1121592Srgrimes
1131592Srgrimes%union {
11492272Smaxim	struct {
11592272Smaxim		off_t	o;
11692272Smaxim		int	i;
11792272Smaxim	} u;
1181592Srgrimes	char   *s;
1191592Srgrimes}
1201592Srgrimes
1211592Srgrimes%token
1221592Srgrimes	A	B	C	E	F	I
1231592Srgrimes	L	N	P	R	S	T
12456668Sshin	ALL
1251592Srgrimes
1261592Srgrimes	SP	CRLF	COMMA
1271592Srgrimes
1281592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1291592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1301592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1311592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1321592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1331592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1341592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
13556668Sshin	LPRT	LPSV	EPRT	EPSV
1361592Srgrimes
13775535Sphk	UMASK	IDLE	CHMOD	MDFIVE
1381592Srgrimes
1391592Srgrimes	LEXERR
1401592Srgrimes
1411592Srgrimes%token	<s> STRING
14292272Smaxim%token	<u> NUMBER
1431592Srgrimes
14492272Smaxim%type	<u.i> check_login octal_number byte_size
14592272Smaxim%type	<u.i> check_login_ro check_login_epsv
14692272Smaxim%type	<u.i> struct_code mode_code type_code form_code
14775567Speter%type	<s> pathstring pathname password username
14856668Sshin%type	<s> ALL
1491592Srgrimes
1501592Srgrimes%start	cmd_list
1511592Srgrimes
1521592Srgrimes%%
1531592Srgrimes
1541592Srgrimescmd_list
1551592Srgrimes	: /* empty */
1561592Srgrimes	| cmd_list cmd
1571592Srgrimes		{
15888935Sdwmalone			if (fromname)
15988935Sdwmalone				free(fromname);
1601592Srgrimes			fromname = (char *) 0;
1611592Srgrimes			restart_point = (off_t) 0;
1621592Srgrimes		}
1631592Srgrimes	| cmd_list rcmd
1641592Srgrimes	;
1651592Srgrimes
1661592Srgrimescmd
1671592Srgrimes	: USER SP username CRLF
1681592Srgrimes		{
1691592Srgrimes			user($3);
1701592Srgrimes			free($3);
1711592Srgrimes		}
1721592Srgrimes	| PASS SP password CRLF
1731592Srgrimes		{
1741592Srgrimes			pass($3);
1751592Srgrimes			free($3);
1761592Srgrimes		}
17775556Sgreen	| PASS CRLF
17875556Sgreen		{
17975556Sgreen			pass("");
18075556Sgreen		}
18117433Spst	| PORT check_login SP host_port CRLF
1821592Srgrimes		{
18356668Sshin			if (epsvall) {
18456668Sshin				reply(501, "no PORT allowed after EPSV ALL");
18556668Sshin				goto port_done;
18656668Sshin			}
18756668Sshin			if (!$2)
18856668Sshin				goto port_done;
18956668Sshin			if (port_check("PORT") == 1)
19056668Sshin				goto port_done;
19156668Sshin#ifdef INET6
19256668Sshin			if ((his_addr.su_family != AF_INET6 ||
19356668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
19456668Sshin				/* shoud never happen */
19556668Sshin				usedefault = 1;
19656668Sshin				reply(500, "Invalid address rejected.");
19756668Sshin				goto port_done;
19856668Sshin			}
19956668Sshin			port_check_v6("pcmd");
20056668Sshin#endif
20156668Sshin		port_done:
20256668Sshin		}
20356668Sshin	| LPRT check_login SP host_long_port CRLF
20456668Sshin		{
20556668Sshin			if (epsvall) {
20656668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
20756668Sshin				goto lprt_done;
20856668Sshin			}
20956668Sshin			if (!$2)
21056668Sshin				goto lprt_done;
21156668Sshin			if (port_check("LPRT") == 1)
21256668Sshin				goto lprt_done;
21356668Sshin#ifdef INET6
21456668Sshin			if (his_addr.su_family != AF_INET6) {
21556668Sshin				usedefault = 1;
21656668Sshin				reply(500, "Invalid address rejected.");
21756668Sshin				goto lprt_done;
21856668Sshin			}
21956668Sshin			if (port_check_v6("LPRT") == 1)
22056668Sshin				goto lprt_done;
22156668Sshin#endif
22256668Sshin		lprt_done:
22356668Sshin		}
22456668Sshin	| EPRT check_login SP STRING CRLF
22556668Sshin		{
22656668Sshin			char delim;
22756668Sshin			char *tmp = NULL;
22856668Sshin			char *p, *q;
22956668Sshin			char *result[3];
23056668Sshin			struct addrinfo hints;
23156668Sshin			struct addrinfo *res;
23256668Sshin			int i;
23356668Sshin
23456668Sshin			if (epsvall) {
23556668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
23656668Sshin				goto eprt_done;
23756668Sshin			}
23856668Sshin			if (!$2)
23956668Sshin				goto eprt_done;
24056668Sshin
24156668Sshin			memset(&data_dest, 0, sizeof(data_dest));
24256668Sshin			tmp = strdup($4);
24376096Smarkm			if (ftpdebug)
24456668Sshin				syslog(LOG_DEBUG, "%s", tmp);
24556668Sshin			if (!tmp) {
24676096Smarkm				fatalerror("not enough core");
24756668Sshin				/*NOTREACHED*/
24856668Sshin			}
24956668Sshin			p = tmp;
25056668Sshin			delim = p[0];
25156668Sshin			p++;
25256668Sshin			memset(result, 0, sizeof(result));
25356668Sshin			for (i = 0; i < 3; i++) {
25456668Sshin				q = strchr(p, delim);
25556668Sshin				if (!q || *q != delim) {
25656668Sshin		parsefail:
25756668Sshin					reply(500,
25856668Sshin						"Invalid argument, rejected.");
25956668Sshin					if (tmp)
26056668Sshin						free(tmp);
26117433Spst					usedefault = 1;
26256668Sshin					goto eprt_done;
26317433Spst				}
26456668Sshin				*q++ = '\0';
26556668Sshin				result[i] = p;
26676096Smarkm				if (ftpdebug)
26756668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
26856668Sshin				p = q;
2691592Srgrimes			}
27056668Sshin
27156668Sshin			/* some more sanity check */
27256668Sshin			p = result[0];
27356668Sshin			while (*p) {
27456668Sshin				if (!isdigit(*p))
27556668Sshin					goto parsefail;
27656668Sshin				p++;
27756668Sshin			}
27856668Sshin			p = result[2];
27956668Sshin			while (*p) {
28056668Sshin				if (!isdigit(*p))
28156668Sshin					goto parsefail;
28256668Sshin				p++;
28356668Sshin			}
28456668Sshin
28556668Sshin			/* grab address */
28656668Sshin			memset(&hints, 0, sizeof(hints));
28756668Sshin			if (atoi(result[0]) == 1)
28856668Sshin				hints.ai_family = PF_INET;
28956668Sshin#ifdef INET6
29056668Sshin			else if (atoi(result[0]) == 2)
29156668Sshin				hints.ai_family = PF_INET6;
29256668Sshin#endif
29356668Sshin			else
29456668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
29556668Sshin			hints.ai_socktype = SOCK_STREAM;
29656668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
29756668Sshin			if (i)
29856668Sshin				goto parsefail;
29956668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
30056668Sshin#ifdef INET6
30156668Sshin			if (his_addr.su_family == AF_INET6
30256668Sshin			    && data_dest.su_family == AF_INET6) {
30356668Sshin				/* XXX more sanity checks! */
30456668Sshin				data_dest.su_sin6.sin6_scope_id =
30556668Sshin					his_addr.su_sin6.sin6_scope_id;
30656668Sshin			}
30756668Sshin#endif
30856668Sshin			free(tmp);
30956668Sshin			tmp = NULL;
31056668Sshin
31156668Sshin			if (port_check("EPRT") == 1)
31256668Sshin				goto eprt_done;
31356668Sshin#ifdef INET6
31456668Sshin			if (his_addr.su_family != AF_INET6) {
31556668Sshin				usedefault = 1;
31656668Sshin				reply(500, "Invalid address rejected.");
31756668Sshin				goto eprt_done;
31856668Sshin			}
31956668Sshin			if (port_check_v6("EPRT") == 1)
32056668Sshin				goto eprt_done;
32156668Sshin#endif
32288935Sdwmalone		eprt_done:
32388935Sdwmalone			free($4);
3241592Srgrimes		}
32517433Spst	| PASV check_login CRLF
3261592Srgrimes		{
32756668Sshin			if (epsvall)
32856668Sshin				reply(501, "no PASV allowed after EPSV ALL");
32956668Sshin			else if ($2)
33017433Spst				passive();
3311592Srgrimes		}
33256668Sshin	| LPSV check_login CRLF
33356668Sshin		{
33456668Sshin			if (epsvall)
33556668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
33656668Sshin			else if ($2)
33756668Sshin				long_passive("LPSV", PF_UNSPEC);
33856668Sshin		}
33970102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
34056668Sshin		{
34156668Sshin			if ($2) {
34256668Sshin				int pf;
34392272Smaxim				switch ($4.i) {
34456668Sshin				case 1:
34556668Sshin					pf = PF_INET;
34656668Sshin					break;
34756668Sshin#ifdef INET6
34856668Sshin				case 2:
34956668Sshin					pf = PF_INET6;
35056668Sshin					break;
35156668Sshin#endif
35256668Sshin				default:
35356668Sshin					pf = -1;	/*junk value*/
35456668Sshin					break;
35556668Sshin				}
35656668Sshin				long_passive("EPSV", pf);
35756668Sshin			}
35856668Sshin		}
35970102Sphk	| EPSV check_login_epsv SP ALL CRLF
36056668Sshin		{
36156668Sshin			if ($2) {
36256668Sshin				reply(200,
36356668Sshin				      "EPSV ALL command successful.");
36456668Sshin				epsvall++;
36556668Sshin			}
36656668Sshin		}
36770102Sphk	| EPSV check_login_epsv CRLF
36856668Sshin		{
36956668Sshin			if ($2)
37056668Sshin				long_passive("EPSV", PF_UNSPEC);
37156668Sshin		}
37271278Sjedgar	| TYPE check_login SP type_code CRLF
3731592Srgrimes		{
37471278Sjedgar			if ($2) {
37571278Sjedgar				switch (cmd_type) {
3761592Srgrimes
37771278Sjedgar				case TYPE_A:
37871278Sjedgar					if (cmd_form == FORM_N) {
37971278Sjedgar						reply(200, "Type set to A.");
38071278Sjedgar						type = cmd_type;
38171278Sjedgar						form = cmd_form;
38271278Sjedgar					} else
38371278Sjedgar						reply(504, "Form must be N.");
38471278Sjedgar					break;
3851592Srgrimes
38671278Sjedgar				case TYPE_E:
38771278Sjedgar					reply(504, "Type E not implemented.");
38871278Sjedgar					break;
3891592Srgrimes
39071278Sjedgar				case TYPE_I:
39171278Sjedgar					reply(200, "Type set to I.");
39271278Sjedgar					type = cmd_type;
39371278Sjedgar					break;
3941592Srgrimes
39571278Sjedgar				case TYPE_L:
3961592Srgrimes#if NBBY == 8
39771278Sjedgar					if (cmd_bytesz == 8) {
39871278Sjedgar						reply(200,
39971278Sjedgar						    "Type set to L (byte size 8).");
40071278Sjedgar						type = cmd_type;
40171278Sjedgar					} else
40271278Sjedgar						reply(504, "Byte size must be 8.");
4031592Srgrimes#else /* NBBY == 8 */
40471278Sjedgar					UNIMPLEMENTED for NBBY != 8
4051592Srgrimes#endif /* NBBY == 8 */
40671278Sjedgar				}
4071592Srgrimes			}
4081592Srgrimes		}
40971278Sjedgar	| STRU check_login SP struct_code CRLF
4101592Srgrimes		{
41171278Sjedgar			if ($2) {
41271278Sjedgar				switch ($4) {
4131592Srgrimes
41471278Sjedgar				case STRU_F:
41571278Sjedgar					reply(200, "STRU F ok.");
41671278Sjedgar					break;
4171592Srgrimes
41871278Sjedgar				default:
41971278Sjedgar					reply(504, "Unimplemented STRU type.");
42071278Sjedgar				}
4211592Srgrimes			}
4221592Srgrimes		}
42371278Sjedgar	| MODE check_login SP mode_code CRLF
4241592Srgrimes		{
42571278Sjedgar			if ($2) {
42671278Sjedgar				switch ($4) {
4271592Srgrimes
42871278Sjedgar				case MODE_S:
42971278Sjedgar					reply(200, "MODE S ok.");
43071278Sjedgar					break;
43171278Sjedgar
43271278Sjedgar				default:
43371278Sjedgar					reply(502, "Unimplemented MODE type.");
43471278Sjedgar				}
4351592Srgrimes			}
4361592Srgrimes		}
43771278Sjedgar	| ALLO check_login SP NUMBER CRLF
4381592Srgrimes		{
43971278Sjedgar			if ($2) {
44071278Sjedgar				reply(202, "ALLO command ignored.");
44171278Sjedgar			}
4421592Srgrimes		}
44371278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4441592Srgrimes		{
44571278Sjedgar			if ($2) {
44671278Sjedgar				reply(202, "ALLO command ignored.");
44771278Sjedgar			}
4481592Srgrimes		}
4491592Srgrimes	| RETR check_login SP pathname CRLF
4501592Srgrimes		{
45182796Ssheldonh			if (noretr || (guest && noguestretr))
45282460Snik				reply(500, "RETR command is disabled");
45382460Snik			else if ($2 && $4 != NULL)
4541592Srgrimes				retrieve((char *) 0, $4);
45582460Snik
4561592Srgrimes			if ($4 != NULL)
4571592Srgrimes				free($4);
4581592Srgrimes		}
45970102Sphk	| STOR check_login_ro SP pathname CRLF
4601592Srgrimes		{
4611592Srgrimes			if ($2 && $4 != NULL)
4621592Srgrimes				store($4, "w", 0);
4631592Srgrimes			if ($4 != NULL)
4641592Srgrimes				free($4);
4651592Srgrimes		}
46670102Sphk	| APPE check_login_ro SP pathname CRLF
4671592Srgrimes		{
4681592Srgrimes			if ($2 && $4 != NULL)
4691592Srgrimes				store($4, "a", 0);
4701592Srgrimes			if ($4 != NULL)
4711592Srgrimes				free($4);
4721592Srgrimes		}
4731592Srgrimes	| NLST check_login CRLF
4741592Srgrimes		{
4751592Srgrimes			if ($2)
4761592Srgrimes				send_file_list(".");
4771592Srgrimes		}
4781592Srgrimes	| NLST check_login SP STRING CRLF
4791592Srgrimes		{
4801592Srgrimes			if ($2 && $4 != NULL)
4811592Srgrimes				send_file_list($4);
4821592Srgrimes			if ($4 != NULL)
4831592Srgrimes				free($4);
4841592Srgrimes		}
4851592Srgrimes	| LIST check_login CRLF
4861592Srgrimes		{
4871592Srgrimes			if ($2)
4881592Srgrimes				retrieve("/bin/ls -lgA", "");
4891592Srgrimes		}
49075567Speter	| LIST check_login SP pathstring CRLF
4911592Srgrimes		{
4921592Srgrimes			if ($2 && $4 != NULL)
4931592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4941592Srgrimes			if ($4 != NULL)
4951592Srgrimes				free($4);
4961592Srgrimes		}
4971592Srgrimes	| STAT check_login SP pathname CRLF
4981592Srgrimes		{
4991592Srgrimes			if ($2 && $4 != NULL)
5001592Srgrimes				statfilecmd($4);
5011592Srgrimes			if ($4 != NULL)
5021592Srgrimes				free($4);
5031592Srgrimes		}
50471278Sjedgar	| STAT check_login CRLF
5051592Srgrimes		{
50671278Sjedgar			if ($2) {
50771278Sjedgar				statcmd();
50871278Sjedgar			}
5091592Srgrimes		}
51070102Sphk	| DELE check_login_ro SP pathname CRLF
5111592Srgrimes		{
5121592Srgrimes			if ($2 && $4 != NULL)
5131592Srgrimes				delete($4);
5141592Srgrimes			if ($4 != NULL)
5151592Srgrimes				free($4);
5161592Srgrimes		}
51770102Sphk	| RNTO check_login_ro SP pathname CRLF
5181592Srgrimes		{
519101379Syar			if ($2 && $4 != NULL) {
52017433Spst				if (fromname) {
52117433Spst					renamecmd(fromname, $4);
52217433Spst					free(fromname);
52317433Spst					fromname = (char *) 0;
52417433Spst				} else {
52517433Spst					reply(503, "Bad sequence of commands.");
52617433Spst				}
5271592Srgrimes			}
528101379Syar			if ($4 != NULL)
529101379Syar				free($4);
5301592Srgrimes		}
53171278Sjedgar	| ABOR check_login CRLF
5321592Srgrimes		{
53371278Sjedgar			if ($2)
53471278Sjedgar				reply(225, "ABOR command successful.");
5351592Srgrimes		}
5361592Srgrimes	| CWD check_login CRLF
5371592Srgrimes		{
53869234Sdanny			if ($2) {
53969234Sdanny				if (guest)
54069234Sdanny					cwd("/");
54169234Sdanny				else
54269234Sdanny					cwd(pw->pw_dir);
54369234Sdanny			}
5441592Srgrimes		}
5451592Srgrimes	| CWD check_login SP pathname CRLF
5461592Srgrimes		{
5471592Srgrimes			if ($2 && $4 != NULL)
5481592Srgrimes				cwd($4);
5491592Srgrimes			if ($4 != NULL)
5501592Srgrimes				free($4);
5511592Srgrimes		}
5521592Srgrimes	| HELP CRLF
5531592Srgrimes		{
5541592Srgrimes			help(cmdtab, (char *) 0);
5551592Srgrimes		}
5561592Srgrimes	| HELP SP STRING CRLF
5571592Srgrimes		{
5581592Srgrimes			char *cp = $3;
5591592Srgrimes
5601592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5611592Srgrimes				cp = $3 + 4;
5621592Srgrimes				if (*cp == ' ')
5631592Srgrimes					cp++;
5641592Srgrimes				if (*cp)
5651592Srgrimes					help(sitetab, cp);
5661592Srgrimes				else
5671592Srgrimes					help(sitetab, (char *) 0);
5681592Srgrimes			} else
5691592Srgrimes				help(cmdtab, $3);
57088935Sdwmalone			free($3);
5711592Srgrimes		}
5721592Srgrimes	| NOOP CRLF
5731592Srgrimes		{
5741592Srgrimes			reply(200, "NOOP command successful.");
5751592Srgrimes		}
57670102Sphk	| MKD check_login_ro SP pathname CRLF
5771592Srgrimes		{
5781592Srgrimes			if ($2 && $4 != NULL)
5791592Srgrimes				makedir($4);
5801592Srgrimes			if ($4 != NULL)
5811592Srgrimes				free($4);
5821592Srgrimes		}
58370102Sphk	| RMD check_login_ro SP pathname CRLF
5841592Srgrimes		{
5851592Srgrimes			if ($2 && $4 != NULL)
5861592Srgrimes				removedir($4);
5871592Srgrimes			if ($4 != NULL)
5881592Srgrimes				free($4);
5891592Srgrimes		}
5901592Srgrimes	| PWD check_login CRLF
5911592Srgrimes		{
5921592Srgrimes			if ($2)
5931592Srgrimes				pwd();
5941592Srgrimes		}
5951592Srgrimes	| CDUP check_login CRLF
5961592Srgrimes		{
5971592Srgrimes			if ($2)
5981592Srgrimes				cwd("..");
5991592Srgrimes		}
6001592Srgrimes	| SITE SP HELP CRLF
6011592Srgrimes		{
6021592Srgrimes			help(sitetab, (char *) 0);
6031592Srgrimes		}
6041592Srgrimes	| SITE SP HELP SP STRING CRLF
6051592Srgrimes		{
6061592Srgrimes			help(sitetab, $5);
60788935Sdwmalone			free($5);
6081592Srgrimes		}
60975535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
61075535Sphk		{
61175535Sphk			char p[64], *q;
61275535Sphk
613101379Syar			if ($4 && $6) {
61475535Sphk				q = MD5File($6, p);
61575535Sphk				if (q != NULL)
61675535Sphk					reply(200, "MD5(%s) = %s", $6, p);
61775535Sphk				else
61875535Sphk					perror_reply(550, $6);
61975535Sphk			}
62088935Sdwmalone			if ($6)
62188935Sdwmalone				free($6);
62275535Sphk		}
6231592Srgrimes	| SITE SP UMASK check_login CRLF
6241592Srgrimes		{
6251592Srgrimes			int oldmask;
6261592Srgrimes
6271592Srgrimes			if ($4) {
6281592Srgrimes				oldmask = umask(0);
6291592Srgrimes				(void) umask(oldmask);
6301592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6311592Srgrimes			}
6321592Srgrimes		}
6331592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6341592Srgrimes		{
6351592Srgrimes			int oldmask;
6361592Srgrimes
6371592Srgrimes			if ($4) {
6381592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6391592Srgrimes					reply(501, "Bad UMASK value");
6401592Srgrimes				} else {
6411592Srgrimes					oldmask = umask($6);
6421592Srgrimes					reply(200,
6431592Srgrimes					    "UMASK set to %03o (was %03o)",
6441592Srgrimes					    $6, oldmask);
6451592Srgrimes				}
6461592Srgrimes			}
6471592Srgrimes		}
64870102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6491592Srgrimes		{
6501592Srgrimes			if ($4 && ($8 != NULL)) {
651101378Syar				if (($6 == -1 ) || ($6 > 0777))
652101378Syar					reply(501, "Bad mode value");
6531592Srgrimes				else if (chmod($8, $6) < 0)
6541592Srgrimes					perror_reply(550, $8);
6551592Srgrimes				else
6561592Srgrimes					reply(200, "CHMOD command successful.");
6571592Srgrimes			}
6581592Srgrimes			if ($8 != NULL)
6591592Srgrimes				free($8);
6601592Srgrimes		}
66171278Sjedgar	| SITE SP check_login IDLE CRLF
6621592Srgrimes		{
66371278Sjedgar			if ($3)
66471278Sjedgar				reply(200,
66571278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
66671278Sjedgar				    timeout, maxtimeout);
6671592Srgrimes		}
66871278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6691592Srgrimes		{
67071278Sjedgar			if ($3) {
67192272Smaxim				if ($6.i < 30 || $6.i > maxtimeout) {
67271278Sjedgar					reply(501,
67371278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
67471278Sjedgar					    maxtimeout);
67571278Sjedgar				} else {
67692272Smaxim					timeout = $6.i;
67771278Sjedgar					(void) alarm((unsigned) timeout);
67871278Sjedgar					reply(200,
67971278Sjedgar					    "Maximum IDLE time set to %d seconds",
68071278Sjedgar					    timeout);
68171278Sjedgar				}
6821592Srgrimes			}
6831592Srgrimes		}
68470102Sphk	| STOU check_login_ro SP pathname CRLF
6851592Srgrimes		{
6861592Srgrimes			if ($2 && $4 != NULL)
6871592Srgrimes				store($4, "w", 1);
6881592Srgrimes			if ($4 != NULL)
6891592Srgrimes				free($4);
6901592Srgrimes		}
69171278Sjedgar	| SYST check_login CRLF
6921592Srgrimes		{
69371278Sjedgar			if ($2)
6941592Srgrimes#ifdef unix
6951592Srgrimes#ifdef BSD
6961592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6971592Srgrimes				NBBY, BSD);
6981592Srgrimes#else /* BSD */
6991592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
7001592Srgrimes#endif /* BSD */
7011592Srgrimes#else /* unix */
7021592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
7031592Srgrimes#endif /* unix */
7041592Srgrimes		}
7051592Srgrimes
7061592Srgrimes		/*
7071592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
7081592Srgrimes		 * it will be in the updated RFC.
7091592Srgrimes		 *
7101592Srgrimes		 * Return size of file in a format suitable for
7111592Srgrimes		 * using with RESTART (we just count bytes).
7121592Srgrimes		 */
7131592Srgrimes	| SIZE check_login SP pathname CRLF
7141592Srgrimes		{
7151592Srgrimes			if ($2 && $4 != NULL)
7161592Srgrimes				sizecmd($4);
7171592Srgrimes			if ($4 != NULL)
7181592Srgrimes				free($4);
7191592Srgrimes		}
7201592Srgrimes
7211592Srgrimes		/*
7221592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7231592Srgrimes		 * it will be in the updated RFC.
7241592Srgrimes		 *
7251592Srgrimes		 * Return modification time of file as an ISO 3307
7261592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7271592Srgrimes		 * where xxx is the fractional second (of any precision,
7281592Srgrimes		 * not necessarily 3 digits)
7291592Srgrimes		 */
7301592Srgrimes	| MDTM check_login SP pathname CRLF
7311592Srgrimes		{
7321592Srgrimes			if ($2 && $4 != NULL) {
7331592Srgrimes				struct stat stbuf;
7341592Srgrimes				if (stat($4, &stbuf) < 0)
7351592Srgrimes					reply(550, "%s: %s",
7361592Srgrimes					    $4, strerror(errno));
7371592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7381592Srgrimes					reply(550, "%s: not a plain file.", $4);
7391592Srgrimes				} else {
7401592Srgrimes					struct tm *t;
7411592Srgrimes					t = gmtime(&stbuf.st_mtime);
7421592Srgrimes					reply(213,
74317435Spst					    "%04d%02d%02d%02d%02d%02d",
74417435Spst					    1900 + t->tm_year,
74517435Spst					    t->tm_mon+1, t->tm_mday,
7461592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7471592Srgrimes				}
7481592Srgrimes			}
7491592Srgrimes			if ($4 != NULL)
7501592Srgrimes				free($4);
7511592Srgrimes		}
7521592Srgrimes	| QUIT CRLF
7531592Srgrimes		{
7541592Srgrimes			reply(221, "Goodbye.");
7551592Srgrimes			dologout(0);
7561592Srgrimes		}
75789935Syar	| error
7581592Srgrimes		{
75989935Syar			yyclearin;		/* discard lookahead data */
76089935Syar			yyerrok;		/* clear error condition */
76189935Syar			state = 0;		/* reset lexer state */
7621592Srgrimes		}
7631592Srgrimes	;
7641592Srgrimesrcmd
76570102Sphk	: RNFR check_login_ro SP pathname CRLF
7661592Srgrimes		{
7671592Srgrimes			restart_point = (off_t) 0;
7681592Srgrimes			if ($2 && $4) {
76988935Sdwmalone				if (fromname)
77088935Sdwmalone					free(fromname);
77188935Sdwmalone				fromname = (char *) 0;
77288935Sdwmalone				if (renamefrom($4))
77388935Sdwmalone					fromname = $4;
77488935Sdwmalone				else
7751592Srgrimes					free($4);
77688935Sdwmalone			} else if ($4) {
77788935Sdwmalone				free($4);
7781592Srgrimes			}
7791592Srgrimes		}
78092272Smaxim	| REST check_login SP NUMBER CRLF
7811592Srgrimes		{
78271278Sjedgar			if ($2) {
78388935Sdwmalone				if (fromname)
78488935Sdwmalone					free(fromname);
78571278Sjedgar				fromname = (char *) 0;
78692272Smaxim				restart_point = $4.o;
78792272Smaxim				reply(350, "Restarting at %llu. %s",
78871278Sjedgar				    restart_point,
78971278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
79071278Sjedgar			}
7911592Srgrimes		}
7921592Srgrimes	;
7931592Srgrimes
7941592Srgrimesusername
7951592Srgrimes	: STRING
7961592Srgrimes	;
7971592Srgrimes
7981592Srgrimespassword
7991592Srgrimes	: /* empty */
8001592Srgrimes		{
8011592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
8021592Srgrimes		}
8031592Srgrimes	| STRING
8041592Srgrimes	;
8051592Srgrimes
8061592Srgrimesbyte_size
8071592Srgrimes	: NUMBER
80892272Smaxim		{
80992272Smaxim			$$ = $1.i;
81092272Smaxim		}
8111592Srgrimes	;
8121592Srgrimes
8131592Srgrimeshost_port
8141592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8151592Srgrimes		NUMBER COMMA NUMBER
8161592Srgrimes		{
8171592Srgrimes			char *a, *p;
8181592Srgrimes
81956668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
82056668Sshin			data_dest.su_family = AF_INET;
82156668Sshin			p = (char *)&data_dest.su_sin.sin_port;
82292272Smaxim			p[0] = $9.i; p[1] = $11.i;
82356668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
82492272Smaxim			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
8251592Srgrimes		}
8261592Srgrimes	;
8271592Srgrimes
82856668Sshinhost_long_port
82956668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83156668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83256668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83356668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83456668Sshin		NUMBER
83556668Sshin		{
83656668Sshin			char *a, *p;
83756668Sshin
83856668Sshin			memset(&data_dest, 0, sizeof(data_dest));
83956668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
84056668Sshin			data_dest.su_family = AF_INET6;
84156668Sshin			p = (char *)&data_dest.su_port;
84292272Smaxim			p[0] = $39.i; p[1] = $41.i;
84356668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
84492272Smaxim			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
84592272Smaxim			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
84692272Smaxim			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
84792272Smaxim			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
84856668Sshin			if (his_addr.su_family == AF_INET6) {
84956668Sshin				/* XXX more sanity checks! */
85056668Sshin				data_dest.su_sin6.sin6_scope_id =
85156668Sshin					his_addr.su_sin6.sin6_scope_id;
85256668Sshin			}
85392272Smaxim			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
85456668Sshin				memset(&data_dest, 0, sizeof(data_dest));
85556668Sshin		}
85656668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
85756668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
85856668Sshin		NUMBER
85956668Sshin		{
86056668Sshin			char *a, *p;
86156668Sshin
86256668Sshin			memset(&data_dest, 0, sizeof(data_dest));
86356668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
86456668Sshin			data_dest.su_family = AF_INET;
86556668Sshin			p = (char *)&data_dest.su_port;
86692272Smaxim			p[0] = $15.i; p[1] = $17.i;
86756668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
86892272Smaxim			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
86992272Smaxim			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
87056668Sshin				memset(&data_dest, 0, sizeof(data_dest));
87156668Sshin		}
87256668Sshin	;
87356668Sshin
8741592Srgrimesform_code
8751592Srgrimes	: N
8761592Srgrimes		{
8771592Srgrimes			$$ = FORM_N;
8781592Srgrimes		}
8791592Srgrimes	| T
8801592Srgrimes		{
8811592Srgrimes			$$ = FORM_T;
8821592Srgrimes		}
8831592Srgrimes	| C
8841592Srgrimes		{
8851592Srgrimes			$$ = FORM_C;
8861592Srgrimes		}
8871592Srgrimes	;
8881592Srgrimes
8891592Srgrimestype_code
8901592Srgrimes	: A
8911592Srgrimes		{
8921592Srgrimes			cmd_type = TYPE_A;
8931592Srgrimes			cmd_form = FORM_N;
8941592Srgrimes		}
8951592Srgrimes	| A SP form_code
8961592Srgrimes		{
8971592Srgrimes			cmd_type = TYPE_A;
8981592Srgrimes			cmd_form = $3;
8991592Srgrimes		}
9001592Srgrimes	| E
9011592Srgrimes		{
9021592Srgrimes			cmd_type = TYPE_E;
9031592Srgrimes			cmd_form = FORM_N;
9041592Srgrimes		}
9051592Srgrimes	| E SP form_code
9061592Srgrimes		{
9071592Srgrimes			cmd_type = TYPE_E;
9081592Srgrimes			cmd_form = $3;
9091592Srgrimes		}
9101592Srgrimes	| I
9111592Srgrimes		{
9121592Srgrimes			cmd_type = TYPE_I;
9131592Srgrimes		}
9141592Srgrimes	| L
9151592Srgrimes		{
9161592Srgrimes			cmd_type = TYPE_L;
9171592Srgrimes			cmd_bytesz = NBBY;
9181592Srgrimes		}
9191592Srgrimes	| L SP byte_size
9201592Srgrimes		{
9211592Srgrimes			cmd_type = TYPE_L;
9221592Srgrimes			cmd_bytesz = $3;
9231592Srgrimes		}
9241592Srgrimes		/* this is for a bug in the BBN ftp */
9251592Srgrimes	| L byte_size
9261592Srgrimes		{
9271592Srgrimes			cmd_type = TYPE_L;
9281592Srgrimes			cmd_bytesz = $2;
9291592Srgrimes		}
9301592Srgrimes	;
9311592Srgrimes
9321592Srgrimesstruct_code
9331592Srgrimes	: F
9341592Srgrimes		{
9351592Srgrimes			$$ = STRU_F;
9361592Srgrimes		}
9371592Srgrimes	| R
9381592Srgrimes		{
9391592Srgrimes			$$ = STRU_R;
9401592Srgrimes		}
9411592Srgrimes	| P
9421592Srgrimes		{
9431592Srgrimes			$$ = STRU_P;
9441592Srgrimes		}
9451592Srgrimes	;
9461592Srgrimes
9471592Srgrimesmode_code
9481592Srgrimes	: S
9491592Srgrimes		{
9501592Srgrimes			$$ = MODE_S;
9511592Srgrimes		}
9521592Srgrimes	| B
9531592Srgrimes		{
9541592Srgrimes			$$ = MODE_B;
9551592Srgrimes		}
9561592Srgrimes	| C
9571592Srgrimes		{
9581592Srgrimes			$$ = MODE_C;
9591592Srgrimes		}
9601592Srgrimes	;
9611592Srgrimes
9621592Srgrimespathname
9631592Srgrimes	: pathstring
9641592Srgrimes		{
9651592Srgrimes			/*
9661592Srgrimes			 * Problem: this production is used for all pathname
9671592Srgrimes			 * processing, but only gives a 550 error reply.
9681592Srgrimes			 * This is a valid reply in some cases but not in others.
9691592Srgrimes			 */
97075567Speter			if (logged_in && $1) {
9711592Srgrimes				glob_t gl;
9721592Srgrimes				int flags =
973100222Smikeh				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
9741592Srgrimes
9751592Srgrimes				memset(&gl, 0, sizeof(gl));
97675560Sjedgar				flags |= GLOB_MAXPATH;
97775560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9781592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9791592Srgrimes				    gl.gl_pathc == 0) {
9801592Srgrimes					reply(550, "not found");
9811592Srgrimes					$$ = NULL;
98275567Speter				} else if (gl.gl_pathc > 1) {
98375567Speter					reply(550, "ambiguous");
98475567Speter					$$ = NULL;
9851592Srgrimes				} else {
9861592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9871592Srgrimes				}
9881592Srgrimes				globfree(&gl);
9891592Srgrimes				free($1);
9901592Srgrimes			} else
9911592Srgrimes				$$ = $1;
9921592Srgrimes		}
9931592Srgrimes	;
9941592Srgrimes
9951592Srgrimespathstring
9961592Srgrimes	: STRING
9971592Srgrimes	;
9981592Srgrimes
9991592Srgrimesoctal_number
10001592Srgrimes	: NUMBER
10011592Srgrimes		{
10021592Srgrimes			int ret, dec, multby, digit;
10031592Srgrimes
10041592Srgrimes			/*
10051592Srgrimes			 * Convert a number that was read as decimal number
10061592Srgrimes			 * to what it would be if it had been read as octal.
10071592Srgrimes			 */
100892272Smaxim			dec = $1.i;
10091592Srgrimes			multby = 1;
10101592Srgrimes			ret = 0;
10111592Srgrimes			while (dec) {
10121592Srgrimes				digit = dec%10;
10131592Srgrimes				if (digit > 7) {
10141592Srgrimes					ret = -1;
10151592Srgrimes					break;
10161592Srgrimes				}
10171592Srgrimes				ret += digit * multby;
10181592Srgrimes				multby *= 8;
10191592Srgrimes				dec /= 10;
10201592Srgrimes			}
10211592Srgrimes			$$ = ret;
10221592Srgrimes		}
10231592Srgrimes	;
10241592Srgrimes
10251592Srgrimes
10261592Srgrimescheck_login
10271592Srgrimes	: /* empty */
10281592Srgrimes		{
102970102Sphk		$$ = check_login1();
10301592Srgrimes		}
10311592Srgrimes	;
10321592Srgrimes
103370102Sphkcheck_login_epsv
103470102Sphk	: /* empty */
103570102Sphk		{
103670102Sphk		if (noepsv) {
103770102Sphk			reply(500, "EPSV command disabled");
103870102Sphk			$$ = 0;
103970102Sphk		}
104070102Sphk		else
104170102Sphk			$$ = check_login1();
104270102Sphk		}
104370102Sphk	;
104470102Sphk
104570102Sphkcheck_login_ro
104670102Sphk	: /* empty */
104770102Sphk		{
104870102Sphk		if (readonly) {
104972710Sdes			reply(550, "Permission denied.");
105070102Sphk			$$ = 0;
105170102Sphk		}
105270102Sphk		else
105370102Sphk			$$ = check_login1();
105470102Sphk		}
105570102Sphk	;
105670102Sphk
10571592Srgrimes%%
10581592Srgrimes
10591592Srgrimes#define	CMD	0	/* beginning of command */
10601592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10611592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10621592Srgrimes#define	STR2	3	/* expect STRING */
10631592Srgrimes#define	OSTR	4	/* optional SP then STRING */
106475556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10651592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10661592Srgrimes#define	SITECMD	7	/* SITE command */
10671592Srgrimes#define	NSTR	8	/* Number followed by a string */
10681592Srgrimes
106975560Sjedgar#define	MAXGLOBARGS	1000
107075560Sjedgar
1071101034Syar#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1072101034Syar
10731592Srgrimesstruct tab {
10741592Srgrimes	char	*name;
10751592Srgrimes	short	token;
10761592Srgrimes	short	state;
10771592Srgrimes	short	implemented;	/* 1 if command is implemented */
10781592Srgrimes	char	*help;
10791592Srgrimes};
10801592Srgrimes
10811592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10821592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
108375556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10841592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10851592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10861592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10871592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10881592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
108956668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
109056668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10911592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
109256668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
109356668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10941592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10951592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10961592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10971592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10981592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10991592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
11001592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
11011592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
11021592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
11031592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
11041592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
11051592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
11061592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
11071592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
11081592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
11091592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
11101592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
11111592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
11121592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
11131592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
11141592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
11151592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
11161592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
11171592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
11181592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
11191592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
11201592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11211592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
11221592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
11231592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
11241592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
11251592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11261592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11271592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11281592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11291592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11301592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11311592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11321592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11331592Srgrimes	{ NULL,   0,    0,    0,	0 }
11341592Srgrimes};
11351592Srgrimes
11361592Srgrimesstruct tab sitetab[] = {
113775535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11381592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11391592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11401592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11411592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11421592Srgrimes	{ NULL,   0,    0,    0,	0 }
11431592Srgrimes};
11441592Srgrimes
114590148Simpstatic char	*copy(char *);
114690148Simpstatic void	 help(struct tab *, char *);
11471592Srgrimesstatic struct tab *
114890148Simp		 lookup(struct tab *, char *);
114990148Simpstatic int	 port_check(const char *);
115090148Simpstatic int	 port_check_v6(const char *);
115190148Simpstatic void	 sizecmd(char *);
115290148Simpstatic void	 toolong(int);
115390148Simpstatic void	 v4map_data_dest(void);
115490148Simpstatic int	 yylex(void);
11551592Srgrimes
11561592Srgrimesstatic struct tab *
115790148Simplookup(struct tab *p, char *cmd)
11581592Srgrimes{
11591592Srgrimes
11601592Srgrimes	for (; p->name != NULL; p++)
11611592Srgrimes		if (strcmp(cmd, p->name) == 0)
11621592Srgrimes			return (p);
11631592Srgrimes	return (0);
11641592Srgrimes}
11651592Srgrimes
11661592Srgrimes#include <arpa/telnet.h>
11671592Srgrimes
11681592Srgrimes/*
11691592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11701592Srgrimes */
11711592Srgrimeschar *
117290148Simpgetline(char *s, int n, FILE *iop)
11731592Srgrimes{
11741592Srgrimes	int c;
11751592Srgrimes	register char *cs;
11761592Srgrimes
11771592Srgrimes	cs = s;
11781592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11791592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11801592Srgrimes		*cs++ = tmpline[c];
11811592Srgrimes		if (tmpline[c] == '\n') {
11821592Srgrimes			*cs++ = '\0';
118376096Smarkm			if (ftpdebug)
11841592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11851592Srgrimes			tmpline[0] = '\0';
11861592Srgrimes			return(s);
11871592Srgrimes		}
11881592Srgrimes		if (c == 0)
11891592Srgrimes			tmpline[0] = '\0';
11901592Srgrimes	}
11911592Srgrimes	while ((c = getc(iop)) != EOF) {
11921592Srgrimes		c &= 0377;
11931592Srgrimes		if (c == IAC) {
11941592Srgrimes		    if ((c = getc(iop)) != EOF) {
11951592Srgrimes			c &= 0377;
11961592Srgrimes			switch (c) {
11971592Srgrimes			case WILL:
11981592Srgrimes			case WONT:
11991592Srgrimes				c = getc(iop);
12001592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
12011592Srgrimes				(void) fflush(stdout);
12021592Srgrimes				continue;
12031592Srgrimes			case DO:
12041592Srgrimes			case DONT:
12051592Srgrimes				c = getc(iop);
12061592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
12071592Srgrimes				(void) fflush(stdout);
12081592Srgrimes				continue;
12091592Srgrimes			case IAC:
12101592Srgrimes				break;
12111592Srgrimes			default:
12121592Srgrimes				continue;	/* ignore command */
12131592Srgrimes			}
12141592Srgrimes		    }
12151592Srgrimes		}
12161592Srgrimes		*cs++ = c;
12171592Srgrimes		if (--n <= 0 || c == '\n')
12181592Srgrimes			break;
12191592Srgrimes	}
12201592Srgrimes	if (c == EOF && cs == s)
12211592Srgrimes		return (NULL);
12221592Srgrimes	*cs++ = '\0';
122376096Smarkm	if (ftpdebug) {
12241592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12251592Srgrimes			/* Don't syslog passwords */
12261592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12271592Srgrimes		} else {
12281592Srgrimes			register char *cp;
12291592Srgrimes			register int len;
12301592Srgrimes
12311592Srgrimes			/* Don't syslog trailing CR-LF */
12321592Srgrimes			len = strlen(s);
12331592Srgrimes			cp = s + len - 1;
12341592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12351592Srgrimes				--cp;
12361592Srgrimes				--len;
12371592Srgrimes			}
12381592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12391592Srgrimes		}
12401592Srgrimes	}
12411592Srgrimes	return (s);
12421592Srgrimes}
12431592Srgrimes
12441592Srgrimesstatic void
124590148Simptoolong(int signo)
12461592Srgrimes{
12471592Srgrimes
12481592Srgrimes	reply(421,
12491592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12501592Srgrimes	if (logging)
12511592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12521592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12531592Srgrimes	dologout(1);
12541592Srgrimes}
12551592Srgrimes
12561592Srgrimesstatic int
125790148Simpyylex(void)
12581592Srgrimes{
125989935Syar	static int cpos;
12601592Srgrimes	char *cp, *cp2;
12611592Srgrimes	struct tab *p;
12621592Srgrimes	int n;
12631592Srgrimes	char c;
12641592Srgrimes
12651592Srgrimes	for (;;) {
12661592Srgrimes		switch (state) {
12671592Srgrimes
12681592Srgrimes		case CMD:
12691592Srgrimes			(void) signal(SIGALRM, toolong);
12701592Srgrimes			(void) alarm((unsigned) timeout);
12711592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12721592Srgrimes				reply(221, "You could at least say goodbye.");
12731592Srgrimes				dologout(0);
12741592Srgrimes			}
12751592Srgrimes			(void) alarm(0);
12761592Srgrimes#ifdef SETPROCTITLE
127729574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12781592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12791592Srgrimes#endif /* SETPROCTITLE */
12801592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12811592Srgrimes				*cp++ = '\n';
12821592Srgrimes				*cp = '\0';
12831592Srgrimes			}
12841592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12851592Srgrimes				cpos = cp - cbuf;
12861592Srgrimes			if (cpos == 0)
12871592Srgrimes				cpos = 4;
12881592Srgrimes			c = cbuf[cpos];
12891592Srgrimes			cbuf[cpos] = '\0';
12901592Srgrimes			upper(cbuf);
12911592Srgrimes			p = lookup(cmdtab, cbuf);
12921592Srgrimes			cbuf[cpos] = c;
12933776Spst			if (p != 0) {
12941592Srgrimes				if (p->implemented == 0) {
12951592Srgrimes					nack(p->name);
129689935Syar					return (LEXERR);
12971592Srgrimes				}
12981592Srgrimes				state = p->state;
12991592Srgrimes				yylval.s = p->name;
13001592Srgrimes				return (p->token);
13011592Srgrimes			}
13021592Srgrimes			break;
13031592Srgrimes
13041592Srgrimes		case SITECMD:
13051592Srgrimes			if (cbuf[cpos] == ' ') {
13061592Srgrimes				cpos++;
13071592Srgrimes				return (SP);
13081592Srgrimes			}
13091592Srgrimes			cp = &cbuf[cpos];
13101592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
13111592Srgrimes				cpos = cp2 - cbuf;
13121592Srgrimes			c = cbuf[cpos];
13131592Srgrimes			cbuf[cpos] = '\0';
13141592Srgrimes			upper(cp);
13151592Srgrimes			p = lookup(sitetab, cp);
13161592Srgrimes			cbuf[cpos] = c;
13173777Spst			if (guest == 0 && p != 0) {
13181592Srgrimes				if (p->implemented == 0) {
13191592Srgrimes					state = CMD;
13201592Srgrimes					nack(p->name);
132189935Syar					return (LEXERR);
13221592Srgrimes				}
13231592Srgrimes				state = p->state;
13241592Srgrimes				yylval.s = p->name;
13251592Srgrimes				return (p->token);
13261592Srgrimes			}
13271592Srgrimes			state = CMD;
13281592Srgrimes			break;
13291592Srgrimes
133075556Sgreen		case ZSTR1:
13311592Srgrimes		case OSTR:
13321592Srgrimes			if (cbuf[cpos] == '\n') {
13331592Srgrimes				state = CMD;
13341592Srgrimes				return (CRLF);
13351592Srgrimes			}
13361592Srgrimes			/* FALLTHROUGH */
13371592Srgrimes
13381592Srgrimes		case STR1:
13391592Srgrimes		dostr1:
13401592Srgrimes			if (cbuf[cpos] == ' ') {
13411592Srgrimes				cpos++;
134251979Salfred				state = state == OSTR ? STR2 : state+1;
13431592Srgrimes				return (SP);
13441592Srgrimes			}
13451592Srgrimes			break;
13461592Srgrimes
13471592Srgrimes		case ZSTR2:
13481592Srgrimes			if (cbuf[cpos] == '\n') {
13491592Srgrimes				state = CMD;
13501592Srgrimes				return (CRLF);
13511592Srgrimes			}
13521592Srgrimes			/* FALLTHROUGH */
13531592Srgrimes
13541592Srgrimes		case STR2:
13551592Srgrimes			cp = &cbuf[cpos];
13561592Srgrimes			n = strlen(cp);
13571592Srgrimes			cpos += n - 1;
13581592Srgrimes			/*
13591592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13601592Srgrimes			 */
13611592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13621592Srgrimes				cbuf[cpos] = '\0';
13631592Srgrimes				yylval.s = copy(cp);
13641592Srgrimes				cbuf[cpos] = '\n';
13651592Srgrimes				state = ARGS;
13661592Srgrimes				return (STRING);
13671592Srgrimes			}
13681592Srgrimes			break;
13691592Srgrimes
13701592Srgrimes		case NSTR:
13711592Srgrimes			if (cbuf[cpos] == ' ') {
13721592Srgrimes				cpos++;
13731592Srgrimes				return (SP);
13741592Srgrimes			}
13751592Srgrimes			if (isdigit(cbuf[cpos])) {
13761592Srgrimes				cp = &cbuf[cpos];
13771592Srgrimes				while (isdigit(cbuf[++cpos]))
13781592Srgrimes					;
13791592Srgrimes				c = cbuf[cpos];
13801592Srgrimes				cbuf[cpos] = '\0';
138192272Smaxim				yylval.u.i = atoi(cp);
13821592Srgrimes				cbuf[cpos] = c;
13831592Srgrimes				state = STR1;
13841592Srgrimes				return (NUMBER);
13851592Srgrimes			}
13861592Srgrimes			state = STR1;
13871592Srgrimes			goto dostr1;
13881592Srgrimes
13891592Srgrimes		case ARGS:
13901592Srgrimes			if (isdigit(cbuf[cpos])) {
13911592Srgrimes				cp = &cbuf[cpos];
13921592Srgrimes				while (isdigit(cbuf[++cpos]))
13931592Srgrimes					;
13941592Srgrimes				c = cbuf[cpos];
13951592Srgrimes				cbuf[cpos] = '\0';
139692272Smaxim				yylval.u.i = atoi(cp);
139792272Smaxim				yylval.u.o = strtoull(cp, (char **)NULL, 10);
13981592Srgrimes				cbuf[cpos] = c;
13991592Srgrimes				return (NUMBER);
14001592Srgrimes			}
140156668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
140256668Sshin			 && !isalnum(cbuf[cpos + 3])) {
140356668Sshin				cpos += 3;
140456668Sshin				return ALL;
140556668Sshin			}
14061592Srgrimes			switch (cbuf[cpos++]) {
14071592Srgrimes
14081592Srgrimes			case '\n':
14091592Srgrimes				state = CMD;
14101592Srgrimes				return (CRLF);
14111592Srgrimes
14121592Srgrimes			case ' ':
14131592Srgrimes				return (SP);
14141592Srgrimes
14151592Srgrimes			case ',':
14161592Srgrimes				return (COMMA);
14171592Srgrimes
14181592Srgrimes			case 'A':
14191592Srgrimes			case 'a':
14201592Srgrimes				return (A);
14211592Srgrimes
14221592Srgrimes			case 'B':
14231592Srgrimes			case 'b':
14241592Srgrimes				return (B);
14251592Srgrimes
14261592Srgrimes			case 'C':
14271592Srgrimes			case 'c':
14281592Srgrimes				return (C);
14291592Srgrimes
14301592Srgrimes			case 'E':
14311592Srgrimes			case 'e':
14321592Srgrimes				return (E);
14331592Srgrimes
14341592Srgrimes			case 'F':
14351592Srgrimes			case 'f':
14361592Srgrimes				return (F);
14371592Srgrimes
14381592Srgrimes			case 'I':
14391592Srgrimes			case 'i':
14401592Srgrimes				return (I);
14411592Srgrimes
14421592Srgrimes			case 'L':
14431592Srgrimes			case 'l':
14441592Srgrimes				return (L);
14451592Srgrimes
14461592Srgrimes			case 'N':
14471592Srgrimes			case 'n':
14481592Srgrimes				return (N);
14491592Srgrimes
14501592Srgrimes			case 'P':
14511592Srgrimes			case 'p':
14521592Srgrimes				return (P);
14531592Srgrimes
14541592Srgrimes			case 'R':
14551592Srgrimes			case 'r':
14561592Srgrimes				return (R);
14571592Srgrimes
14581592Srgrimes			case 'S':
14591592Srgrimes			case 's':
14601592Srgrimes				return (S);
14611592Srgrimes
14621592Srgrimes			case 'T':
14631592Srgrimes			case 't':
14641592Srgrimes				return (T);
14651592Srgrimes
14661592Srgrimes			}
14671592Srgrimes			break;
14681592Srgrimes
14691592Srgrimes		default:
147076096Smarkm			fatalerror("Unknown state in scanner.");
14711592Srgrimes		}
14721592Srgrimes		state = CMD;
147389935Syar		return (LEXERR);
14741592Srgrimes	}
14751592Srgrimes}
14761592Srgrimes
14771592Srgrimesvoid
147890148Simpupper(char *s)
14791592Srgrimes{
14801592Srgrimes	while (*s != '\0') {
14811592Srgrimes		if (islower(*s))
14821592Srgrimes			*s = toupper(*s);
14831592Srgrimes		s++;
14841592Srgrimes	}
14851592Srgrimes}
14861592Srgrimes
14871592Srgrimesstatic char *
148890148Simpcopy(char *s)
14891592Srgrimes{
14901592Srgrimes	char *p;
14911592Srgrimes
14921592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14931592Srgrimes	if (p == NULL)
149476096Smarkm		fatalerror("Ran out of memory.");
14951592Srgrimes	(void) strcpy(p, s);
14961592Srgrimes	return (p);
14971592Srgrimes}
14981592Srgrimes
14991592Srgrimesstatic void
150090148Simphelp(struct tab *ctab, char *s)
15011592Srgrimes{
15021592Srgrimes	struct tab *c;
15031592Srgrimes	int width, NCMDS;
15041592Srgrimes	char *type;
15051592Srgrimes
15061592Srgrimes	if (ctab == sitetab)
15071592Srgrimes		type = "SITE ";
15081592Srgrimes	else
15091592Srgrimes		type = "";
15101592Srgrimes	width = 0, NCMDS = 0;
15111592Srgrimes	for (c = ctab; c->name != NULL; c++) {
15121592Srgrimes		int len = strlen(c->name);
15131592Srgrimes
15141592Srgrimes		if (len > width)
15151592Srgrimes			width = len;
15161592Srgrimes		NCMDS++;
15171592Srgrimes	}
15181592Srgrimes	width = (width + 8) &~ 7;
15191592Srgrimes	if (s == 0) {
15201592Srgrimes		int i, j, w;
15211592Srgrimes		int columns, lines;
15221592Srgrimes
15231592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15241592Srgrimes		    type, "(* =>'s unimplemented)");
15251592Srgrimes		columns = 76 / width;
15261592Srgrimes		if (columns == 0)
15271592Srgrimes			columns = 1;
15281592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15291592Srgrimes		for (i = 0; i < lines; i++) {
15301592Srgrimes			printf("   ");
15311592Srgrimes			for (j = 0; j < columns; j++) {
15321592Srgrimes				c = ctab + j * lines + i;
15331592Srgrimes				printf("%s%c", c->name,
15341592Srgrimes					c->implemented ? ' ' : '*');
15351592Srgrimes				if (c + lines >= &ctab[NCMDS])
15361592Srgrimes					break;
15371592Srgrimes				w = strlen(c->name) + 1;
15381592Srgrimes				while (w < width) {
15391592Srgrimes					putchar(' ');
15401592Srgrimes					w++;
15411592Srgrimes				}
15421592Srgrimes			}
15431592Srgrimes			printf("\r\n");
15441592Srgrimes		}
15451592Srgrimes		(void) fflush(stdout);
15461592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15471592Srgrimes		return;
15481592Srgrimes	}
15491592Srgrimes	upper(s);
15501592Srgrimes	c = lookup(ctab, s);
15511592Srgrimes	if (c == (struct tab *)0) {
15521592Srgrimes		reply(502, "Unknown command %s.", s);
15531592Srgrimes		return;
15541592Srgrimes	}
15551592Srgrimes	if (c->implemented)
15561592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15571592Srgrimes	else
15581592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15591592Srgrimes		    c->name, c->help);
15601592Srgrimes}
15611592Srgrimes
15621592Srgrimesstatic void
156390148Simpsizecmd(char *filename)
15641592Srgrimes{
15651592Srgrimes	switch (type) {
15661592Srgrimes	case TYPE_L:
15671592Srgrimes	case TYPE_I: {
15681592Srgrimes		struct stat stbuf;
156963350Sdes		if (stat(filename, &stbuf) < 0)
157063350Sdes			perror_reply(550, filename);
157163350Sdes		else if (!S_ISREG(stbuf.st_mode))
15721592Srgrimes			reply(550, "%s: not a plain file.", filename);
15731592Srgrimes		else
15741592Srgrimes			reply(213, "%qu", stbuf.st_size);
15751592Srgrimes		break; }
15761592Srgrimes	case TYPE_A: {
15771592Srgrimes		FILE *fin;
15781592Srgrimes		int c;
15791592Srgrimes		off_t count;
15801592Srgrimes		struct stat stbuf;
15811592Srgrimes		fin = fopen(filename, "r");
15821592Srgrimes		if (fin == NULL) {
15831592Srgrimes			perror_reply(550, filename);
15841592Srgrimes			return;
15851592Srgrimes		}
158663350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
158763350Sdes			perror_reply(550, filename);
158863350Sdes			(void) fclose(fin);
158963350Sdes			return;
159063350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15911592Srgrimes			reply(550, "%s: not a plain file.", filename);
15921592Srgrimes			(void) fclose(fin);
15931592Srgrimes			return;
1594101034Syar		} else if (stbuf.st_size > MAXASIZE) {
1595101034Syar			reply(550, "%s: too large for type A SIZE.", filename);
1596101034Syar			(void) fclose(fin);
1597101034Syar			return;
15981592Srgrimes		}
15991592Srgrimes
16001592Srgrimes		count = 0;
16011592Srgrimes		while((c=getc(fin)) != EOF) {
16021592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
16031592Srgrimes				count++;
16041592Srgrimes			count++;
16051592Srgrimes		}
16061592Srgrimes		(void) fclose(fin);
16071592Srgrimes
16081592Srgrimes		reply(213, "%qd", count);
16091592Srgrimes		break; }
16101592Srgrimes	default:
1611100684Syar		reply(504, "SIZE not implemented for type %s.",
1612100684Syar		           typenames[type]);
16131592Srgrimes	}
16141592Srgrimes}
161556668Sshin
161656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
161756668Sshinstatic int
161890148Simpport_check(const char *pcmd)
161956668Sshin{
162056668Sshin	if (his_addr.su_family == AF_INET) {
162156668Sshin		if (data_dest.su_family != AF_INET) {
162256668Sshin			usedefault = 1;
162356668Sshin			reply(500, "Invalid address rejected.");
162456668Sshin			return 1;
162556668Sshin		}
162656668Sshin		if (paranoid &&
162756668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
162856668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
162956668Sshin			    &his_addr.su_sin.sin_addr,
163056668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
163156668Sshin			usedefault = 1;
163256668Sshin			reply(500, "Illegal PORT range rejected.");
163356668Sshin		} else {
163456668Sshin			usedefault = 0;
163556668Sshin			if (pdata >= 0) {
163656668Sshin				(void) close(pdata);
163756668Sshin				pdata = -1;
163856668Sshin			}
163956668Sshin			reply(200, "%s command successful.", pcmd);
164056668Sshin		}
164156668Sshin		return 1;
164256668Sshin	}
164356668Sshin	return 0;
164456668Sshin}
164556668Sshin
164670102Sphkstatic int
164790148Simpcheck_login1(void)
164870102Sphk{
164970102Sphk	if (logged_in)
165070102Sphk		return 1;
165170102Sphk	else {
165270102Sphk		reply(530, "Please login with USER and PASS.");
165370102Sphk		return 0;
165470102Sphk	}
165570102Sphk}
165670102Sphk
165756668Sshin#ifdef INET6
165856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
165956668Sshinstatic int
166090148Simpport_check_v6(const char *pcmd)
166156668Sshin{
166256668Sshin	if (his_addr.su_family == AF_INET6) {
166356668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
166456668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
166556668Sshin			v4map_data_dest();
166656668Sshin		if (data_dest.su_family != AF_INET6) {
166756668Sshin			usedefault = 1;
166856668Sshin			reply(500, "Invalid address rejected.");
166956668Sshin			return 1;
167056668Sshin		}
167156668Sshin		if (paranoid &&
167256668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
167356668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
167456668Sshin			    &his_addr.su_sin6.sin6_addr,
167556668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
167656668Sshin			usedefault = 1;
167756668Sshin			reply(500, "Illegal PORT range rejected.");
167856668Sshin		} else {
167956668Sshin			usedefault = 0;
168056668Sshin			if (pdata >= 0) {
168156668Sshin				(void) close(pdata);
168256668Sshin				pdata = -1;
168356668Sshin			}
168456668Sshin			reply(200, "%s command successful.", pcmd);
168556668Sshin		}
168656668Sshin		return 1;
168756668Sshin	}
168856668Sshin	return 0;
168956668Sshin}
169056668Sshin
169156668Sshinstatic void
169290148Simpv4map_data_dest(void)
169356668Sshin{
169456668Sshin	struct in_addr savedaddr;
169556668Sshin	int savedport;
169656668Sshin
169756668Sshin	if (data_dest.su_family != AF_INET) {
169856668Sshin		usedefault = 1;
169956668Sshin		reply(500, "Invalid address rejected.");
170056668Sshin		return;
170156668Sshin	}
170256668Sshin
170356668Sshin	savedaddr = data_dest.su_sin.sin_addr;
170456668Sshin	savedport = data_dest.su_port;
170556668Sshin
170656668Sshin	memset(&data_dest, 0, sizeof(data_dest));
170756668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
170856668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
170956668Sshin	data_dest.su_sin6.sin6_port = savedport;
171056668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
171156668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
171256668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
171356668Sshin}
171456668Sshin#endif
1715