ftpcmd.y revision 51979
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 51979 1999-10-07 08:41:55Z alfred $";
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>
611592Srgrimes#include <pwd.h>
621592Srgrimes#include <setjmp.h>
631592Srgrimes#include <signal.h>
641592Srgrimes#include <stdio.h>
651592Srgrimes#include <stdlib.h>
661592Srgrimes#include <string.h>
671592Srgrimes#include <syslog.h>
681592Srgrimes#include <time.h>
691592Srgrimes#include <unistd.h>
7013139Speter#include <libutil.h>
711592Srgrimes
721592Srgrimes#include "extern.h"
731592Srgrimes
7417433Spstextern	struct sockaddr_in data_dest, his_addr;
751592Srgrimesextern	int logged_in;
761592Srgrimesextern	struct passwd *pw;
771592Srgrimesextern	int guest;
7817435Spstextern 	int paranoid;
791592Srgrimesextern	int logging;
801592Srgrimesextern	int type;
811592Srgrimesextern	int form;
821592Srgrimesextern	int debug;
831592Srgrimesextern	int timeout;
841592Srgrimesextern	int maxtimeout;
851592Srgrimesextern  int pdata;
8627650Sdavidnextern	char *hostname;
8727650Sdavidnextern	char remotehost[];
881592Srgrimesextern	char proctitle[];
891592Srgrimesextern	int usedefault;
901592Srgrimesextern  int transflag;
911592Srgrimesextern  char tmpline[];
921592Srgrimes
931592Srgrimesoff_t	restart_point;
941592Srgrimes
951592Srgrimesstatic	int cmd_type;
961592Srgrimesstatic	int cmd_form;
971592Srgrimesstatic	int cmd_bytesz;
981592Srgrimeschar	cbuf[512];
991592Srgrimeschar	*fromname;
1001592Srgrimes
1011592Srgrimes%}
1021592Srgrimes
1031592Srgrimes%union {
1041592Srgrimes	int	i;
1051592Srgrimes	char   *s;
1061592Srgrimes}
1071592Srgrimes
1081592Srgrimes%token
1091592Srgrimes	A	B	C	E	F	I
1101592Srgrimes	L	N	P	R	S	T
1111592Srgrimes
1121592Srgrimes	SP	CRLF	COMMA
1131592Srgrimes
1141592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1151592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1161592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1171592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1181592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1191592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1201592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
1211592Srgrimes
1221592Srgrimes	UMASK	IDLE	CHMOD
1231592Srgrimes
1241592Srgrimes	LEXERR
1251592Srgrimes
1261592Srgrimes%token	<s> STRING
1271592Srgrimes%token	<i> NUMBER
1281592Srgrimes
1291592Srgrimes%type	<i> check_login octal_number byte_size
1301592Srgrimes%type	<i> struct_code mode_code type_code form_code
1311592Srgrimes%type	<s> pathstring pathname password username
1321592Srgrimes
1331592Srgrimes%start	cmd_list
1341592Srgrimes
1351592Srgrimes%%
1361592Srgrimes
1371592Srgrimescmd_list
1381592Srgrimes	: /* empty */
1391592Srgrimes	| cmd_list cmd
1401592Srgrimes		{
1411592Srgrimes			fromname = (char *) 0;
1421592Srgrimes			restart_point = (off_t) 0;
1431592Srgrimes		}
1441592Srgrimes	| cmd_list rcmd
1451592Srgrimes	;
1461592Srgrimes
1471592Srgrimescmd
1481592Srgrimes	: USER SP username CRLF
1491592Srgrimes		{
1501592Srgrimes			user($3);
1511592Srgrimes			free($3);
1521592Srgrimes		}
1531592Srgrimes	| PASS SP password CRLF
1541592Srgrimes		{
1551592Srgrimes			pass($3);
1561592Srgrimes			free($3);
1571592Srgrimes		}
15817433Spst	| PORT check_login SP host_port CRLF
1591592Srgrimes		{
16017433Spst			if ($2) {
16117435Spst				if (paranoid &&
16217435Spst				    ((ntohs(data_dest.sin_port) <
16317435Spst				      IPPORT_RESERVED) ||
16417435Spst				     memcmp(&data_dest.sin_addr,
16517435Spst					    &his_addr.sin_addr,
16617435Spst					    sizeof(data_dest.sin_addr)))) {
16717433Spst					usedefault = 1;
16817433Spst					reply(500,
16917433Spst					      "Illegal PORT range rejected.");
17017435Spst				} else {
17117433Spst					usedefault = 0;
17217433Spst					if (pdata >= 0) {
17317433Spst						(void) close(pdata);
17417433Spst						pdata = -1;
17517433Spst					}
17617433Spst					reply(200, "PORT command successful.");
17717433Spst				}
1781592Srgrimes			}
1791592Srgrimes		}
18017433Spst	| PASV check_login CRLF
1811592Srgrimes		{
18217433Spst			if ($2)
18317433Spst				passive();
1841592Srgrimes		}
1851592Srgrimes	| TYPE SP type_code CRLF
1861592Srgrimes		{
1871592Srgrimes			switch (cmd_type) {
1881592Srgrimes
1891592Srgrimes			case TYPE_A:
1901592Srgrimes				if (cmd_form == FORM_N) {
1911592Srgrimes					reply(200, "Type set to A.");
1921592Srgrimes					type = cmd_type;
1931592Srgrimes					form = cmd_form;
1941592Srgrimes				} else
1951592Srgrimes					reply(504, "Form must be N.");
1961592Srgrimes				break;
1971592Srgrimes
1981592Srgrimes			case TYPE_E:
1991592Srgrimes				reply(504, "Type E not implemented.");
2001592Srgrimes				break;
2011592Srgrimes
2021592Srgrimes			case TYPE_I:
2031592Srgrimes				reply(200, "Type set to I.");
2041592Srgrimes				type = cmd_type;
2051592Srgrimes				break;
2061592Srgrimes
2071592Srgrimes			case TYPE_L:
2081592Srgrimes#if NBBY == 8
2091592Srgrimes				if (cmd_bytesz == 8) {
2101592Srgrimes					reply(200,
2111592Srgrimes					    "Type set to L (byte size 8).");
2121592Srgrimes					type = cmd_type;
2131592Srgrimes				} else
2141592Srgrimes					reply(504, "Byte size must be 8.");
2151592Srgrimes#else /* NBBY == 8 */
2161592Srgrimes				UNIMPLEMENTED for NBBY != 8
2171592Srgrimes#endif /* NBBY == 8 */
2181592Srgrimes			}
2191592Srgrimes		}
2201592Srgrimes	| STRU SP struct_code CRLF
2211592Srgrimes		{
2221592Srgrimes			switch ($3) {
2231592Srgrimes
2241592Srgrimes			case STRU_F:
2251592Srgrimes				reply(200, "STRU F ok.");
2261592Srgrimes				break;
2271592Srgrimes
2281592Srgrimes			default:
2291592Srgrimes				reply(504, "Unimplemented STRU type.");
2301592Srgrimes			}
2311592Srgrimes		}
2321592Srgrimes	| MODE SP mode_code CRLF
2331592Srgrimes		{
2341592Srgrimes			switch ($3) {
2351592Srgrimes
2361592Srgrimes			case MODE_S:
2371592Srgrimes				reply(200, "MODE S ok.");
2381592Srgrimes				break;
2391592Srgrimes
2401592Srgrimes			default:
2411592Srgrimes				reply(502, "Unimplemented MODE type.");
2421592Srgrimes			}
2431592Srgrimes		}
2441592Srgrimes	| ALLO SP NUMBER CRLF
2451592Srgrimes		{
2461592Srgrimes			reply(202, "ALLO command ignored.");
2471592Srgrimes		}
2481592Srgrimes	| ALLO SP NUMBER SP R SP NUMBER CRLF
2491592Srgrimes		{
2501592Srgrimes			reply(202, "ALLO command ignored.");
2511592Srgrimes		}
2521592Srgrimes	| RETR check_login SP pathname CRLF
2531592Srgrimes		{
2541592Srgrimes			if ($2 && $4 != NULL)
2551592Srgrimes				retrieve((char *) 0, $4);
2561592Srgrimes			if ($4 != NULL)
2571592Srgrimes				free($4);
2581592Srgrimes		}
2591592Srgrimes	| STOR check_login SP pathname CRLF
2601592Srgrimes		{
2611592Srgrimes			if ($2 && $4 != NULL)
2621592Srgrimes				store($4, "w", 0);
2631592Srgrimes			if ($4 != NULL)
2641592Srgrimes				free($4);
2651592Srgrimes		}
2661592Srgrimes	| APPE check_login SP pathname CRLF
2671592Srgrimes		{
2681592Srgrimes			if ($2 && $4 != NULL)
2691592Srgrimes				store($4, "a", 0);
2701592Srgrimes			if ($4 != NULL)
2711592Srgrimes				free($4);
2721592Srgrimes		}
2731592Srgrimes	| NLST check_login CRLF
2741592Srgrimes		{
2751592Srgrimes			if ($2)
2761592Srgrimes				send_file_list(".");
2771592Srgrimes		}
2781592Srgrimes	| NLST check_login SP STRING CRLF
2791592Srgrimes		{
2801592Srgrimes			if ($2 && $4 != NULL)
2811592Srgrimes				send_file_list($4);
2821592Srgrimes			if ($4 != NULL)
2831592Srgrimes				free($4);
2841592Srgrimes		}
2851592Srgrimes	| LIST check_login CRLF
2861592Srgrimes		{
2871592Srgrimes			if ($2)
2881592Srgrimes				retrieve("/bin/ls -lgA", "");
2891592Srgrimes		}
2901592Srgrimes	| LIST check_login SP pathname CRLF
2911592Srgrimes		{
2921592Srgrimes			if ($2 && $4 != NULL)
2931592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
2941592Srgrimes			if ($4 != NULL)
2951592Srgrimes				free($4);
2961592Srgrimes		}
2971592Srgrimes	| STAT check_login SP pathname CRLF
2981592Srgrimes		{
2991592Srgrimes			if ($2 && $4 != NULL)
3001592Srgrimes				statfilecmd($4);
3011592Srgrimes			if ($4 != NULL)
3021592Srgrimes				free($4);
3031592Srgrimes		}
3041592Srgrimes	| STAT CRLF
3051592Srgrimes		{
3061592Srgrimes			statcmd();
3071592Srgrimes		}
3081592Srgrimes	| DELE check_login SP pathname CRLF
3091592Srgrimes		{
3101592Srgrimes			if ($2 && $4 != NULL)
3111592Srgrimes				delete($4);
3121592Srgrimes			if ($4 != NULL)
3131592Srgrimes				free($4);
3141592Srgrimes		}
31517433Spst	| RNTO check_login SP pathname CRLF
3161592Srgrimes		{
31717433Spst			if ($2) {
31817433Spst				if (fromname) {
31917433Spst					renamecmd(fromname, $4);
32017433Spst					free(fromname);
32117433Spst					fromname = (char *) 0;
32217433Spst				} else {
32317433Spst					reply(503, "Bad sequence of commands.");
32417433Spst				}
3251592Srgrimes			}
32617433Spst			free($4);
3271592Srgrimes		}
3281592Srgrimes	| ABOR CRLF
3291592Srgrimes		{
3301592Srgrimes			reply(225, "ABOR command successful.");
3311592Srgrimes		}
3321592Srgrimes	| CWD check_login CRLF
3331592Srgrimes		{
3341592Srgrimes			if ($2)
3351592Srgrimes				cwd(pw->pw_dir);
3361592Srgrimes		}
3371592Srgrimes	| CWD check_login SP pathname CRLF
3381592Srgrimes		{
3391592Srgrimes			if ($2 && $4 != NULL)
3401592Srgrimes				cwd($4);
3411592Srgrimes			if ($4 != NULL)
3421592Srgrimes				free($4);
3431592Srgrimes		}
3441592Srgrimes	| HELP CRLF
3451592Srgrimes		{
3461592Srgrimes			help(cmdtab, (char *) 0);
3471592Srgrimes		}
3481592Srgrimes	| HELP SP STRING CRLF
3491592Srgrimes		{
3501592Srgrimes			char *cp = $3;
3511592Srgrimes
3521592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
3531592Srgrimes				cp = $3 + 4;
3541592Srgrimes				if (*cp == ' ')
3551592Srgrimes					cp++;
3561592Srgrimes				if (*cp)
3571592Srgrimes					help(sitetab, cp);
3581592Srgrimes				else
3591592Srgrimes					help(sitetab, (char *) 0);
3601592Srgrimes			} else
3611592Srgrimes				help(cmdtab, $3);
3621592Srgrimes		}
3631592Srgrimes	| NOOP CRLF
3641592Srgrimes		{
3651592Srgrimes			reply(200, "NOOP command successful.");
3661592Srgrimes		}
3671592Srgrimes	| MKD check_login SP pathname CRLF
3681592Srgrimes		{
3691592Srgrimes			if ($2 && $4 != NULL)
3701592Srgrimes				makedir($4);
3711592Srgrimes			if ($4 != NULL)
3721592Srgrimes				free($4);
3731592Srgrimes		}
3741592Srgrimes	| RMD check_login SP pathname CRLF
3751592Srgrimes		{
3761592Srgrimes			if ($2 && $4 != NULL)
3771592Srgrimes				removedir($4);
3781592Srgrimes			if ($4 != NULL)
3791592Srgrimes				free($4);
3801592Srgrimes		}
3811592Srgrimes	| PWD check_login CRLF
3821592Srgrimes		{
3831592Srgrimes			if ($2)
3841592Srgrimes				pwd();
3851592Srgrimes		}
3861592Srgrimes	| CDUP check_login CRLF
3871592Srgrimes		{
3881592Srgrimes			if ($2)
3891592Srgrimes				cwd("..");
3901592Srgrimes		}
3911592Srgrimes	| SITE SP HELP CRLF
3921592Srgrimes		{
3931592Srgrimes			help(sitetab, (char *) 0);
3941592Srgrimes		}
3951592Srgrimes	| SITE SP HELP SP STRING CRLF
3961592Srgrimes		{
3971592Srgrimes			help(sitetab, $5);
3981592Srgrimes		}
3991592Srgrimes	| SITE SP UMASK check_login CRLF
4001592Srgrimes		{
4011592Srgrimes			int oldmask;
4021592Srgrimes
4031592Srgrimes			if ($4) {
4041592Srgrimes				oldmask = umask(0);
4051592Srgrimes				(void) umask(oldmask);
4061592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
4071592Srgrimes			}
4081592Srgrimes		}
4091592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
4101592Srgrimes		{
4111592Srgrimes			int oldmask;
4121592Srgrimes
4131592Srgrimes			if ($4) {
4141592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
4151592Srgrimes					reply(501, "Bad UMASK value");
4161592Srgrimes				} else {
4171592Srgrimes					oldmask = umask($6);
4181592Srgrimes					reply(200,
4191592Srgrimes					    "UMASK set to %03o (was %03o)",
4201592Srgrimes					    $6, oldmask);
4211592Srgrimes				}
4221592Srgrimes			}
4231592Srgrimes		}
4241592Srgrimes	| SITE SP CHMOD check_login SP octal_number SP pathname CRLF
4251592Srgrimes		{
4261592Srgrimes			if ($4 && ($8 != NULL)) {
4271592Srgrimes				if ($6 > 0777)
4281592Srgrimes					reply(501,
4291592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
4301592Srgrimes				else if (chmod($8, $6) < 0)
4311592Srgrimes					perror_reply(550, $8);
4321592Srgrimes				else
4331592Srgrimes					reply(200, "CHMOD command successful.");
4341592Srgrimes			}
4351592Srgrimes			if ($8 != NULL)
4361592Srgrimes				free($8);
4371592Srgrimes		}
4381592Srgrimes	| SITE SP IDLE CRLF
4391592Srgrimes		{
4401592Srgrimes			reply(200,
4411592Srgrimes			    "Current IDLE time limit is %d seconds; max %d",
4421592Srgrimes				timeout, maxtimeout);
4431592Srgrimes		}
4441592Srgrimes	| SITE SP IDLE SP NUMBER CRLF
4451592Srgrimes		{
4461592Srgrimes			if ($5 < 30 || $5 > maxtimeout) {
4471592Srgrimes				reply(501,
4481592Srgrimes			"Maximum IDLE time must be between 30 and %d seconds",
4491592Srgrimes				    maxtimeout);
4501592Srgrimes			} else {
4511592Srgrimes				timeout = $5;
4521592Srgrimes				(void) alarm((unsigned) timeout);
4531592Srgrimes				reply(200,
4541592Srgrimes				    "Maximum IDLE time set to %d seconds",
4551592Srgrimes				    timeout);
4561592Srgrimes			}
4571592Srgrimes		}
4581592Srgrimes	| STOU check_login SP pathname CRLF
4591592Srgrimes		{
4601592Srgrimes			if ($2 && $4 != NULL)
4611592Srgrimes				store($4, "w", 1);
4621592Srgrimes			if ($4 != NULL)
4631592Srgrimes				free($4);
4641592Srgrimes		}
4651592Srgrimes	| SYST CRLF
4661592Srgrimes		{
4671592Srgrimes#ifdef unix
4681592Srgrimes#ifdef BSD
4691592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
4701592Srgrimes				NBBY, BSD);
4711592Srgrimes#else /* BSD */
4721592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
4731592Srgrimes#endif /* BSD */
4741592Srgrimes#else /* unix */
4751592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
4761592Srgrimes#endif /* unix */
4771592Srgrimes		}
4781592Srgrimes
4791592Srgrimes		/*
4801592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
4811592Srgrimes		 * it will be in the updated RFC.
4821592Srgrimes		 *
4831592Srgrimes		 * Return size of file in a format suitable for
4841592Srgrimes		 * using with RESTART (we just count bytes).
4851592Srgrimes		 */
4861592Srgrimes	| SIZE check_login SP pathname CRLF
4871592Srgrimes		{
4881592Srgrimes			if ($2 && $4 != NULL)
4891592Srgrimes				sizecmd($4);
4901592Srgrimes			if ($4 != NULL)
4911592Srgrimes				free($4);
4921592Srgrimes		}
4931592Srgrimes
4941592Srgrimes		/*
4951592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
4961592Srgrimes		 * it will be in the updated RFC.
4971592Srgrimes		 *
4981592Srgrimes		 * Return modification time of file as an ISO 3307
4991592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
5001592Srgrimes		 * where xxx is the fractional second (of any precision,
5011592Srgrimes		 * not necessarily 3 digits)
5021592Srgrimes		 */
5031592Srgrimes	| MDTM check_login SP pathname CRLF
5041592Srgrimes		{
5051592Srgrimes			if ($2 && $4 != NULL) {
5061592Srgrimes				struct stat stbuf;
5071592Srgrimes				if (stat($4, &stbuf) < 0)
5081592Srgrimes					reply(550, "%s: %s",
5091592Srgrimes					    $4, strerror(errno));
5101592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
5111592Srgrimes					reply(550, "%s: not a plain file.", $4);
5121592Srgrimes				} else {
5131592Srgrimes					struct tm *t;
5141592Srgrimes					t = gmtime(&stbuf.st_mtime);
5151592Srgrimes					reply(213,
51617435Spst					    "%04d%02d%02d%02d%02d%02d",
51717435Spst					    1900 + t->tm_year,
51817435Spst					    t->tm_mon+1, t->tm_mday,
5191592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
5201592Srgrimes				}
5211592Srgrimes			}
5221592Srgrimes			if ($4 != NULL)
5231592Srgrimes				free($4);
5241592Srgrimes		}
5251592Srgrimes	| QUIT CRLF
5261592Srgrimes		{
5271592Srgrimes			reply(221, "Goodbye.");
5281592Srgrimes			dologout(0);
5291592Srgrimes		}
5301592Srgrimes	| error CRLF
5311592Srgrimes		{
5321592Srgrimes			yyerrok;
5331592Srgrimes		}
5341592Srgrimes	;
5351592Srgrimesrcmd
5361592Srgrimes	: RNFR check_login SP pathname CRLF
5371592Srgrimes		{
5381592Srgrimes			char *renamefrom();
5391592Srgrimes
5401592Srgrimes			restart_point = (off_t) 0;
5411592Srgrimes			if ($2 && $4) {
5421592Srgrimes				fromname = renamefrom($4);
5431592Srgrimes				if (fromname == (char *) 0 && $4) {
5441592Srgrimes					free($4);
5451592Srgrimes				}
5461592Srgrimes			}
5471592Srgrimes		}
5481592Srgrimes	| REST SP byte_size CRLF
5491592Srgrimes		{
5501592Srgrimes			fromname = (char *) 0;
5511592Srgrimes			restart_point = $3;	/* XXX $3 is only "int" */
5521592Srgrimes			reply(350, "Restarting at %qd. %s", restart_point,
5531592Srgrimes			    "Send STORE or RETRIEVE to initiate transfer.");
5541592Srgrimes		}
5551592Srgrimes	;
5561592Srgrimes
5571592Srgrimesusername
5581592Srgrimes	: STRING
5591592Srgrimes	;
5601592Srgrimes
5611592Srgrimespassword
5621592Srgrimes	: /* empty */
5631592Srgrimes		{
5641592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
5651592Srgrimes		}
5661592Srgrimes	| STRING
5671592Srgrimes	;
5681592Srgrimes
5691592Srgrimesbyte_size
5701592Srgrimes	: NUMBER
5711592Srgrimes	;
5721592Srgrimes
5731592Srgrimeshost_port
5741592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
5751592Srgrimes		NUMBER COMMA NUMBER
5761592Srgrimes		{
5771592Srgrimes			char *a, *p;
5781592Srgrimes
57917435Spst			data_dest.sin_len = sizeof(struct sockaddr_in);
58017435Spst			data_dest.sin_family = AF_INET;
58117435Spst			p = (char *)&data_dest.sin_port;
58217435Spst			p[0] = $9; p[1] = $11;
5831592Srgrimes			a = (char *)&data_dest.sin_addr;
5841592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
5851592Srgrimes		}
5861592Srgrimes	;
5871592Srgrimes
5881592Srgrimesform_code
5891592Srgrimes	: N
5901592Srgrimes		{
5911592Srgrimes			$$ = FORM_N;
5921592Srgrimes		}
5931592Srgrimes	| T
5941592Srgrimes		{
5951592Srgrimes			$$ = FORM_T;
5961592Srgrimes		}
5971592Srgrimes	| C
5981592Srgrimes		{
5991592Srgrimes			$$ = FORM_C;
6001592Srgrimes		}
6011592Srgrimes	;
6021592Srgrimes
6031592Srgrimestype_code
6041592Srgrimes	: A
6051592Srgrimes		{
6061592Srgrimes			cmd_type = TYPE_A;
6071592Srgrimes			cmd_form = FORM_N;
6081592Srgrimes		}
6091592Srgrimes	| A SP form_code
6101592Srgrimes		{
6111592Srgrimes			cmd_type = TYPE_A;
6121592Srgrimes			cmd_form = $3;
6131592Srgrimes		}
6141592Srgrimes	| E
6151592Srgrimes		{
6161592Srgrimes			cmd_type = TYPE_E;
6171592Srgrimes			cmd_form = FORM_N;
6181592Srgrimes		}
6191592Srgrimes	| E SP form_code
6201592Srgrimes		{
6211592Srgrimes			cmd_type = TYPE_E;
6221592Srgrimes			cmd_form = $3;
6231592Srgrimes		}
6241592Srgrimes	| I
6251592Srgrimes		{
6261592Srgrimes			cmd_type = TYPE_I;
6271592Srgrimes		}
6281592Srgrimes	| L
6291592Srgrimes		{
6301592Srgrimes			cmd_type = TYPE_L;
6311592Srgrimes			cmd_bytesz = NBBY;
6321592Srgrimes		}
6331592Srgrimes	| L SP byte_size
6341592Srgrimes		{
6351592Srgrimes			cmd_type = TYPE_L;
6361592Srgrimes			cmd_bytesz = $3;
6371592Srgrimes		}
6381592Srgrimes		/* this is for a bug in the BBN ftp */
6391592Srgrimes	| L byte_size
6401592Srgrimes		{
6411592Srgrimes			cmd_type = TYPE_L;
6421592Srgrimes			cmd_bytesz = $2;
6431592Srgrimes		}
6441592Srgrimes	;
6451592Srgrimes
6461592Srgrimesstruct_code
6471592Srgrimes	: F
6481592Srgrimes		{
6491592Srgrimes			$$ = STRU_F;
6501592Srgrimes		}
6511592Srgrimes	| R
6521592Srgrimes		{
6531592Srgrimes			$$ = STRU_R;
6541592Srgrimes		}
6551592Srgrimes	| P
6561592Srgrimes		{
6571592Srgrimes			$$ = STRU_P;
6581592Srgrimes		}
6591592Srgrimes	;
6601592Srgrimes
6611592Srgrimesmode_code
6621592Srgrimes	: S
6631592Srgrimes		{
6641592Srgrimes			$$ = MODE_S;
6651592Srgrimes		}
6661592Srgrimes	| B
6671592Srgrimes		{
6681592Srgrimes			$$ = MODE_B;
6691592Srgrimes		}
6701592Srgrimes	| C
6711592Srgrimes		{
6721592Srgrimes			$$ = MODE_C;
6731592Srgrimes		}
6741592Srgrimes	;
6751592Srgrimes
6761592Srgrimespathname
6771592Srgrimes	: pathstring
6781592Srgrimes		{
6791592Srgrimes			/*
6801592Srgrimes			 * Problem: this production is used for all pathname
6811592Srgrimes			 * processing, but only gives a 550 error reply.
6821592Srgrimes			 * This is a valid reply in some cases but not in others.
6831592Srgrimes			 */
6841592Srgrimes			if (logged_in && $1 && *$1 == '~') {
6851592Srgrimes				glob_t gl;
6861592Srgrimes				int flags =
6871592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
6881592Srgrimes
6891592Srgrimes				memset(&gl, 0, sizeof(gl));
6901592Srgrimes				if (glob($1, flags, NULL, &gl) ||
6911592Srgrimes				    gl.gl_pathc == 0) {
6921592Srgrimes					reply(550, "not found");
6931592Srgrimes					$$ = NULL;
6941592Srgrimes				} else {
6951592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
6961592Srgrimes				}
6971592Srgrimes				globfree(&gl);
6981592Srgrimes				free($1);
6991592Srgrimes			} else
7001592Srgrimes				$$ = $1;
7011592Srgrimes		}
7021592Srgrimes	;
7031592Srgrimes
7041592Srgrimespathstring
7051592Srgrimes	: STRING
7061592Srgrimes	;
7071592Srgrimes
7081592Srgrimesoctal_number
7091592Srgrimes	: NUMBER
7101592Srgrimes		{
7111592Srgrimes			int ret, dec, multby, digit;
7121592Srgrimes
7131592Srgrimes			/*
7141592Srgrimes			 * Convert a number that was read as decimal number
7151592Srgrimes			 * to what it would be if it had been read as octal.
7161592Srgrimes			 */
7171592Srgrimes			dec = $1;
7181592Srgrimes			multby = 1;
7191592Srgrimes			ret = 0;
7201592Srgrimes			while (dec) {
7211592Srgrimes				digit = dec%10;
7221592Srgrimes				if (digit > 7) {
7231592Srgrimes					ret = -1;
7241592Srgrimes					break;
7251592Srgrimes				}
7261592Srgrimes				ret += digit * multby;
7271592Srgrimes				multby *= 8;
7281592Srgrimes				dec /= 10;
7291592Srgrimes			}
7301592Srgrimes			$$ = ret;
7311592Srgrimes		}
7321592Srgrimes	;
7331592Srgrimes
7341592Srgrimes
7351592Srgrimescheck_login
7361592Srgrimes	: /* empty */
7371592Srgrimes		{
7381592Srgrimes			if (logged_in)
7391592Srgrimes				$$ = 1;
7401592Srgrimes			else {
7411592Srgrimes				reply(530, "Please login with USER and PASS.");
7421592Srgrimes				$$ = 0;
7431592Srgrimes			}
7441592Srgrimes		}
7451592Srgrimes	;
7461592Srgrimes
7471592Srgrimes%%
7481592Srgrimes
7491592Srgrimesextern jmp_buf errcatch;
7501592Srgrimes
7511592Srgrimes#define	CMD	0	/* beginning of command */
7521592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
7531592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
7541592Srgrimes#define	STR2	3	/* expect STRING */
7551592Srgrimes#define	OSTR	4	/* optional SP then STRING */
7561592Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
7571592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
7581592Srgrimes#define	SITECMD	7	/* SITE command */
7591592Srgrimes#define	NSTR	8	/* Number followed by a string */
7601592Srgrimes
7611592Srgrimesstruct tab {
7621592Srgrimes	char	*name;
7631592Srgrimes	short	token;
7641592Srgrimes	short	state;
7651592Srgrimes	short	implemented;	/* 1 if command is implemented */
7661592Srgrimes	char	*help;
7671592Srgrimes};
7681592Srgrimes
7691592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
7701592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
7711592Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
7721592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
7731592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
7741592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
7751592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
7761592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
7771592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
7781592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
7791592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
7801592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
7811592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
7821592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
7831592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
7841592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
7851592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
7861592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
7871592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
7881592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
7891592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
7901592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
7911592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
7921592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
7931592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
7941592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
7951592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
7961592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
7971592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
7981592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
7991592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
8001592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
8011592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
8021592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
8031592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
8041592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
8051592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
8061592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
8071592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
8081592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
8091592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
8101592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
8111592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
8121592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
8131592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
8141592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
8151592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
8161592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
8171592Srgrimes	{ NULL,   0,    0,    0,	0 }
8181592Srgrimes};
8191592Srgrimes
8201592Srgrimesstruct tab sitetab[] = {
8211592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
8221592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
8231592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
8241592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
8251592Srgrimes	{ NULL,   0,    0,    0,	0 }
8261592Srgrimes};
8271592Srgrimes
8281592Srgrimesstatic char	*copy __P((char *));
8291592Srgrimesstatic void	 help __P((struct tab *, char *));
8301592Srgrimesstatic struct tab *
8311592Srgrimes		 lookup __P((struct tab *, char *));
8321592Srgrimesstatic void	 sizecmd __P((char *));
8331592Srgrimesstatic void	 toolong __P((int));
8341592Srgrimesstatic int	 yylex __P((void));
8351592Srgrimes
8361592Srgrimesstatic struct tab *
8371592Srgrimeslookup(p, cmd)
8381592Srgrimes	struct tab *p;
8391592Srgrimes	char *cmd;
8401592Srgrimes{
8411592Srgrimes
8421592Srgrimes	for (; p->name != NULL; p++)
8431592Srgrimes		if (strcmp(cmd, p->name) == 0)
8441592Srgrimes			return (p);
8451592Srgrimes	return (0);
8461592Srgrimes}
8471592Srgrimes
8481592Srgrimes#include <arpa/telnet.h>
8491592Srgrimes
8501592Srgrimes/*
8511592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
8521592Srgrimes */
8531592Srgrimeschar *
8541592Srgrimesgetline(s, n, iop)
8551592Srgrimes	char *s;
8561592Srgrimes	int n;
8571592Srgrimes	FILE *iop;
8581592Srgrimes{
8591592Srgrimes	int c;
8601592Srgrimes	register char *cs;
8611592Srgrimes
8621592Srgrimes	cs = s;
8631592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
8641592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
8651592Srgrimes		*cs++ = tmpline[c];
8661592Srgrimes		if (tmpline[c] == '\n') {
8671592Srgrimes			*cs++ = '\0';
8681592Srgrimes			if (debug)
8691592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
8701592Srgrimes			tmpline[0] = '\0';
8711592Srgrimes			return(s);
8721592Srgrimes		}
8731592Srgrimes		if (c == 0)
8741592Srgrimes			tmpline[0] = '\0';
8751592Srgrimes	}
8761592Srgrimes	while ((c = getc(iop)) != EOF) {
8771592Srgrimes		c &= 0377;
8781592Srgrimes		if (c == IAC) {
8791592Srgrimes		    if ((c = getc(iop)) != EOF) {
8801592Srgrimes			c &= 0377;
8811592Srgrimes			switch (c) {
8821592Srgrimes			case WILL:
8831592Srgrimes			case WONT:
8841592Srgrimes				c = getc(iop);
8851592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
8861592Srgrimes				(void) fflush(stdout);
8871592Srgrimes				continue;
8881592Srgrimes			case DO:
8891592Srgrimes			case DONT:
8901592Srgrimes				c = getc(iop);
8911592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
8921592Srgrimes				(void) fflush(stdout);
8931592Srgrimes				continue;
8941592Srgrimes			case IAC:
8951592Srgrimes				break;
8961592Srgrimes			default:
8971592Srgrimes				continue;	/* ignore command */
8981592Srgrimes			}
8991592Srgrimes		    }
9001592Srgrimes		}
9011592Srgrimes		*cs++ = c;
9021592Srgrimes		if (--n <= 0 || c == '\n')
9031592Srgrimes			break;
9041592Srgrimes	}
9051592Srgrimes	if (c == EOF && cs == s)
9061592Srgrimes		return (NULL);
9071592Srgrimes	*cs++ = '\0';
9081592Srgrimes	if (debug) {
9091592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
9101592Srgrimes			/* Don't syslog passwords */
9111592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
9121592Srgrimes		} else {
9131592Srgrimes			register char *cp;
9141592Srgrimes			register int len;
9151592Srgrimes
9161592Srgrimes			/* Don't syslog trailing CR-LF */
9171592Srgrimes			len = strlen(s);
9181592Srgrimes			cp = s + len - 1;
9191592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
9201592Srgrimes				--cp;
9211592Srgrimes				--len;
9221592Srgrimes			}
9231592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
9241592Srgrimes		}
9251592Srgrimes	}
9261592Srgrimes	return (s);
9271592Srgrimes}
9281592Srgrimes
9291592Srgrimesstatic void
9301592Srgrimestoolong(signo)
9311592Srgrimes	int signo;
9321592Srgrimes{
9331592Srgrimes
9341592Srgrimes	reply(421,
9351592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
9361592Srgrimes	if (logging)
9371592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
9381592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
9391592Srgrimes	dologout(1);
9401592Srgrimes}
9411592Srgrimes
9421592Srgrimesstatic int
9431592Srgrimesyylex()
9441592Srgrimes{
9451592Srgrimes	static int cpos, state;
9461592Srgrimes	char *cp, *cp2;
9471592Srgrimes	struct tab *p;
9481592Srgrimes	int n;
9491592Srgrimes	char c;
9501592Srgrimes
9511592Srgrimes	for (;;) {
9521592Srgrimes		switch (state) {
9531592Srgrimes
9541592Srgrimes		case CMD:
9551592Srgrimes			(void) signal(SIGALRM, toolong);
9561592Srgrimes			(void) alarm((unsigned) timeout);
9571592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
9581592Srgrimes				reply(221, "You could at least say goodbye.");
9591592Srgrimes				dologout(0);
9601592Srgrimes			}
9611592Srgrimes			(void) alarm(0);
9621592Srgrimes#ifdef SETPROCTITLE
96329574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
9641592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
9651592Srgrimes#endif /* SETPROCTITLE */
9661592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
9671592Srgrimes				*cp++ = '\n';
9681592Srgrimes				*cp = '\0';
9691592Srgrimes			}
9701592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
9711592Srgrimes				cpos = cp - cbuf;
9721592Srgrimes			if (cpos == 0)
9731592Srgrimes				cpos = 4;
9741592Srgrimes			c = cbuf[cpos];
9751592Srgrimes			cbuf[cpos] = '\0';
9761592Srgrimes			upper(cbuf);
9771592Srgrimes			p = lookup(cmdtab, cbuf);
9781592Srgrimes			cbuf[cpos] = c;
9793776Spst			if (p != 0) {
9801592Srgrimes				if (p->implemented == 0) {
9811592Srgrimes					nack(p->name);
9821592Srgrimes					longjmp(errcatch,0);
9831592Srgrimes					/* NOTREACHED */
9841592Srgrimes				}
9851592Srgrimes				state = p->state;
9861592Srgrimes				yylval.s = p->name;
9871592Srgrimes				return (p->token);
9881592Srgrimes			}
9891592Srgrimes			break;
9901592Srgrimes
9911592Srgrimes		case SITECMD:
9921592Srgrimes			if (cbuf[cpos] == ' ') {
9931592Srgrimes				cpos++;
9941592Srgrimes				return (SP);
9951592Srgrimes			}
9961592Srgrimes			cp = &cbuf[cpos];
9971592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
9981592Srgrimes				cpos = cp2 - cbuf;
9991592Srgrimes			c = cbuf[cpos];
10001592Srgrimes			cbuf[cpos] = '\0';
10011592Srgrimes			upper(cp);
10021592Srgrimes			p = lookup(sitetab, cp);
10031592Srgrimes			cbuf[cpos] = c;
10043777Spst			if (guest == 0 && p != 0) {
10051592Srgrimes				if (p->implemented == 0) {
10061592Srgrimes					state = CMD;
10071592Srgrimes					nack(p->name);
10081592Srgrimes					longjmp(errcatch,0);
10091592Srgrimes					/* NOTREACHED */
10101592Srgrimes				}
10111592Srgrimes				state = p->state;
10121592Srgrimes				yylval.s = p->name;
10131592Srgrimes				return (p->token);
10141592Srgrimes			}
10151592Srgrimes			state = CMD;
10161592Srgrimes			break;
10171592Srgrimes
10181592Srgrimes		case OSTR:
10191592Srgrimes			if (cbuf[cpos] == '\n') {
10201592Srgrimes				state = CMD;
10211592Srgrimes				return (CRLF);
10221592Srgrimes			}
10231592Srgrimes			/* FALLTHROUGH */
10241592Srgrimes
10251592Srgrimes		case STR1:
10261592Srgrimes		case ZSTR1:
10271592Srgrimes		dostr1:
10281592Srgrimes			if (cbuf[cpos] == ' ') {
10291592Srgrimes				cpos++;
103051979Salfred				state = state == OSTR ? STR2 : state+1;
10311592Srgrimes				return (SP);
10321592Srgrimes			}
10331592Srgrimes			break;
10341592Srgrimes
10351592Srgrimes		case ZSTR2:
10361592Srgrimes			if (cbuf[cpos] == '\n') {
10371592Srgrimes				state = CMD;
10381592Srgrimes				return (CRLF);
10391592Srgrimes			}
10401592Srgrimes			/* FALLTHROUGH */
10411592Srgrimes
10421592Srgrimes		case STR2:
10431592Srgrimes			cp = &cbuf[cpos];
10441592Srgrimes			n = strlen(cp);
10451592Srgrimes			cpos += n - 1;
10461592Srgrimes			/*
10471592Srgrimes			 * Make sure the string is nonempty and \n terminated.
10481592Srgrimes			 */
10491592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
10501592Srgrimes				cbuf[cpos] = '\0';
10511592Srgrimes				yylval.s = copy(cp);
10521592Srgrimes				cbuf[cpos] = '\n';
10531592Srgrimes				state = ARGS;
10541592Srgrimes				return (STRING);
10551592Srgrimes			}
10561592Srgrimes			break;
10571592Srgrimes
10581592Srgrimes		case NSTR:
10591592Srgrimes			if (cbuf[cpos] == ' ') {
10601592Srgrimes				cpos++;
10611592Srgrimes				return (SP);
10621592Srgrimes			}
10631592Srgrimes			if (isdigit(cbuf[cpos])) {
10641592Srgrimes				cp = &cbuf[cpos];
10651592Srgrimes				while (isdigit(cbuf[++cpos]))
10661592Srgrimes					;
10671592Srgrimes				c = cbuf[cpos];
10681592Srgrimes				cbuf[cpos] = '\0';
10691592Srgrimes				yylval.i = atoi(cp);
10701592Srgrimes				cbuf[cpos] = c;
10711592Srgrimes				state = STR1;
10721592Srgrimes				return (NUMBER);
10731592Srgrimes			}
10741592Srgrimes			state = STR1;
10751592Srgrimes			goto dostr1;
10761592Srgrimes
10771592Srgrimes		case ARGS:
10781592Srgrimes			if (isdigit(cbuf[cpos])) {
10791592Srgrimes				cp = &cbuf[cpos];
10801592Srgrimes				while (isdigit(cbuf[++cpos]))
10811592Srgrimes					;
10821592Srgrimes				c = cbuf[cpos];
10831592Srgrimes				cbuf[cpos] = '\0';
10841592Srgrimes				yylval.i = atoi(cp);
10851592Srgrimes				cbuf[cpos] = c;
10861592Srgrimes				return (NUMBER);
10871592Srgrimes			}
10881592Srgrimes			switch (cbuf[cpos++]) {
10891592Srgrimes
10901592Srgrimes			case '\n':
10911592Srgrimes				state = CMD;
10921592Srgrimes				return (CRLF);
10931592Srgrimes
10941592Srgrimes			case ' ':
10951592Srgrimes				return (SP);
10961592Srgrimes
10971592Srgrimes			case ',':
10981592Srgrimes				return (COMMA);
10991592Srgrimes
11001592Srgrimes			case 'A':
11011592Srgrimes			case 'a':
11021592Srgrimes				return (A);
11031592Srgrimes
11041592Srgrimes			case 'B':
11051592Srgrimes			case 'b':
11061592Srgrimes				return (B);
11071592Srgrimes
11081592Srgrimes			case 'C':
11091592Srgrimes			case 'c':
11101592Srgrimes				return (C);
11111592Srgrimes
11121592Srgrimes			case 'E':
11131592Srgrimes			case 'e':
11141592Srgrimes				return (E);
11151592Srgrimes
11161592Srgrimes			case 'F':
11171592Srgrimes			case 'f':
11181592Srgrimes				return (F);
11191592Srgrimes
11201592Srgrimes			case 'I':
11211592Srgrimes			case 'i':
11221592Srgrimes				return (I);
11231592Srgrimes
11241592Srgrimes			case 'L':
11251592Srgrimes			case 'l':
11261592Srgrimes				return (L);
11271592Srgrimes
11281592Srgrimes			case 'N':
11291592Srgrimes			case 'n':
11301592Srgrimes				return (N);
11311592Srgrimes
11321592Srgrimes			case 'P':
11331592Srgrimes			case 'p':
11341592Srgrimes				return (P);
11351592Srgrimes
11361592Srgrimes			case 'R':
11371592Srgrimes			case 'r':
11381592Srgrimes				return (R);
11391592Srgrimes
11401592Srgrimes			case 'S':
11411592Srgrimes			case 's':
11421592Srgrimes				return (S);
11431592Srgrimes
11441592Srgrimes			case 'T':
11451592Srgrimes			case 't':
11461592Srgrimes				return (T);
11471592Srgrimes
11481592Srgrimes			}
11491592Srgrimes			break;
11501592Srgrimes
11511592Srgrimes		default:
11521592Srgrimes			fatal("Unknown state in scanner.");
11531592Srgrimes		}
11541592Srgrimes		yyerror((char *) 0);
11551592Srgrimes		state = CMD;
11561592Srgrimes		longjmp(errcatch,0);
11571592Srgrimes	}
11581592Srgrimes}
11591592Srgrimes
11601592Srgrimesvoid
11611592Srgrimesupper(s)
11621592Srgrimes	char *s;
11631592Srgrimes{
11641592Srgrimes	while (*s != '\0') {
11651592Srgrimes		if (islower(*s))
11661592Srgrimes			*s = toupper(*s);
11671592Srgrimes		s++;
11681592Srgrimes	}
11691592Srgrimes}
11701592Srgrimes
11711592Srgrimesstatic char *
11721592Srgrimescopy(s)
11731592Srgrimes	char *s;
11741592Srgrimes{
11751592Srgrimes	char *p;
11761592Srgrimes
11771592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
11781592Srgrimes	if (p == NULL)
11791592Srgrimes		fatal("Ran out of memory.");
11801592Srgrimes	(void) strcpy(p, s);
11811592Srgrimes	return (p);
11821592Srgrimes}
11831592Srgrimes
11841592Srgrimesstatic void
11851592Srgrimeshelp(ctab, s)
11861592Srgrimes	struct tab *ctab;
11871592Srgrimes	char *s;
11881592Srgrimes{
11891592Srgrimes	struct tab *c;
11901592Srgrimes	int width, NCMDS;
11911592Srgrimes	char *type;
11921592Srgrimes
11931592Srgrimes	if (ctab == sitetab)
11941592Srgrimes		type = "SITE ";
11951592Srgrimes	else
11961592Srgrimes		type = "";
11971592Srgrimes	width = 0, NCMDS = 0;
11981592Srgrimes	for (c = ctab; c->name != NULL; c++) {
11991592Srgrimes		int len = strlen(c->name);
12001592Srgrimes
12011592Srgrimes		if (len > width)
12021592Srgrimes			width = len;
12031592Srgrimes		NCMDS++;
12041592Srgrimes	}
12051592Srgrimes	width = (width + 8) &~ 7;
12061592Srgrimes	if (s == 0) {
12071592Srgrimes		int i, j, w;
12081592Srgrimes		int columns, lines;
12091592Srgrimes
12101592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
12111592Srgrimes		    type, "(* =>'s unimplemented)");
12121592Srgrimes		columns = 76 / width;
12131592Srgrimes		if (columns == 0)
12141592Srgrimes			columns = 1;
12151592Srgrimes		lines = (NCMDS + columns - 1) / columns;
12161592Srgrimes		for (i = 0; i < lines; i++) {
12171592Srgrimes			printf("   ");
12181592Srgrimes			for (j = 0; j < columns; j++) {
12191592Srgrimes				c = ctab + j * lines + i;
12201592Srgrimes				printf("%s%c", c->name,
12211592Srgrimes					c->implemented ? ' ' : '*');
12221592Srgrimes				if (c + lines >= &ctab[NCMDS])
12231592Srgrimes					break;
12241592Srgrimes				w = strlen(c->name) + 1;
12251592Srgrimes				while (w < width) {
12261592Srgrimes					putchar(' ');
12271592Srgrimes					w++;
12281592Srgrimes				}
12291592Srgrimes			}
12301592Srgrimes			printf("\r\n");
12311592Srgrimes		}
12321592Srgrimes		(void) fflush(stdout);
12331592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
12341592Srgrimes		return;
12351592Srgrimes	}
12361592Srgrimes	upper(s);
12371592Srgrimes	c = lookup(ctab, s);
12381592Srgrimes	if (c == (struct tab *)0) {
12391592Srgrimes		reply(502, "Unknown command %s.", s);
12401592Srgrimes		return;
12411592Srgrimes	}
12421592Srgrimes	if (c->implemented)
12431592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
12441592Srgrimes	else
12451592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
12461592Srgrimes		    c->name, c->help);
12471592Srgrimes}
12481592Srgrimes
12491592Srgrimesstatic void
12501592Srgrimessizecmd(filename)
12511592Srgrimes	char *filename;
12521592Srgrimes{
12531592Srgrimes	switch (type) {
12541592Srgrimes	case TYPE_L:
12551592Srgrimes	case TYPE_I: {
12561592Srgrimes		struct stat stbuf;
12571592Srgrimes		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
12581592Srgrimes			reply(550, "%s: not a plain file.", filename);
12591592Srgrimes		else
12601592Srgrimes			reply(213, "%qu", stbuf.st_size);
12611592Srgrimes		break; }
12621592Srgrimes	case TYPE_A: {
12631592Srgrimes		FILE *fin;
12641592Srgrimes		int c;
12651592Srgrimes		off_t count;
12661592Srgrimes		struct stat stbuf;
12671592Srgrimes		fin = fopen(filename, "r");
12681592Srgrimes		if (fin == NULL) {
12691592Srgrimes			perror_reply(550, filename);
12701592Srgrimes			return;
12711592Srgrimes		}
12721592Srgrimes		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
12731592Srgrimes			reply(550, "%s: not a plain file.", filename);
12741592Srgrimes			(void) fclose(fin);
12751592Srgrimes			return;
12761592Srgrimes		}
12771592Srgrimes
12781592Srgrimes		count = 0;
12791592Srgrimes		while((c=getc(fin)) != EOF) {
12801592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
12811592Srgrimes				count++;
12821592Srgrimes			count++;
12831592Srgrimes		}
12841592Srgrimes		(void) fclose(fin);
12851592Srgrimes
12861592Srgrimes		reply(213, "%qd", count);
12871592Srgrimes		break; }
12881592Srgrimes	default:
12891592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
12901592Srgrimes	}
12911592Srgrimes}
1292