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