ftpcmd.y revision 103949
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 103949 2002-09-25 04:06:37Z mike $";
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
139102565Syar	LEXERR	NOTIMPL
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
148102565Syar%type	<s> ALL NOTIMPL
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:
396103949Smike#if CHAR_BIT == 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.");
403103949Smike#else /* CHAR_BIT == 8 */
404103949Smike					UNIMPLEMENTED for CHAR_BIT != 8
405103949Smike#endif /* CHAR_BIT == 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		}
478101395Syar	| NLST check_login SP pathstring CRLF
4791592Srgrimes		{
480101395Syar			if ($2)
4811592Srgrimes				send_file_list($4);
482101395Syar			free($4);
4831592Srgrimes		}
4841592Srgrimes	| LIST check_login CRLF
4851592Srgrimes		{
4861592Srgrimes			if ($2)
4871592Srgrimes				retrieve("/bin/ls -lgA", "");
4881592Srgrimes		}
48975567Speter	| LIST check_login SP pathstring CRLF
4901592Srgrimes		{
491101395Syar			if ($2)
4921592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
493101395Syar			free($4);
4941592Srgrimes		}
4951592Srgrimes	| STAT check_login SP pathname CRLF
4961592Srgrimes		{
4971592Srgrimes			if ($2 && $4 != NULL)
4981592Srgrimes				statfilecmd($4);
4991592Srgrimes			if ($4 != NULL)
5001592Srgrimes				free($4);
5011592Srgrimes		}
50271278Sjedgar	| STAT check_login CRLF
5031592Srgrimes		{
50471278Sjedgar			if ($2) {
50571278Sjedgar				statcmd();
50671278Sjedgar			}
5071592Srgrimes		}
50870102Sphk	| DELE check_login_ro SP pathname CRLF
5091592Srgrimes		{
5101592Srgrimes			if ($2 && $4 != NULL)
5111592Srgrimes				delete($4);
5121592Srgrimes			if ($4 != NULL)
5131592Srgrimes				free($4);
5141592Srgrimes		}
51570102Sphk	| RNTO check_login_ro SP pathname CRLF
5161592Srgrimes		{
517101379Syar			if ($2 && $4 != NULL) {
51817433Spst				if (fromname) {
51917433Spst					renamecmd(fromname, $4);
52017433Spst					free(fromname);
52117433Spst					fromname = (char *) 0;
52217433Spst				} else {
52317433Spst					reply(503, "Bad sequence of commands.");
52417433Spst				}
5251592Srgrimes			}
526101379Syar			if ($4 != NULL)
527101379Syar				free($4);
5281592Srgrimes		}
52971278Sjedgar	| ABOR check_login CRLF
5301592Srgrimes		{
53171278Sjedgar			if ($2)
53271278Sjedgar				reply(225, "ABOR command successful.");
5331592Srgrimes		}
5341592Srgrimes	| CWD check_login CRLF
5351592Srgrimes		{
53669234Sdanny			if ($2) {
53769234Sdanny				if (guest)
53869234Sdanny					cwd("/");
53969234Sdanny				else
54069234Sdanny					cwd(pw->pw_dir);
54169234Sdanny			}
5421592Srgrimes		}
5431592Srgrimes	| CWD check_login SP pathname CRLF
5441592Srgrimes		{
5451592Srgrimes			if ($2 && $4 != NULL)
5461592Srgrimes				cwd($4);
5471592Srgrimes			if ($4 != NULL)
5481592Srgrimes				free($4);
5491592Srgrimes		}
5501592Srgrimes	| HELP CRLF
5511592Srgrimes		{
5521592Srgrimes			help(cmdtab, (char *) 0);
5531592Srgrimes		}
5541592Srgrimes	| HELP SP STRING CRLF
5551592Srgrimes		{
5561592Srgrimes			char *cp = $3;
5571592Srgrimes
5581592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5591592Srgrimes				cp = $3 + 4;
5601592Srgrimes				if (*cp == ' ')
5611592Srgrimes					cp++;
5621592Srgrimes				if (*cp)
5631592Srgrimes					help(sitetab, cp);
5641592Srgrimes				else
5651592Srgrimes					help(sitetab, (char *) 0);
5661592Srgrimes			} else
5671592Srgrimes				help(cmdtab, $3);
56888935Sdwmalone			free($3);
5691592Srgrimes		}
5701592Srgrimes	| NOOP CRLF
5711592Srgrimes		{
5721592Srgrimes			reply(200, "NOOP command successful.");
5731592Srgrimes		}
57470102Sphk	| MKD check_login_ro SP pathname CRLF
5751592Srgrimes		{
5761592Srgrimes			if ($2 && $4 != NULL)
5771592Srgrimes				makedir($4);
5781592Srgrimes			if ($4 != NULL)
5791592Srgrimes				free($4);
5801592Srgrimes		}
58170102Sphk	| RMD check_login_ro SP pathname CRLF
5821592Srgrimes		{
5831592Srgrimes			if ($2 && $4 != NULL)
5841592Srgrimes				removedir($4);
5851592Srgrimes			if ($4 != NULL)
5861592Srgrimes				free($4);
5871592Srgrimes		}
5881592Srgrimes	| PWD check_login CRLF
5891592Srgrimes		{
5901592Srgrimes			if ($2)
5911592Srgrimes				pwd();
5921592Srgrimes		}
5931592Srgrimes	| CDUP check_login CRLF
5941592Srgrimes		{
5951592Srgrimes			if ($2)
5961592Srgrimes				cwd("..");
5971592Srgrimes		}
5981592Srgrimes	| SITE SP HELP CRLF
5991592Srgrimes		{
6001592Srgrimes			help(sitetab, (char *) 0);
6011592Srgrimes		}
6021592Srgrimes	| SITE SP HELP SP STRING CRLF
6031592Srgrimes		{
6041592Srgrimes			help(sitetab, $5);
60588935Sdwmalone			free($5);
6061592Srgrimes		}
60775535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
60875535Sphk		{
60975535Sphk			char p[64], *q;
61075535Sphk
611101379Syar			if ($4 && $6) {
61275535Sphk				q = MD5File($6, p);
61375535Sphk				if (q != NULL)
61475535Sphk					reply(200, "MD5(%s) = %s", $6, p);
61575535Sphk				else
61675535Sphk					perror_reply(550, $6);
61775535Sphk			}
61888935Sdwmalone			if ($6)
61988935Sdwmalone				free($6);
62075535Sphk		}
6211592Srgrimes	| SITE SP UMASK check_login CRLF
6221592Srgrimes		{
6231592Srgrimes			int oldmask;
6241592Srgrimes
6251592Srgrimes			if ($4) {
6261592Srgrimes				oldmask = umask(0);
6271592Srgrimes				(void) umask(oldmask);
6281592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6291592Srgrimes			}
6301592Srgrimes		}
6311592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6321592Srgrimes		{
6331592Srgrimes			int oldmask;
6341592Srgrimes
6351592Srgrimes			if ($4) {
6361592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6371592Srgrimes					reply(501, "Bad UMASK value");
6381592Srgrimes				} else {
6391592Srgrimes					oldmask = umask($6);
6401592Srgrimes					reply(200,
6411592Srgrimes					    "UMASK set to %03o (was %03o)",
6421592Srgrimes					    $6, oldmask);
6431592Srgrimes				}
6441592Srgrimes			}
6451592Srgrimes		}
64670102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6471592Srgrimes		{
6481592Srgrimes			if ($4 && ($8 != NULL)) {
649101378Syar				if (($6 == -1 ) || ($6 > 0777))
650101378Syar					reply(501, "Bad mode value");
6511592Srgrimes				else if (chmod($8, $6) < 0)
6521592Srgrimes					perror_reply(550, $8);
6531592Srgrimes				else
6541592Srgrimes					reply(200, "CHMOD command successful.");
6551592Srgrimes			}
6561592Srgrimes			if ($8 != NULL)
6571592Srgrimes				free($8);
6581592Srgrimes		}
65971278Sjedgar	| SITE SP check_login IDLE CRLF
6601592Srgrimes		{
66171278Sjedgar			if ($3)
66271278Sjedgar				reply(200,
66371278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
66471278Sjedgar				    timeout, maxtimeout);
6651592Srgrimes		}
66671278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6671592Srgrimes		{
66871278Sjedgar			if ($3) {
66992272Smaxim				if ($6.i < 30 || $6.i > maxtimeout) {
67071278Sjedgar					reply(501,
67171278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
67271278Sjedgar					    maxtimeout);
67371278Sjedgar				} else {
67492272Smaxim					timeout = $6.i;
67571278Sjedgar					(void) alarm((unsigned) timeout);
67671278Sjedgar					reply(200,
67771278Sjedgar					    "Maximum IDLE time set to %d seconds",
67871278Sjedgar					    timeout);
67971278Sjedgar				}
6801592Srgrimes			}
6811592Srgrimes		}
68270102Sphk	| STOU check_login_ro SP pathname CRLF
6831592Srgrimes		{
6841592Srgrimes			if ($2 && $4 != NULL)
6851592Srgrimes				store($4, "w", 1);
6861592Srgrimes			if ($4 != NULL)
6871592Srgrimes				free($4);
6881592Srgrimes		}
68971278Sjedgar	| SYST check_login CRLF
6901592Srgrimes		{
69171278Sjedgar			if ($2)
6921592Srgrimes#ifdef unix
6931592Srgrimes#ifdef BSD
6941592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
695103949Smike				CHAR_BIT, BSD);
6961592Srgrimes#else /* BSD */
697103949Smike			reply(215, "UNIX Type: L%d", CHAR_BIT);
6981592Srgrimes#endif /* BSD */
6991592Srgrimes#else /* unix */
700103949Smike			reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
7011592Srgrimes#endif /* unix */
7021592Srgrimes		}
7031592Srgrimes
7041592Srgrimes		/*
7051592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
7061592Srgrimes		 * it will be in the updated RFC.
7071592Srgrimes		 *
7081592Srgrimes		 * Return size of file in a format suitable for
7091592Srgrimes		 * using with RESTART (we just count bytes).
7101592Srgrimes		 */
7111592Srgrimes	| SIZE check_login SP pathname CRLF
7121592Srgrimes		{
7131592Srgrimes			if ($2 && $4 != NULL)
7141592Srgrimes				sizecmd($4);
7151592Srgrimes			if ($4 != NULL)
7161592Srgrimes				free($4);
7171592Srgrimes		}
7181592Srgrimes
7191592Srgrimes		/*
7201592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7211592Srgrimes		 * it will be in the updated RFC.
7221592Srgrimes		 *
7231592Srgrimes		 * Return modification time of file as an ISO 3307
7241592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7251592Srgrimes		 * where xxx is the fractional second (of any precision,
7261592Srgrimes		 * not necessarily 3 digits)
7271592Srgrimes		 */
7281592Srgrimes	| MDTM check_login SP pathname CRLF
7291592Srgrimes		{
7301592Srgrimes			if ($2 && $4 != NULL) {
7311592Srgrimes				struct stat stbuf;
7321592Srgrimes				if (stat($4, &stbuf) < 0)
7331592Srgrimes					reply(550, "%s: %s",
7341592Srgrimes					    $4, strerror(errno));
7351592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7361592Srgrimes					reply(550, "%s: not a plain file.", $4);
7371592Srgrimes				} else {
7381592Srgrimes					struct tm *t;
7391592Srgrimes					t = gmtime(&stbuf.st_mtime);
7401592Srgrimes					reply(213,
74117435Spst					    "%04d%02d%02d%02d%02d%02d",
74217435Spst					    1900 + t->tm_year,
74317435Spst					    t->tm_mon+1, t->tm_mday,
7441592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7451592Srgrimes				}
7461592Srgrimes			}
7471592Srgrimes			if ($4 != NULL)
7481592Srgrimes				free($4);
7491592Srgrimes		}
7501592Srgrimes	| QUIT CRLF
7511592Srgrimes		{
7521592Srgrimes			reply(221, "Goodbye.");
7531592Srgrimes			dologout(0);
7541592Srgrimes		}
755102565Syar	| NOTIMPL
756102565Syar		{
757102565Syar			nack($1);
758102565Syar		}
75989935Syar	| error
7601592Srgrimes		{
76189935Syar			yyclearin;		/* discard lookahead data */
76289935Syar			yyerrok;		/* clear error condition */
763102565Syar			state = CMD;		/* reset lexer state */
7641592Srgrimes		}
7651592Srgrimes	;
7661592Srgrimesrcmd
76770102Sphk	: RNFR check_login_ro SP pathname CRLF
7681592Srgrimes		{
7691592Srgrimes			restart_point = (off_t) 0;
7701592Srgrimes			if ($2 && $4) {
77188935Sdwmalone				if (fromname)
77288935Sdwmalone					free(fromname);
77388935Sdwmalone				fromname = (char *) 0;
77488935Sdwmalone				if (renamefrom($4))
77588935Sdwmalone					fromname = $4;
77688935Sdwmalone				else
7771592Srgrimes					free($4);
77888935Sdwmalone			} else if ($4) {
77988935Sdwmalone				free($4);
7801592Srgrimes			}
7811592Srgrimes		}
78292272Smaxim	| REST check_login SP NUMBER CRLF
7831592Srgrimes		{
78471278Sjedgar			if ($2) {
78588935Sdwmalone				if (fromname)
78688935Sdwmalone					free(fromname);
78771278Sjedgar				fromname = (char *) 0;
78892272Smaxim				restart_point = $4.o;
78992272Smaxim				reply(350, "Restarting at %llu. %s",
79071278Sjedgar				    restart_point,
79171278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
79271278Sjedgar			}
7931592Srgrimes		}
7941592Srgrimes	;
7951592Srgrimes
7961592Srgrimesusername
7971592Srgrimes	: STRING
7981592Srgrimes	;
7991592Srgrimes
8001592Srgrimespassword
8011592Srgrimes	: /* empty */
8021592Srgrimes		{
8031592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
8041592Srgrimes		}
8051592Srgrimes	| STRING
8061592Srgrimes	;
8071592Srgrimes
8081592Srgrimesbyte_size
8091592Srgrimes	: NUMBER
81092272Smaxim		{
81192272Smaxim			$$ = $1.i;
81292272Smaxim		}
8131592Srgrimes	;
8141592Srgrimes
8151592Srgrimeshost_port
8161592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8171592Srgrimes		NUMBER COMMA NUMBER
8181592Srgrimes		{
8191592Srgrimes			char *a, *p;
8201592Srgrimes
82156668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
82256668Sshin			data_dest.su_family = AF_INET;
82356668Sshin			p = (char *)&data_dest.su_sin.sin_port;
82492272Smaxim			p[0] = $9.i; p[1] = $11.i;
82556668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
82692272Smaxim			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
8271592Srgrimes		}
8281592Srgrimes	;
8291592Srgrimes
83056668Sshinhost_long_port
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 COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83556668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83656668Sshin		NUMBER
83756668Sshin		{
83856668Sshin			char *a, *p;
83956668Sshin
84056668Sshin			memset(&data_dest, 0, sizeof(data_dest));
84156668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
84256668Sshin			data_dest.su_family = AF_INET6;
84356668Sshin			p = (char *)&data_dest.su_port;
84492272Smaxim			p[0] = $39.i; p[1] = $41.i;
84556668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
84692272Smaxim			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
84792272Smaxim			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
84892272Smaxim			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
84992272Smaxim			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
85056668Sshin			if (his_addr.su_family == AF_INET6) {
85156668Sshin				/* XXX more sanity checks! */
85256668Sshin				data_dest.su_sin6.sin6_scope_id =
85356668Sshin					his_addr.su_sin6.sin6_scope_id;
85456668Sshin			}
85592272Smaxim			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
85656668Sshin				memset(&data_dest, 0, sizeof(data_dest));
85756668Sshin		}
85856668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
85956668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
86056668Sshin		NUMBER
86156668Sshin		{
86256668Sshin			char *a, *p;
86356668Sshin
86456668Sshin			memset(&data_dest, 0, sizeof(data_dest));
86556668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
86656668Sshin			data_dest.su_family = AF_INET;
86756668Sshin			p = (char *)&data_dest.su_port;
86892272Smaxim			p[0] = $15.i; p[1] = $17.i;
86956668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
87092272Smaxim			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
87192272Smaxim			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
87256668Sshin				memset(&data_dest, 0, sizeof(data_dest));
87356668Sshin		}
87456668Sshin	;
87556668Sshin
8761592Srgrimesform_code
8771592Srgrimes	: N
8781592Srgrimes		{
8791592Srgrimes			$$ = FORM_N;
8801592Srgrimes		}
8811592Srgrimes	| T
8821592Srgrimes		{
8831592Srgrimes			$$ = FORM_T;
8841592Srgrimes		}
8851592Srgrimes	| C
8861592Srgrimes		{
8871592Srgrimes			$$ = FORM_C;
8881592Srgrimes		}
8891592Srgrimes	;
8901592Srgrimes
8911592Srgrimestype_code
8921592Srgrimes	: A
8931592Srgrimes		{
8941592Srgrimes			cmd_type = TYPE_A;
8951592Srgrimes			cmd_form = FORM_N;
8961592Srgrimes		}
8971592Srgrimes	| A SP form_code
8981592Srgrimes		{
8991592Srgrimes			cmd_type = TYPE_A;
9001592Srgrimes			cmd_form = $3;
9011592Srgrimes		}
9021592Srgrimes	| E
9031592Srgrimes		{
9041592Srgrimes			cmd_type = TYPE_E;
9051592Srgrimes			cmd_form = FORM_N;
9061592Srgrimes		}
9071592Srgrimes	| E SP form_code
9081592Srgrimes		{
9091592Srgrimes			cmd_type = TYPE_E;
9101592Srgrimes			cmd_form = $3;
9111592Srgrimes		}
9121592Srgrimes	| I
9131592Srgrimes		{
9141592Srgrimes			cmd_type = TYPE_I;
9151592Srgrimes		}
9161592Srgrimes	| L
9171592Srgrimes		{
9181592Srgrimes			cmd_type = TYPE_L;
919103949Smike			cmd_bytesz = CHAR_BIT;
9201592Srgrimes		}
9211592Srgrimes	| L SP byte_size
9221592Srgrimes		{
9231592Srgrimes			cmd_type = TYPE_L;
9241592Srgrimes			cmd_bytesz = $3;
9251592Srgrimes		}
9261592Srgrimes		/* this is for a bug in the BBN ftp */
9271592Srgrimes	| L byte_size
9281592Srgrimes		{
9291592Srgrimes			cmd_type = TYPE_L;
9301592Srgrimes			cmd_bytesz = $2;
9311592Srgrimes		}
9321592Srgrimes	;
9331592Srgrimes
9341592Srgrimesstruct_code
9351592Srgrimes	: F
9361592Srgrimes		{
9371592Srgrimes			$$ = STRU_F;
9381592Srgrimes		}
9391592Srgrimes	| R
9401592Srgrimes		{
9411592Srgrimes			$$ = STRU_R;
9421592Srgrimes		}
9431592Srgrimes	| P
9441592Srgrimes		{
9451592Srgrimes			$$ = STRU_P;
9461592Srgrimes		}
9471592Srgrimes	;
9481592Srgrimes
9491592Srgrimesmode_code
9501592Srgrimes	: S
9511592Srgrimes		{
9521592Srgrimes			$$ = MODE_S;
9531592Srgrimes		}
9541592Srgrimes	| B
9551592Srgrimes		{
9561592Srgrimes			$$ = MODE_B;
9571592Srgrimes		}
9581592Srgrimes	| C
9591592Srgrimes		{
9601592Srgrimes			$$ = MODE_C;
9611592Srgrimes		}
9621592Srgrimes	;
9631592Srgrimes
9641592Srgrimespathname
9651592Srgrimes	: pathstring
9661592Srgrimes		{
9671592Srgrimes			/*
9681592Srgrimes			 * Problem: this production is used for all pathname
9691592Srgrimes			 * processing, but only gives a 550 error reply.
9701592Srgrimes			 * This is a valid reply in some cases but not in others.
9711592Srgrimes			 */
97275567Speter			if (logged_in && $1) {
9731592Srgrimes				glob_t gl;
9741592Srgrimes				int flags =
975100222Smikeh				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
9761592Srgrimes
9771592Srgrimes				memset(&gl, 0, sizeof(gl));
97875560Sjedgar				flags |= GLOB_MAXPATH;
97975560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9801592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9811592Srgrimes				    gl.gl_pathc == 0) {
982101380Syar					reply(550, "wildcard expansion error");
9831592Srgrimes					$$ = NULL;
98475567Speter				} else if (gl.gl_pathc > 1) {
98575567Speter					reply(550, "ambiguous");
98675567Speter					$$ = NULL;
9871592Srgrimes				} else {
9881592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9891592Srgrimes				}
9901592Srgrimes				globfree(&gl);
9911592Srgrimes				free($1);
9921592Srgrimes			} else
9931592Srgrimes				$$ = $1;
9941592Srgrimes		}
9951592Srgrimes	;
9961592Srgrimes
9971592Srgrimespathstring
9981592Srgrimes	: STRING
9991592Srgrimes	;
10001592Srgrimes
10011592Srgrimesoctal_number
10021592Srgrimes	: NUMBER
10031592Srgrimes		{
10041592Srgrimes			int ret, dec, multby, digit;
10051592Srgrimes
10061592Srgrimes			/*
10071592Srgrimes			 * Convert a number that was read as decimal number
10081592Srgrimes			 * to what it would be if it had been read as octal.
10091592Srgrimes			 */
101092272Smaxim			dec = $1.i;
10111592Srgrimes			multby = 1;
10121592Srgrimes			ret = 0;
10131592Srgrimes			while (dec) {
10141592Srgrimes				digit = dec%10;
10151592Srgrimes				if (digit > 7) {
10161592Srgrimes					ret = -1;
10171592Srgrimes					break;
10181592Srgrimes				}
10191592Srgrimes				ret += digit * multby;
10201592Srgrimes				multby *= 8;
10211592Srgrimes				dec /= 10;
10221592Srgrimes			}
10231592Srgrimes			$$ = ret;
10241592Srgrimes		}
10251592Srgrimes	;
10261592Srgrimes
10271592Srgrimes
10281592Srgrimescheck_login
10291592Srgrimes	: /* empty */
10301592Srgrimes		{
103170102Sphk		$$ = check_login1();
10321592Srgrimes		}
10331592Srgrimes	;
10341592Srgrimes
103570102Sphkcheck_login_epsv
103670102Sphk	: /* empty */
103770102Sphk		{
103870102Sphk		if (noepsv) {
103970102Sphk			reply(500, "EPSV command disabled");
104070102Sphk			$$ = 0;
104170102Sphk		}
104270102Sphk		else
104370102Sphk			$$ = check_login1();
104470102Sphk		}
104570102Sphk	;
104670102Sphk
104770102Sphkcheck_login_ro
104870102Sphk	: /* empty */
104970102Sphk		{
105070102Sphk		if (readonly) {
105172710Sdes			reply(550, "Permission denied.");
105270102Sphk			$$ = 0;
105370102Sphk		}
105470102Sphk		else
105570102Sphk			$$ = check_login1();
105670102Sphk		}
105770102Sphk	;
105870102Sphk
10591592Srgrimes%%
10601592Srgrimes
10611592Srgrimes#define	CMD	0	/* beginning of command */
10621592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10631592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10641592Srgrimes#define	STR2	3	/* expect STRING */
10651592Srgrimes#define	OSTR	4	/* optional SP then STRING */
106675556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10671592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10681592Srgrimes#define	SITECMD	7	/* SITE command */
10691592Srgrimes#define	NSTR	8	/* Number followed by a string */
10701592Srgrimes
107175560Sjedgar#define	MAXGLOBARGS	1000
107275560Sjedgar
1073101034Syar#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1074101034Syar
10751592Srgrimesstruct tab {
10761592Srgrimes	char	*name;
10771592Srgrimes	short	token;
10781592Srgrimes	short	state;
10791592Srgrimes	short	implemented;	/* 1 if command is implemented */
10801592Srgrimes	char	*help;
10811592Srgrimes};
10821592Srgrimes
10831592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10841592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
108575556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10861592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10871592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10881592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10891592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1090101806Syar	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
109156668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
109256668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10931592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
109456668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
109556668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1096101806Syar	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
10971592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10981592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10991592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
11001592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
11011592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
11021592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
11031592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
11041592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
11051592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
11061592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
11071592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
11081592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
11091592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
11101592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
11111592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
11121592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
11131592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
11141592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
11151592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
11161592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
11171592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
11181592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
11191592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
11201592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
11211592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
11221592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11231592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
11241592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
11251592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
11261592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
11271592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11281592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11291592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11301592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11311592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11321592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11331592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11341592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11351592Srgrimes	{ NULL,   0,    0,    0,	0 }
11361592Srgrimes};
11371592Srgrimes
11381592Srgrimesstruct tab sitetab[] = {
113975535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11401592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11411592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11421592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11431592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11441592Srgrimes	{ NULL,   0,    0,    0,	0 }
11451592Srgrimes};
11461592Srgrimes
114790148Simpstatic char	*copy(char *);
114890148Simpstatic void	 help(struct tab *, char *);
11491592Srgrimesstatic struct tab *
115090148Simp		 lookup(struct tab *, char *);
115190148Simpstatic int	 port_check(const char *);
115290148Simpstatic int	 port_check_v6(const char *);
115390148Simpstatic void	 sizecmd(char *);
115490148Simpstatic void	 toolong(int);
115590148Simpstatic void	 v4map_data_dest(void);
115690148Simpstatic int	 yylex(void);
11571592Srgrimes
11581592Srgrimesstatic struct tab *
115990148Simplookup(struct tab *p, char *cmd)
11601592Srgrimes{
11611592Srgrimes
11621592Srgrimes	for (; p->name != NULL; p++)
11631592Srgrimes		if (strcmp(cmd, p->name) == 0)
11641592Srgrimes			return (p);
11651592Srgrimes	return (0);
11661592Srgrimes}
11671592Srgrimes
11681592Srgrimes#include <arpa/telnet.h>
11691592Srgrimes
11701592Srgrimes/*
11711592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11721592Srgrimes */
11731592Srgrimeschar *
117490148Simpgetline(char *s, int n, FILE *iop)
11751592Srgrimes{
11761592Srgrimes	int c;
11771592Srgrimes	register char *cs;
11781592Srgrimes
11791592Srgrimes	cs = s;
11801592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11811592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11821592Srgrimes		*cs++ = tmpline[c];
11831592Srgrimes		if (tmpline[c] == '\n') {
11841592Srgrimes			*cs++ = '\0';
118576096Smarkm			if (ftpdebug)
11861592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11871592Srgrimes			tmpline[0] = '\0';
11881592Srgrimes			return(s);
11891592Srgrimes		}
11901592Srgrimes		if (c == 0)
11911592Srgrimes			tmpline[0] = '\0';
11921592Srgrimes	}
11931592Srgrimes	while ((c = getc(iop)) != EOF) {
11941592Srgrimes		c &= 0377;
11951592Srgrimes		if (c == IAC) {
11961592Srgrimes		    if ((c = getc(iop)) != EOF) {
11971592Srgrimes			c &= 0377;
11981592Srgrimes			switch (c) {
11991592Srgrimes			case WILL:
12001592Srgrimes			case WONT:
12011592Srgrimes				c = getc(iop);
12021592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
12031592Srgrimes				(void) fflush(stdout);
12041592Srgrimes				continue;
12051592Srgrimes			case DO:
12061592Srgrimes			case DONT:
12071592Srgrimes				c = getc(iop);
12081592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
12091592Srgrimes				(void) fflush(stdout);
12101592Srgrimes				continue;
12111592Srgrimes			case IAC:
12121592Srgrimes				break;
12131592Srgrimes			default:
12141592Srgrimes				continue;	/* ignore command */
12151592Srgrimes			}
12161592Srgrimes		    }
12171592Srgrimes		}
12181592Srgrimes		*cs++ = c;
12191592Srgrimes		if (--n <= 0 || c == '\n')
12201592Srgrimes			break;
12211592Srgrimes	}
12221592Srgrimes	if (c == EOF && cs == s)
12231592Srgrimes		return (NULL);
12241592Srgrimes	*cs++ = '\0';
122576096Smarkm	if (ftpdebug) {
12261592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12271592Srgrimes			/* Don't syslog passwords */
12281592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12291592Srgrimes		} else {
12301592Srgrimes			register char *cp;
12311592Srgrimes			register int len;
12321592Srgrimes
12331592Srgrimes			/* Don't syslog trailing CR-LF */
12341592Srgrimes			len = strlen(s);
12351592Srgrimes			cp = s + len - 1;
12361592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12371592Srgrimes				--cp;
12381592Srgrimes				--len;
12391592Srgrimes			}
12401592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12411592Srgrimes		}
12421592Srgrimes	}
12431592Srgrimes	return (s);
12441592Srgrimes}
12451592Srgrimes
12461592Srgrimesstatic void
124790148Simptoolong(int signo)
12481592Srgrimes{
12491592Srgrimes
12501592Srgrimes	reply(421,
12511592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12521592Srgrimes	if (logging)
12531592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12541592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12551592Srgrimes	dologout(1);
12561592Srgrimes}
12571592Srgrimes
12581592Srgrimesstatic int
125990148Simpyylex(void)
12601592Srgrimes{
126189935Syar	static int cpos;
12621592Srgrimes	char *cp, *cp2;
12631592Srgrimes	struct tab *p;
12641592Srgrimes	int n;
12651592Srgrimes	char c;
12661592Srgrimes
12671592Srgrimes	for (;;) {
12681592Srgrimes		switch (state) {
12691592Srgrimes
12701592Srgrimes		case CMD:
12711592Srgrimes			(void) signal(SIGALRM, toolong);
12721592Srgrimes			(void) alarm((unsigned) timeout);
12731592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12741592Srgrimes				reply(221, "You could at least say goodbye.");
12751592Srgrimes				dologout(0);
12761592Srgrimes			}
12771592Srgrimes			(void) alarm(0);
12781592Srgrimes#ifdef SETPROCTITLE
127929574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12801592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12811592Srgrimes#endif /* SETPROCTITLE */
12821592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12831592Srgrimes				*cp++ = '\n';
12841592Srgrimes				*cp = '\0';
12851592Srgrimes			}
12861592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12871592Srgrimes				cpos = cp - cbuf;
12881592Srgrimes			if (cpos == 0)
12891592Srgrimes				cpos = 4;
12901592Srgrimes			c = cbuf[cpos];
12911592Srgrimes			cbuf[cpos] = '\0';
12921592Srgrimes			upper(cbuf);
12931592Srgrimes			p = lookup(cmdtab, cbuf);
12941592Srgrimes			cbuf[cpos] = c;
12953776Spst			if (p != 0) {
1296102565Syar				yylval.s = p->name;
1297102565Syar				if (!p->implemented)
1298102565Syar					return (NOTIMPL); /* state remains CMD */
12991592Srgrimes				state = p->state;
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) {
1318102565Syar				yylval.s = p->name;
1319102565Syar				if (!p->implemented) {
13201592Srgrimes					state = CMD;
1321102565Syar					return (NOTIMPL);
13221592Srgrimes				}
13231592Srgrimes				state = p->state;
13241592Srgrimes				return (p->token);
13251592Srgrimes			}
13261592Srgrimes			state = CMD;
13271592Srgrimes			break;
13281592Srgrimes
132975556Sgreen		case ZSTR1:
13301592Srgrimes		case OSTR:
13311592Srgrimes			if (cbuf[cpos] == '\n') {
13321592Srgrimes				state = CMD;
13331592Srgrimes				return (CRLF);
13341592Srgrimes			}
13351592Srgrimes			/* FALLTHROUGH */
13361592Srgrimes
13371592Srgrimes		case STR1:
13381592Srgrimes		dostr1:
13391592Srgrimes			if (cbuf[cpos] == ' ') {
13401592Srgrimes				cpos++;
134151979Salfred				state = state == OSTR ? STR2 : state+1;
13421592Srgrimes				return (SP);
13431592Srgrimes			}
13441592Srgrimes			break;
13451592Srgrimes
13461592Srgrimes		case ZSTR2:
13471592Srgrimes			if (cbuf[cpos] == '\n') {
13481592Srgrimes				state = CMD;
13491592Srgrimes				return (CRLF);
13501592Srgrimes			}
13511592Srgrimes			/* FALLTHROUGH */
13521592Srgrimes
13531592Srgrimes		case STR2:
13541592Srgrimes			cp = &cbuf[cpos];
13551592Srgrimes			n = strlen(cp);
13561592Srgrimes			cpos += n - 1;
13571592Srgrimes			/*
13581592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13591592Srgrimes			 */
13601592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13611592Srgrimes				cbuf[cpos] = '\0';
13621592Srgrimes				yylval.s = copy(cp);
13631592Srgrimes				cbuf[cpos] = '\n';
13641592Srgrimes				state = ARGS;
13651592Srgrimes				return (STRING);
13661592Srgrimes			}
13671592Srgrimes			break;
13681592Srgrimes
13691592Srgrimes		case NSTR:
13701592Srgrimes			if (cbuf[cpos] == ' ') {
13711592Srgrimes				cpos++;
13721592Srgrimes				return (SP);
13731592Srgrimes			}
13741592Srgrimes			if (isdigit(cbuf[cpos])) {
13751592Srgrimes				cp = &cbuf[cpos];
13761592Srgrimes				while (isdigit(cbuf[++cpos]))
13771592Srgrimes					;
13781592Srgrimes				c = cbuf[cpos];
13791592Srgrimes				cbuf[cpos] = '\0';
138092272Smaxim				yylval.u.i = atoi(cp);
13811592Srgrimes				cbuf[cpos] = c;
13821592Srgrimes				state = STR1;
13831592Srgrimes				return (NUMBER);
13841592Srgrimes			}
13851592Srgrimes			state = STR1;
13861592Srgrimes			goto dostr1;
13871592Srgrimes
13881592Srgrimes		case ARGS:
13891592Srgrimes			if (isdigit(cbuf[cpos])) {
13901592Srgrimes				cp = &cbuf[cpos];
13911592Srgrimes				while (isdigit(cbuf[++cpos]))
13921592Srgrimes					;
13931592Srgrimes				c = cbuf[cpos];
13941592Srgrimes				cbuf[cpos] = '\0';
139592272Smaxim				yylval.u.i = atoi(cp);
139692272Smaxim				yylval.u.o = strtoull(cp, (char **)NULL, 10);
13971592Srgrimes				cbuf[cpos] = c;
13981592Srgrimes				return (NUMBER);
13991592Srgrimes			}
140056668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
140156668Sshin			 && !isalnum(cbuf[cpos + 3])) {
140256668Sshin				cpos += 3;
140356668Sshin				return ALL;
140456668Sshin			}
14051592Srgrimes			switch (cbuf[cpos++]) {
14061592Srgrimes
14071592Srgrimes			case '\n':
14081592Srgrimes				state = CMD;
14091592Srgrimes				return (CRLF);
14101592Srgrimes
14111592Srgrimes			case ' ':
14121592Srgrimes				return (SP);
14131592Srgrimes
14141592Srgrimes			case ',':
14151592Srgrimes				return (COMMA);
14161592Srgrimes
14171592Srgrimes			case 'A':
14181592Srgrimes			case 'a':
14191592Srgrimes				return (A);
14201592Srgrimes
14211592Srgrimes			case 'B':
14221592Srgrimes			case 'b':
14231592Srgrimes				return (B);
14241592Srgrimes
14251592Srgrimes			case 'C':
14261592Srgrimes			case 'c':
14271592Srgrimes				return (C);
14281592Srgrimes
14291592Srgrimes			case 'E':
14301592Srgrimes			case 'e':
14311592Srgrimes				return (E);
14321592Srgrimes
14331592Srgrimes			case 'F':
14341592Srgrimes			case 'f':
14351592Srgrimes				return (F);
14361592Srgrimes
14371592Srgrimes			case 'I':
14381592Srgrimes			case 'i':
14391592Srgrimes				return (I);
14401592Srgrimes
14411592Srgrimes			case 'L':
14421592Srgrimes			case 'l':
14431592Srgrimes				return (L);
14441592Srgrimes
14451592Srgrimes			case 'N':
14461592Srgrimes			case 'n':
14471592Srgrimes				return (N);
14481592Srgrimes
14491592Srgrimes			case 'P':
14501592Srgrimes			case 'p':
14511592Srgrimes				return (P);
14521592Srgrimes
14531592Srgrimes			case 'R':
14541592Srgrimes			case 'r':
14551592Srgrimes				return (R);
14561592Srgrimes
14571592Srgrimes			case 'S':
14581592Srgrimes			case 's':
14591592Srgrimes				return (S);
14601592Srgrimes
14611592Srgrimes			case 'T':
14621592Srgrimes			case 't':
14631592Srgrimes				return (T);
14641592Srgrimes
14651592Srgrimes			}
14661592Srgrimes			break;
14671592Srgrimes
14681592Srgrimes		default:
146976096Smarkm			fatalerror("Unknown state in scanner.");
14701592Srgrimes		}
14711592Srgrimes		state = CMD;
147289935Syar		return (LEXERR);
14731592Srgrimes	}
14741592Srgrimes}
14751592Srgrimes
14761592Srgrimesvoid
147790148Simpupper(char *s)
14781592Srgrimes{
14791592Srgrimes	while (*s != '\0') {
14801592Srgrimes		if (islower(*s))
14811592Srgrimes			*s = toupper(*s);
14821592Srgrimes		s++;
14831592Srgrimes	}
14841592Srgrimes}
14851592Srgrimes
14861592Srgrimesstatic char *
148790148Simpcopy(char *s)
14881592Srgrimes{
14891592Srgrimes	char *p;
14901592Srgrimes
14911592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14921592Srgrimes	if (p == NULL)
149376096Smarkm		fatalerror("Ran out of memory.");
14941592Srgrimes	(void) strcpy(p, s);
14951592Srgrimes	return (p);
14961592Srgrimes}
14971592Srgrimes
14981592Srgrimesstatic void
149990148Simphelp(struct tab *ctab, char *s)
15001592Srgrimes{
15011592Srgrimes	struct tab *c;
15021592Srgrimes	int width, NCMDS;
15031592Srgrimes	char *type;
15041592Srgrimes
15051592Srgrimes	if (ctab == sitetab)
15061592Srgrimes		type = "SITE ";
15071592Srgrimes	else
15081592Srgrimes		type = "";
15091592Srgrimes	width = 0, NCMDS = 0;
15101592Srgrimes	for (c = ctab; c->name != NULL; c++) {
15111592Srgrimes		int len = strlen(c->name);
15121592Srgrimes
15131592Srgrimes		if (len > width)
15141592Srgrimes			width = len;
15151592Srgrimes		NCMDS++;
15161592Srgrimes	}
15171592Srgrimes	width = (width + 8) &~ 7;
15181592Srgrimes	if (s == 0) {
15191592Srgrimes		int i, j, w;
15201592Srgrimes		int columns, lines;
15211592Srgrimes
15221592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15231592Srgrimes		    type, "(* =>'s unimplemented)");
15241592Srgrimes		columns = 76 / width;
15251592Srgrimes		if (columns == 0)
15261592Srgrimes			columns = 1;
15271592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15281592Srgrimes		for (i = 0; i < lines; i++) {
15291592Srgrimes			printf("   ");
15301592Srgrimes			for (j = 0; j < columns; j++) {
15311592Srgrimes				c = ctab + j * lines + i;
15321592Srgrimes				printf("%s%c", c->name,
15331592Srgrimes					c->implemented ? ' ' : '*');
15341592Srgrimes				if (c + lines >= &ctab[NCMDS])
15351592Srgrimes					break;
15361592Srgrimes				w = strlen(c->name) + 1;
15371592Srgrimes				while (w < width) {
15381592Srgrimes					putchar(' ');
15391592Srgrimes					w++;
15401592Srgrimes				}
15411592Srgrimes			}
15421592Srgrimes			printf("\r\n");
15431592Srgrimes		}
15441592Srgrimes		(void) fflush(stdout);
15451592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15461592Srgrimes		return;
15471592Srgrimes	}
15481592Srgrimes	upper(s);
15491592Srgrimes	c = lookup(ctab, s);
15501592Srgrimes	if (c == (struct tab *)0) {
15511592Srgrimes		reply(502, "Unknown command %s.", s);
15521592Srgrimes		return;
15531592Srgrimes	}
15541592Srgrimes	if (c->implemented)
15551592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15561592Srgrimes	else
15571592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15581592Srgrimes		    c->name, c->help);
15591592Srgrimes}
15601592Srgrimes
15611592Srgrimesstatic void
156290148Simpsizecmd(char *filename)
15631592Srgrimes{
15641592Srgrimes	switch (type) {
15651592Srgrimes	case TYPE_L:
15661592Srgrimes	case TYPE_I: {
15671592Srgrimes		struct stat stbuf;
156863350Sdes		if (stat(filename, &stbuf) < 0)
156963350Sdes			perror_reply(550, filename);
157063350Sdes		else if (!S_ISREG(stbuf.st_mode))
15711592Srgrimes			reply(550, "%s: not a plain file.", filename);
15721592Srgrimes		else
15731592Srgrimes			reply(213, "%qu", stbuf.st_size);
15741592Srgrimes		break; }
15751592Srgrimes	case TYPE_A: {
15761592Srgrimes		FILE *fin;
15771592Srgrimes		int c;
15781592Srgrimes		off_t count;
15791592Srgrimes		struct stat stbuf;
15801592Srgrimes		fin = fopen(filename, "r");
15811592Srgrimes		if (fin == NULL) {
15821592Srgrimes			perror_reply(550, filename);
15831592Srgrimes			return;
15841592Srgrimes		}
158563350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
158663350Sdes			perror_reply(550, filename);
158763350Sdes			(void) fclose(fin);
158863350Sdes			return;
158963350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15901592Srgrimes			reply(550, "%s: not a plain file.", filename);
15911592Srgrimes			(void) fclose(fin);
15921592Srgrimes			return;
1593101034Syar		} else if (stbuf.st_size > MAXASIZE) {
1594101034Syar			reply(550, "%s: too large for type A SIZE.", filename);
1595101034Syar			(void) fclose(fin);
1596101034Syar			return;
15971592Srgrimes		}
15981592Srgrimes
15991592Srgrimes		count = 0;
16001592Srgrimes		while((c=getc(fin)) != EOF) {
16011592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
16021592Srgrimes				count++;
16031592Srgrimes			count++;
16041592Srgrimes		}
16051592Srgrimes		(void) fclose(fin);
16061592Srgrimes
16071592Srgrimes		reply(213, "%qd", count);
16081592Srgrimes		break; }
16091592Srgrimes	default:
1610100684Syar		reply(504, "SIZE not implemented for type %s.",
1611100684Syar		           typenames[type]);
16121592Srgrimes	}
16131592Srgrimes}
161456668Sshin
161556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
161656668Sshinstatic int
161790148Simpport_check(const char *pcmd)
161856668Sshin{
161956668Sshin	if (his_addr.su_family == AF_INET) {
162056668Sshin		if (data_dest.su_family != AF_INET) {
162156668Sshin			usedefault = 1;
162256668Sshin			reply(500, "Invalid address rejected.");
162356668Sshin			return 1;
162456668Sshin		}
162556668Sshin		if (paranoid &&
162656668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
162756668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
162856668Sshin			    &his_addr.su_sin.sin_addr,
162956668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
163056668Sshin			usedefault = 1;
163156668Sshin			reply(500, "Illegal PORT range rejected.");
163256668Sshin		} else {
163356668Sshin			usedefault = 0;
163456668Sshin			if (pdata >= 0) {
163556668Sshin				(void) close(pdata);
163656668Sshin				pdata = -1;
163756668Sshin			}
163856668Sshin			reply(200, "%s command successful.", pcmd);
163956668Sshin		}
164056668Sshin		return 1;
164156668Sshin	}
164256668Sshin	return 0;
164356668Sshin}
164456668Sshin
164570102Sphkstatic int
164690148Simpcheck_login1(void)
164770102Sphk{
164870102Sphk	if (logged_in)
164970102Sphk		return 1;
165070102Sphk	else {
165170102Sphk		reply(530, "Please login with USER and PASS.");
165270102Sphk		return 0;
165370102Sphk	}
165470102Sphk}
165570102Sphk
165656668Sshin#ifdef INET6
165756668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
165856668Sshinstatic int
165990148Simpport_check_v6(const char *pcmd)
166056668Sshin{
166156668Sshin	if (his_addr.su_family == AF_INET6) {
166256668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
166356668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
166456668Sshin			v4map_data_dest();
166556668Sshin		if (data_dest.su_family != AF_INET6) {
166656668Sshin			usedefault = 1;
166756668Sshin			reply(500, "Invalid address rejected.");
166856668Sshin			return 1;
166956668Sshin		}
167056668Sshin		if (paranoid &&
167156668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
167256668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
167356668Sshin			    &his_addr.su_sin6.sin6_addr,
167456668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
167556668Sshin			usedefault = 1;
167656668Sshin			reply(500, "Illegal PORT range rejected.");
167756668Sshin		} else {
167856668Sshin			usedefault = 0;
167956668Sshin			if (pdata >= 0) {
168056668Sshin				(void) close(pdata);
168156668Sshin				pdata = -1;
168256668Sshin			}
168356668Sshin			reply(200, "%s command successful.", pcmd);
168456668Sshin		}
168556668Sshin		return 1;
168656668Sshin	}
168756668Sshin	return 0;
168856668Sshin}
168956668Sshin
169056668Sshinstatic void
169190148Simpv4map_data_dest(void)
169256668Sshin{
169356668Sshin	struct in_addr savedaddr;
169456668Sshin	int savedport;
169556668Sshin
169656668Sshin	if (data_dest.su_family != AF_INET) {
169756668Sshin		usedefault = 1;
169856668Sshin		reply(500, "Invalid address rejected.");
169956668Sshin		return;
170056668Sshin	}
170156668Sshin
170256668Sshin	savedaddr = data_dest.su_sin.sin_addr;
170356668Sshin	savedport = data_dest.su_port;
170456668Sshin
170556668Sshin	memset(&data_dest, 0, sizeof(data_dest));
170656668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
170756668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
170856668Sshin	data_dest.su_sin6.sin6_port = savedport;
170956668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
171056668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
171156668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
171256668Sshin}
171356668Sshin#endif
1714