xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/ftpcmd.y (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*	$NetBSD: ftpcmd.y,v 1.6 1995/06/03 22:46:45 mycroft Exp $	*/
2b528cefcSMark Murray 
3b528cefcSMark Murray /*
4b528cefcSMark Murray  * Copyright (c) 1985, 1988, 1993, 1994
5b528cefcSMark Murray  *	The Regents of the University of California.  All rights reserved.
6b528cefcSMark Murray  *
7b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
8b528cefcSMark Murray  * modification, are permitted provided that the following conditions
9b528cefcSMark Murray  * are met:
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
13b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
14b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
15b528cefcSMark Murray  * 3. All advertising materials mentioning features or use of this software
16b528cefcSMark Murray  *    must display the following acknowledgement:
17b528cefcSMark Murray  *	This product includes software developed by the University of
18b528cefcSMark Murray  *	California, Berkeley and its contributors.
19b528cefcSMark Murray  * 4. Neither the name of the University nor the names of its contributors
20b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
21b528cefcSMark Murray  *    without specific prior written permission.
22b528cefcSMark Murray  *
23b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33b528cefcSMark Murray  * SUCH DAMAGE.
34b528cefcSMark Murray  *
35b528cefcSMark Murray  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
36b528cefcSMark Murray  */
37b528cefcSMark Murray 
38b528cefcSMark Murray /*
39b528cefcSMark Murray  * Grammar for FTP commands.
40b528cefcSMark Murray  * See RFC 959.
41b528cefcSMark Murray  */
42b528cefcSMark Murray 
43b528cefcSMark Murray %{
44b528cefcSMark Murray 
45b528cefcSMark Murray #include "ftpd_locl.h"
46*ae771770SStanislav Sedov RCSID("$Id$");
47b528cefcSMark Murray 
48b528cefcSMark Murray off_t	restart_point;
49b528cefcSMark Murray 
508d4ba808SJacques Vidrine static	int hasyyerrored;
518d4ba808SJacques Vidrine 
528d4ba808SJacques Vidrine 
53b528cefcSMark Murray static	int cmd_type;
54b528cefcSMark Murray static	int cmd_form;
55b528cefcSMark Murray static	int cmd_bytesz;
568d4ba808SJacques Vidrine char	cbuf[64*1024];
57b528cefcSMark Murray char	*fromname;
58b528cefcSMark Murray 
59b528cefcSMark Murray struct tab {
60b528cefcSMark Murray 	char	*name;
61b528cefcSMark Murray 	short	token;
62b528cefcSMark Murray 	short	state;
63b528cefcSMark Murray 	short	implemented;	/* 1 if command is implemented */
64b528cefcSMark Murray 	char	*help;
65b528cefcSMark Murray };
66b528cefcSMark Murray 
67b528cefcSMark Murray extern struct tab cmdtab[];
68b528cefcSMark Murray extern struct tab sitetab[];
69b528cefcSMark Murray 
70b528cefcSMark Murray static char		*copy (char *);
71b528cefcSMark Murray static void		 help (struct tab *, char *);
72b528cefcSMark Murray static struct tab *
73b528cefcSMark Murray 			 lookup (struct tab *, char *);
74b528cefcSMark Murray static void		 sizecmd (char *);
75b528cefcSMark Murray static RETSIGTYPE	 toolong (int);
76b528cefcSMark Murray static int		 yylex (void);
77b528cefcSMark Murray 
78b528cefcSMark Murray /* This is for bison */
79b528cefcSMark Murray 
80b528cefcSMark Murray #if !defined(alloca) && !defined(HAVE_ALLOCA)
81b528cefcSMark Murray #define alloca(x) malloc(x)
82b528cefcSMark Murray #endif
83b528cefcSMark Murray 
84b528cefcSMark Murray %}
85b528cefcSMark Murray 
86b528cefcSMark Murray %union {
87b528cefcSMark Murray 	int	i;
88b528cefcSMark Murray 	char   *s;
89b528cefcSMark Murray }
90b528cefcSMark Murray 
91b528cefcSMark Murray %token
92b528cefcSMark Murray 	A	B	C	E	F	I
93b528cefcSMark Murray 	L	N	P	R	S	T
94b528cefcSMark Murray 
95b528cefcSMark Murray 	SP	CRLF	COMMA
96b528cefcSMark Murray 
97b528cefcSMark Murray 	USER	PASS	ACCT	REIN	QUIT	PORT
98b528cefcSMark Murray 	PASV	TYPE	STRU	MODE	RETR	STOR
99b528cefcSMark Murray 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
100b528cefcSMark Murray 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
101b528cefcSMark Murray 	ABOR	DELE	CWD	LIST	NLST	SITE
102b528cefcSMark Murray 	sTAT	HELP	NOOP	MKD	RMD	PWD
103b528cefcSMark Murray 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
104b528cefcSMark Murray 	EPRT	EPSV
105b528cefcSMark Murray 
106b528cefcSMark Murray 	UMASK	IDLE	CHMOD
107b528cefcSMark Murray 
108b528cefcSMark Murray 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
109b528cefcSMark Murray 	CONF	ENC
110b528cefcSMark Murray 
111b528cefcSMark Murray 	KAUTH	KLIST	KDESTROY KRBTKFILE AFSLOG
112b528cefcSMark Murray 	LOCATE	URL
113b528cefcSMark Murray 
114b528cefcSMark Murray 	FEAT	OPTS
115b528cefcSMark Murray 
116b528cefcSMark Murray 	LEXERR
117b528cefcSMark Murray 
118b528cefcSMark Murray %token	<s> STRING
119b528cefcSMark Murray %token	<i> NUMBER
120b528cefcSMark Murray 
121b528cefcSMark Murray %type	<i> check_login check_login_no_guest check_secure octal_number byte_size
122b528cefcSMark Murray %type	<i> struct_code mode_code type_code form_code
123b528cefcSMark Murray %type	<s> pathstring pathname password username
124b528cefcSMark Murray 
125b528cefcSMark Murray %start	cmd_list
126b528cefcSMark Murray 
127b528cefcSMark Murray %%
128b528cefcSMark Murray 
129b528cefcSMark Murray cmd_list
130b528cefcSMark Murray 	: /* empty */
131b528cefcSMark Murray 	| cmd_list cmd
132b528cefcSMark Murray 		{
133b528cefcSMark Murray 			fromname = (char *) 0;
134b528cefcSMark Murray 			restart_point = (off_t) 0;
135b528cefcSMark Murray 		}
136b528cefcSMark Murray 	| cmd_list rcmd
137b528cefcSMark Murray 	;
138b528cefcSMark Murray 
139b528cefcSMark Murray cmd
140c19800e8SDoug Rabson 	: USER SP username CRLF check_secure
141b528cefcSMark Murray 		{
142c19800e8SDoug Rabson 		    if ($5)
143b528cefcSMark Murray 			user($3);
144b528cefcSMark Murray 		    free($3);
145b528cefcSMark Murray 		}
146c19800e8SDoug Rabson 	| PASS SP password CRLF check_secure
147b528cefcSMark Murray 		{
148c19800e8SDoug Rabson 		    if ($5)
149b528cefcSMark Murray 			pass($3);
150b528cefcSMark Murray 		    memset ($3, 0, strlen($3));
151b528cefcSMark Murray 		    free($3);
152b528cefcSMark Murray 		}
153*ae771770SStanislav Sedov 
154c19800e8SDoug Rabson 	| PORT SP host_port CRLF check_secure
155b528cefcSMark Murray 		{
156c19800e8SDoug Rabson 		    if ($5) {
157*ae771770SStanislav Sedov 			if (paranoid &&
158*ae771770SStanislav Sedov 			    (data_dest->sa_family != his_addr->sa_family ||
159*ae771770SStanislav Sedov 			     (socket_get_port(data_dest) < IPPORT_RESERVED) ||
160*ae771770SStanislav Sedov 			     memcmp(socket_get_address(data_dest),
161*ae771770SStanislav Sedov 				    socket_get_address(his_addr),
162*ae771770SStanislav Sedov 				    socket_addr_size(his_addr)) != 0)) {
163*ae771770SStanislav Sedov 			    usedefault = 1;
164*ae771770SStanislav Sedov 			    reply(500, "Illegal PORT range rejected.");
165*ae771770SStanislav Sedov 			} else {
166b528cefcSMark Murray 			    usedefault = 0;
167b528cefcSMark Murray 			    if (pdata >= 0) {
168b528cefcSMark Murray 				close(pdata);
169b528cefcSMark Murray 				pdata = -1;
170b528cefcSMark Murray 			    }
171b528cefcSMark Murray 			    reply(200, "PORT command successful.");
172b528cefcSMark Murray 			}
173c19800e8SDoug Rabson 		    }
174*ae771770SStanislav Sedov 		}
175c19800e8SDoug Rabson 	| EPRT SP STRING CRLF check_secure
176b528cefcSMark Murray 		{
177c19800e8SDoug Rabson 		    if ($5)
178b528cefcSMark Murray 			eprt ($3);
179b528cefcSMark Murray 		    free ($3);
180b528cefcSMark Murray 		}
1815e9cd1aeSAssar Westerlund 	| PASV CRLF check_login
182b528cefcSMark Murray 		{
1835e9cd1aeSAssar Westerlund 		    if($3)
184b528cefcSMark Murray 			pasv ();
185b528cefcSMark Murray 		}
1865e9cd1aeSAssar Westerlund 	| EPSV CRLF check_login
187b528cefcSMark Murray 		{
1885e9cd1aeSAssar Westerlund 		    if($3)
189b528cefcSMark Murray 			epsv (NULL);
190b528cefcSMark Murray 		}
1915e9cd1aeSAssar Westerlund 	| EPSV SP STRING CRLF check_login
192b528cefcSMark Murray 		{
1935e9cd1aeSAssar Westerlund 		    if($5)
194b528cefcSMark Murray 			epsv ($3);
195b528cefcSMark Murray 		    free ($3);
196b528cefcSMark Murray 		}
197c19800e8SDoug Rabson 	| TYPE SP type_code CRLF check_secure
198b528cefcSMark Murray 		{
199c19800e8SDoug Rabson 		    if ($5) {
200b528cefcSMark Murray 			switch (cmd_type) {
201b528cefcSMark Murray 
202b528cefcSMark Murray 			case TYPE_A:
203b528cefcSMark Murray 				if (cmd_form == FORM_N) {
204b528cefcSMark Murray 					reply(200, "Type set to A.");
205b528cefcSMark Murray 					type = cmd_type;
206b528cefcSMark Murray 					form = cmd_form;
207b528cefcSMark Murray 				} else
208b528cefcSMark Murray 					reply(504, "Form must be N.");
209b528cefcSMark Murray 				break;
210b528cefcSMark Murray 
211b528cefcSMark Murray 			case TYPE_E:
212b528cefcSMark Murray 				reply(504, "Type E not implemented.");
213b528cefcSMark Murray 				break;
214b528cefcSMark Murray 
215b528cefcSMark Murray 			case TYPE_I:
216b528cefcSMark Murray 				reply(200, "Type set to I.");
217b528cefcSMark Murray 				type = cmd_type;
218b528cefcSMark Murray 				break;
219b528cefcSMark Murray 
220b528cefcSMark Murray 			case TYPE_L:
221b528cefcSMark Murray #if NBBY == 8
222b528cefcSMark Murray 				if (cmd_bytesz == 8) {
223b528cefcSMark Murray 					reply(200,
224b528cefcSMark Murray 					    "Type set to L (byte size 8).");
225b528cefcSMark Murray 					type = cmd_type;
226b528cefcSMark Murray 				} else
227b528cefcSMark Murray 					reply(504, "Byte size must be 8.");
228b528cefcSMark Murray #else /* NBBY == 8 */
229b528cefcSMark Murray 				UNIMPLEMENTED for NBBY != 8
230b528cefcSMark Murray #endif /* NBBY == 8 */
231b528cefcSMark Murray 			}
232b528cefcSMark Murray 		    }
233c19800e8SDoug Rabson 		}
234c19800e8SDoug Rabson 	| STRU SP struct_code CRLF check_secure
235b528cefcSMark Murray 		{
236c19800e8SDoug Rabson 		    if ($5) {
237b528cefcSMark Murray 			switch ($3) {
238b528cefcSMark Murray 
239b528cefcSMark Murray 			case STRU_F:
240b528cefcSMark Murray 				reply(200, "STRU F ok.");
241b528cefcSMark Murray 				break;
242b528cefcSMark Murray 
243b528cefcSMark Murray 			default:
244b528cefcSMark Murray 				reply(504, "Unimplemented STRU type.");
245b528cefcSMark Murray 			}
246b528cefcSMark Murray 		    }
247c19800e8SDoug Rabson 		}
248c19800e8SDoug Rabson 	| MODE SP mode_code CRLF check_secure
249b528cefcSMark Murray 		{
250c19800e8SDoug Rabson 		    if ($5) {
251b528cefcSMark Murray 			switch ($3) {
252b528cefcSMark Murray 
253b528cefcSMark Murray 			case MODE_S:
254b528cefcSMark Murray 				reply(200, "MODE S ok.");
255b528cefcSMark Murray 				break;
256b528cefcSMark Murray 
257b528cefcSMark Murray 			default:
258b528cefcSMark Murray 				reply(502, "Unimplemented MODE type.");
259b528cefcSMark Murray 			}
260b528cefcSMark Murray 		    }
261c19800e8SDoug Rabson 		}
262c19800e8SDoug Rabson 	| ALLO SP NUMBER CRLF check_secure
263b528cefcSMark Murray 		{
264c19800e8SDoug Rabson 		    if ($5) {
265b528cefcSMark Murray 			reply(202, "ALLO command ignored.");
266b528cefcSMark Murray 		    }
267c19800e8SDoug Rabson 		}
268c19800e8SDoug Rabson 	| ALLO SP NUMBER SP R SP NUMBER CRLF check_secure
269b528cefcSMark Murray 		{
270c19800e8SDoug Rabson 		    if ($9) {
271b528cefcSMark Murray 			reply(202, "ALLO command ignored.");
272b528cefcSMark Murray 		    }
273c19800e8SDoug Rabson 		}
274b528cefcSMark Murray 	| RETR SP pathname CRLF check_login
275b528cefcSMark Murray 		{
276b528cefcSMark Murray 			char *name = $3;
277b528cefcSMark Murray 
278b528cefcSMark Murray 			if ($5 && name != NULL)
279b528cefcSMark Murray 				retrieve(0, name);
280b528cefcSMark Murray 			if (name != NULL)
281b528cefcSMark Murray 				free(name);
282b528cefcSMark Murray 		}
283b528cefcSMark Murray 	| STOR SP pathname CRLF check_login
284b528cefcSMark Murray 		{
285b528cefcSMark Murray 			char *name = $3;
286b528cefcSMark Murray 
287b528cefcSMark Murray 			if ($5 && name != NULL)
288b528cefcSMark Murray 				do_store(name, "w", 0);
289b528cefcSMark Murray 			if (name != NULL)
290b528cefcSMark Murray 				free(name);
291b528cefcSMark Murray 		}
292b528cefcSMark Murray 	| APPE SP pathname CRLF check_login
293b528cefcSMark Murray 		{
294b528cefcSMark Murray 			char *name = $3;
295b528cefcSMark Murray 
296b528cefcSMark Murray 			if ($5 && name != NULL)
297b528cefcSMark Murray 				do_store(name, "a", 0);
298b528cefcSMark Murray 			if (name != NULL)
299b528cefcSMark Murray 				free(name);
300b528cefcSMark Murray 		}
301b528cefcSMark Murray 	| NLST CRLF check_login
302b528cefcSMark Murray 		{
303b528cefcSMark Murray 			if ($3)
304b528cefcSMark Murray 				send_file_list(".");
305b528cefcSMark Murray 		}
306b528cefcSMark Murray 	| NLST SP STRING CRLF check_login
307b528cefcSMark Murray 		{
308b528cefcSMark Murray 			char *name = $3;
309b528cefcSMark Murray 
310b528cefcSMark Murray 			if ($5 && name != NULL)
311b528cefcSMark Murray 				send_file_list(name);
312b528cefcSMark Murray 			if (name != NULL)
313b528cefcSMark Murray 				free(name);
314b528cefcSMark Murray 		}
315b528cefcSMark Murray 	| LIST CRLF check_login
316b528cefcSMark Murray 		{
317b528cefcSMark Murray 		    if($3)
318b528cefcSMark Murray 			list_file(".");
319b528cefcSMark Murray 		}
320b528cefcSMark Murray 	| LIST SP pathname CRLF check_login
321b528cefcSMark Murray 		{
322b528cefcSMark Murray 		    if($5)
323b528cefcSMark Murray 			list_file($3);
324b528cefcSMark Murray 		    free($3);
325b528cefcSMark Murray 		}
326b528cefcSMark Murray 	| sTAT SP pathname CRLF check_login
327b528cefcSMark Murray 		{
328b528cefcSMark Murray 			if ($5 && $3 != NULL)
329b528cefcSMark Murray 				statfilecmd($3);
330b528cefcSMark Murray 			if ($3 != NULL)
331b528cefcSMark Murray 				free($3);
332b528cefcSMark Murray 		}
333c19800e8SDoug Rabson 	| sTAT CRLF check_secure
334b528cefcSMark Murray 		{
335c19800e8SDoug Rabson 		    if ($3)
336b528cefcSMark Murray 			statcmd();
337b528cefcSMark Murray 		}
338b528cefcSMark Murray 	| DELE SP pathname CRLF check_login_no_guest
339b528cefcSMark Murray 		{
340b528cefcSMark Murray 			if ($5 && $3 != NULL)
341b528cefcSMark Murray 				do_delete($3);
342b528cefcSMark Murray 			if ($3 != NULL)
343b528cefcSMark Murray 				free($3);
344b528cefcSMark Murray 		}
345b528cefcSMark Murray 	| RNTO SP pathname CRLF check_login_no_guest
346b528cefcSMark Murray 		{
347b528cefcSMark Murray 			if($5){
348b528cefcSMark Murray 				if (fromname) {
349b528cefcSMark Murray 					renamecmd(fromname, $3);
350b528cefcSMark Murray 					free(fromname);
351b528cefcSMark Murray 					fromname = (char *) 0;
352b528cefcSMark Murray 				} else {
353b528cefcSMark Murray 					reply(503, "Bad sequence of commands.");
354b528cefcSMark Murray 				}
355b528cefcSMark Murray 			}
356b528cefcSMark Murray 			if ($3 != NULL)
357b528cefcSMark Murray 				free($3);
358b528cefcSMark Murray 		}
359c19800e8SDoug Rabson 	| ABOR CRLF check_secure
360b528cefcSMark Murray 		{
361c19800e8SDoug Rabson 		    if ($3)
362b528cefcSMark Murray 			reply(225, "ABOR command successful.");
363b528cefcSMark Murray 		}
364b528cefcSMark Murray 	| CWD CRLF check_login
365b528cefcSMark Murray 		{
366*ae771770SStanislav Sedov 			if ($3) {
367*ae771770SStanislav Sedov 				const char *path = pw->pw_dir;
368*ae771770SStanislav Sedov 				if (dochroot || guest)
369*ae771770SStanislav Sedov 					path = "/";
370*ae771770SStanislav Sedov 				cwd(path);
371*ae771770SStanislav Sedov 			}
372b528cefcSMark Murray 		}
373b528cefcSMark Murray 	| CWD SP pathname CRLF check_login
374b528cefcSMark Murray 		{
375b528cefcSMark Murray 			if ($5 && $3 != NULL)
376b528cefcSMark Murray 				cwd($3);
377b528cefcSMark Murray 			if ($3 != NULL)
378b528cefcSMark Murray 				free($3);
379b528cefcSMark Murray 		}
380c19800e8SDoug Rabson 	| HELP CRLF check_secure
381b528cefcSMark Murray 		{
382c19800e8SDoug Rabson 		    if ($3)
383b528cefcSMark Murray 			help(cmdtab, (char *) 0);
384b528cefcSMark Murray 		}
385c19800e8SDoug Rabson 	| HELP SP STRING CRLF check_secure
386b528cefcSMark Murray 		{
387c19800e8SDoug Rabson 		    if ($5) {
388b528cefcSMark Murray 			char *cp = $3;
389b528cefcSMark Murray 
390b528cefcSMark Murray 			if (strncasecmp(cp, "SITE", 4) == 0) {
391b528cefcSMark Murray 				cp = $3 + 4;
392b528cefcSMark Murray 				if (*cp == ' ')
393b528cefcSMark Murray 					cp++;
394b528cefcSMark Murray 				if (*cp)
395b528cefcSMark Murray 					help(sitetab, cp);
396b528cefcSMark Murray 				else
397b528cefcSMark Murray 					help(sitetab, (char *) 0);
398b528cefcSMark Murray 			} else
399b528cefcSMark Murray 				help(cmdtab, $3);
400b528cefcSMark Murray 		    }
401c19800e8SDoug Rabson 		}
402c19800e8SDoug Rabson 	| NOOP CRLF check_secure
403b528cefcSMark Murray 		{
404c19800e8SDoug Rabson 		    if ($3)
405b528cefcSMark Murray 			reply(200, "NOOP command successful.");
406b528cefcSMark Murray 		}
407b528cefcSMark Murray 	| MKD SP pathname CRLF check_login
408b528cefcSMark Murray 		{
409b528cefcSMark Murray 			if ($5 && $3 != NULL)
410b528cefcSMark Murray 				makedir($3);
411b528cefcSMark Murray 			if ($3 != NULL)
412b528cefcSMark Murray 				free($3);
413b528cefcSMark Murray 		}
414b528cefcSMark Murray 	| RMD SP pathname CRLF check_login_no_guest
415b528cefcSMark Murray 		{
416b528cefcSMark Murray 			if ($5 && $3 != NULL)
417b528cefcSMark Murray 				removedir($3);
418b528cefcSMark Murray 			if ($3 != NULL)
419b528cefcSMark Murray 				free($3);
420b528cefcSMark Murray 		}
421b528cefcSMark Murray 	| PWD CRLF check_login
422b528cefcSMark Murray 		{
423b528cefcSMark Murray 			if ($3)
424b528cefcSMark Murray 				pwd();
425b528cefcSMark Murray 		}
426b528cefcSMark Murray 	| CDUP CRLF check_login
427b528cefcSMark Murray 		{
428b528cefcSMark Murray 			if ($3)
429b528cefcSMark Murray 				cwd("..");
430b528cefcSMark Murray 		}
431c19800e8SDoug Rabson 	| FEAT CRLF check_secure
432b528cefcSMark Murray 		{
433c19800e8SDoug Rabson 		    if ($3) {
434b528cefcSMark Murray 			lreply(211, "Supported features:");
435b528cefcSMark Murray 			lreply(0, " MDTM");
436b528cefcSMark Murray 			lreply(0, " REST STREAM");
437b528cefcSMark Murray 			lreply(0, " SIZE");
438b528cefcSMark Murray 			reply(211, "End");
439b528cefcSMark Murray 		    }
440c19800e8SDoug Rabson 		}
441c19800e8SDoug Rabson 	| OPTS SP STRING CRLF check_secure
442b528cefcSMark Murray 		{
443c19800e8SDoug Rabson 		    if ($5)
444b528cefcSMark Murray 			reply(501, "Bad options");
445c19800e8SDoug Rabson 		    free ($3);
446b528cefcSMark Murray 		}
447b528cefcSMark Murray 
448c19800e8SDoug Rabson 	| SITE SP HELP CRLF check_secure
449b528cefcSMark Murray 		{
450c19800e8SDoug Rabson 		    if ($5)
451b528cefcSMark Murray 			help(sitetab, (char *) 0);
452b528cefcSMark Murray 		}
453c19800e8SDoug Rabson 	| SITE SP HELP SP STRING CRLF check_secure
454b528cefcSMark Murray 		{
455c19800e8SDoug Rabson 		    if ($7)
456b528cefcSMark Murray 			help(sitetab, $5);
457b528cefcSMark Murray 		}
458b528cefcSMark Murray 	| SITE SP UMASK CRLF check_login
459b528cefcSMark Murray 		{
460b528cefcSMark Murray 			if ($5) {
461b528cefcSMark Murray 				int oldmask = umask(0);
462b528cefcSMark Murray 				umask(oldmask);
463b528cefcSMark Murray 				reply(200, "Current UMASK is %03o", oldmask);
464b528cefcSMark Murray 			}
465b528cefcSMark Murray 		}
466b528cefcSMark Murray 	| SITE SP UMASK SP octal_number CRLF check_login_no_guest
467b528cefcSMark Murray 		{
468b528cefcSMark Murray 			if ($7) {
469b528cefcSMark Murray 				if (($5 == -1) || ($5 > 0777)) {
470b528cefcSMark Murray 					reply(501, "Bad UMASK value");
471b528cefcSMark Murray 				} else {
472b528cefcSMark Murray 					int oldmask = umask($5);
473b528cefcSMark Murray 					reply(200,
474b528cefcSMark Murray 					      "UMASK set to %03o (was %03o)",
475b528cefcSMark Murray 					      $5, oldmask);
476b528cefcSMark Murray 				}
477b528cefcSMark Murray 			}
478b528cefcSMark Murray 		}
479b528cefcSMark Murray 	| SITE SP CHMOD SP octal_number SP pathname CRLF check_login_no_guest
480b528cefcSMark Murray 		{
481b528cefcSMark Murray 			if ($9 && $7 != NULL) {
482b528cefcSMark Murray 				if ($5 > 0777)
483b528cefcSMark Murray 					reply(501,
484b528cefcSMark Murray 				"CHMOD: Mode value must be between 0 and 0777");
485b528cefcSMark Murray 				else if (chmod($7, $5) < 0)
486b528cefcSMark Murray 					perror_reply(550, $7);
487b528cefcSMark Murray 				else
488b528cefcSMark Murray 					reply(200, "CHMOD command successful.");
489b528cefcSMark Murray 			}
490b528cefcSMark Murray 			if ($7 != NULL)
491b528cefcSMark Murray 				free($7);
492b528cefcSMark Murray 		}
493c19800e8SDoug Rabson 	| SITE SP IDLE CRLF check_secure
494b528cefcSMark Murray 		{
495c19800e8SDoug Rabson 		    if ($5)
496b528cefcSMark Murray 			reply(200,
497b528cefcSMark Murray 			    "Current IDLE time limit is %d seconds; max %d",
498b528cefcSMark Murray 				ftpd_timeout, maxtimeout);
499b528cefcSMark Murray 		}
500c19800e8SDoug Rabson 	| SITE SP IDLE SP NUMBER CRLF check_secure
501b528cefcSMark Murray 		{
502c19800e8SDoug Rabson 		    if ($7) {
503b528cefcSMark Murray 			if ($5 < 30 || $5 > maxtimeout) {
504b528cefcSMark Murray 				reply(501,
505b528cefcSMark Murray 			"Maximum IDLE time must be between 30 and %d seconds",
506b528cefcSMark Murray 				    maxtimeout);
507b528cefcSMark Murray 			} else {
508b528cefcSMark Murray 				ftpd_timeout = $5;
509b528cefcSMark Murray 				alarm((unsigned) ftpd_timeout);
510b528cefcSMark Murray 				reply(200,
511b528cefcSMark Murray 				    "Maximum IDLE time set to %d seconds",
512b528cefcSMark Murray 				    ftpd_timeout);
513b528cefcSMark Murray 			}
514b528cefcSMark Murray 		    }
515c19800e8SDoug Rabson 		}
516b528cefcSMark Murray 
517b528cefcSMark Murray 	| SITE SP KAUTH SP STRING CRLF check_login
518b528cefcSMark Murray 		{
519b528cefcSMark Murray 			reply(500, "Command not implemented.");
520b528cefcSMark Murray 		}
521b528cefcSMark Murray 	| SITE SP KLIST CRLF check_login
522b528cefcSMark Murray 		{
523b528cefcSMark Murray 		    if($5)
524b528cefcSMark Murray 			klist();
525b528cefcSMark Murray 		}
526b528cefcSMark Murray 	| SITE SP KDESTROY CRLF check_login
527b528cefcSMark Murray 		{
528b528cefcSMark Murray 		    reply(500, "Command not implemented.");
529b528cefcSMark Murray 		}
530b528cefcSMark Murray 	| SITE SP KRBTKFILE SP STRING CRLF check_login
531b528cefcSMark Murray 		{
532b528cefcSMark Murray 		    reply(500, "Command not implemented.");
533b528cefcSMark Murray 		}
534b528cefcSMark Murray 	| SITE SP AFSLOG CRLF check_login
535b528cefcSMark Murray 		{
536*ae771770SStanislav Sedov #if defined(KRB5)
537b528cefcSMark Murray 		    if(guest)
538b528cefcSMark Murray 			reply(500, "Can't be done as guest.");
539b528cefcSMark Murray 		    else if($5)
540c19800e8SDoug Rabson 			afslog(NULL, 0);
541b528cefcSMark Murray #else
542b528cefcSMark Murray 		    reply(500, "Command not implemented.");
543b528cefcSMark Murray #endif
544b528cefcSMark Murray 		}
545b528cefcSMark Murray 	| SITE SP AFSLOG SP STRING CRLF check_login
546b528cefcSMark Murray 		{
547*ae771770SStanislav Sedov #if defined(KRB5)
548b528cefcSMark Murray 		    if(guest)
549b528cefcSMark Murray 			reply(500, "Can't be done as guest.");
550b528cefcSMark Murray 		    else if($7)
551c19800e8SDoug Rabson 			afslog($5, 0);
552b528cefcSMark Murray 		    if($5)
553b528cefcSMark Murray 			free($5);
554b528cefcSMark Murray #else
555b528cefcSMark Murray 		    reply(500, "Command not implemented.");
556b528cefcSMark Murray #endif
557b528cefcSMark Murray 		}
558b528cefcSMark Murray 	| SITE SP LOCATE SP STRING CRLF check_login
559b528cefcSMark Murray 		{
560b528cefcSMark Murray 		    if($7 && $5 != NULL)
561b528cefcSMark Murray 			find($5);
562b528cefcSMark Murray 		    if($5 != NULL)
563b528cefcSMark Murray 			free($5);
564b528cefcSMark Murray 		}
565c19800e8SDoug Rabson 	| SITE SP URL CRLF check_secure
566b528cefcSMark Murray 		{
567c19800e8SDoug Rabson 		    if ($5)
568c19800e8SDoug Rabson 			reply(200, "http://www.pdc.kth.se/heimdal/");
569b528cefcSMark Murray 		}
570b528cefcSMark Murray 	| STOU SP pathname CRLF check_login
571b528cefcSMark Murray 		{
572b528cefcSMark Murray 			if ($5 && $3 != NULL)
573b528cefcSMark Murray 				do_store($3, "w", 1);
574b528cefcSMark Murray 			if ($3 != NULL)
575b528cefcSMark Murray 				free($3);
576b528cefcSMark Murray 		}
577c19800e8SDoug Rabson 	| SYST CRLF check_secure
578b528cefcSMark Murray 		{
579c19800e8SDoug Rabson 		    if ($3) {
5805e9cd1aeSAssar Westerlund #if !defined(WIN32) && !defined(__EMX__) && !defined(__OS2__) && !defined(__CYGWIN32__)
581b528cefcSMark Murray 			reply(215, "UNIX Type: L%d", NBBY);
582b528cefcSMark Murray #else
583b528cefcSMark Murray 			reply(215, "UNKNOWN Type: L%d", NBBY);
584b528cefcSMark Murray #endif
585b528cefcSMark Murray 		    }
586c19800e8SDoug Rabson 		}
587b528cefcSMark Murray 
588b528cefcSMark Murray 		/*
589b528cefcSMark Murray 		 * SIZE is not in RFC959, but Postel has blessed it and
590b528cefcSMark Murray 		 * it will be in the updated RFC.
591b528cefcSMark Murray 		 *
592b528cefcSMark Murray 		 * Return size of file in a format suitable for
593b528cefcSMark Murray 		 * using with RESTART (we just count bytes).
594b528cefcSMark Murray 		 */
595b528cefcSMark Murray 	| SIZE SP pathname CRLF check_login
596b528cefcSMark Murray 		{
597b528cefcSMark Murray 			if ($5 && $3 != NULL)
598b528cefcSMark Murray 				sizecmd($3);
599b528cefcSMark Murray 			if ($3 != NULL)
600b528cefcSMark Murray 				free($3);
601b528cefcSMark Murray 		}
602b528cefcSMark Murray 
603b528cefcSMark Murray 		/*
604b528cefcSMark Murray 		 * MDTM is not in RFC959, but Postel has blessed it and
605b528cefcSMark Murray 		 * it will be in the updated RFC.
606b528cefcSMark Murray 		 *
607b528cefcSMark Murray 		 * Return modification time of file as an ISO 3307
608b528cefcSMark Murray 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
609b528cefcSMark Murray 		 * where xxx is the fractional second (of any precision,
610b528cefcSMark Murray 		 * not necessarily 3 digits)
611b528cefcSMark Murray 		 */
612b528cefcSMark Murray 	| MDTM SP pathname CRLF check_login
613b528cefcSMark Murray 		{
614b528cefcSMark Murray 			if ($5 && $3 != NULL) {
615b528cefcSMark Murray 				struct stat stbuf;
616b528cefcSMark Murray 				if (stat($3, &stbuf) < 0)
617b528cefcSMark Murray 					reply(550, "%s: %s",
618b528cefcSMark Murray 					    $3, strerror(errno));
619b528cefcSMark Murray 				else if (!S_ISREG(stbuf.st_mode)) {
620b528cefcSMark Murray 					reply(550,
621b528cefcSMark Murray 					      "%s: not a plain file.", $3);
622b528cefcSMark Murray 				} else {
623b528cefcSMark Murray 					struct tm *t;
6245e9cd1aeSAssar Westerlund 					time_t mtime = stbuf.st_mtime;
6255e9cd1aeSAssar Westerlund 
6265e9cd1aeSAssar Westerlund 					t = gmtime(&mtime);
627b528cefcSMark Murray 					reply(213,
628b528cefcSMark Murray 					      "%04d%02d%02d%02d%02d%02d",
629b528cefcSMark Murray 					      t->tm_year + 1900,
630b528cefcSMark Murray 					      t->tm_mon + 1,
631b528cefcSMark Murray 					      t->tm_mday,
632b528cefcSMark Murray 					      t->tm_hour,
633b528cefcSMark Murray 					      t->tm_min,
634b528cefcSMark Murray 					      t->tm_sec);
635b528cefcSMark Murray 				}
636b528cefcSMark Murray 			}
637b528cefcSMark Murray 			if ($3 != NULL)
638b528cefcSMark Murray 				free($3);
639b528cefcSMark Murray 		}
640c19800e8SDoug Rabson 	| QUIT CRLF check_secure
641b528cefcSMark Murray 		{
642c19800e8SDoug Rabson 		    if ($3) {
643b528cefcSMark Murray 			reply(221, "Goodbye.");
644b528cefcSMark Murray 			dologout(0);
645b528cefcSMark Murray 		    }
646c19800e8SDoug Rabson 		}
647b528cefcSMark Murray 	| error CRLF
648b528cefcSMark Murray 		{
649b528cefcSMark Murray 			yyerrok;
650b528cefcSMark Murray 		}
651b528cefcSMark Murray 	;
652b528cefcSMark Murray rcmd
653b528cefcSMark Murray 	: RNFR SP pathname CRLF check_login_no_guest
654b528cefcSMark Murray 		{
655b528cefcSMark Murray 			restart_point = (off_t) 0;
656b528cefcSMark Murray 			if ($5 && $3) {
657b528cefcSMark Murray 				fromname = renamefrom($3);
658b528cefcSMark Murray 				if (fromname == (char *) 0 && $3) {
659b528cefcSMark Murray 					free($3);
660b528cefcSMark Murray 				}
661b528cefcSMark Murray 			}
662b528cefcSMark Murray 		}
663c19800e8SDoug Rabson 	| REST SP byte_size CRLF check_secure
664b528cefcSMark Murray 		{
665c19800e8SDoug Rabson 		    if ($5) {
666b528cefcSMark Murray 			fromname = (char *) 0;
667b528cefcSMark Murray 			restart_point = $3;	/* XXX $3 is only "int" */
668b528cefcSMark Murray 			reply(350, "Restarting at %ld. %s",
669b528cefcSMark Murray 			      (long)restart_point,
670b528cefcSMark Murray 			      "Send STORE or RETRIEVE to initiate transfer.");
671b528cefcSMark Murray 		    }
672c19800e8SDoug Rabson 		}
673b528cefcSMark Murray 	| AUTH SP STRING CRLF
674b528cefcSMark Murray 		{
675b528cefcSMark Murray 			auth($3);
676b528cefcSMark Murray 			free($3);
677b528cefcSMark Murray 		}
678b528cefcSMark Murray 	| ADAT SP STRING CRLF
679b528cefcSMark Murray 		{
680b528cefcSMark Murray 			adat($3);
681b528cefcSMark Murray 			free($3);
682b528cefcSMark Murray 		}
683c19800e8SDoug Rabson 	| PBSZ SP NUMBER CRLF check_secure
684b528cefcSMark Murray 		{
685c19800e8SDoug Rabson 		    if ($5)
686b528cefcSMark Murray 			pbsz($3);
687b528cefcSMark Murray 		}
688c19800e8SDoug Rabson 	| PROT SP STRING CRLF check_secure
689b528cefcSMark Murray 		{
690c19800e8SDoug Rabson 		    if ($5)
691b528cefcSMark Murray 			prot($3);
692b528cefcSMark Murray 		}
693c19800e8SDoug Rabson 	| CCC CRLF check_secure
694b528cefcSMark Murray 		{
695c19800e8SDoug Rabson 		    if ($3)
696b528cefcSMark Murray 			ccc();
697b528cefcSMark Murray 		}
698b528cefcSMark Murray 	| MIC SP STRING CRLF
699b528cefcSMark Murray 		{
700b528cefcSMark Murray 			mec($3, prot_safe);
701b528cefcSMark Murray 			free($3);
702b528cefcSMark Murray 		}
703b528cefcSMark Murray 	| CONF SP STRING CRLF
704b528cefcSMark Murray 		{
705b528cefcSMark Murray 			mec($3, prot_confidential);
706b528cefcSMark Murray 			free($3);
707b528cefcSMark Murray 		}
708b528cefcSMark Murray 	| ENC SP STRING CRLF
709b528cefcSMark Murray 		{
710b528cefcSMark Murray 			mec($3, prot_private);
711b528cefcSMark Murray 			free($3);
712b528cefcSMark Murray 		}
713b528cefcSMark Murray 	;
714b528cefcSMark Murray 
715b528cefcSMark Murray username
716b528cefcSMark Murray 	: STRING
717b528cefcSMark Murray 	;
718b528cefcSMark Murray 
719b528cefcSMark Murray password
720b528cefcSMark Murray 	: /* empty */
721b528cefcSMark Murray 		{
722b528cefcSMark Murray 			$$ = (char *)calloc(1, sizeof(char));
723b528cefcSMark Murray 		}
724b528cefcSMark Murray 	| STRING
725b528cefcSMark Murray 	;
726b528cefcSMark Murray 
727b528cefcSMark Murray byte_size
728b528cefcSMark Murray 	: NUMBER
729b528cefcSMark Murray 	;
730b528cefcSMark Murray 
731b528cefcSMark Murray host_port
732b528cefcSMark Murray 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
733b528cefcSMark Murray 		NUMBER COMMA NUMBER
734b528cefcSMark Murray 		{
735c19800e8SDoug Rabson 			struct sockaddr_in *sin4 = (struct sockaddr_in *)data_dest;
736b528cefcSMark Murray 
737c19800e8SDoug Rabson 			sin4->sin_family = AF_INET;
738c19800e8SDoug Rabson 			sin4->sin_port = htons($9 * 256 + $11);
739c19800e8SDoug Rabson 			sin4->sin_addr.s_addr =
740b528cefcSMark Murray 			    htonl(($1 << 24) | ($3 << 16) | ($5 << 8) | $7);
741b528cefcSMark Murray 		}
742b528cefcSMark Murray 	;
743b528cefcSMark Murray 
744b528cefcSMark Murray form_code
745b528cefcSMark Murray 	: N
746b528cefcSMark Murray 		{
747b528cefcSMark Murray 			$$ = FORM_N;
748b528cefcSMark Murray 		}
749b528cefcSMark Murray 	| T
750b528cefcSMark Murray 		{
751b528cefcSMark Murray 			$$ = FORM_T;
752b528cefcSMark Murray 		}
753b528cefcSMark Murray 	| C
754b528cefcSMark Murray 		{
755b528cefcSMark Murray 			$$ = FORM_C;
756b528cefcSMark Murray 		}
757b528cefcSMark Murray 	;
758b528cefcSMark Murray 
759b528cefcSMark Murray type_code
760b528cefcSMark Murray 	: A
761b528cefcSMark Murray 		{
762b528cefcSMark Murray 			cmd_type = TYPE_A;
763b528cefcSMark Murray 			cmd_form = FORM_N;
764b528cefcSMark Murray 		}
765b528cefcSMark Murray 	| A SP form_code
766b528cefcSMark Murray 		{
767b528cefcSMark Murray 			cmd_type = TYPE_A;
768b528cefcSMark Murray 			cmd_form = $3;
769b528cefcSMark Murray 		}
770b528cefcSMark Murray 	| E
771b528cefcSMark Murray 		{
772b528cefcSMark Murray 			cmd_type = TYPE_E;
773b528cefcSMark Murray 			cmd_form = FORM_N;
774b528cefcSMark Murray 		}
775b528cefcSMark Murray 	| E SP form_code
776b528cefcSMark Murray 		{
777b528cefcSMark Murray 			cmd_type = TYPE_E;
778b528cefcSMark Murray 			cmd_form = $3;
779b528cefcSMark Murray 		}
780b528cefcSMark Murray 	| I
781b528cefcSMark Murray 		{
782b528cefcSMark Murray 			cmd_type = TYPE_I;
783b528cefcSMark Murray 		}
784b528cefcSMark Murray 	| L
785b528cefcSMark Murray 		{
786b528cefcSMark Murray 			cmd_type = TYPE_L;
787b528cefcSMark Murray 			cmd_bytesz = NBBY;
788b528cefcSMark Murray 		}
789b528cefcSMark Murray 	| L SP byte_size
790b528cefcSMark Murray 		{
791b528cefcSMark Murray 			cmd_type = TYPE_L;
792b528cefcSMark Murray 			cmd_bytesz = $3;
793b528cefcSMark Murray 		}
794b528cefcSMark Murray 		/* this is for a bug in the BBN ftp */
795b528cefcSMark Murray 	| L byte_size
796b528cefcSMark Murray 		{
797b528cefcSMark Murray 			cmd_type = TYPE_L;
798b528cefcSMark Murray 			cmd_bytesz = $2;
799b528cefcSMark Murray 		}
800b528cefcSMark Murray 	;
801b528cefcSMark Murray 
802b528cefcSMark Murray struct_code
803b528cefcSMark Murray 	: F
804b528cefcSMark Murray 		{
805b528cefcSMark Murray 			$$ = STRU_F;
806b528cefcSMark Murray 		}
807b528cefcSMark Murray 	| R
808b528cefcSMark Murray 		{
809b528cefcSMark Murray 			$$ = STRU_R;
810b528cefcSMark Murray 		}
811b528cefcSMark Murray 	| P
812b528cefcSMark Murray 		{
813b528cefcSMark Murray 			$$ = STRU_P;
814b528cefcSMark Murray 		}
815b528cefcSMark Murray 	;
816b528cefcSMark Murray 
817b528cefcSMark Murray mode_code
818b528cefcSMark Murray 	: S
819b528cefcSMark Murray 		{
820b528cefcSMark Murray 			$$ = MODE_S;
821b528cefcSMark Murray 		}
822b528cefcSMark Murray 	| B
823b528cefcSMark Murray 		{
824b528cefcSMark Murray 			$$ = MODE_B;
825b528cefcSMark Murray 		}
826b528cefcSMark Murray 	| C
827b528cefcSMark Murray 		{
828b528cefcSMark Murray 			$$ = MODE_C;
829b528cefcSMark Murray 		}
830b528cefcSMark Murray 	;
831b528cefcSMark Murray 
832b528cefcSMark Murray pathname
833b528cefcSMark Murray 	: pathstring
834b528cefcSMark Murray 		{
835b528cefcSMark Murray 			/*
836b528cefcSMark Murray 			 * Problem: this production is used for all pathname
837b528cefcSMark Murray 			 * processing, but only gives a 550 error reply.
838b528cefcSMark Murray 			 * This is a valid reply in some cases but not in others.
839b528cefcSMark Murray 			 */
840b528cefcSMark Murray 			if (logged_in && $1 && *$1 == '~') {
841b528cefcSMark Murray 				glob_t gl;
842b528cefcSMark Murray 				int flags =
843b528cefcSMark Murray 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
844b528cefcSMark Murray 
845b528cefcSMark Murray 				memset(&gl, 0, sizeof(gl));
846b528cefcSMark Murray 				if (glob($1, flags, NULL, &gl) ||
847b528cefcSMark Murray 				    gl.gl_pathc == 0) {
848b528cefcSMark Murray 					reply(550, "not found");
849b528cefcSMark Murray 					$$ = NULL;
850b528cefcSMark Murray 				} else {
851b528cefcSMark Murray 					$$ = strdup(gl.gl_pathv[0]);
852b528cefcSMark Murray 				}
853b528cefcSMark Murray 				globfree(&gl);
854b528cefcSMark Murray 				free($1);
855b528cefcSMark Murray 			} else
856b528cefcSMark Murray 				$$ = $1;
857b528cefcSMark Murray 		}
858b528cefcSMark Murray 	;
859b528cefcSMark Murray 
860b528cefcSMark Murray pathstring
861b528cefcSMark Murray 	: STRING
862b528cefcSMark Murray 	;
863b528cefcSMark Murray 
864b528cefcSMark Murray octal_number
865b528cefcSMark Murray 	: NUMBER
866b528cefcSMark Murray 		{
867b528cefcSMark Murray 			int ret, dec, multby, digit;
868b528cefcSMark Murray 
869b528cefcSMark Murray 			/*
870b528cefcSMark Murray 			 * Convert a number that was read as decimal number
871b528cefcSMark Murray 			 * to what it would be if it had been read as octal.
872b528cefcSMark Murray 			 */
873b528cefcSMark Murray 			dec = $1;
874b528cefcSMark Murray 			multby = 1;
875b528cefcSMark Murray 			ret = 0;
876b528cefcSMark Murray 			while (dec) {
877b528cefcSMark Murray 				digit = dec%10;
878b528cefcSMark Murray 				if (digit > 7) {
879b528cefcSMark Murray 					ret = -1;
880b528cefcSMark Murray 					break;
881b528cefcSMark Murray 				}
882b528cefcSMark Murray 				ret += digit * multby;
883b528cefcSMark Murray 				multby *= 8;
884b528cefcSMark Murray 				dec /= 10;
885b528cefcSMark Murray 			}
886b528cefcSMark Murray 			$$ = ret;
887b528cefcSMark Murray 		}
888b528cefcSMark Murray 	;
889b528cefcSMark Murray 
890b528cefcSMark Murray 
891b528cefcSMark Murray check_login_no_guest : check_login
892b528cefcSMark Murray 		{
893b528cefcSMark Murray 			$$ = $1 && !guest;
894b528cefcSMark Murray 			if($1 && !$$)
895b528cefcSMark Murray 				reply(550, "Permission denied");
896b528cefcSMark Murray 		}
897b528cefcSMark Murray 	;
898b528cefcSMark Murray 
899b528cefcSMark Murray check_login : check_secure
900b528cefcSMark Murray 		{
901b528cefcSMark Murray 		    if($1) {
902b528cefcSMark Murray 			if(($$ = logged_in) == 0)
903b528cefcSMark Murray 			    reply(530, "Please login with USER and PASS.");
904b528cefcSMark Murray 		    } else
905b528cefcSMark Murray 			$$ = 0;
906b528cefcSMark Murray 		}
907b528cefcSMark Murray 	;
908b528cefcSMark Murray 
909b528cefcSMark Murray check_secure : /* empty */
910b528cefcSMark Murray 		{
911b528cefcSMark Murray 		    $$ = 1;
912c19800e8SDoug Rabson 		    if(sec_complete && !ccc_passed && !secure_command()) {
913b528cefcSMark Murray 			$$ = 0;
914b528cefcSMark Murray 			reply(533, "Command protection level denied "
915b528cefcSMark Murray 			      "for paranoid reasons.");
916b528cefcSMark Murray 		    }
917b528cefcSMark Murray 		}
918b528cefcSMark Murray 	;
919b528cefcSMark Murray 
920b528cefcSMark Murray %%
921b528cefcSMark Murray 
922b528cefcSMark Murray #define	CMD	0	/* beginning of command */
923b528cefcSMark Murray #define	ARGS	1	/* expect miscellaneous arguments */
924b528cefcSMark Murray #define	STR1	2	/* expect SP followed by STRING */
925b528cefcSMark Murray #define	STR2	3	/* expect STRING */
926b528cefcSMark Murray #define	OSTR	4	/* optional SP then STRING */
927b528cefcSMark Murray #define	ZSTR1	5	/* SP then optional STRING */
928b528cefcSMark Murray #define	ZSTR2	6	/* optional STRING after SP */
929b528cefcSMark Murray #define	SITECMD	7	/* SITE command */
930b528cefcSMark Murray #define	NSTR	8	/* Number followed by a string */
931b528cefcSMark Murray 
932b528cefcSMark Murray struct tab cmdtab[] = {		/* In order defined in RFC 765 */
933b528cefcSMark Murray 	{ "USER", USER, STR1, 1,	"<sp> username" },
934b528cefcSMark Murray 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
935b528cefcSMark Murray 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
936b528cefcSMark Murray 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
937b528cefcSMark Murray 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
938b528cefcSMark Murray 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
939b528cefcSMark Murray 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
940b528cefcSMark Murray 	{ "EPRT", EPRT, STR1, 1,	"<sp> string" },
941b528cefcSMark Murray 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
942b528cefcSMark Murray 	{ "EPSV", EPSV, OSTR, 1,	"[<sp> foo]" },
943b528cefcSMark Murray 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
944b528cefcSMark Murray 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
945b528cefcSMark Murray 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
946b528cefcSMark Murray 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
947b528cefcSMark Murray 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
948b528cefcSMark Murray 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
949b528cefcSMark Murray 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
950b528cefcSMark Murray 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
951b528cefcSMark Murray 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
952b528cefcSMark Murray 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
953b528cefcSMark Murray 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
954b528cefcSMark Murray 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
955b528cefcSMark Murray 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
956b528cefcSMark Murray 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
957b528cefcSMark Murray 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
958b528cefcSMark Murray 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
959b528cefcSMark Murray 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
960b528cefcSMark Murray 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
961b528cefcSMark Murray 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
962b528cefcSMark Murray 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
963b528cefcSMark Murray 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
964b528cefcSMark Murray 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
965b528cefcSMark Murray 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
966b528cefcSMark Murray 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
967b528cefcSMark Murray 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
968b528cefcSMark Murray 	{ "STAT", sTAT, OSTR, 1,	"[ <sp> path-name ]" },
969b528cefcSMark Murray 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
970b528cefcSMark Murray 	{ "NOOP", NOOP, ARGS, 1,	"" },
971b528cefcSMark Murray 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
972b528cefcSMark Murray 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
973b528cefcSMark Murray 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
974b528cefcSMark Murray 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
975b528cefcSMark Murray 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
976b528cefcSMark Murray 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
977b528cefcSMark Murray 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
978b528cefcSMark Murray 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
979b528cefcSMark Murray 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
980b528cefcSMark Murray 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
981b528cefcSMark Murray 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
982b528cefcSMark Murray 
983b528cefcSMark Murray 	/* extensions from RFC2228 */
984b528cefcSMark Murray 	{ "AUTH", AUTH,	STR1, 1,	"<sp> auth-type" },
985b528cefcSMark Murray 	{ "ADAT", ADAT,	STR1, 1,	"<sp> auth-data" },
986b528cefcSMark Murray 	{ "PBSZ", PBSZ,	ARGS, 1,	"<sp> buffer-size" },
987b528cefcSMark Murray 	{ "PROT", PROT,	STR1, 1,	"<sp> prot-level" },
988b528cefcSMark Murray 	{ "CCC",  CCC,	ARGS, 1,	"" },
989b528cefcSMark Murray 	{ "MIC",  MIC,	STR1, 1,	"<sp> integrity command" },
990b528cefcSMark Murray 	{ "CONF", CONF,	STR1, 1,	"<sp> confidentiality command" },
991b528cefcSMark Murray 	{ "ENC",  ENC,	STR1, 1,	"<sp> privacy command" },
992b528cefcSMark Murray 
993b528cefcSMark Murray 	/* RFC2389 */
994b528cefcSMark Murray 	{ "FEAT", FEAT, ARGS, 1,	"" },
995b528cefcSMark Murray 	{ "OPTS", OPTS, ARGS, 1,	"<sp> command [<sp> options]" },
996b528cefcSMark Murray 
997b528cefcSMark Murray 	{ NULL,   0,    0,    0,	0 }
998b528cefcSMark Murray };
999b528cefcSMark Murray 
1000b528cefcSMark Murray struct tab sitetab[] = {
1001b528cefcSMark Murray 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1002b528cefcSMark Murray 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1003b528cefcSMark Murray 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1004b528cefcSMark Murray 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1005b528cefcSMark Murray 
1006b528cefcSMark Murray 	{ "KAUTH", KAUTH, STR1, 1,	"<sp> principal [ <sp> ticket ]" },
1007b528cefcSMark Murray 	{ "KLIST", KLIST, ARGS, 1,	"(show ticket file)" },
1008b528cefcSMark Murray 	{ "KDESTROY", KDESTROY, ARGS, 1, "(destroy tickets)" },
1009b528cefcSMark Murray 	{ "KRBTKFILE", KRBTKFILE, STR1, 1, "<sp> ticket-file" },
1010b528cefcSMark Murray 	{ "AFSLOG", AFSLOG, OSTR, 1,	"[<sp> cell]" },
1011b528cefcSMark Murray 
1012b528cefcSMark Murray 	{ "LOCATE", LOCATE, STR1, 1,	"<sp> globexpr" },
1013b528cefcSMark Murray 	{ "FIND", LOCATE, STR1, 1,	"<sp> globexpr" },
1014b528cefcSMark Murray 
1015b528cefcSMark Murray 	{ "URL",  URL,  ARGS, 1,	"?" },
1016b528cefcSMark Murray 
1017b528cefcSMark Murray 	{ NULL,   0,    0,    0,	0 }
1018b528cefcSMark Murray };
1019b528cefcSMark Murray 
1020b528cefcSMark Murray static struct tab *
1021b528cefcSMark Murray lookup(struct tab *p, char *cmd)
1022b528cefcSMark Murray {
1023b528cefcSMark Murray 
1024b528cefcSMark Murray 	for (; p->name != NULL; p++)
1025b528cefcSMark Murray 		if (strcmp(cmd, p->name) == 0)
1026b528cefcSMark Murray 			return (p);
1027b528cefcSMark Murray 	return (0);
1028b528cefcSMark Murray }
1029b528cefcSMark Murray 
1030b528cefcSMark Murray /*
1031b528cefcSMark Murray  * ftpd_getline - a hacked up version of fgets to ignore TELNET escape codes.
1032b528cefcSMark Murray  */
1033b528cefcSMark Murray char *
1034b528cefcSMark Murray ftpd_getline(char *s, int n)
1035b528cefcSMark Murray {
1036b528cefcSMark Murray 	int c;
1037b528cefcSMark Murray 	char *cs;
1038b528cefcSMark Murray 
1039b528cefcSMark Murray 	cs = s;
10408d4ba808SJacques Vidrine 
10418d4ba808SJacques Vidrine 	/* might still be data within the security MIC/CONF/ENC */
1042b528cefcSMark Murray 	if(ftp_command){
1043b528cefcSMark Murray 	    strlcpy(s, ftp_command, n);
1044b528cefcSMark Murray 	    if (debug)
1045b528cefcSMark Murray 		syslog(LOG_DEBUG, "command: %s", s);
1046b528cefcSMark Murray 	    return s;
1047b528cefcSMark Murray 	}
1048b528cefcSMark Murray 	while ((c = getc(stdin)) != EOF) {
1049b528cefcSMark Murray 		c &= 0377;
1050b528cefcSMark Murray 		if (c == IAC) {
1051b528cefcSMark Murray 		    if ((c = getc(stdin)) != EOF) {
1052b528cefcSMark Murray 			c &= 0377;
1053b528cefcSMark Murray 			switch (c) {
1054b528cefcSMark Murray 			case WILL:
1055b528cefcSMark Murray 			case WONT:
1056b528cefcSMark Murray 				c = getc(stdin);
1057b528cefcSMark Murray 				printf("%c%c%c", IAC, DONT, 0377&c);
1058b528cefcSMark Murray 				fflush(stdout);
1059b528cefcSMark Murray 				continue;
1060b528cefcSMark Murray 			case DO:
1061b528cefcSMark Murray 			case DONT:
1062b528cefcSMark Murray 				c = getc(stdin);
1063b528cefcSMark Murray 				printf("%c%c%c", IAC, WONT, 0377&c);
1064b528cefcSMark Murray 				fflush(stdout);
1065b528cefcSMark Murray 				continue;
1066b528cefcSMark Murray 			case IAC:
1067b528cefcSMark Murray 				break;
1068b528cefcSMark Murray 			default:
1069b528cefcSMark Murray 				continue;	/* ignore command */
1070b528cefcSMark Murray 			}
1071b528cefcSMark Murray 		    }
1072b528cefcSMark Murray 		}
1073b528cefcSMark Murray 		*cs++ = c;
1074b528cefcSMark Murray 		if (--n <= 0 || c == '\n')
1075b528cefcSMark Murray 			break;
1076b528cefcSMark Murray 	}
1077b528cefcSMark Murray 	if (c == EOF && cs == s)
1078b528cefcSMark Murray 		return (NULL);
1079b528cefcSMark Murray 	*cs++ = '\0';
1080b528cefcSMark Murray 	if (debug) {
1081b528cefcSMark Murray 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1082b528cefcSMark Murray 			/* Don't syslog passwords */
1083b528cefcSMark Murray 			syslog(LOG_DEBUG, "command: %.5s ???", s);
1084b528cefcSMark Murray 		} else {
1085b528cefcSMark Murray 			char *cp;
1086b528cefcSMark Murray 			int len;
1087b528cefcSMark Murray 
1088b528cefcSMark Murray 			/* Don't syslog trailing CR-LF */
1089b528cefcSMark Murray 			len = strlen(s);
1090b528cefcSMark Murray 			cp = s + len - 1;
1091b528cefcSMark Murray 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1092b528cefcSMark Murray 				--cp;
1093b528cefcSMark Murray 				--len;
1094b528cefcSMark Murray 			}
1095b528cefcSMark Murray 			syslog(LOG_DEBUG, "command: %.*s", len, s);
1096b528cefcSMark Murray 		}
1097b528cefcSMark Murray 	}
1098b528cefcSMark Murray #ifdef XXX
1099b528cefcSMark Murray 	fprintf(stderr, "%s\n", s);
1100b528cefcSMark Murray #endif
1101b528cefcSMark Murray 	return (s);
1102b528cefcSMark Murray }
1103b528cefcSMark Murray 
1104b528cefcSMark Murray static RETSIGTYPE
1105b528cefcSMark Murray toolong(int signo)
1106b528cefcSMark Murray {
1107b528cefcSMark Murray 
1108b528cefcSMark Murray 	reply(421,
1109b528cefcSMark Murray 	    "Timeout (%d seconds): closing control connection.",
1110b528cefcSMark Murray 	      ftpd_timeout);
1111b528cefcSMark Murray 	if (logging)
1112b528cefcSMark Murray 		syslog(LOG_INFO, "User %s timed out after %d seconds",
1113b528cefcSMark Murray 		    (pw ? pw -> pw_name : "unknown"), ftpd_timeout);
1114b528cefcSMark Murray 	dologout(1);
1115b528cefcSMark Murray 	SIGRETURN(0);
1116b528cefcSMark Murray }
1117b528cefcSMark Murray 
1118b528cefcSMark Murray static int
1119b528cefcSMark Murray yylex(void)
1120b528cefcSMark Murray {
1121b528cefcSMark Murray 	static int cpos, state;
1122b528cefcSMark Murray 	char *cp, *cp2;
1123b528cefcSMark Murray 	struct tab *p;
1124b528cefcSMark Murray 	int n;
1125b528cefcSMark Murray 	char c;
1126b528cefcSMark Murray 
1127b528cefcSMark Murray 	for (;;) {
1128b528cefcSMark Murray 		switch (state) {
1129b528cefcSMark Murray 
1130b528cefcSMark Murray 		case CMD:
11318d4ba808SJacques Vidrine 			hasyyerrored = 0;
11328d4ba808SJacques Vidrine 
1133b528cefcSMark Murray 			signal(SIGALRM, toolong);
1134b528cefcSMark Murray 			alarm((unsigned) ftpd_timeout);
1135b528cefcSMark Murray 			if (ftpd_getline(cbuf, sizeof(cbuf)-1) == NULL) {
1136b528cefcSMark Murray 				reply(221, "You could at least say goodbye.");
1137b528cefcSMark Murray 				dologout(0);
1138b528cefcSMark Murray 			}
1139b528cefcSMark Murray 			alarm(0);
1140b528cefcSMark Murray #ifdef HAVE_SETPROCTITLE
11418d4ba808SJacques Vidrine 			if (strncasecmp(cbuf, "PASS", 4) != 0)
1142b528cefcSMark Murray 				setproctitle("%s: %s", proctitle, cbuf);
1143b528cefcSMark Murray #endif /* HAVE_SETPROCTITLE */
1144b528cefcSMark Murray 			if ((cp = strchr(cbuf, '\r'))) {
1145b528cefcSMark Murray 				*cp++ = '\n';
1146b528cefcSMark Murray 				*cp = '\0';
1147b528cefcSMark Murray 			}
1148b528cefcSMark Murray 			if ((cp = strpbrk(cbuf, " \n")))
1149b528cefcSMark Murray 				cpos = cp - cbuf;
1150b528cefcSMark Murray 			if (cpos == 0)
1151b528cefcSMark Murray 				cpos = 4;
1152b528cefcSMark Murray 			c = cbuf[cpos];
1153b528cefcSMark Murray 			cbuf[cpos] = '\0';
1154b528cefcSMark Murray 			strupr(cbuf);
1155b528cefcSMark Murray 			p = lookup(cmdtab, cbuf);
1156b528cefcSMark Murray 			cbuf[cpos] = c;
1157b528cefcSMark Murray 			if (p != 0) {
1158b528cefcSMark Murray 				if (p->implemented == 0) {
1159b528cefcSMark Murray 					nack(p->name);
11608d4ba808SJacques Vidrine 					hasyyerrored = 1;
11618d4ba808SJacques Vidrine 					break;
1162b528cefcSMark Murray 				}
1163b528cefcSMark Murray 				state = p->state;
1164b528cefcSMark Murray 				yylval.s = p->name;
1165b528cefcSMark Murray 				return (p->token);
1166b528cefcSMark Murray 			}
1167b528cefcSMark Murray 			break;
1168b528cefcSMark Murray 
1169b528cefcSMark Murray 		case SITECMD:
1170b528cefcSMark Murray 			if (cbuf[cpos] == ' ') {
1171b528cefcSMark Murray 				cpos++;
1172b528cefcSMark Murray 				return (SP);
1173b528cefcSMark Murray 			}
1174b528cefcSMark Murray 			cp = &cbuf[cpos];
1175b528cefcSMark Murray 			if ((cp2 = strpbrk(cp, " \n")))
1176b528cefcSMark Murray 				cpos = cp2 - cbuf;
1177b528cefcSMark Murray 			c = cbuf[cpos];
1178b528cefcSMark Murray 			cbuf[cpos] = '\0';
1179b528cefcSMark Murray 			strupr(cp);
1180b528cefcSMark Murray 			p = lookup(sitetab, cp);
1181b528cefcSMark Murray 			cbuf[cpos] = c;
1182b528cefcSMark Murray 			if (p != 0) {
1183b528cefcSMark Murray 				if (p->implemented == 0) {
1184b528cefcSMark Murray 					state = CMD;
1185b528cefcSMark Murray 					nack(p->name);
11868d4ba808SJacques Vidrine 					hasyyerrored = 1;
11878d4ba808SJacques Vidrine 					break;
1188b528cefcSMark Murray 				}
1189b528cefcSMark Murray 				state = p->state;
1190b528cefcSMark Murray 				yylval.s = p->name;
1191b528cefcSMark Murray 				return (p->token);
1192b528cefcSMark Murray 			}
1193b528cefcSMark Murray 			state = CMD;
1194b528cefcSMark Murray 			break;
1195b528cefcSMark Murray 
1196b528cefcSMark Murray 		case OSTR:
1197b528cefcSMark Murray 			if (cbuf[cpos] == '\n') {
1198b528cefcSMark Murray 				state = CMD;
1199b528cefcSMark Murray 				return (CRLF);
1200b528cefcSMark Murray 			}
1201b528cefcSMark Murray 			/* FALLTHROUGH */
1202b528cefcSMark Murray 
1203b528cefcSMark Murray 		case STR1:
1204b528cefcSMark Murray 		case ZSTR1:
1205b528cefcSMark Murray 		dostr1:
1206b528cefcSMark Murray 			if (cbuf[cpos] == ' ') {
1207b528cefcSMark Murray 				cpos++;
1208b528cefcSMark Murray 				if(state == OSTR)
1209b528cefcSMark Murray 				    state = STR2;
1210b528cefcSMark Murray 				else
1211b528cefcSMark Murray 				    state++;
1212b528cefcSMark Murray 				return (SP);
1213b528cefcSMark Murray 			}
1214b528cefcSMark Murray 			break;
1215b528cefcSMark Murray 
1216b528cefcSMark Murray 		case ZSTR2:
1217b528cefcSMark Murray 			if (cbuf[cpos] == '\n') {
1218b528cefcSMark Murray 				state = CMD;
1219b528cefcSMark Murray 				return (CRLF);
1220b528cefcSMark Murray 			}
1221b528cefcSMark Murray 			/* FALLTHROUGH */
1222b528cefcSMark Murray 
1223b528cefcSMark Murray 		case STR2:
1224b528cefcSMark Murray 			cp = &cbuf[cpos];
1225b528cefcSMark Murray 			n = strlen(cp);
1226b528cefcSMark Murray 			cpos += n - 1;
1227b528cefcSMark Murray 			/*
1228b528cefcSMark Murray 			 * Make sure the string is nonempty and \n terminated.
1229b528cefcSMark Murray 			 */
1230b528cefcSMark Murray 			if (n > 1 && cbuf[cpos] == '\n') {
1231b528cefcSMark Murray 				cbuf[cpos] = '\0';
1232b528cefcSMark Murray 				yylval.s = copy(cp);
1233b528cefcSMark Murray 				cbuf[cpos] = '\n';
1234b528cefcSMark Murray 				state = ARGS;
1235b528cefcSMark Murray 				return (STRING);
1236b528cefcSMark Murray 			}
1237b528cefcSMark Murray 			break;
1238b528cefcSMark Murray 
1239b528cefcSMark Murray 		case NSTR:
1240b528cefcSMark Murray 			if (cbuf[cpos] == ' ') {
1241b528cefcSMark Murray 				cpos++;
1242b528cefcSMark Murray 				return (SP);
1243b528cefcSMark Murray 			}
12444137ff4cSJacques Vidrine 			if (isdigit((unsigned char)cbuf[cpos])) {
1245b528cefcSMark Murray 				cp = &cbuf[cpos];
12464137ff4cSJacques Vidrine 				while (isdigit((unsigned char)cbuf[++cpos]))
1247b528cefcSMark Murray 					;
1248b528cefcSMark Murray 				c = cbuf[cpos];
1249b528cefcSMark Murray 				cbuf[cpos] = '\0';
1250b528cefcSMark Murray 				yylval.i = atoi(cp);
1251b528cefcSMark Murray 				cbuf[cpos] = c;
1252b528cefcSMark Murray 				state = STR1;
1253b528cefcSMark Murray 				return (NUMBER);
1254b528cefcSMark Murray 			}
1255b528cefcSMark Murray 			state = STR1;
1256b528cefcSMark Murray 			goto dostr1;
1257b528cefcSMark Murray 
1258b528cefcSMark Murray 		case ARGS:
12594137ff4cSJacques Vidrine 			if (isdigit((unsigned char)cbuf[cpos])) {
1260b528cefcSMark Murray 				cp = &cbuf[cpos];
12614137ff4cSJacques Vidrine 				while (isdigit((unsigned char)cbuf[++cpos]))
1262b528cefcSMark Murray 					;
1263b528cefcSMark Murray 				c = cbuf[cpos];
1264b528cefcSMark Murray 				cbuf[cpos] = '\0';
1265b528cefcSMark Murray 				yylval.i = atoi(cp);
1266b528cefcSMark Murray 				cbuf[cpos] = c;
1267b528cefcSMark Murray 				return (NUMBER);
1268b528cefcSMark Murray 			}
1269b528cefcSMark Murray 			switch (cbuf[cpos++]) {
1270b528cefcSMark Murray 
1271b528cefcSMark Murray 			case '\n':
1272b528cefcSMark Murray 				state = CMD;
1273b528cefcSMark Murray 				return (CRLF);
1274b528cefcSMark Murray 
1275b528cefcSMark Murray 			case ' ':
1276b528cefcSMark Murray 				return (SP);
1277b528cefcSMark Murray 
1278b528cefcSMark Murray 			case ',':
1279b528cefcSMark Murray 				return (COMMA);
1280b528cefcSMark Murray 
1281b528cefcSMark Murray 			case 'A':
1282b528cefcSMark Murray 			case 'a':
1283b528cefcSMark Murray 				return (A);
1284b528cefcSMark Murray 
1285b528cefcSMark Murray 			case 'B':
1286b528cefcSMark Murray 			case 'b':
1287b528cefcSMark Murray 				return (B);
1288b528cefcSMark Murray 
1289b528cefcSMark Murray 			case 'C':
1290b528cefcSMark Murray 			case 'c':
1291b528cefcSMark Murray 				return (C);
1292b528cefcSMark Murray 
1293b528cefcSMark Murray 			case 'E':
1294b528cefcSMark Murray 			case 'e':
1295b528cefcSMark Murray 				return (E);
1296b528cefcSMark Murray 
1297b528cefcSMark Murray 			case 'F':
1298b528cefcSMark Murray 			case 'f':
1299b528cefcSMark Murray 				return (F);
1300b528cefcSMark Murray 
1301b528cefcSMark Murray 			case 'I':
1302b528cefcSMark Murray 			case 'i':
1303b528cefcSMark Murray 				return (I);
1304b528cefcSMark Murray 
1305b528cefcSMark Murray 			case 'L':
1306b528cefcSMark Murray 			case 'l':
1307b528cefcSMark Murray 				return (L);
1308b528cefcSMark Murray 
1309b528cefcSMark Murray 			case 'N':
1310b528cefcSMark Murray 			case 'n':
1311b528cefcSMark Murray 				return (N);
1312b528cefcSMark Murray 
1313b528cefcSMark Murray 			case 'P':
1314b528cefcSMark Murray 			case 'p':
1315b528cefcSMark Murray 				return (P);
1316b528cefcSMark Murray 
1317b528cefcSMark Murray 			case 'R':
1318b528cefcSMark Murray 			case 'r':
1319b528cefcSMark Murray 				return (R);
1320b528cefcSMark Murray 
1321b528cefcSMark Murray 			case 'S':
1322b528cefcSMark Murray 			case 's':
1323b528cefcSMark Murray 				return (S);
1324b528cefcSMark Murray 
1325b528cefcSMark Murray 			case 'T':
1326b528cefcSMark Murray 			case 't':
1327b528cefcSMark Murray 				return (T);
1328b528cefcSMark Murray 
1329b528cefcSMark Murray 			}
1330b528cefcSMark Murray 			break;
1331b528cefcSMark Murray 
1332b528cefcSMark Murray 		default:
1333b528cefcSMark Murray 			fatal("Unknown state in scanner.");
1334b528cefcSMark Murray 		}
13358d4ba808SJacques Vidrine 		yyerror(NULL);
1336b528cefcSMark Murray 		state = CMD;
13378d4ba808SJacques Vidrine 		return (0);
1338b528cefcSMark Murray 	}
1339b528cefcSMark Murray }
1340b528cefcSMark Murray 
13418d4ba808SJacques Vidrine /* ARGSUSED */
13428d4ba808SJacques Vidrine void
13438d4ba808SJacques Vidrine yyerror(char *s)
13448d4ba808SJacques Vidrine {
13458d4ba808SJacques Vidrine 	char *cp;
13468d4ba808SJacques Vidrine 
13478d4ba808SJacques Vidrine 	if (hasyyerrored)
13488d4ba808SJacques Vidrine 	    return;
13498d4ba808SJacques Vidrine 
13508d4ba808SJacques Vidrine 	if ((cp = strchr(cbuf,'\n')))
13518d4ba808SJacques Vidrine 		*cp = '\0';
13528d4ba808SJacques Vidrine 	reply(500, "'%s': command not understood.", cbuf);
13538d4ba808SJacques Vidrine 	hasyyerrored = 1;
13548d4ba808SJacques Vidrine }
13558d4ba808SJacques Vidrine 
1356b528cefcSMark Murray static char *
1357b528cefcSMark Murray copy(char *s)
1358b528cefcSMark Murray {
1359b528cefcSMark Murray 	char *p;
1360b528cefcSMark Murray 
1361b528cefcSMark Murray 	p = strdup(s);
1362b528cefcSMark Murray 	if (p == NULL)
1363b528cefcSMark Murray 		fatal("Ran out of memory.");
1364b528cefcSMark Murray 	return p;
1365b528cefcSMark Murray }
1366b528cefcSMark Murray 
1367b528cefcSMark Murray static void
1368b528cefcSMark Murray help(struct tab *ctab, char *s)
1369b528cefcSMark Murray {
1370b528cefcSMark Murray 	struct tab *c;
1371b528cefcSMark Murray 	int width, NCMDS;
1372c19800e8SDoug Rabson 	char *t;
1373b528cefcSMark Murray 	char buf[1024];
1374b528cefcSMark Murray 
1375b528cefcSMark Murray 	if (ctab == sitetab)
1376c19800e8SDoug Rabson 		t = "SITE ";
1377b528cefcSMark Murray 	else
1378c19800e8SDoug Rabson 		t = "";
1379b528cefcSMark Murray 	width = 0, NCMDS = 0;
1380b528cefcSMark Murray 	for (c = ctab; c->name != NULL; c++) {
1381b528cefcSMark Murray 		int len = strlen(c->name);
1382b528cefcSMark Murray 
1383b528cefcSMark Murray 		if (len > width)
1384b528cefcSMark Murray 			width = len;
1385b528cefcSMark Murray 		NCMDS++;
1386b528cefcSMark Murray 	}
1387b528cefcSMark Murray 	width = (width + 8) &~ 7;
1388b528cefcSMark Murray 	if (s == 0) {
1389b528cefcSMark Murray 		int i, j, w;
1390b528cefcSMark Murray 		int columns, lines;
1391b528cefcSMark Murray 
1392b528cefcSMark Murray 		lreply(214, "The following %scommands are recognized %s.",
1393c19800e8SDoug Rabson 		    t, "(* =>'s unimplemented)");
1394b528cefcSMark Murray 		columns = 76 / width;
1395b528cefcSMark Murray 		if (columns == 0)
1396b528cefcSMark Murray 			columns = 1;
1397b528cefcSMark Murray 		lines = (NCMDS + columns - 1) / columns;
1398b528cefcSMark Murray 		for (i = 0; i < lines; i++) {
1399b528cefcSMark Murray 		    strlcpy (buf, "   ", sizeof(buf));
1400b528cefcSMark Murray 		    for (j = 0; j < columns; j++) {
1401b528cefcSMark Murray 			c = ctab + j * lines + i;
1402b528cefcSMark Murray 			snprintf (buf + strlen(buf),
1403b528cefcSMark Murray 				  sizeof(buf) - strlen(buf),
1404b528cefcSMark Murray 				  "%s%c",
1405b528cefcSMark Murray 				  c->name,
1406b528cefcSMark Murray 				  c->implemented ? ' ' : '*');
1407b528cefcSMark Murray 			if (c + lines >= &ctab[NCMDS])
1408b528cefcSMark Murray 			    break;
1409b528cefcSMark Murray 			w = strlen(c->name) + 1;
1410b528cefcSMark Murray 			while (w < width) {
1411b528cefcSMark Murray 			    strlcat (buf,
1412b528cefcSMark Murray 					     " ",
1413b528cefcSMark Murray 					     sizeof(buf));
1414b528cefcSMark Murray 			    w++;
1415b528cefcSMark Murray 			}
1416b528cefcSMark Murray 		    }
1417b528cefcSMark Murray 		    lreply(214, "%s", buf);
1418b528cefcSMark Murray 		}
1419b528cefcSMark Murray 		reply(214, "Direct comments to kth-krb-bugs@pdc.kth.se");
1420b528cefcSMark Murray 		return;
1421b528cefcSMark Murray 	}
1422b528cefcSMark Murray 	strupr(s);
1423b528cefcSMark Murray 	c = lookup(ctab, s);
1424b528cefcSMark Murray 	if (c == (struct tab *)0) {
1425b528cefcSMark Murray 		reply(502, "Unknown command %s.", s);
1426b528cefcSMark Murray 		return;
1427b528cefcSMark Murray 	}
1428b528cefcSMark Murray 	if (c->implemented)
1429c19800e8SDoug Rabson 		reply(214, "Syntax: %s%s %s", t, c->name, c->help);
1430b528cefcSMark Murray 	else
1431c19800e8SDoug Rabson 		reply(214, "%s%-*s\t%s; unimplemented.", t, width,
1432b528cefcSMark Murray 		    c->name, c->help);
1433b528cefcSMark Murray }
1434b528cefcSMark Murray 
1435b528cefcSMark Murray static void
1436b528cefcSMark Murray sizecmd(char *filename)
1437b528cefcSMark Murray {
1438b528cefcSMark Murray 	switch (type) {
1439b528cefcSMark Murray 	case TYPE_L:
1440b528cefcSMark Murray 	case TYPE_I: {
1441b528cefcSMark Murray 		struct stat stbuf;
1442b528cefcSMark Murray 		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1443b528cefcSMark Murray 			reply(550, "%s: not a plain file.", filename);
1444b528cefcSMark Murray 		else
1445b528cefcSMark Murray 			reply(213, "%lu", (unsigned long)stbuf.st_size);
1446b528cefcSMark Murray 		break;
1447b528cefcSMark Murray 	}
1448b528cefcSMark Murray 	case TYPE_A: {
1449b528cefcSMark Murray 		FILE *fin;
1450b528cefcSMark Murray 		int c;
1451b528cefcSMark Murray 		size_t count;
1452b528cefcSMark Murray 		struct stat stbuf;
1453b528cefcSMark Murray 		fin = fopen(filename, "r");
1454b528cefcSMark Murray 		if (fin == NULL) {
1455b528cefcSMark Murray 			perror_reply(550, filename);
1456b528cefcSMark Murray 			return;
1457b528cefcSMark Murray 		}
1458b528cefcSMark Murray 		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1459b528cefcSMark Murray 			reply(550, "%s: not a plain file.", filename);
1460b528cefcSMark Murray 			fclose(fin);
1461b528cefcSMark Murray 			return;
1462b528cefcSMark Murray 		}
1463b528cefcSMark Murray 
1464b528cefcSMark Murray 		count = 0;
1465b528cefcSMark Murray 		while((c=getc(fin)) != EOF) {
1466b528cefcSMark Murray 			if (c == '\n')	/* will get expanded to \r\n */
1467b528cefcSMark Murray 				count++;
1468b528cefcSMark Murray 			count++;
1469b528cefcSMark Murray 		}
1470b528cefcSMark Murray 		fclose(fin);
1471b528cefcSMark Murray 
1472b528cefcSMark Murray 		reply(213, "%lu", (unsigned long)count);
1473b528cefcSMark Murray 		break;
1474b528cefcSMark Murray 	}
1475b528cefcSMark Murray 	default:
1476b528cefcSMark Murray 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1477b528cefcSMark Murray 	}
1478b528cefcSMark Murray }
1479