ftpcmd.y revision 233294
118334Speter/*	$NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $	*/
290075Sobrien
3169689Skan/*
418334Speter * Copyright (c) 1985, 1988, 1993, 1994
590075Sobrien *	The Regents of the University of California.  All rights reserved.
618334Speter *
790075Sobrien * Redistribution and use in source and binary forms, with or without
890075Sobrien * modification, are permitted provided that the following conditions
990075Sobrien * are met:
1090075Sobrien * 1. Redistributions of source code must retain the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer.
1290075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1390075Sobrien *    notice, this list of conditions and the following disclaimer in the
1490075Sobrien *    documentation and/or other materials provided with the distribution.
1590075Sobrien * 3. All advertising materials mentioning features or use of this software
1618334Speter *    must display the following acknowledgement:
1718334Speter *	This product includes software developed by the University of
1890075Sobrien *	California, Berkeley and its contributors.
19169689Skan * 4. Neither the name of the University nor the names of its contributors
20169689Skan *    may be used to endorse or promote products derived from this software
2118334Speter *    without specific prior written permission.
22117395Skan *
23117395Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24117395Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25223262Sbenl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26223262Sbenl * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27223262Sbenl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28223262Sbenl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29223262Sbenl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3118334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33169689Skan * SUCH DAMAGE.
34169689Skan *
3518334Speter *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
3618334Speter */
3718334Speter
3818334Speter/*
3918334Speter * Grammar for FTP commands.
4018334Speter * See RFC 959.
4118334Speter */
4218334Speter
4318334Speter%{
44169689Skan
4518334Speter#include "ftpd_locl.h"
4618334SpeterRCSID("$Id$");
4718334Speter
4818334Speteroff_t	restart_point;
49169689Skan
50169689Skanstatic	int hasyyerrored;
51169689Skan
5290075Sobrien
5390075Sobrienstatic	int cmd_type;
5418334Speterstatic	int cmd_form;
55169689Skanstatic	int cmd_bytesz;
5618334Speterchar	cbuf[64*1024];
57169689Skanchar	*fromname;
58169689Skan
59169689Skanstruct tab {
60169689Skan	char	*name;
61169689Skan	short	token;
62169689Skan	short	state;
63169689Skan	short	implemented;	/* 1 if command is implemented */
64169689Skan	char	*help;
65169689Skan};
66169689Skan
67169689Skanextern struct tab cmdtab[];
68169689Skanextern struct tab sitetab[];
69169689Skan
70169689Skanstatic char		*copy (char *);
71169689Skanstatic void		 help (struct tab *, char *);
72169689Skanstatic struct tab *
73169689Skan			 lookup (struct tab *, char *);
74169689Skanstatic void		 sizecmd (char *);
75169689Skanstatic RETSIGTYPE	 toolong (int);
76169689Skanstatic int		 yylex (void);
77169689Skan
78169689Skan/* This is for bison */
79169689Skan
80169689Skan#if !defined(alloca) && !defined(HAVE_ALLOCA)
81169689Skan#define alloca(x) malloc(x)
82169689Skan#endif
83169689Skan
84169689Skan%}
8550397Sobrien
86169689Skan%union {
8750397Sobrien	int	i;
8818334Speter	char   *s;
89169689Skan}
90169689Skan
91169689Skan%token
92169689Skan	A	B	C	E	F	I
93169689Skan	L	N	P	R	S	T
94169689Skan
95169689Skan	SP	CRLF	COMMA
96169689Skan
97169689Skan	USER	PASS	ACCT	REIN	QUIT	PORT
98169689Skan	PASV	TYPE	STRU	MODE	RETR	STOR
99169689Skan	APPE	MLFL	MAIL	MSND	MSOM	MSAM
100169689Skan	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
101169689Skan	ABOR	DELE	CWD	LIST	NLST	SITE
102169689Skan	sTAT	HELP	NOOP	MKD	RMD	PWD
103169689Skan	CDUP	STOU	SMNT	SYST	SIZE	MDTM
104169689Skan	EPRT	EPSV
105169689Skan
106169689Skan	UMASK	IDLE	CHMOD
107169689Skan
108169689Skan	AUTH	ADAT	PROT	PBSZ	CCC	MIC
109169689Skan	CONF	ENC
110169689Skan
111169689Skan	KAUTH	KLIST	KDESTROY KRBTKFILE AFSLOG
112169689Skan	LOCATE	URL
113169689Skan
114169689Skan	FEAT	OPTS
115169689Skan
116169689Skan	LEXERR
117169689Skan
118169689Skan%token	<s> STRING
119169689Skan%token	<i> NUMBER
120169689Skan
121169689Skan%type	<i> check_login check_login_no_guest check_secure octal_number byte_size
122169689Skan%type	<i> struct_code mode_code type_code form_code
123169689Skan%type	<s> pathstring pathname password username
124169689Skan
125169689Skan%start	cmd_list
126169689Skan
127169689Skan%%
128169689Skan
129169689Skancmd_list
130169689Skan	: /* empty */
131169689Skan	| cmd_list cmd
132169689Skan		{
133169689Skan			fromname = (char *) 0;
134169689Skan			restart_point = (off_t) 0;
135169689Skan		}
136169689Skan	| cmd_list rcmd
137169689Skan	;
138169689Skan
139169689Skancmd
140169689Skan	: USER SP username CRLF check_secure
141169689Skan		{
142169689Skan		    if ($5)
143169689Skan			user($3);
144169689Skan		    free($3);
145169689Skan		}
146169689Skan	| PASS SP password CRLF check_secure
147169689Skan		{
148169689Skan		    if ($5)
149169689Skan			pass($3);
150169689Skan		    memset ($3, 0, strlen($3));
151169689Skan		    free($3);
152169689Skan		}
153169689Skan
154169689Skan	| PORT SP host_port CRLF check_secure
155169689Skan		{
156169689Skan		    if ($5) {
157169689Skan			if (paranoid &&
158169689Skan			    (data_dest->sa_family != his_addr->sa_family ||
159169689Skan			     (socket_get_port(data_dest) < IPPORT_RESERVED) ||
160169689Skan			     memcmp(socket_get_address(data_dest),
161169689Skan				    socket_get_address(his_addr),
162169689Skan				    socket_addr_size(his_addr)) != 0)) {
163169689Skan			    usedefault = 1;
164169689Skan			    reply(500, "Illegal PORT range rejected.");
165117395Skan			} else {
16650397Sobrien			    usedefault = 0;
16750397Sobrien			    if (pdata >= 0) {
168169689Skan				close(pdata);
169169689Skan				pdata = -1;
17050397Sobrien			    }
171132718Skan			    reply(200, "PORT command successful.");
172132718Skan			}
173132718Skan		    }
174132718Skan		}
175169689Skan	| EPRT SP STRING CRLF check_secure
176169689Skan		{
177169689Skan		    if ($5)
178169689Skan			eprt ($3);
179169689Skan		    free ($3);
180169689Skan		}
181169689Skan	| PASV CRLF check_login
182169689Skan		{
183169689Skan		    if($3)
184169689Skan			pasv ();
185169689Skan		}
186169689Skan	| EPSV CRLF check_login
187169689Skan		{
188169689Skan		    if($3)
18918334Speter			epsv (NULL);
19018334Speter		}
191117395Skan	| EPSV SP STRING CRLF check_login
19290075Sobrien		{
19318334Speter		    if($5)
19418334Speter			epsv ($3);
19518334Speter		    free ($3);
196117395Skan		}
197169689Skan	| TYPE SP type_code CRLF check_secure
198169689Skan		{
199169689Skan		    if ($5) {
200169689Skan			switch (cmd_type) {
201169689Skan
202169689Skan			case TYPE_A:
20318334Speter				if (cmd_form == FORM_N) {
20490075Sobrien					reply(200, "Type set to A.");
20590075Sobrien					type = cmd_type;
20690075Sobrien					form = cmd_form;
20790075Sobrien				} else
20890075Sobrien					reply(504, "Form must be N.");
20990075Sobrien				break;
21090075Sobrien
21190075Sobrien			case TYPE_E:
21290075Sobrien				reply(504, "Type E not implemented.");
21390075Sobrien				break;
21490075Sobrien
21590075Sobrien			case TYPE_I:
21690075Sobrien				reply(200, "Type set to I.");
21718334Speter				type = cmd_type;
21818334Speter				break;
21918334Speter
220169689Skan			case TYPE_L:
22118334Speter#if NBBY == 8
22218334Speter				if (cmd_bytesz == 8) {
22390075Sobrien					reply(200,
22418334Speter					    "Type set to L (byte size 8).");
225169689Skan					type = cmd_type;
226169689Skan				} else
227169689Skan					reply(504, "Byte size must be 8.");
228169689Skan#else /* NBBY == 8 */
229169689Skan				UNIMPLEMENTED for NBBY != 8
230169689Skan#endif /* NBBY == 8 */
231169689Skan			}
232169689Skan		    }
233169689Skan		}
234169689Skan	| STRU SP struct_code CRLF check_secure
235169689Skan		{
236169689Skan		    if ($5) {
237169689Skan			switch ($3) {
238169689Skan
23990075Sobrien			case STRU_F:
24090075Sobrien				reply(200, "STRU F ok.");
24190075Sobrien				break;
24290075Sobrien
24350397Sobrien			default:
24490075Sobrien				reply(504, "Unimplemented STRU type.");
245169689Skan			}
24618334Speter		    }
247169689Skan		}
248169689Skan	| MODE SP mode_code CRLF check_secure
249169689Skan		{
250169689Skan		    if ($5) {
251169689Skan			switch ($3) {
252169689Skan
253169689Skan			case MODE_S:
254169689Skan				reply(200, "MODE S ok.");
255169689Skan				break;
256169689Skan
257169689Skan			default:
258169689Skan				reply(502, "Unimplemented MODE type.");
259169689Skan			}
260169689Skan		    }
261169689Skan		}
262169689Skan	| ALLO SP NUMBER CRLF check_secure
263169689Skan		{
264169689Skan		    if ($5) {
265169689Skan			reply(202, "ALLO command ignored.");
266169689Skan		    }
267169689Skan		}
26890075Sobrien	| ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
269132718Skan		{
270132718Skan		    if ($9) {
27118334Speter			reply(202, "ALLO command ignored.");
272169689Skan		    }
273169689Skan		}
274169689Skan	| RETR SP pathname CRLF check_login
275169689Skan		{
276169689Skan			char *name = $3;
277169689Skan
278169689Skan			if ($5 && name != NULL)
279169689Skan				retrieve(0, name);
280169689Skan			if (name != NULL)
281169689Skan				free(name);
282169689Skan		}
283169689Skan	| STOR SP pathname CRLF check_login
284169689Skan		{
285169689Skan			char *name = $3;
286169689Skan
287169689Skan			if ($5 && name != NULL)
288169689Skan				do_store(name, "w", 0);
289169689Skan			if (name != NULL)
290169689Skan				free(name);
291169689Skan		}
292169689Skan	| APPE SP pathname CRLF check_login
293169689Skan		{
294169689Skan			char *name = $3;
295169689Skan
296169689Skan			if ($5 && name != NULL)
297169689Skan				do_store(name, "a", 0);
298169689Skan			if (name != NULL)
299169689Skan				free(name);
300169689Skan		}
301169689Skan	| NLST CRLF check_login
302169689Skan		{
303169689Skan			if ($3)
304169689Skan				send_file_list(".");
305169689Skan		}
306169689Skan	| NLST SP STRING CRLF check_login
307169689Skan		{
308169689Skan			char *name = $3;
309169689Skan
310169689Skan			if ($5 && name != NULL)
311169689Skan				send_file_list(name);
312169689Skan			if (name != NULL)
313169689Skan				free(name);
314169689Skan		}
315169689Skan	| LIST CRLF check_login
316169689Skan		{
317169689Skan		    if($3)
318169689Skan			list_file(".");
319169689Skan		}
320169689Skan	| LIST SP pathname CRLF check_login
321169689Skan		{
322169689Skan		    if($5)
323169689Skan			list_file($3);
324169689Skan		    free($3);
325169689Skan		}
326169689Skan	| sTAT SP pathname CRLF check_login
327169689Skan		{
328169689Skan			if ($5 && $3 != NULL)
329169689Skan				statfilecmd($3);
330169689Skan			if ($3 != NULL)
331169689Skan				free($3);
33218334Speter		}
33318334Speter	| sTAT CRLF check_secure
33418334Speter		{
33518334Speter		    if ($3)
33618334Speter			statcmd();
33718334Speter		}
33818334Speter	| DELE SP pathname CRLF check_login_no_guest
33918334Speter		{
34018334Speter			if ($5 && $3 != NULL)
34118334Speter				do_delete($3);
34218334Speter			if ($3 != NULL)
34318334Speter				free($3);
34418334Speter		}
34518334Speter	| RNTO SP pathname CRLF check_login_no_guest
34618334Speter		{
34718334Speter			if($5){
34818334Speter				if (fromname) {
34918334Speter					renamecmd(fromname, $3);
35018334Speter					free(fromname);
35190075Sobrien					fromname = (char *) 0;
35290075Sobrien				} else {
353169689Skan					reply(503, "Bad sequence of commands.");
35418334Speter				}
355117395Skan			}
35618334Speter			if ($3 != NULL)
35790075Sobrien				free($3);
35890075Sobrien		}
359169689Skan	| ABOR CRLF check_secure
36096263Sobrien		{
36190075Sobrien		    if ($3)
36296263Sobrien			reply(225, "ABOR command successful.");
36318334Speter		}
36418334Speter	| CWD CRLF check_login
36518334Speter		{
36618334Speter			if ($3) {
36718334Speter				const char *path = pw->pw_dir;
36818334Speter				if (dochroot || guest)
36918334Speter					path = "/";
370169689Skan				cwd(path);
37118334Speter			}
37218334Speter		}
37390075Sobrien	| CWD SP pathname CRLF check_login
37418334Speter		{
37518334Speter			if ($5 && $3 != NULL)
37618334Speter				cwd($3);
37718334Speter			if ($3 != NULL)
37890075Sobrien				free($3);
379169689Skan		}
38018334Speter	| HELP CRLF check_secure
38118334Speter		{
38218334Speter		    if ($3)
38318334Speter			help(cmdtab, (char *) 0);
38418334Speter		}
38518334Speter	| HELP SP STRING CRLF check_secure
38618334Speter		{
38718334Speter		    if ($5) {
388169689Skan			char *cp = $3;
389260918Spfg
390260918Spfg			if (strncasecmp(cp, "SITE", 4) == 0) {
39118334Speter				cp = $3 + 4;
39218334Speter				if (*cp == ' ')
39350397Sobrien					cp++;
39450397Sobrien				if (*cp)
39550397Sobrien					help(sitetab, cp);
39650397Sobrien				else
39750397Sobrien					help(sitetab, (char *) 0);
39850397Sobrien			} else
39950397Sobrien				help(cmdtab, $3);
400132718Skan		    }
40190075Sobrien		}
40290075Sobrien	| NOOP CRLF check_secure
40390075Sobrien		{
404169689Skan		    if ($3)
405169689Skan			reply(200, "NOOP command successful.");
40650397Sobrien		}
40750397Sobrien	| MKD SP pathname CRLF check_login
40850397Sobrien		{
40950397Sobrien			if ($5 && $3 != NULL)
41050397Sobrien				makedir($3);
411169689Skan			if ($3 != NULL)
412169689Skan				free($3);
41350397Sobrien		}
41496263Sobrien	| RMD SP pathname CRLF check_login_no_guest
41550397Sobrien		{
41650397Sobrien			if ($5 && $3 != NULL)
41796263Sobrien				removedir($3);
418169689Skan			if ($3 != NULL)
419169689Skan				free($3);
420169689Skan		}
421169689Skan	| PWD CRLF check_login
422169689Skan		{
423169689Skan			if ($3)
424169689Skan				pwd();
42550397Sobrien		}
42650397Sobrien	| CDUP CRLF check_login
42750397Sobrien		{
42850397Sobrien			if ($3)
42996263Sobrien				cwd("..");
43050397Sobrien		}
43190075Sobrien	| FEAT CRLF check_secure
432169689Skan		{
433169689Skan		    if ($3) {
434169689Skan			lreply(211, "Supported features:");
435169689Skan			lreply(0, " MDTM");
436169689Skan			lreply(0, " REST STREAM");
437169689Skan			lreply(0, " SIZE");
438169689Skan			reply(211, "End");
439169689Skan		    }
440169689Skan		}
441169689Skan	| OPTS SP STRING CRLF check_secure
44250397Sobrien		{
44350397Sobrien		    if ($5)
44450397Sobrien			reply(501, "Bad options");
44550397Sobrien		    free ($3);
44696263Sobrien		}
447169689Skan
448132718Skan	| SITE SP HELP CRLF check_secure
449169689Skan		{
450169689Skan		    if ($5)
451169689Skan			help(sitetab, (char *) 0);
452169689Skan		}
453169689Skan	| SITE SP HELP SP STRING CRLF check_secure
454169689Skan		{
455169689Skan		    if ($7)
456169689Skan			help(sitetab, $5);
45750397Sobrien		}
45850397Sobrien	| SITE SP UMASK CRLF check_login
45950397Sobrien		{
46050397Sobrien			if ($5) {
46150397Sobrien				int oldmask = umask(0);
46296263Sobrien				umask(oldmask);
463132718Skan				reply(200, "Current UMASK is %03o", oldmask);
464169689Skan			}
46550397Sobrien		}
46650397Sobrien	| SITE SP UMASK SP octal_number CRLF check_login_no_guest
46750397Sobrien		{
46850397Sobrien			if ($7) {
46950397Sobrien				if (($5 == -1) || ($5 > 0777)) {
470169689Skan					reply(501, "Bad UMASK value");
471169689Skan				} else {
47250397Sobrien					int oldmask = umask($5);
473169689Skan					reply(200,
474169689Skan					      "UMASK set to %03o (was %03o)",
475169689Skan					      $5, oldmask);
47650397Sobrien				}
47750397Sobrien			}
47850397Sobrien		}
47950397Sobrien	| SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
48050397Sobrien		{
48150397Sobrien			if ($9 && $7 != NULL) {
48250397Sobrien				if ($5 > 0777)
48350397Sobrien					reply(501,
48450397Sobrien				"CHMOD: Mode value must be between 0 and 0777");
48550397Sobrien				else if (chmod($7, $5) < 0)
48650397Sobrien					perror_reply(550, $7);
48750397Sobrien				else
48850397Sobrien					reply(200, "CHMOD command successful.");
48950397Sobrien			}
49050397Sobrien			if ($7 != NULL)
49150397Sobrien				free($7);
49250397Sobrien		}
49350397Sobrien	| SITE SP IDLE CRLF check_secure
494169689Skan		{
495169689Skan		    if ($5)
496169689Skan			reply(200,
497169689Skan			    "Current IDLE time limit is %d seconds; max %d",
49850397Sobrien				ftpd_timeout, maxtimeout);
49950397Sobrien		}
50050397Sobrien	| SITE SP IDLE SP NUMBER CRLF check_secure
501169689Skan		{
502169689Skan		    if ($7) {
503169689Skan			if ($5 < 30 || $5 > maxtimeout) {
504169689Skan				reply(501,
505169689Skan			"Maximum IDLE time must be between 30 and %d seconds",
506169689Skan				    maxtimeout);
50750397Sobrien			} else {
50850397Sobrien				ftpd_timeout = $5;
50950397Sobrien				alarm((unsigned) ftpd_timeout);
51050397Sobrien				reply(200,
51150397Sobrien				    "Maximum IDLE time set to %d seconds",
512169689Skan				    ftpd_timeout);
51350397Sobrien			}
51450397Sobrien		    }
51550397Sobrien		}
51650397Sobrien
51750397Sobrien	| SITE SP KAUTH SP STRING CRLF check_login
51850397Sobrien		{
51990075Sobrien			reply(500, "Command not implemented.");
52050397Sobrien		}
52190075Sobrien	| SITE SP KLIST CRLF check_login
52290075Sobrien		{
52350397Sobrien		    if($5)
524132718Skan			klist();
52590075Sobrien		}
52690075Sobrien	| SITE SP KDESTROY CRLF check_login
527169689Skan		{
528169689Skan		    reply(500, "Command not implemented.");
529169689Skan		}
53090075Sobrien	| SITE SP KRBTKFILE SP STRING CRLF check_login
53190075Sobrien		{
53290075Sobrien		    reply(500, "Command not implemented.");
53390075Sobrien		}
534132718Skan	| SITE SP AFSLOG CRLF check_login
535169689Skan		{
536169689Skan#if defined(KRB5)
537169689Skan		    if(guest)
538260918Spfg			reply(500, "Can't be done as guest.");
539260918Spfg		    else if($5)
540260918Spfg			afslog(NULL, 0);
541260918Spfg#else
542260918Spfg		    reply(500, "Command not implemented.");
543260918Spfg#endif
544260918Spfg		}
545169689Skan	| SITE SP AFSLOG SP STRING CRLF check_login
546169689Skan		{
547169689Skan#if defined(KRB5)
548169689Skan		    if(guest)
549169689Skan			reply(500, "Can't be done as guest.");
550169689Skan		    else if($7)
551169689Skan			afslog($5, 0);
552169689Skan		    if($5)
553169689Skan			free($5);
554169689Skan#else
555169689Skan		    reply(500, "Command not implemented.");
556169689Skan#endif
557169689Skan		}
55890075Sobrien	| SITE SP LOCATE SP STRING CRLF check_login
559169689Skan		{
560169689Skan		    if($7 && $5 != NULL)
561169689Skan			find($5);
562169689Skan		    if($5 != NULL)
563169689Skan			free($5);
564169689Skan		}
565169689Skan	| SITE SP URL CRLF check_secure
56690075Sobrien		{
56718334Speter		    if ($5)
56818334Speter			reply(200, "http://www.pdc.kth.se/heimdal/");
56918334Speter		}
57018334Speter	| STOU SP pathname CRLF check_login
57118334Speter		{
57218334Speter			if ($5 && $3 != NULL)
573132718Skan				do_store($3, "w", 1);
57418334Speter			if ($3 != NULL)
57550397Sobrien				free($3);
576169689Skan		}
57790075Sobrien	| SYST CRLF check_secure
57850397Sobrien		{
579132718Skan		    if ($3) {
580132718Skan#if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
581132718Skan			reply(215, "UNIX Type: L%d", NBBY);
582169689Skan#else
583169689Skan			reply(215, "UNKNOWN Type: L%d", NBBY);
58490075Sobrien#endif
585132718Skan		    }
586169689Skan		}
587169689Skan
588169689Skan		/*
589169689Skan		 * SIZE is not in RFC959, but Postel has blessed it and
590169689Skan		 * it will be in the updated RFC.
591169689Skan		 *
592169689Skan		 * Return size of file in a format suitable for
593169689Skan		 * using with RESTART (we just count bytes).
594169689Skan		 */
595169689Skan	| SIZE SP pathname CRLF check_login
596169689Skan		{
597169689Skan			if ($5 && $3 != NULL)
598169689Skan				sizecmd($3);
599169689Skan			if ($3 != NULL)
600169689Skan				free($3);
601169689Skan		}
602169689Skan
603169689Skan		/*
604169689Skan		 * MDTM is not in RFC959, but Postel has blessed it and
605169689Skan		 * it will be in the updated RFC.
606169689Skan		 *
607169689Skan		 * Return modification time of file as an ISO 3307
608169689Skan		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
609169689Skan		 * where xxx is the fractional second (of any precision,
610169689Skan		 * not necessarily 3 digits)
611169689Skan		 */
612169689Skan	| MDTM SP pathname CRLF check_login
613169689Skan		{
614169689Skan			if ($5 && $3 != NULL) {
615169689Skan				struct stat stbuf;
616169689Skan				if (stat($3, &stbuf) < 0)
617169689Skan					reply(550, "%s: %s",
618169689Skan					    $3, strerror(errno));
619169689Skan				else if (!S_ISREG(stbuf.st_mode)) {
620169689Skan					reply(550,
621169689Skan					      "%s: not a plain file.", $3);
622169689Skan				} else {
623169689Skan					struct tm *t;
624169689Skan					time_t mtime = stbuf.st_mtime;
625169689Skan
626169689Skan					t = gmtime(&mtime);
627169689Skan					reply(213,
628169689Skan					      "%04d%02d%02d%02d%02d%02d",
629169689Skan					      t->tm_year + 1900,
630169689Skan					      t->tm_mon + 1,
631169689Skan					      t->tm_mday,
632169689Skan					      t->tm_hour,
633169689Skan					      t->tm_min,
634169689Skan					      t->tm_sec);
635169689Skan				}
636169689Skan			}
637169689Skan			if ($3 != NULL)
638169689Skan				free($3);
639169689Skan		}
640169689Skan	| QUIT CRLF check_secure
641169689Skan		{
642169689Skan		    if ($3) {
643169689Skan			reply(221, "Goodbye.");
644169689Skan			dologout(0);
645169689Skan		    }
646169689Skan		}
647169689Skan	| error CRLF
648169689Skan		{
649169689Skan			yyerrok;
650169689Skan		}
651169689Skan	;
652169689Skanrcmd
653169689Skan	: RNFR SP pathname CRLF check_login_no_guest
654169689Skan		{
655169689Skan			restart_point = (off_t) 0;
656169689Skan			if ($5 && $3) {
657169689Skan				fromname = renamefrom($3);
658169689Skan				if (fromname == (char *) 0 && $3) {
659169689Skan					free($3);
660169689Skan				}
661169689Skan			}
662169689Skan		}
663169689Skan	| REST SP byte_size CRLF check_secure
664169689Skan		{
665169689Skan		    if ($5) {
666169689Skan			fromname = (char *) 0;
667169689Skan			restart_point = $3;	/* XXX $3 is only "int" */
668169689Skan			reply(350, "Restarting at %ld. %s",
669169689Skan			      (long)restart_point,
670169689Skan			      "Send STORE or RETRIEVE to initiate transfer.");
671169689Skan		    }
672169689Skan		}
673169689Skan	| AUTH SP STRING CRLF
674169689Skan		{
675169689Skan			auth($3);
676132718Skan			free($3);
677132718Skan		}
678132718Skan	| ADAT SP STRING CRLF
679132718Skan		{
68090075Sobrien			adat($3);
68190075Sobrien			free($3);
68290075Sobrien		}
683169689Skan	| PBSZ SP NUMBER CRLF check_secure
684169689Skan		{
685169689Skan		    if ($5)
686169689Skan			pbsz($3);
687169689Skan		}
688169689Skan	| PROT SP STRING CRLF check_secure
689169689Skan		{
690169689Skan		    if ($5)
691169689Skan			prot($3);
692169689Skan		}
693169689Skan	| CCC CRLF check_secure
694169689Skan		{
695169689Skan		    if ($3)
696169689Skan			ccc();
697169689Skan		}
698169689Skan	| MIC SP STRING CRLF
699169689Skan		{
700169689Skan			mec($3, prot_safe);
701169689Skan			free($3);
702169689Skan		}
703169689Skan	| CONF SP STRING CRLF
704169689Skan		{
705169689Skan			mec($3, prot_confidential);
706169689Skan			free($3);
707169689Skan		}
708169689Skan	| ENC SP STRING CRLF
709169689Skan		{
710169689Skan			mec($3, prot_private);
71190075Sobrien			free($3);
712132718Skan		}
713132718Skan	;
714132718Skan
715132718Skanusername
716169689Skan	: STRING
71790075Sobrien	;
71890075Sobrien
71990075Sobrienpassword
720169689Skan	: /* empty */
721169689Skan		{
722169689Skan			$$ = (char *)calloc(1, sizeof(char));
723169689Skan		}
724169689Skan	| STRING
725169689Skan	;
726169689Skan
727169689Skanbyte_size
728132718Skan	: NUMBER
729132718Skan	;
730132718Skan
731117395Skanhost_port
732169689Skan	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
733169689Skan		NUMBER COMMA NUMBER
734117395Skan		{
735117395Skan			struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
736117395Skan
737117395Skan			sin4->sin_family = AF_INET;
738117395Skan			sin4->sin_port = htons($9 * 256 + $11);
739169689Skan			sin4->sin_addr.s_addr =
740169689Skan			    htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
741169689Skan		}
742169689Skan	;
743169689Skan
744169689Skanform_code
745169689Skan	: N
746169689Skan		{
747169689Skan			$$ = FORM_N;
748169689Skan		}
749169689Skan	| T
750169689Skan		{
751169689Skan			$$ = FORM_T;
752169689Skan		}
753169689Skan	| C
754169689Skan		{
755169689Skan			$$ = FORM_C;
756169689Skan		}
757169689Skan	;
758169689Skan
759169689Skantype_code
760169689Skan	: A
761132718Skan		{
762132718Skan			cmd_type = TYPE_A;
763132718Skan			cmd_form = FORM_N;
764132718Skan		}
765132718Skan	| A SP form_code
766132718Skan		{
767132718Skan			cmd_type = TYPE_A;
768132718Skan			cmd_form = $3;
769132718Skan		}
770132718Skan	| E
771132718Skan		{
772132718Skan			cmd_type = TYPE_E;
773132718Skan			cmd_form = FORM_N;
774169689Skan		}
775132718Skan	| E SP form_code
776132718Skan		{
777132718Skan			cmd_type = TYPE_E;
778132718Skan			cmd_form = $3;
779132718Skan		}
780132718Skan	| I
781132718Skan		{
782132718Skan			cmd_type = TYPE_I;
783132718Skan		}
784132718Skan	| L
785169689Skan		{
786132718Skan			cmd_type = TYPE_L;
787132718Skan			cmd_bytesz = NBBY;
788132718Skan		}
789132718Skan	| L SP byte_size
790132718Skan		{
791169689Skan			cmd_type = TYPE_L;
792169689Skan			cmd_bytesz = $3;
793169689Skan		}
794169689Skan		/* this is for a bug in the BBN ftp */
795169689Skan	| L byte_size
796169689Skan		{
797169689Skan			cmd_type = TYPE_L;
798169689Skan			cmd_bytesz = $2;
799169689Skan		}
800169689Skan	;
801132718Skan
80290075Sobrienstruct_code
803169689Skan	: F
804169689Skan		{
805169689Skan			$$ = STRU_F;
806169689Skan		}
807169689Skan	| R
808169689Skan		{
809169689Skan			$$ = STRU_R;
810132718Skan		}
811132718Skan	| P
812117395Skan		{
813169689Skan			$$ = STRU_P;
814169689Skan		}
815169689Skan	;
816132718Skan
817132718Skanmode_code
818132718Skan	: S
819169689Skan		{
820169689Skan			$$ = MODE_S;
821169689Skan		}
822169689Skan	| B
823169689Skan		{
824169689Skan			$$ = MODE_B;
825169689Skan		}
826169689Skan	| C
827169689Skan		{
828169689Skan			$$ = MODE_C;
829132718Skan		}
83090075Sobrien	;
83190075Sobrien
832169689Skanpathname
833169689Skan	: pathstring
834169689Skan		{
835169689Skan			/*
836169689Skan			 * Problem: this production is used for all pathname
837169689Skan			 * processing, but only gives a 550 error reply.
838169689Skan			 * This is a valid reply in some cases but not in others.
839169689Skan			 */
840169689Skan			if (logged_in && $1 && *$1 == '~') {
841169689Skan				glob_t gl;
842169689Skan				int flags =
843169689Skan				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
844169689Skan
845169689Skan				memset(&gl, 0, sizeof(gl));
846169689Skan				if (glob($1, flags, NULL, &gl) ||
847223262Sbenl				    gl.gl_pathc == 0) {
848223262Sbenl					reply(550, "not found");
849223262Sbenl					$$ = NULL;
850132718Skan				} else {
851169689Skan					$$ = strdup(gl.gl_pathv[0]);
852223262Sbenl				}
853169689Skan				globfree(&gl);
854169689Skan				free($1);
85590075Sobrien			} else
85650397Sobrien				$$ = $1;
85750397Sobrien		}
858169689Skan	;
859169689Skan
86050397Sobrienpathstring
86150397Sobrien	: STRING
862169689Skan	;
863169689Skan
864169689Skanoctal_number
865169689Skan	: NUMBER
866169689Skan		{
867169689Skan			int ret, dec, multby, digit;
868169689Skan
869169689Skan			/*
870169689Skan			 * Convert a number that was read as decimal number
871169689Skan			 * to what it would be if it had been read as octal.
872261188Spfg			 */
873261188Spfg			dec = $1;
87450397Sobrien			multby = 1;
875169689Skan			ret = 0;
876169689Skan			while (dec) {
877169689Skan				digit = dec%10;
878169689Skan				if (digit > 7) {
879169689Skan					ret = -1;
880169689Skan					break;
881169689Skan				}
882169689Skan				ret += digit * multby;
88318334Speter				multby *= 8;
88418334Speter				dec /= 10;
88590075Sobrien			}
88690075Sobrien			$$ = ret;
88718334Speter		}
88818334Speter	;
88990075Sobrien
89090075Sobrien
891169689Skancheck_login_no_guest : check_login
89290075Sobrien		{
893169689Skan			$$ = $1 && !guest;
894169689Skan			if($1 && !$$)
895169689Skan				reply(550, "Permission denied");
896169689Skan		}
89718334Speter	;
89818334Speter
89918334Spetercheck_login : check_secure
90018334Speter		{
90118334Speter		    if($1) {
90218334Speter			if(($$ = logged_in) == 0)
90318334Speter			    reply(530, "Please login with USER and PASS.");
90418334Speter		    } else
90518334Speter			$$ = 0;
90618334Speter		}
90718334Speter	;
90818334Speter
90918334Spetercheck_secure : /* empty */
91018334Speter		{
91190075Sobrien		    $$ = 1;
91218334Speter		    if(sec_complete && !ccc_passed && !secure_command()) {
91318334Speter			$$ = 0;
91418334Speter			reply(533, "Command protection level denied "
91590075Sobrien			      "for paranoid reasons.");
91618334Speter		    }
91718334Speter		}
91890075Sobrien	;
91918334Speter
92090075Sobrien%%
92190075Sobrien
92290075Sobrien#define	CMD	0	/* beginning of command */
92390075Sobrien#define	ARGS	1	/* expect miscellaneous arguments */
92490075Sobrien#define	STR1	2	/* expect SP followed by STRING */
92590075Sobrien#define	STR2	3	/* expect STRING */
92690075Sobrien#define	OSTR	4	/* optional SP then STRING */
92790075Sobrien#define	ZSTR1	5	/* SP then optional STRING */
92890075Sobrien#define	ZSTR2	6	/* optional STRING after SP */
929169689Skan#define	SITECMD	7	/* SITE command */
930169689Skan#define	NSTR	8	/* Number followed by a string */
93190075Sobrien
93290075Sobrienstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
93318334Speter	{ "USER", USER, STR1, 1,	"<sp> username" },
93418334Speter	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
93518334Speter	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
93618334Speter	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
93718334Speter	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
93818334Speter	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
93990075Sobrien	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
94018334Speter	{ "EPRT", EPRT, STR1, 1,	"<sp> string" },
94118334Speter	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
94290075Sobrien	{ "EPSV", EPSV, OSTR, 1,	"[<sp> foo]" },
94318334Speter	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
944169689Skan	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
945169689Skan	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
946169689Skan	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
947169689Skan	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
948169689Skan	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
949169689Skan	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
950169689Skan	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
95118334Speter	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
952169689Skan	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
953169689Skan	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
95418334Speter	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
95518334Speter	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
956169689Skan	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
957169689Skan	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
958169689Skan	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
95918334Speter	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
960132718Skan	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
961132718Skan	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
962132718Skan	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
963132718Skan	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
964132718Skan	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
965132718Skan	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
966132718Skan	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
967132718Skan	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
968132718Skan	{ "STAT", sTAT, OSTR, 1,	"[ <sp> path-name ]" },
969132718Skan	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
970169689Skan	{ "NOOP", NOOP, ARGS, 1,	"" },
971169689Skan	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
972169689Skan	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
973169689Skan	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
974169689Skan	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
975169689Skan	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
97618334Speter	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
977169689Skan	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
978169689Skan	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
97918334Speter	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
980169689Skan	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
981169689Skan	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
982169689Skan
983169689Skan	/* extensions from RFC2228 */
984169689Skan	{ "AUTH", AUTH,	STR1, 1,	"<sp> auth-type" },
98518334Speter	{ "ADAT", ADAT,	STR1, 1,	"<sp> auth-data" },
986169689Skan	{ "PBSZ", PBSZ,	ARGS, 1,	"<sp> buffer-size" },
987169689Skan	{ "PROT", PROT,	STR1, 1,	"<sp> prot-level" },
988169689Skan	{ "CCC",  CCC,	ARGS, 1,	"" },
989169689Skan	{ "MIC",  MIC,	STR1, 1,	"<sp> integrity command" },
99018334Speter	{ "CONF", CONF,	STR1, 1,	"<sp> confidentiality command" },
991169689Skan	{ "ENC",  ENC,	STR1, 1,	"<sp> privacy command" },
992169689Skan
993169689Skan	/* RFC2389 */
99418334Speter	{ "FEAT", FEAT, ARGS, 1,	"" },
99518334Speter	{ "OPTS", OPTS, ARGS, 1,	"<sp> command [<sp> options]" },
996169689Skan
99718334Speter	{ NULL,   0,    0,    0,	0 }
998132718Skan};
999169689Skan
1000169689Skanstruct tab sitetab[] = {
100118334Speter	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1002261188Spfg	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
100318334Speter	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1004261188Spfg	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1005261188Spfg
1006261188Spfg	{ "KAUTH", KAUTH, STR1, 1,	"<sp> principal [ <sp> ticket ]" },
1007261188Spfg	{ "KLIST", KLIST, ARGS, 1,	"(show ticket file)" },
100850397Sobrien	{ "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
100990075Sobrien	{ "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
101090075Sobrien	{ "AFSLOG", AFSLOG, OSTR, 1,	"[<sp> cell]" },
101190075Sobrien
101290075Sobrien	{ "LOCATE", LOCATE, STR1, 1,	"<sp> globexpr" },
101390075Sobrien	{ "FIND", LOCATE, STR1, 1,	"<sp> globexpr" },
101490075Sobrien
101590075Sobrien	{ "URL",  URL,  ARGS, 1,	"?" },
101690075Sobrien
101790075Sobrien	{ NULL,   0,    0,    0,	0 }
101890075Sobrien};
101990075Sobrien
102090075Sobrienstatic struct tab *
102190075Sobrienlookup(struct tab *p, char *cmd)
102290075Sobrien{
102318334Speter
102418334Speter	for (; p->name != NULL; p++)
102518334Speter		if (strcmp(cmd, p->name) == 0)
102618334Speter			return (p);
102718334Speter	return (0);
102818334Speter}
102918334Speter
103090075Sobrien/*
103190075Sobrien * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
103290075Sobrien */
103318334Speterchar *
1034117395Skanftpd_getline(char *s, int n)
103518334Speter{
103618334Speter	int c;
103718334Speter	char *cs;
103818334Speter
103918334Speter	cs = s;
104018334Speter
104118334Speter	/* might still be data within the security MIC/CONF/ENC */
104218334Speter	if(ftp_command){
1043169689Skan	    strlcpy(s, ftp_command, n);
1044169689Skan	    if (debug)
1045169689Skan		syslog(LOG_DEBUG, "command: %s", s);
1046169689Skan	    return s;
1047169689Skan	}
1048169689Skan	while ((c = getc(stdin)) != EOF) {
1049169689Skan		c &= 0377;
1050169689Skan		if (c == IAC) {
1051169689Skan		    if ((c = getc(stdin)) != EOF) {
1052169689Skan			c &= 0377;
105318334Speter			switch (c) {
105418334Speter			case WILL:
1055169689Skan			case WONT:
1056169689Skan				c = getc(stdin);
1057169689Skan				printf("%c%c%c", IAC, DONT, 0377&c);
1058169689Skan				fflush(stdout);
105918334Speter				continue;
106018334Speter			case DO:
1061169689Skan			case DONT:
1062169689Skan				c = getc(stdin);
1063169689Skan				printf("%c%c%c", IAC, WONT, 0377&c);
106496263Sobrien				fflush(stdout);
106596263Sobrien				continue;
1066169689Skan			case IAC:
1067169689Skan				break;
1068169689Skan			default:
1069169689Skan				continue;	/* ignore command */
107018334Speter			}
1071169689Skan		    }
1072169689Skan		}
1073169689Skan		*cs++ = c;
1074169689Skan		if (--n <= 0 || c == '\n')
1075169689Skan			break;
1076169689Skan	}
107718334Speter	if (c == EOF && cs == s)
107896263Sobrien		return (NULL);
107996263Sobrien	*cs++ = '\0';
108096263Sobrien	if (debug) {
108196263Sobrien		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1082169689Skan			/* Don't syslog passwords */
108318334Speter			syslog(LOG_DEBUG, "command: %.5s ???", s);
108418334Speter		} else {
108518334Speter			char *cp;
108690075Sobrien			int len;
108790075Sobrien
108818334Speter			/* Don't syslog trailing CR-LF */
1089169689Skan			len = strlen(s);
1090169689Skan			cp = s + len - 1;
1091169689Skan			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1092169689Skan				--cp;
1093169689Skan				--len;
109496263Sobrien			}
109596263Sobrien			syslog(LOG_DEBUG, "command: %.*s", len, s);
1096169689Skan		}
1097169689Skan	}
109818334Speter#ifdef XXX
1099169689Skan	fprintf(stderr, "%s\n", s);
1100169689Skan#endif
1101259268Spfg	return (s);
1102259268Spfg}
1103259268Spfg
1104259268Spfgstatic RETSIGTYPE
1105259268Spfgtoolong(int signo)
1106169689Skan{
110718334Speter
110890075Sobrien	reply(421,
110918334Speter	    "Timeout (%d seconds): closing control connection.",
111018334Speter	      ftpd_timeout);
111118334Speter	if (logging)
111218334Speter		syslog(LOG_INFO, "User %s timed out after %d seconds",
1113169689Skan		    (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1114169689Skan	dologout(1);
1115169689Skan	SIGRETURN(0);
111618334Speter}
1117169689Skan
1118169689Skanstatic int
1119169689Skanyylex(void)
1120169689Skan{
1121169689Skan	static int cpos, state;
1122169689Skan	char *cp, *cp2;
1123169689Skan	struct tab *p;
1124169689Skan	int n;
1125169689Skan	char c;
1126169689Skan
1127169689Skan	for (;;) {
1128169689Skan		switch (state) {
1129169689Skan
1130169689Skan		case CMD:
1131169689Skan			hasyyerrored = 0;
1132169689Skan
1133169689Skan			signal(SIGALRM, toolong);
1134169689Skan			alarm((unsigned) ftpd_timeout);
113518334Speter			if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
113618334Speter				reply(221, "You could at least say goodbye.");
113718334Speter				dologout(0);
113818334Speter			}
1139169689Skan			alarm(0);
1140169689Skan#ifdef HAVE_SETPROCTITLE
1141169689Skan			if (strncasecmp(cbuf, "PASS", 4) != 0)
114218334Speter				setproctitle("%s: %s", proctitle, cbuf);
114318334Speter#endif /* HAVE_SETPROCTITLE */
114418334Speter			if ((cp = strchr(cbuf, '\r'))) {
114518334Speter				*cp++ = '\n';
114618334Speter				*cp = '\0';
114718334Speter			}
114818334Speter			if ((cp = strpbrk(cbuf, " \n")))
114918334Speter				cpos = cp - cbuf;
1150169689Skan			if (cpos == 0)
1151169689Skan				cpos = 4;
1152169689Skan			c = cbuf[cpos];
1153169689Skan			cbuf[cpos] = '\0';
1154169689Skan			strupr(cbuf);
1155169689Skan			p = lookup(cmdtab, cbuf);
1156169689Skan			cbuf[cpos] = c;
1157169689Skan			if (p != 0) {
1158169689Skan				if (p->implemented == 0) {
1159169689Skan					nack(p->name);
1160169689Skan					hasyyerrored = 1;
1161169689Skan					break;
116218334Speter				}
1163169689Skan				state = p->state;
1164169689Skan				yylval.s = p->name;
116518334Speter				return (p->token);
1166132718Skan			}
1167169689Skan			break;
1168169689Skan
116990075Sobrien		case SITECMD:
1170169689Skan			if (cbuf[cpos] == ' ') {
1171169689Skan				cpos++;
1172169689Skan				return (SP);
117318334Speter			}
1174169689Skan			cp = &cbuf[cpos];
1175169689Skan			if ((cp2 = strpbrk(cp, " \n")))
117618334Speter				cpos = cp2 - cbuf;
1177169689Skan			c = cbuf[cpos];
1178169689Skan			cbuf[cpos] = '\0';
117990075Sobrien			strupr(cp);
1180169689Skan			p = lookup(sitetab, cp);
1181169689Skan			cbuf[cpos] = c;
1182169689Skan			if (p != 0) {
1183169689Skan				if (p->implemented == 0) {
1184169689Skan					state = CMD;
1185169689Skan					nack(p->name);
1186169689Skan					hasyyerrored = 1;
118718334Speter					break;
118818334Speter				}
118918334Speter				state = p->state;
119018334Speter				yylval.s = p->name;
119118334Speter				return (p->token);
119218334Speter			}
1193169689Skan			state = CMD;
1194169689Skan			break;
1195169689Skan
119618334Speter		case OSTR:
119718334Speter			if (cbuf[cpos] == '\n') {
119818334Speter				state = CMD;
119918334Speter				return (CRLF);
120018334Speter			}
1201169689Skan			/* FALLTHROUGH */
1202169689Skan
120318334Speter		case STR1:
120418334Speter		case ZSTR1:
120590075Sobrien		dostr1:
120690075Sobrien			if (cbuf[cpos] == ' ') {
120790075Sobrien				cpos++;
120818334Speter				if(state == OSTR)
1209169689Skan				    state = STR2;
1210169689Skan				else
1211169689Skan				    state++;
1212132718Skan				return (SP);
1213169689Skan			}
1214169689Skan			break;
1215169689Skan
1216169689Skan		case ZSTR2:
1217132718Skan			if (cbuf[cpos] == '\n') {
1218132718Skan				state = CMD;
1219169689Skan				return (CRLF);
1220132718Skan			}
122190075Sobrien			/* FALLTHROUGH */
122290075Sobrien
122390075Sobrien		case STR2:
122490075Sobrien			cp = &cbuf[cpos];
122590075Sobrien			n = strlen(cp);
1226169689Skan			cpos += n - 1;
1227169689Skan			/*
1228169689Skan			 * Make sure the string is nonempty and \n terminated.
1229169689Skan			 */
123090075Sobrien			if (n > 1 && cbuf[cpos] == '\n') {
123190075Sobrien				cbuf[cpos] = '\0';
123218334Speter				yylval.s = copy(cp);
123318334Speter				cbuf[cpos] = '\n';
123418334Speter				state = ARGS;
123518334Speter				return (STRING);
123618334Speter			}
123718334Speter			break;
1238169689Skan
123990075Sobrien		case NSTR:
1240169689Skan			if (cbuf[cpos] == ' ') {
1241169689Skan				cpos++;
124290075Sobrien				return (SP);
1243260918Spfg			}
1244260918Spfg			if (isdigit((unsigned char)cbuf[cpos])) {
1245260918Spfg				cp = &cbuf[cpos];
1246260918Spfg				while (isdigit((unsigned char)cbuf[++cpos]))
1247260918Spfg					;
1248260918Spfg				c = cbuf[cpos];
1249169689Skan				cbuf[cpos] = '\0';
1250169689Skan				yylval.i = atoi(cp);
1251169689Skan				cbuf[cpos] = c;
1252169689Skan				state = STR1;
1253169689Skan				return (NUMBER);
1254169689Skan			}
1255169689Skan			state = STR1;
1256169689Skan			goto dostr1;
1257169689Skan
1258169689Skan		case ARGS:
1259169689Skan			if (isdigit((unsigned char)cbuf[cpos])) {
126018334Speter				cp = &cbuf[cpos];
126118334Speter				while (isdigit((unsigned char)cbuf[++cpos]))
126218334Speter					;
126318334Speter				c = cbuf[cpos];
126418334Speter				cbuf[cpos] = '\0';
126518334Speter				yylval.i = atoi(cp);
126618334Speter				cbuf[cpos] = c;
126718334Speter				return (NUMBER);
126818334Speter			}
126918334Speter			switch (cbuf[cpos++]) {
127018334Speter
127118334Speter			case '\n':
127218334Speter				state = CMD;
127318334Speter				return (CRLF);
127418334Speter
127590075Sobrien			case ' ':
127690075Sobrien				return (SP);
127790075Sobrien
127818334Speter			case ',':
127990075Sobrien				return (COMMA);
128090075Sobrien
128190075Sobrien			case 'A':
128290075Sobrien			case 'a':
128318334Speter				return (A);
128490075Sobrien
128590075Sobrien			case 'B':
128690075Sobrien			case 'b':
128790075Sobrien				return (B);
1288117395Skan
128990075Sobrien			case 'C':
1290132718Skan			case 'c':
1291117395Skan				return (C);
129218334Speter
129390075Sobrien			case 'E':
1294169689Skan			case 'e':
129518334Speter				return (E);
129618334Speter
1297117395Skan			case 'F':
1298117395Skan			case 'f':
1299117395Skan				return (F);
1300117395Skan
130118334Speter			case 'I':
1302117395Skan			case 'i':
1303117395Skan				return (I);
130490075Sobrien
1305117395Skan			case 'L':
130618334Speter			case 'l':
130790075Sobrien				return (L);
1308117395Skan
130918334Speter			case 'N':
131018334Speter			case 'n':
131118334Speter				return (N);
131250397Sobrien
1313169689Skan			case 'P':
1314169689Skan			case 'p':
131518334Speter				return (P);
1316117395Skan
131718334Speter			case 'R':
131890075Sobrien			case 'r':
131918334Speter				return (R);
1320169689Skan
132118334Speter			case 'S':
132218334Speter			case 's':
132318334Speter				return (S);
132450397Sobrien
132550397Sobrien			case 'T':
132618334Speter			case 't':
1327117395Skan				return (T);
132818334Speter
132990075Sobrien			}
133090075Sobrien			break;
133190075Sobrien
133218334Speter		default:
133396263Sobrien			fatal("Unknown state in scanner.");
133496263Sobrien		}
133596263Sobrien		yyerror(NULL);
133696263Sobrien		state = CMD;
1337117395Skan		return (0);
133896263Sobrien	}
133996263Sobrien}
134096263Sobrien
134196263Sobrien/* ARGSUSED */
134218334Spetervoid
1343169689Skanyyerror(char *s)
134490075Sobrien{
134518334Speter	char *cp;
134618334Speter
134790075Sobrien	if (hasyyerrored)
134890075Sobrien	    return;
134990075Sobrien
135090075Sobrien	if ((cp = strchr(cbuf,'\n')))
1351117395Skan		*cp = '\0';
1352117395Skan	reply(500, "'%s': command not understood.", cbuf);
135318334Speter	hasyyerrored = 1;
135490075Sobrien}
135590075Sobrien
135690075Sobrienstatic char *
135790075Sobriencopy(char *s)
135890075Sobrien{
135990075Sobrien	char *p;
136090075Sobrien
1361117395Skan	p = strdup(s);
136218334Speter	if (p == NULL)
136390075Sobrien		fatal("Ran out of memory.");
136490075Sobrien	return p;
136518334Speter}
136618334Speter
136718334Speterstatic void
136850397Sobrienhelp(struct tab *ctab, char *s)
136950397Sobrien{
137018334Speter	struct tab *c;
1371117395Skan	int width, NCMDS;
137218334Speter	char *t;
137390075Sobrien	char buf[1024];
137490075Sobrien
137590075Sobrien	if (ctab == sitetab)
137618334Speter		t = "SITE ";
137718334Speter	else
137818334Speter		t = "";
137950397Sobrien	width = 0, NCMDS = 0;
138090075Sobrien	for (c = ctab; c->name != NULL; c++) {
138190075Sobrien		int len = strlen(c->name);
138218334Speter
1383117395Skan		if (len > width)
1384117395Skan			width = len;
1385117395Skan		NCMDS++;
138618334Speter	}
138790075Sobrien	width = (width + 8) &~ 7;
138818334Speter	if (s == 0) {
1389117395Skan		int i, j, w;
139018334Speter		int columns, lines;
139118334Speter
1392169689Skan		lreply(214, "The following %scommands are recognized %s.",
1393169689Skan		    t, "(* =>'s unimplemented)");
139418334Speter		columns = 76 / width;
1395169689Skan		if (columns == 0)
1396169689Skan			columns = 1;
1397169689Skan		lines = (NCMDS + columns - 1) / columns;
1398169689Skan		for (i = 0; i < lines; i++) {
1399169689Skan		    strlcpy (buf, "   ", sizeof(buf));
1400169689Skan		    for (j = 0; j < columns; j++) {
1401169689Skan			c = ctab + j * lines + i;
1402169689Skan			snprintf (buf + strlen(buf),
1403169689Skan				  sizeof(buf) - strlen(buf),
1404132718Skan				  "%s%c",
1405169689Skan				  c->name,
1406169689Skan				  c->implemented ? ' ' : '*');
1407169689Skan			if (c + lines >= &ctab[NCMDS])
1408169689Skan			    break;
1409169689Skan			w = strlen(c->name) + 1;
1410169689Skan			while (w < width) {
1411169689Skan			    strlcat (buf,
1412169689Skan					     " ",
1413169689Skan					     sizeof(buf));
1414169689Skan			    w++;
141518334Speter			}
1416169689Skan		    }
1417169689Skan		    lreply(214, "%s", buf);
1418169689Skan		}
1419169689Skan		reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1420169689Skan		return;
1421169689Skan	}
1422169689Skan	strupr(s);
142318334Speter	c = lookup(ctab, s);
1424169689Skan	if (c == (struct tab *)0) {
1425169689Skan		reply(502, "Unknown command %s.", s);
1426169689Skan		return;
1427169689Skan	}
1428169689Skan	if (c->implemented)
1429169689Skan		reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1430169689Skan	else
1431169689Skan		reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1432169689Skan		    c->name, c->help);
143318334Speter}
1434169689Skan
1435169689Skanstatic void
143618334Spetersizecmd(char *filename)
1437169689Skan{
1438169689Skan	switch (type) {
1439169689Skan	case TYPE_L:
1440169689Skan	case TYPE_I: {
1441169689Skan		struct stat stbuf;
1442169689Skan		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1443169689Skan			reply(550, "%s: not a plain file.", filename);
1444169689Skan		else
1445169689Skan			reply(213, "%lu", (unsigned long)stbuf.st_size);
1446169689Skan		break;
1447169689Skan	}
1448169689Skan	case TYPE_A: {
1449169689Skan		FILE *fin;
145018334Speter		int c;
1451132718Skan		size_t count;
145250397Sobrien		struct stat stbuf;
145318334Speter		fin = fopen(filename, "r");
145452284Sobrien		if (fin == NULL) {
1455132718Skan			perror_reply(550, filename);
145652284Sobrien			return;
1457169689Skan		}
1458169689Skan		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1459169689Skan			reply(550, "%s: not a plain file.", filename);
1460169689Skan			fclose(fin);
1461169689Skan			return;
1462169689Skan		}
1463169689Skan
1464169689Skan		count = 0;
1465169689Skan		while((c=getc(fin)) != EOF) {
1466169689Skan			if (c == '\n')	/* will get expanded to \r\n */
1467169689Skan				count++;
1468169689Skan			count++;
1469169689Skan		}
1470169689Skan		fclose(fin);
1471169689Skan
1472169689Skan		reply(213, "%lu", (unsigned long)count);
1473169689Skan		break;
1474169689Skan	}
1475169689Skan	default:
1476169689Skan		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1477169689Skan	}
1478169689Skan}
1479169689Skan