xref: /openbsd-src/libexec/ftpd/ftpcmd.y (revision 73fe6daa500a9a42075e60ad1f2083099184ce22)
1*73fe6daaSflorian /*	$OpenBSD: ftpcmd.y,v 1.75 2024/04/28 16:42:53 florian Exp $	*/
269a3ff95Sderaadt /*	$NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1985, 1988, 1993, 1994
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
16e33d3bd3Smillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  *
32df930be7Sderaadt  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
33df930be7Sderaadt  */
34df930be7Sderaadt 
35df930be7Sderaadt /*
36df930be7Sderaadt  * Grammar for FTP commands.
37df930be7Sderaadt  * See RFC 959.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt %{
41df930be7Sderaadt 
42b9fc9a72Sderaadt #include <sys/types.h>
43df930be7Sderaadt #include <sys/socket.h>
44df930be7Sderaadt #include <sys/stat.h>
45df930be7Sderaadt 
46df930be7Sderaadt #include <netinet/in.h>
47df930be7Sderaadt #include <arpa/ftp.h>
48df930be7Sderaadt 
49df930be7Sderaadt #include <ctype.h>
50df930be7Sderaadt #include <errno.h>
51df930be7Sderaadt #include <glob.h>
52df930be7Sderaadt #include <pwd.h>
53df930be7Sderaadt #include <signal.h>
54df930be7Sderaadt #include <stdio.h>
55df930be7Sderaadt #include <stdlib.h>
56df930be7Sderaadt #include <string.h>
57df930be7Sderaadt #include <syslog.h>
58df930be7Sderaadt #include <time.h>
59df930be7Sderaadt #include <unistd.h>
60b1750805Sitojun #include <netdb.h>
6109a2692aSmillert #include <limits.h>
62df930be7Sderaadt 
63b96c0bc5Shenning #include "monitor.h"
6492f68775Sragge #include "extern.h"
65df930be7Sderaadt 
66b1750805Sitojun extern	union sockunion data_dest;
67df930be7Sderaadt extern	int logged_in;
68df930be7Sderaadt extern	struct passwd *pw;
69df930be7Sderaadt extern	int guest;
70df930be7Sderaadt extern	int logging;
71df930be7Sderaadt extern	int type;
72df930be7Sderaadt extern	int form;
73df930be7Sderaadt extern	int debug;
74df930be7Sderaadt extern	int timeout;
75df930be7Sderaadt extern	int maxtimeout;
76df930be7Sderaadt extern  int pdata;
77df930be7Sderaadt extern	char hostname[], remotehost[];
78df930be7Sderaadt extern	char proctitle[];
79df930be7Sderaadt extern	int usedefault;
80df930be7Sderaadt extern  int transflag;
81df930be7Sderaadt extern  char tmpline[];
82fc12f3acSderaadt extern	int portcheck;
83b1750805Sitojun extern	union sockunion his_addr;
84b3f5c309Sderaadt extern	int umaskchange;
85df930be7Sderaadt 
86df930be7Sderaadt off_t	restart_point;
87df930be7Sderaadt 
88df930be7Sderaadt static	int cmd_type;
89df930be7Sderaadt static	int cmd_form;
90df930be7Sderaadt static	int cmd_bytesz;
912378d5feSmillert static	int state;
92b96c0bc5Shenning static	int quit;
93df930be7Sderaadt char	cbuf[512];
94df930be7Sderaadt char	*fromname;
95df930be7Sderaadt 
96df930be7Sderaadt %}
97df930be7Sderaadt 
98df930be7Sderaadt %union {
99df930be7Sderaadt 	int	i;
10009a2692aSmillert 	off_t	o;
101df930be7Sderaadt 	char   *s;
102df930be7Sderaadt }
103df930be7Sderaadt 
104df930be7Sderaadt %token
105df930be7Sderaadt 	A	B	C	E	F	I
106df930be7Sderaadt 	L	N	P	R	S	T
107df930be7Sderaadt 
108b77009c8Smpech 	SP	CRLF	COMMA	ALL
109df930be7Sderaadt 
110df930be7Sderaadt 	USER	PASS	ACCT	REIN	QUIT	PORT
111df930be7Sderaadt 	PASV	TYPE	STRU	MODE	RETR	STOR
112df930be7Sderaadt 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
113df930be7Sderaadt 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
114df930be7Sderaadt 	ABOR	DELE	CWD	LIST	NLST	SITE
115df930be7Sderaadt 	STAT	HELP	NOOP	MKD	RMD	PWD
116df930be7Sderaadt 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
117df930be7Sderaadt 
118b1750805Sitojun 	LPRT	LPSV	EPRT	EPSV
119b1750805Sitojun 
120df930be7Sderaadt 	UMASK	IDLE	CHMOD
121df930be7Sderaadt 
122df930be7Sderaadt 	LEXERR
123df930be7Sderaadt 
124df930be7Sderaadt %token	<s> STRING
125df930be7Sderaadt %token	<i> NUMBER
12609a2692aSmillert %token	<o> BIGNUM
127df930be7Sderaadt 
12841fb778fSitojun %type	<i> check_login check_login_epsvall octal_number byte_size
129df930be7Sderaadt %type	<i> struct_code mode_code type_code form_code
130b1750805Sitojun %type	<i> host_port host_long_port4 host_long_port6
13109a2692aSmillert %type	<o> file_size
13209a2692aSmillert %type	<s> pathstring pathname password username
133df930be7Sderaadt 
134df930be7Sderaadt %start	cmd_list
135df930be7Sderaadt 
136df930be7Sderaadt %%
137df930be7Sderaadt 
138df930be7Sderaadt cmd_list
139df930be7Sderaadt 	: /* empty */
140df930be7Sderaadt 	| cmd_list cmd
141df930be7Sderaadt 		{
142f59925a5Smillert 			if (fromname) {
143f59925a5Smillert 				free(fromname);
144f59925a5Smillert 				fromname = NULL;
145f59925a5Smillert 			}
146a20a5177Sguenther 			restart_point = 0;
147df930be7Sderaadt 		}
148df930be7Sderaadt 	| cmd_list rcmd
149df930be7Sderaadt 	;
150df930be7Sderaadt 
151df930be7Sderaadt cmd
152df930be7Sderaadt 	: USER SP username CRLF
153df930be7Sderaadt 		{
154b96c0bc5Shenning 			monitor_user($3);
155df930be7Sderaadt 			free($3);
156df930be7Sderaadt 		}
157df930be7Sderaadt 	| PASS SP password CRLF
158df930be7Sderaadt 		{
159b96c0bc5Shenning 			quit = monitor_pass($3);
16072547754Sderaadt 			explicit_bzero($3, strlen($3));
161df930be7Sderaadt 			free($3);
162b96c0bc5Shenning 
163b96c0bc5Shenning 			/* Terminate unprivileged pre-auth slave */
164b96c0bc5Shenning 			if (quit)
1653e113d76Smoritz 				_exit(0);
166df930be7Sderaadt 		}
16741fb778fSitojun 	| PORT check_login_epsvall SP host_port CRLF
168df930be7Sderaadt 		{
16941fb778fSitojun 			if ($2) {
17041fb778fSitojun 				if ($4) {
171e7d279e1Sderaadt 					usedefault = 1;
172e7d279e1Sderaadt 					reply(500,
173e7d279e1Sderaadt 					    "Illegal PORT rejected (range errors).");
174e7d279e1Sderaadt 				} else if (portcheck &&
175b1750805Sitojun 				    ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) {
176e7d279e1Sderaadt 					usedefault = 1;
177e7d279e1Sderaadt 					reply(500,
178e7d279e1Sderaadt 					    "Illegal PORT rejected (reserved port).");
179e7d279e1Sderaadt 				} else if (portcheck &&
180b1750805Sitojun 				    memcmp(&data_dest.su_sin.sin_addr,
181b1750805Sitojun 				    &his_addr.su_sin.sin_addr,
182b1750805Sitojun 				    sizeof data_dest.su_sin.sin_addr)) {
183fc12f3acSderaadt 					usedefault = 1;
184e7d279e1Sderaadt 					reply(500,
185e7d279e1Sderaadt 					    "Illegal PORT rejected (address wrong).");
186fc12f3acSderaadt 				} else {
187df930be7Sderaadt 					usedefault = 0;
188df930be7Sderaadt 					if (pdata >= 0) {
189df930be7Sderaadt 						(void) close(pdata);
190df930be7Sderaadt 						pdata = -1;
191df930be7Sderaadt 					}
192df930be7Sderaadt 					reply(200, "PORT command successful.");
193df930be7Sderaadt 				}
19424713697Sbitblt 			}
195fc12f3acSderaadt 		}
19641fb778fSitojun 	| LPRT check_login_epsvall SP host_long_port4 CRLF
197b1750805Sitojun 		{
19841fb778fSitojun 			if ($2) {
199b1750805Sitojun 				/* reject invalid host_long_port4 */
20041fb778fSitojun 				if ($4) {
201e14c8eb3Sitojun 					reply(500,
202e14c8eb3Sitojun 					    "Illegal LPRT command rejected");
203b1750805Sitojun 					usedefault = 1;
204b1750805Sitojun 				} else {
205b1750805Sitojun 					usedefault = 0;
206b1750805Sitojun 					if (pdata >= 0) {
207b1750805Sitojun 						(void) close(pdata);
208b1750805Sitojun 						pdata = -1;
209b1750805Sitojun 					}
210b1750805Sitojun 					reply(200, "LPRT command successful.");
211b1750805Sitojun 				}
212b1750805Sitojun 			}
213e14c8eb3Sitojun 		}
214b1750805Sitojun 
21541fb778fSitojun 	| LPRT check_login_epsvall SP host_long_port6 CRLF
216b1750805Sitojun 		{
21741fb778fSitojun 			if ($2) {
218b1750805Sitojun 				/* reject invalid host_long_port6 */
21941fb778fSitojun 				if ($4) {
220e14c8eb3Sitojun 					reply(500,
221e14c8eb3Sitojun 					    "Illegal LPRT command rejected");
222b1750805Sitojun 					usedefault = 1;
223b1750805Sitojun 				} else {
224b1750805Sitojun 					usedefault = 0;
225b1750805Sitojun 					if (pdata >= 0) {
226b1750805Sitojun 						(void) close(pdata);
227b1750805Sitojun 						pdata = -1;
228b1750805Sitojun 					}
229b1750805Sitojun 					reply(200, "LPRT command successful.");
230b1750805Sitojun 				}
231b1750805Sitojun 			}
232e14c8eb3Sitojun 		}
233b1750805Sitojun 
23441fb778fSitojun 	| EPRT check_login_epsvall SP STRING CRLF
235b1750805Sitojun 		{
236d94808dfSitojun 			if ($2)
237d94808dfSitojun 				extended_port($4);
23839ff527aSmillert 			free($4);
239b1750805Sitojun 		}
240b1750805Sitojun 
24141fb778fSitojun 	| PASV check_login_epsvall CRLF
242df930be7Sderaadt 		{
24341fb778fSitojun 			if ($2)
244df930be7Sderaadt 				passive();
245df930be7Sderaadt 		}
24641fb778fSitojun 	| LPSV check_login_epsvall CRLF
247b1750805Sitojun 		{
24841fb778fSitojun 			if ($2)
249b1750805Sitojun 				long_passive("LPSV", PF_UNSPEC);
250b1750805Sitojun 		}
251e14c8eb3Sitojun 	| EPSV check_login SP NUMBER CRLF
252b1750805Sitojun 		{
253d94808dfSitojun 			if ($2)
254d94808dfSitojun 				long_passive("EPSV", epsvproto2af($4));
255e14c8eb3Sitojun 		}
256e14c8eb3Sitojun 	| EPSV check_login SP ALL CRLF
257b1750805Sitojun 		{
258e14c8eb3Sitojun 			if ($2) {
259b1750805Sitojun 				reply(200, "EPSV ALL command successful.");
260b1750805Sitojun 				epsvall++;
261b1750805Sitojun 			}
262b1750805Sitojun 		}
263e14c8eb3Sitojun 	| EPSV check_login CRLF
264b1750805Sitojun 		{
265e14c8eb3Sitojun 			if ($2)
266b1750805Sitojun 				long_passive("EPSV", PF_UNSPEC);
267b1750805Sitojun 		}
26824713697Sbitblt 	| TYPE check_login SP type_code CRLF
269df930be7Sderaadt 		{
27024713697Sbitblt 			if ($2) {
271df930be7Sderaadt 				switch (cmd_type) {
272df930be7Sderaadt 
273df930be7Sderaadt 				case TYPE_A:
274df930be7Sderaadt 					if (cmd_form == FORM_N) {
275df930be7Sderaadt 						reply(200, "Type set to A.");
276df930be7Sderaadt 						type = cmd_type;
277df930be7Sderaadt 						form = cmd_form;
278df930be7Sderaadt 					} else
279df930be7Sderaadt 						reply(504, "Form must be N.");
280df930be7Sderaadt 					break;
281df930be7Sderaadt 
282df930be7Sderaadt 				case TYPE_E:
283df930be7Sderaadt 					reply(504, "Type E not implemented.");
284df930be7Sderaadt 					break;
285df930be7Sderaadt 
286df930be7Sderaadt 				case TYPE_I:
287df930be7Sderaadt 					reply(200, "Type set to I.");
288df930be7Sderaadt 					type = cmd_type;
289df930be7Sderaadt 					break;
290df930be7Sderaadt 
291df930be7Sderaadt 				case TYPE_L:
292df930be7Sderaadt 					if (cmd_bytesz == 8) {
293df930be7Sderaadt 						reply(200,
294df930be7Sderaadt 						    "Type set to L (byte size 8).");
295df930be7Sderaadt 						    type = cmd_type;
296df930be7Sderaadt 					} else
297df930be7Sderaadt 						reply(504, "Byte size must be 8.");
29824713697Sbitblt 
299df930be7Sderaadt 				}
300df930be7Sderaadt 			}
30124713697Sbitblt 		}
30224713697Sbitblt 	| STRU check_login SP struct_code CRLF
303df930be7Sderaadt 		{
30424713697Sbitblt 			if ($2) {
30524713697Sbitblt 				switch ($4) {
306df930be7Sderaadt 
307df930be7Sderaadt 				case STRU_F:
308df930be7Sderaadt 					reply(200, "STRU F ok.");
309df930be7Sderaadt 					break;
310df930be7Sderaadt 
311df930be7Sderaadt 				default:
312df930be7Sderaadt 					reply(504, "Unimplemented STRU type.");
313df930be7Sderaadt 				}
314df930be7Sderaadt 			}
31524713697Sbitblt 		}
31624713697Sbitblt 	| MODE check_login SP mode_code CRLF
317df930be7Sderaadt 		{
31824713697Sbitblt 			if ($2) {
31924713697Sbitblt 				switch ($4) {
320df930be7Sderaadt 
321df930be7Sderaadt 				case MODE_S:
322df930be7Sderaadt 					reply(200, "MODE S ok.");
323df930be7Sderaadt 					break;
324df930be7Sderaadt 
325df930be7Sderaadt 				default:
326df930be7Sderaadt 					reply(502, "Unimplemented MODE type.");
327df930be7Sderaadt 				}
328df930be7Sderaadt 			}
32924713697Sbitblt 		}
33024713697Sbitblt 	| ALLO check_login SP NUMBER CRLF
331df930be7Sderaadt 		{
33224713697Sbitblt 			if ($2) {
333df930be7Sderaadt 				reply(202, "ALLO command ignored.");
334df930be7Sderaadt 			}
33524713697Sbitblt 		}
33624713697Sbitblt 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
337df930be7Sderaadt 		{
33824713697Sbitblt 			if ($2) {
339df930be7Sderaadt 				reply(202, "ALLO command ignored.");
340df930be7Sderaadt 			}
34124713697Sbitblt 		}
342df930be7Sderaadt 	| RETR check_login SP pathname CRLF
343df930be7Sderaadt 		{
344df930be7Sderaadt 			if ($2 && $4 != NULL)
3457a7d71fbStedu 				retrieve(RET_FILE, $4);
346df930be7Sderaadt 			if ($4 != NULL)
347df930be7Sderaadt 				free($4);
348df930be7Sderaadt 		}
349df930be7Sderaadt 	| STOR check_login SP pathname CRLF
350df930be7Sderaadt 		{
351df930be7Sderaadt 			if ($2 && $4 != NULL)
352df930be7Sderaadt 				store($4, "w", 0);
353df930be7Sderaadt 			if ($4 != NULL)
354df930be7Sderaadt 				free($4);
355df930be7Sderaadt 		}
356df930be7Sderaadt 	| APPE check_login SP pathname CRLF
357df930be7Sderaadt 		{
358df930be7Sderaadt 			if ($2 && $4 != NULL)
359df930be7Sderaadt 				store($4, "a", 0);
360df930be7Sderaadt 			if ($4 != NULL)
361df930be7Sderaadt 				free($4);
362df930be7Sderaadt 		}
363df930be7Sderaadt 	| NLST check_login CRLF
364df930be7Sderaadt 		{
365df930be7Sderaadt 			if ($2)
366df930be7Sderaadt 				send_file_list(".");
367df930be7Sderaadt 		}
368df930be7Sderaadt 	| NLST check_login SP STRING CRLF
369df930be7Sderaadt 		{
370df930be7Sderaadt 			if ($2 && $4 != NULL)
371df930be7Sderaadt 				send_file_list($4);
372df930be7Sderaadt 			free($4);
373df930be7Sderaadt 		}
374df930be7Sderaadt 	| LIST check_login CRLF
375df930be7Sderaadt 		{
376df930be7Sderaadt 			if ($2)
377aed6457bSmillert 				retrieve(RET_LIST, ".");
378df930be7Sderaadt 		}
379df930be7Sderaadt 	| LIST check_login SP pathname CRLF
380df930be7Sderaadt 		{
381df930be7Sderaadt 			if ($2 && $4 != NULL)
3827a7d71fbStedu 				retrieve(RET_LIST, $4);
383df930be7Sderaadt 			if ($4 != NULL)
384df930be7Sderaadt 				free($4);
385df930be7Sderaadt 		}
386df930be7Sderaadt 	| STAT check_login SP pathname CRLF
387df930be7Sderaadt 		{
388df930be7Sderaadt 			if ($2 && $4 != NULL)
389df930be7Sderaadt 				statfilecmd($4);
390df930be7Sderaadt 			if ($4 != NULL)
391df930be7Sderaadt 				free($4);
392df930be7Sderaadt 		}
39324713697Sbitblt 	| STAT check_login CRLF
394df930be7Sderaadt 		{
39524713697Sbitblt 			if ($2)
396df930be7Sderaadt 				statcmd();
397df930be7Sderaadt 		}
398df930be7Sderaadt 	| DELE check_login SP pathname CRLF
399df930be7Sderaadt 		{
400df930be7Sderaadt 			if ($2 && $4 != NULL)
401df930be7Sderaadt 				delete($4);
402df930be7Sderaadt 			if ($4 != NULL)
403df930be7Sderaadt 				free($4);
404df930be7Sderaadt 		}
40524713697Sbitblt 	| RNTO check_login SP pathname CRLF
406df930be7Sderaadt 		{
407b6679d89Smpech 			if ($2 && $4 != NULL) {
408df930be7Sderaadt 				if (fromname) {
40924713697Sbitblt 					renamecmd(fromname, $4);
410df930be7Sderaadt 					free(fromname);
411a14f8237Sericj 					fromname = NULL;
412df930be7Sderaadt 				} else {
41324713697Sbitblt 					reply(503,
41424713697Sbitblt 					  "Bad sequence of commands.");
415df930be7Sderaadt 				}
416df930be7Sderaadt 			}
417bf165b6aSmpech 			if ($4 != NULL)
41824713697Sbitblt 				free($4);
41924713697Sbitblt 		}
42024713697Sbitblt 	| ABOR check_login CRLF
421df930be7Sderaadt 		{
42224713697Sbitblt 			if ($2)
423df930be7Sderaadt 				reply(225, "ABOR command successful.");
424df930be7Sderaadt 		}
425df930be7Sderaadt 	| CWD check_login CRLF
426df930be7Sderaadt 		{
427df930be7Sderaadt 			if ($2)
428df930be7Sderaadt 				cwd(pw->pw_dir);
429df930be7Sderaadt 		}
430df930be7Sderaadt 	| CWD check_login SP pathname CRLF
431df930be7Sderaadt 		{
432df930be7Sderaadt 			if ($2 && $4 != NULL)
433df930be7Sderaadt 				cwd($4);
434df930be7Sderaadt 			if ($4 != NULL)
435df930be7Sderaadt 				free($4);
436df930be7Sderaadt 		}
437df930be7Sderaadt 	| HELP CRLF
438df930be7Sderaadt 		{
439a14f8237Sericj 			help(cmdtab, NULL);
440df930be7Sderaadt 		}
441df930be7Sderaadt 	| HELP SP STRING CRLF
442df930be7Sderaadt 		{
443df930be7Sderaadt 			char *cp = $3;
444df930be7Sderaadt 
445df930be7Sderaadt 			if (strncasecmp(cp, "SITE", 4) == 0) {
446df930be7Sderaadt 				cp = $3 + 4;
447df930be7Sderaadt 				if (*cp == ' ')
448df930be7Sderaadt 					cp++;
449df930be7Sderaadt 				if (*cp)
450df930be7Sderaadt 					help(sitetab, cp);
451df930be7Sderaadt 				else
452a14f8237Sericj 					help(sitetab, NULL);
453df930be7Sderaadt 			} else
454df930be7Sderaadt 				help(cmdtab, $3);
45590d60234Sbitblt 			free ($3);
456df930be7Sderaadt 		}
457df930be7Sderaadt 	| NOOP CRLF
458df930be7Sderaadt 		{
459df930be7Sderaadt 			reply(200, "NOOP command successful.");
460df930be7Sderaadt 		}
461df930be7Sderaadt 	| MKD check_login SP pathname CRLF
462df930be7Sderaadt 		{
463df930be7Sderaadt 			if ($2 && $4 != NULL)
464df930be7Sderaadt 				makedir($4);
465df930be7Sderaadt 			if ($4 != NULL)
466df930be7Sderaadt 				free($4);
467df930be7Sderaadt 		}
468df930be7Sderaadt 	| RMD check_login SP pathname CRLF
469df930be7Sderaadt 		{
470df930be7Sderaadt 			if ($2 && $4 != NULL)
471df930be7Sderaadt 				removedir($4);
472df930be7Sderaadt 			if ($4 != NULL)
473df930be7Sderaadt 				free($4);
474df930be7Sderaadt 		}
475df930be7Sderaadt 	| PWD check_login CRLF
476df930be7Sderaadt 		{
477df930be7Sderaadt 			if ($2)
478df930be7Sderaadt 				pwd();
479df930be7Sderaadt 		}
480df930be7Sderaadt 	| CDUP check_login CRLF
481df930be7Sderaadt 		{
482df930be7Sderaadt 			if ($2)
483df930be7Sderaadt 				cwd("..");
484df930be7Sderaadt 		}
485df930be7Sderaadt 	| SITE SP HELP CRLF
486df930be7Sderaadt 		{
487a14f8237Sericj 			help(sitetab, NULL);
488df930be7Sderaadt 		}
489df930be7Sderaadt 	| SITE SP HELP SP STRING CRLF
490df930be7Sderaadt 		{
491df930be7Sderaadt 			help(sitetab, $5);
49290d60234Sbitblt 			free ($5);
493df930be7Sderaadt 		}
494df930be7Sderaadt 	| SITE SP UMASK check_login CRLF
495df930be7Sderaadt 		{
49695cc8c4aSderaadt 			mode_t oldmask;
497df930be7Sderaadt 
498df930be7Sderaadt 			if ($4) {
499df930be7Sderaadt 				oldmask = umask(0);
500df930be7Sderaadt 				(void) umask(oldmask);
501df930be7Sderaadt 				reply(200, "Current UMASK is %03o", oldmask);
502df930be7Sderaadt 			}
503df930be7Sderaadt 		}
504df930be7Sderaadt 	| SITE SP UMASK check_login SP octal_number CRLF
505df930be7Sderaadt 		{
50695cc8c4aSderaadt 			mode_t oldmask;
507df930be7Sderaadt 
508df930be7Sderaadt 			if ($4) {
509df930be7Sderaadt 				if (($6 == -1) || ($6 > 0777)) {
510df930be7Sderaadt 					reply(501, "Bad UMASK value");
511b3f5c309Sderaadt 				} else if (!umaskchange) {
512b3f5c309Sderaadt 					reply(550,
513b3f5c309Sderaadt 					    "No permission to change umask.");
514df930be7Sderaadt 				} else {
515df930be7Sderaadt 					oldmask = umask($6);
516df930be7Sderaadt 					reply(200,
517df930be7Sderaadt 					    "UMASK set to %03o (was %03o)",
518df930be7Sderaadt 					    $6, oldmask);
519df930be7Sderaadt 				}
520df930be7Sderaadt 			}
521df930be7Sderaadt 		}
522df930be7Sderaadt 	| SITE SP CHMOD check_login SP octal_number SP pathname CRLF
523df930be7Sderaadt 		{
524df930be7Sderaadt 			if ($4 && ($8 != NULL)) {
525e2b3faedSmpech 				if (($6 == -1) || ($6 > 0777))
526df930be7Sderaadt 					reply(501,
527b3f5c309Sderaadt 					    "CHMOD: Mode value must be between "
528b3f5c309Sderaadt 					    "0 and 0777");
529b3f5c309Sderaadt 				else if (!umaskchange)
530b3f5c309Sderaadt 					reply(550,
531b3f5c309Sderaadt 					    "No permission to change mode of %s.",
532b3f5c309Sderaadt 					    $8);
533df69c215Sderaadt 				else if (chmod($8, $6) == -1)
534df930be7Sderaadt 					perror_reply(550, $8);
535df930be7Sderaadt 				else
536b3f5c309Sderaadt 					reply(200,
537b3f5c309Sderaadt 					    "CHMOD command successful.");
538df930be7Sderaadt 			}
539df930be7Sderaadt 			if ($8 != NULL)
540df930be7Sderaadt 				free($8);
541df930be7Sderaadt 		}
54224713697Sbitblt 	| SITE SP check_login IDLE CRLF
543df930be7Sderaadt 		{
54424713697Sbitblt 			if ($3)
545df930be7Sderaadt 				reply(200,
5465c7d4e7cSderaadt 				    "Current IDLE time limit is %d "
5475c7d4e7cSderaadt 				    "seconds; max %d",
548df930be7Sderaadt 				    timeout, maxtimeout);
549df930be7Sderaadt 		}
55040916ffbSbitblt 	| SITE SP check_login IDLE SP NUMBER CRLF
551df930be7Sderaadt 		{
55240916ffbSbitblt 			if ($3) {
55324713697Sbitblt 				if ($6 < 30 || $6 > maxtimeout) {
554df930be7Sderaadt 					reply(501,
555b3f5c309Sderaadt 					    "Maximum IDLE time must be between "
556b3f5c309Sderaadt 					    "30 and %d seconds",
557df930be7Sderaadt 					    maxtimeout);
558df930be7Sderaadt 				} else {
55924713697Sbitblt 					timeout = $6;
560df930be7Sderaadt 					(void) alarm((unsigned) timeout);
561df930be7Sderaadt 					reply(200,
562df930be7Sderaadt 					    "Maximum IDLE time set to %d seconds",
563df930be7Sderaadt 					    timeout);
564df930be7Sderaadt 				}
565df930be7Sderaadt 			}
56624713697Sbitblt 		}
567df930be7Sderaadt 	| STOU check_login SP pathname CRLF
568df930be7Sderaadt 		{
569df930be7Sderaadt 			if ($2 && $4 != NULL)
570df930be7Sderaadt 				store($4, "w", 1);
571df930be7Sderaadt 			if ($4 != NULL)
572df930be7Sderaadt 				free($4);
573df930be7Sderaadt 		}
57424713697Sbitblt 	| SYST check_login CRLF
575df930be7Sderaadt 		{
57624713697Sbitblt 			if ($2)
57708cb63c2Stedu 			reply(215, "UNIX Type: L8");
578df930be7Sderaadt 		}
579df930be7Sderaadt 
580df930be7Sderaadt 		/*
581df930be7Sderaadt 		 * SIZE is not in RFC959, but Postel has blessed it and
582df930be7Sderaadt 		 * it will be in the updated RFC.
583df930be7Sderaadt 		 *
584df930be7Sderaadt 		 * Return size of file in a format suitable for
585df930be7Sderaadt 		 * using with RESTART (we just count bytes).
586df930be7Sderaadt 		 */
587df930be7Sderaadt 	| SIZE check_login SP pathname CRLF
588df930be7Sderaadt 		{
589df930be7Sderaadt 			if ($2 && $4 != NULL)
590df930be7Sderaadt 				sizecmd($4);
591df930be7Sderaadt 			if ($4 != NULL)
592df930be7Sderaadt 				free($4);
593df930be7Sderaadt 		}
594df930be7Sderaadt 
595df930be7Sderaadt 		/*
596df930be7Sderaadt 		 * MDTM is not in RFC959, but Postel has blessed it and
597df930be7Sderaadt 		 * it will be in the updated RFC.
598df930be7Sderaadt 		 *
599df930be7Sderaadt 		 * Return modification time of file as an ISO 3307
600df930be7Sderaadt 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
601df930be7Sderaadt 		 * where xxx is the fractional second (of any precision,
602df930be7Sderaadt 		 * not necessarily 3 digits)
603df930be7Sderaadt 		 */
604df930be7Sderaadt 	| MDTM check_login SP pathname CRLF
605df930be7Sderaadt 		{
606df930be7Sderaadt 			if ($2 && $4 != NULL) {
607df930be7Sderaadt 				struct stat stbuf;
608df69c215Sderaadt 				if (stat($4, &stbuf) == -1)
609df930be7Sderaadt 					reply(550, "%s: %s",
610df930be7Sderaadt 					    $4, strerror(errno));
611df930be7Sderaadt 				else if (!S_ISREG(stbuf.st_mode)) {
612df930be7Sderaadt 					reply(550, "%s: not a plain file.", $4);
613df930be7Sderaadt 				} else {
614df930be7Sderaadt 					struct tm *t;
615df930be7Sderaadt 					t = gmtime(&stbuf.st_mtime);
616*73fe6daaSflorian 					if (t == NULL) {
617*73fe6daaSflorian 						/* invalid time, use epoch */
618*73fe6daaSflorian 						stbuf.st_mtime = 0;
619*73fe6daaSflorian 						t = gmtime(&stbuf.st_mtime);
620*73fe6daaSflorian 					}
621df930be7Sderaadt 					reply(213,
62269a3ff95Sderaadt 					    "%04d%02d%02d%02d%02d%02d",
62378badebcSmillert 					    1900 + t->tm_year,
62469a3ff95Sderaadt 					    t->tm_mon+1, t->tm_mday,
625df930be7Sderaadt 					    t->tm_hour, t->tm_min, t->tm_sec);
626df930be7Sderaadt 				}
627df930be7Sderaadt 			}
628df930be7Sderaadt 			if ($4 != NULL)
629df930be7Sderaadt 				free($4);
630df930be7Sderaadt 		}
631df930be7Sderaadt 	| QUIT CRLF
632df930be7Sderaadt 		{
633df930be7Sderaadt 			reply(221, "Goodbye.");
634df930be7Sderaadt 			dologout(0);
635df930be7Sderaadt 		}
6362378d5feSmillert 	| error
637df930be7Sderaadt 		{
6382378d5feSmillert 			yyclearin;		/* discard lookahead data */
6392378d5feSmillert 			yyerrok;		/* clear error condition */
6402378d5feSmillert 			state = 0;		/* reset lexer state */
641df930be7Sderaadt 		}
642df930be7Sderaadt 	;
643df930be7Sderaadt rcmd
644df930be7Sderaadt 	: RNFR check_login SP pathname CRLF
645df930be7Sderaadt 		{
646a20a5177Sguenther 			restart_point = 0;
647df930be7Sderaadt 			if ($2 && $4) {
648f59925a5Smillert 				if (fromname)
649f59925a5Smillert 					free(fromname);
650df930be7Sderaadt 				fromname = renamefrom($4);
651f59925a5Smillert 				if (fromname == NULL)
652df930be7Sderaadt 					free($4);
653f59925a5Smillert 			} else if ($4) {
65424713697Sbitblt 				free ($4);
655df930be7Sderaadt 			}
656df930be7Sderaadt 		}
65724713697Sbitblt 
65809a2692aSmillert 	| REST check_login SP file_size CRLF
659df930be7Sderaadt 		{
66024713697Sbitblt 			if ($2) {
661f59925a5Smillert 				if (fromname) {
662f59925a5Smillert 					free(fromname);
663a14f8237Sericj 					fromname = NULL;
664f59925a5Smillert 				}
66509a2692aSmillert 				restart_point = $4;
66609a2692aSmillert 				reply(350, "Restarting at %lld. %s",
66709a2692aSmillert 				    (long long)restart_point,
668df930be7Sderaadt 				    "Send STORE or RETRIEVE to initiate transfer.");
669df930be7Sderaadt 			}
67024713697Sbitblt 		}
671df930be7Sderaadt 	;
672df930be7Sderaadt 
673df930be7Sderaadt username
674df930be7Sderaadt 	: STRING
675df930be7Sderaadt 	;
676df930be7Sderaadt 
677df930be7Sderaadt password
678df930be7Sderaadt 	: /* empty */
679df930be7Sderaadt 		{
680dc88c29eSderaadt 			$$ = calloc(1, sizeof(char));
681df930be7Sderaadt 		}
682df930be7Sderaadt 	| STRING
683df930be7Sderaadt 	;
684df930be7Sderaadt 
685df930be7Sderaadt byte_size
686df930be7Sderaadt 	: NUMBER
687df930be7Sderaadt 	;
688df930be7Sderaadt 
68909a2692aSmillert file_size
69009a2692aSmillert 	: NUMBER
69109a2692aSmillert 		{
69209a2692aSmillert 			$$ = $1;
69309a2692aSmillert 		}
69409a2692aSmillert 	| BIGNUM
69509a2692aSmillert 		{
69609a2692aSmillert 			$$ = $1;
69709a2692aSmillert 		}
69809a2692aSmillert 	;
69909a2692aSmillert 
700df930be7Sderaadt host_port
701df930be7Sderaadt 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
702df930be7Sderaadt 		NUMBER COMMA NUMBER
703df930be7Sderaadt 		{
704df930be7Sderaadt 			char *a, *p;
705df930be7Sderaadt 
706501751e5Sderaadt 			if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 ||
707501751e5Sderaadt 			    $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
708501751e5Sderaadt 			    $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) {
709501751e5Sderaadt 				$$ = 1;
710501751e5Sderaadt 			} else {
711b1750805Sitojun 				data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
712b1750805Sitojun 				data_dest.su_sin.sin_family = AF_INET;
713b1750805Sitojun 				p = (char *)&data_dest.su_sin.sin_port;
714df930be7Sderaadt 				p[0] = $9; p[1] = $11;
715b1750805Sitojun 				a = (char *)&data_dest.su_sin.sin_addr;
716df930be7Sderaadt 				a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
717501751e5Sderaadt 				$$ = 0;
718501751e5Sderaadt 			}
719df930be7Sderaadt 		}
720df930be7Sderaadt 	;
721df930be7Sderaadt 
722b1750805Sitojun host_long_port4
723b1750805Sitojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
724b1750805Sitojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
725b1750805Sitojun 		NUMBER
726b1750805Sitojun 		{
727b1750805Sitojun 			char *a, *p;
728b1750805Sitojun 
729b1750805Sitojun 			/* reject invalid LPRT command */
73095cc8c4aSderaadt 			if ($1 != 4 || $3 != 4 ||
73195cc8c4aSderaadt 			    $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
73295cc8c4aSderaadt 			    $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
73395cc8c4aSderaadt 			    $13 != 2 ||
73495cc8c4aSderaadt 			    $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) {
735b1750805Sitojun 				$$ = 1;
736b1750805Sitojun 			} else {
737b1750805Sitojun 				data_dest.su_sin.sin_len =
738b1750805Sitojun 					sizeof(struct sockaddr_in);
739b1750805Sitojun 				data_dest.su_family = AF_INET;
740b1750805Sitojun 				p = (char *)&data_dest.su_port;
741b1750805Sitojun 				p[0] = $15; p[1] = $17;
742b1750805Sitojun 				a = (char *)&data_dest.su_sin.sin_addr;
743b1750805Sitojun 				a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
744b1750805Sitojun 				$$ = 0;
745b1750805Sitojun 			}
746b1750805Sitojun 		}
747b1750805Sitojun 	;
748b1750805Sitojun 
749b1750805Sitojun host_long_port6
750b1750805Sitojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
751b1750805Sitojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
752b1750805Sitojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
753b1750805Sitojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
754b1750805Sitojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
755b1750805Sitojun 		NUMBER
756b1750805Sitojun 		{
757b1750805Sitojun 			char *a, *p;
758b1750805Sitojun 
759b1750805Sitojun 			/* reject invalid LPRT command */
76095cc8c4aSderaadt 			if ($1 != 6 || $3 != 16 ||
76195cc8c4aSderaadt 			    $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
76295cc8c4aSderaadt 			    $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
76395cc8c4aSderaadt 			    $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 ||
76495cc8c4aSderaadt 			    $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 ||
76595cc8c4aSderaadt 			    $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 ||
76695cc8c4aSderaadt 			    $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 ||
76795cc8c4aSderaadt 			    $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 ||
76895cc8c4aSderaadt 			    $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 ||
76995cc8c4aSderaadt 			    $37 != 2 ||
77095cc8c4aSderaadt 			    $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) {
771b1750805Sitojun 				$$ = 1;
772b1750805Sitojun 			} else {
773b1750805Sitojun 				data_dest.su_sin6.sin6_len =
774b1750805Sitojun 					sizeof(struct sockaddr_in6);
775b1750805Sitojun 				data_dest.su_family = AF_INET6;
776b1750805Sitojun 				p = (char *)&data_dest.su_port;
777b1750805Sitojun 				p[0] = $39; p[1] = $41;
778b1750805Sitojun 				a = (char *)&data_dest.su_sin6.sin6_addr;
779b1750805Sitojun 				 a[0] =  $5;  a[1] =  $7;
780b1750805Sitojun 				 a[2] =  $9;  a[3] = $11;
781b1750805Sitojun 				 a[4] = $13;  a[5] = $15;
782b1750805Sitojun 				 a[6] = $17;  a[7] = $19;
783b1750805Sitojun 				 a[8] = $21;  a[9] = $23;
784b1750805Sitojun 				a[10] = $25; a[11] = $27;
785b1750805Sitojun 				a[12] = $29; a[13] = $31;
786b1750805Sitojun 				a[14] = $33; a[15] = $35;
787b1750805Sitojun 				if (his_addr.su_family == AF_INET6) {
788b1750805Sitojun 					/* XXX more sanity checks! */
789b1750805Sitojun 					data_dest.su_sin6.sin6_scope_id =
790b1750805Sitojun 					    his_addr.su_sin6.sin6_scope_id;
791b1750805Sitojun 				}
792b1750805Sitojun 
793b1750805Sitojun 				$$ = 0;
794b1750805Sitojun 			}
795b1750805Sitojun 		}
796b1750805Sitojun 	;
797b1750805Sitojun 
798df930be7Sderaadt form_code
799df930be7Sderaadt 	: N
800df930be7Sderaadt 		{
801df930be7Sderaadt 			$$ = FORM_N;
802df930be7Sderaadt 		}
803df930be7Sderaadt 	| T
804df930be7Sderaadt 		{
805df930be7Sderaadt 			$$ = FORM_T;
806df930be7Sderaadt 		}
807df930be7Sderaadt 	| C
808df930be7Sderaadt 		{
809df930be7Sderaadt 			$$ = FORM_C;
810df930be7Sderaadt 		}
811df930be7Sderaadt 	;
812df930be7Sderaadt 
813df930be7Sderaadt type_code
814df930be7Sderaadt 	: A
815df930be7Sderaadt 		{
816df930be7Sderaadt 			cmd_type = TYPE_A;
817df930be7Sderaadt 			cmd_form = FORM_N;
818df930be7Sderaadt 		}
819df930be7Sderaadt 	| A SP form_code
820df930be7Sderaadt 		{
821df930be7Sderaadt 			cmd_type = TYPE_A;
822df930be7Sderaadt 			cmd_form = $3;
823df930be7Sderaadt 		}
824df930be7Sderaadt 	| E
825df930be7Sderaadt 		{
826df930be7Sderaadt 			cmd_type = TYPE_E;
827df930be7Sderaadt 			cmd_form = FORM_N;
828df930be7Sderaadt 		}
829df930be7Sderaadt 	| E SP form_code
830df930be7Sderaadt 		{
831df930be7Sderaadt 			cmd_type = TYPE_E;
832df930be7Sderaadt 			cmd_form = $3;
833df930be7Sderaadt 		}
834df930be7Sderaadt 	| I
835df930be7Sderaadt 		{
836df930be7Sderaadt 			cmd_type = TYPE_I;
837df930be7Sderaadt 		}
838df930be7Sderaadt 	| L
839df930be7Sderaadt 		{
840df930be7Sderaadt 			cmd_type = TYPE_L;
84108cb63c2Stedu 			cmd_bytesz = 8;
842df930be7Sderaadt 		}
843df930be7Sderaadt 	| L SP byte_size
844df930be7Sderaadt 		{
845df930be7Sderaadt 			cmd_type = TYPE_L;
846df930be7Sderaadt 			cmd_bytesz = $3;
847df930be7Sderaadt 		}
848df930be7Sderaadt 		/* this is for a bug in the BBN ftp */
849df930be7Sderaadt 	| L byte_size
850df930be7Sderaadt 		{
851df930be7Sderaadt 			cmd_type = TYPE_L;
852df930be7Sderaadt 			cmd_bytesz = $2;
853df930be7Sderaadt 		}
854df930be7Sderaadt 	;
855df930be7Sderaadt 
856df930be7Sderaadt struct_code
857df930be7Sderaadt 	: F
858df930be7Sderaadt 		{
859df930be7Sderaadt 			$$ = STRU_F;
860df930be7Sderaadt 		}
861df930be7Sderaadt 	| R
862df930be7Sderaadt 		{
863df930be7Sderaadt 			$$ = STRU_R;
864df930be7Sderaadt 		}
865df930be7Sderaadt 	| P
866df930be7Sderaadt 		{
867df930be7Sderaadt 			$$ = STRU_P;
868df930be7Sderaadt 		}
869df930be7Sderaadt 	;
870df930be7Sderaadt 
871df930be7Sderaadt mode_code
872df930be7Sderaadt 	: S
873df930be7Sderaadt 		{
874df930be7Sderaadt 			$$ = MODE_S;
875df930be7Sderaadt 		}
876df930be7Sderaadt 	| B
877df930be7Sderaadt 		{
878df930be7Sderaadt 			$$ = MODE_B;
879df930be7Sderaadt 		}
880df930be7Sderaadt 	| C
881df930be7Sderaadt 		{
882df930be7Sderaadt 			$$ = MODE_C;
883df930be7Sderaadt 		}
884df930be7Sderaadt 	;
885df930be7Sderaadt 
886df930be7Sderaadt pathname
887df930be7Sderaadt 	: pathstring
888df930be7Sderaadt 		{
889df930be7Sderaadt 			/*
890df930be7Sderaadt 			 * Problem: this production is used for all pathname
891df930be7Sderaadt 			 * processing, but only gives a 550 error reply.
892df930be7Sderaadt 			 * This is a valid reply in some cases but not in others.
893df930be7Sderaadt 			 */
89435ac9933Sdownsj 			if (logged_in && $1 && strchr($1, '~') != NULL) {
895df930be7Sderaadt 				glob_t gl;
896df930be7Sderaadt 				int flags =
897df930be7Sderaadt 				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
89835ac9933Sdownsj 				char *pptr = $1;
89935ac9933Sdownsj 
90035ac9933Sdownsj 				/*
90135ac9933Sdownsj 				 * glob() will only find a leading ~, but
90235ac9933Sdownsj 				 * Netscape kindly puts a slash in front of
90335ac9933Sdownsj 				 * it for publish URLs.  There needs to be
90435ac9933Sdownsj 				 * a flag for glob() that expands tildes
90535ac9933Sdownsj 				 * anywhere in the string.
90635ac9933Sdownsj 				 */
90735ac9933Sdownsj 				if ((pptr[0] == '/') && (pptr[1] == '~'))
90835ac9933Sdownsj 					pptr++;
909df930be7Sderaadt 
910df930be7Sderaadt 				memset(&gl, 0, sizeof(gl));
91135ac9933Sdownsj 				if (glob(pptr, flags, NULL, &gl) ||
912df930be7Sderaadt 				    gl.gl_pathc == 0) {
913df930be7Sderaadt 					reply(550, "not found");
914df930be7Sderaadt 					$$ = NULL;
915df930be7Sderaadt 				} else {
916df930be7Sderaadt 					$$ = strdup(gl.gl_pathv[0]);
917df930be7Sderaadt 				}
918df930be7Sderaadt 				globfree(&gl);
919df930be7Sderaadt 				free($1);
920df930be7Sderaadt 			} else
921df930be7Sderaadt 				$$ = $1;
922df930be7Sderaadt 		}
923df930be7Sderaadt 	;
924df930be7Sderaadt 
925df930be7Sderaadt pathstring
926df930be7Sderaadt 	: STRING
927df930be7Sderaadt 	;
928df930be7Sderaadt 
929df930be7Sderaadt octal_number
930df930be7Sderaadt 	: NUMBER
931df930be7Sderaadt 		{
932df930be7Sderaadt 			int ret, dec, multby, digit;
933df930be7Sderaadt 
934df930be7Sderaadt 			/*
935df930be7Sderaadt 			 * Convert a number that was read as decimal number
936df930be7Sderaadt 			 * to what it would be if it had been read as octal.
937df930be7Sderaadt 			 */
938df930be7Sderaadt 			dec = $1;
939df930be7Sderaadt 			multby = 1;
940df930be7Sderaadt 			ret = 0;
941df930be7Sderaadt 			while (dec) {
942df930be7Sderaadt 				digit = dec%10;
943df930be7Sderaadt 				if (digit > 7) {
944df930be7Sderaadt 					ret = -1;
945df930be7Sderaadt 					break;
946df930be7Sderaadt 				}
947df930be7Sderaadt 				ret += digit * multby;
948df930be7Sderaadt 				multby *= 8;
949df930be7Sderaadt 				dec /= 10;
950df930be7Sderaadt 			}
951df930be7Sderaadt 			$$ = ret;
952df930be7Sderaadt 		}
953df930be7Sderaadt 	;
954df930be7Sderaadt 
955df930be7Sderaadt 
956df930be7Sderaadt check_login
957df930be7Sderaadt 	: /* empty */
958df930be7Sderaadt 		{
959df930be7Sderaadt 			if (logged_in)
960df930be7Sderaadt 				$$ = 1;
961df930be7Sderaadt 			else {
962df930be7Sderaadt 				reply(530, "Please login with USER and PASS.");
963df930be7Sderaadt 				$$ = 0;
964ad3b829aSmikeb 				state = 0;
965ad3b829aSmikeb 				YYABORT;
966df930be7Sderaadt 			}
967df930be7Sderaadt 		}
968df930be7Sderaadt 	;
969df930be7Sderaadt 
97041fb778fSitojun check_login_epsvall
971e14c8eb3Sitojun 	: /* empty */
972e14c8eb3Sitojun 		{
97341fb778fSitojun 			if (!logged_in) {
97441fb778fSitojun 				reply(530, "Please login with USER and PASS.");
97541fb778fSitojun 				$$ = 0;
976ad3b829aSmikeb 				state = 0;
977ad3b829aSmikeb 				YYABORT;
97841fb778fSitojun 			} else if (epsvall) {
979e14c8eb3Sitojun 				reply(501, "the command is disallowed "
980e14c8eb3Sitojun 				    "after EPSV ALL");
981e14c8eb3Sitojun 				usedefault = 1;
982e14c8eb3Sitojun 				$$ = 0;
983e14c8eb3Sitojun 			} else
984e14c8eb3Sitojun 				$$ = 1;
985e14c8eb3Sitojun 		}
986e14c8eb3Sitojun 	;
987e14c8eb3Sitojun 
988df930be7Sderaadt %%
989df930be7Sderaadt 
990df930be7Sderaadt #define	CMD	0	/* beginning of command */
991df930be7Sderaadt #define	ARGS	1	/* expect miscellaneous arguments */
992df930be7Sderaadt #define	STR1	2	/* expect SP followed by STRING */
993df930be7Sderaadt #define	STR2	3	/* expect STRING */
994df930be7Sderaadt #define	OSTR	4	/* optional SP then STRING */
995df930be7Sderaadt #define	ZSTR1	5	/* SP then optional STRING */
996df930be7Sderaadt #define	ZSTR2	6	/* optional STRING after SP */
997df930be7Sderaadt #define	SITECMD	7	/* SITE command */
998df930be7Sderaadt #define	NSTR	8	/* Number followed by a string */
999df930be7Sderaadt 
1000df930be7Sderaadt struct tab {
1001df930be7Sderaadt 	char	*name;
1002df930be7Sderaadt 	short	token;
1003df930be7Sderaadt 	short	state;
1004df930be7Sderaadt 	short	implemented;	/* 1 if command is implemented */
1005df930be7Sderaadt 	char	*help;
1006df930be7Sderaadt };
1007df930be7Sderaadt 
1008df930be7Sderaadt struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1009df930be7Sderaadt 	{ "USER", USER, STR1, 1,	"<sp> username" },
1010df930be7Sderaadt 	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
1011df930be7Sderaadt 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1012df930be7Sderaadt 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1013df930be7Sderaadt 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1014df930be7Sderaadt 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1015df930be7Sderaadt 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
1016b1750805Sitojun 	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1017b1750805Sitojun 	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1018df930be7Sderaadt 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
1019b1750805Sitojun 	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
1020b1750805Sitojun 	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1021df930be7Sderaadt 	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
1022df930be7Sderaadt 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1023df930be7Sderaadt 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1024df930be7Sderaadt 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1025df930be7Sderaadt 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1026df930be7Sderaadt 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1027df930be7Sderaadt 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1028df930be7Sderaadt 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1029df930be7Sderaadt 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1030df930be7Sderaadt 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1031df930be7Sderaadt 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1032df930be7Sderaadt 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1033df930be7Sderaadt 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1034df930be7Sderaadt 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1035df930be7Sderaadt 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1036df930be7Sderaadt 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1037df930be7Sderaadt 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1038df930be7Sderaadt 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1039df930be7Sderaadt 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1040df930be7Sderaadt 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1041df930be7Sderaadt 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1042df930be7Sderaadt 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1043df930be7Sderaadt 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1044df930be7Sderaadt 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1045df930be7Sderaadt 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
1046df930be7Sderaadt 	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1047df930be7Sderaadt 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1048df930be7Sderaadt 	{ "NOOP", NOOP, ARGS, 1,	"" },
1049df930be7Sderaadt 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1050df930be7Sderaadt 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1051df930be7Sderaadt 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1052df930be7Sderaadt 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1053df930be7Sderaadt 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1054df930be7Sderaadt 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1055df930be7Sderaadt 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1056df930be7Sderaadt 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1057df930be7Sderaadt 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1058df930be7Sderaadt 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1059df930be7Sderaadt 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1060df930be7Sderaadt 	{ NULL,   0,    0,    0,	0 }
1061df930be7Sderaadt };
1062df930be7Sderaadt 
1063df930be7Sderaadt struct tab sitetab[] = {
1064df930be7Sderaadt 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1065df930be7Sderaadt 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1066df930be7Sderaadt 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1067df930be7Sderaadt 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1068df930be7Sderaadt 	{ NULL,   0,    0,    0,	0 }
1069df930be7Sderaadt };
1070df930be7Sderaadt 
1071c72b5b24Smillert static void	 help(struct tab *, char *);
1072df930be7Sderaadt static struct tab *
10730c50dd5dSjan 		 lookup(struct tab *, const char *);
10740c50dd5dSjan static void	 sizecmd(const char *);
1075c72b5b24Smillert static int	 yylex(void);
1076df930be7Sderaadt 
1077b1750805Sitojun extern int epsvall;
1078b1750805Sitojun 
1079df930be7Sderaadt static struct tab *
lookup(struct tab * p,const char * cmd)108018a2fcc3Sjan lookup(struct tab *p, const char *cmd)
1081df930be7Sderaadt {
1082df930be7Sderaadt 
1083df930be7Sderaadt 	for (; p->name != NULL; p++)
1084df930be7Sderaadt 		if (strcmp(cmd, p->name) == 0)
1085df930be7Sderaadt 			return (p);
1086cbe1e71dSmpech 	return (NULL);
1087df930be7Sderaadt }
1088df930be7Sderaadt 
1089df930be7Sderaadt #include <arpa/telnet.h>
1090df930be7Sderaadt 
1091df930be7Sderaadt /*
1092f9bbbf45Sfgsch  * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1093df930be7Sderaadt  */
10940391227fSmoritz int
get_line(char * s,int n)109518a2fcc3Sjan get_line(char *s, int n)
1096df930be7Sderaadt {
1097df930be7Sderaadt 	int c;
109850d2b651Smpech 	char *cs;
1099df930be7Sderaadt 
1100df930be7Sderaadt 	cs = s;
1101df930be7Sderaadt /* tmpline may contain saved command from urgent mode interruption */
1102df930be7Sderaadt 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1103df930be7Sderaadt 		*cs++ = tmpline[c];
1104df930be7Sderaadt 		if (tmpline[c] == '\n') {
1105df930be7Sderaadt 			*cs++ = '\0';
1106df930be7Sderaadt 			if (debug)
1107df930be7Sderaadt 				syslog(LOG_DEBUG, "command: %s", s);
1108df930be7Sderaadt 			tmpline[0] = '\0';
11090391227fSmoritz 			return(0);
1110df930be7Sderaadt 		}
1111df930be7Sderaadt 		if (c == 0)
1112df930be7Sderaadt 			tmpline[0] = '\0';
1113df930be7Sderaadt 	}
1114330fc432Sjan 	while ((c = getc(stdin)) != EOF) {
1115df930be7Sderaadt 		c &= 0377;
1116df930be7Sderaadt 		if (c == IAC) {
1117330fc432Sjan 		    if ((c = getc(stdin)) != EOF) {
1118df930be7Sderaadt 			c &= 0377;
1119df930be7Sderaadt 			switch (c) {
1120df930be7Sderaadt 			case WILL:
1121df930be7Sderaadt 			case WONT:
1122330fc432Sjan 				c = getc(stdin);
1123df930be7Sderaadt 				printf("%c%c%c", IAC, DONT, 0377&c);
1124df930be7Sderaadt 				(void) fflush(stdout);
1125df930be7Sderaadt 				continue;
1126df930be7Sderaadt 			case DO:
1127df930be7Sderaadt 			case DONT:
1128330fc432Sjan 				c = getc(stdin);
1129df930be7Sderaadt 				printf("%c%c%c", IAC, WONT, 0377&c);
1130df930be7Sderaadt 				(void) fflush(stdout);
1131df930be7Sderaadt 				continue;
1132df930be7Sderaadt 			case IAC:
1133df930be7Sderaadt 				break;
1134df930be7Sderaadt 			default:
1135df930be7Sderaadt 				continue;	/* ignore command */
1136df930be7Sderaadt 			}
1137df930be7Sderaadt 		    }
1138df930be7Sderaadt 		}
1139df930be7Sderaadt 		*cs++ = c;
11400391227fSmoritz 		if (--n <= 0) {
11410391227fSmoritz 			/*
11420391227fSmoritz 			 * If command doesn't fit into buffer, discard the
11430391227fSmoritz 			 * rest of the command and indicate truncation.
11440391227fSmoritz 			 * This prevents the command to be split up into
11450391227fSmoritz 			 * multiple commands.
11460391227fSmoritz 			 */
1147330fc432Sjan 			while (c != '\n' && (c = getc(stdin)) != EOF)
11480391227fSmoritz 				;
11490391227fSmoritz 			return (-2);
11500391227fSmoritz 		}
11510391227fSmoritz 		if (c == '\n')
1152df930be7Sderaadt 			break;
1153df930be7Sderaadt 	}
1154df930be7Sderaadt 	if (c == EOF && cs == s)
11550391227fSmoritz 		return (-1);
1156df930be7Sderaadt 	*cs++ = '\0';
1157df930be7Sderaadt 	if (debug) {
1158df930be7Sderaadt 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1159df930be7Sderaadt 			/* Don't syslog passwords */
1160df930be7Sderaadt 			syslog(LOG_DEBUG, "command: %.5s ???", s);
1161df930be7Sderaadt 		} else {
116250d2b651Smpech 			char *cp;
116350d2b651Smpech 			int len;
1164df930be7Sderaadt 
1165df930be7Sderaadt 			/* Don't syslog trailing CR-LF */
1166df930be7Sderaadt 			len = strlen(s);
1167df930be7Sderaadt 			cp = s + len - 1;
1168df930be7Sderaadt 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1169df930be7Sderaadt 				--cp;
1170df930be7Sderaadt 				--len;
1171df930be7Sderaadt 			}
1172df930be7Sderaadt 			syslog(LOG_DEBUG, "command: %.*s", len, s);
1173df930be7Sderaadt 		}
1174df930be7Sderaadt 	}
11750391227fSmoritz 	return (0);
1176df930be7Sderaadt }
1177df930be7Sderaadt 
1178c5d07883Sbitblt void
toolong(int signo)117918a2fcc3Sjan toolong(int signo)
1180df930be7Sderaadt {
1181abe43e41Sderaadt 	struct syslog_data sdata = SYSLOG_DATA_INIT;
1182df930be7Sderaadt 
11838b6f1defSderaadt 	reply_r(421,
1184df930be7Sderaadt 	    "Timeout (%d seconds): closing control connection.", timeout);
1185df930be7Sderaadt 	if (logging)
1186abe43e41Sderaadt 		syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds",
1187df930be7Sderaadt 		    (pw ? pw -> pw_name : "unknown"), timeout);
1188df930be7Sderaadt 	dologout(1);
1189df930be7Sderaadt }
1190df930be7Sderaadt 
1191df930be7Sderaadt static int
yylex(void)119218a2fcc3Sjan yylex(void)
1193df930be7Sderaadt {
11942378d5feSmillert 	static int cpos;
1195df930be7Sderaadt 	char *cp, *cp2;
1196df930be7Sderaadt 	struct tab *p;
1197df930be7Sderaadt 	int n;
1198df930be7Sderaadt 	char c;
1199df930be7Sderaadt 
1200df930be7Sderaadt 	for (;;) {
1201df930be7Sderaadt 		switch (state) {
1202df930be7Sderaadt 
1203df930be7Sderaadt 		case CMD:
1204df930be7Sderaadt 			(void) alarm((unsigned) timeout);
1205330fc432Sjan 			n = get_line(cbuf, sizeof(cbuf)-1);
12060391227fSmoritz 			if (n == -1) {
1207df930be7Sderaadt 				reply(221, "You could at least say goodbye.");
1208df930be7Sderaadt 				dologout(0);
12090391227fSmoritz 			} else if (n == -2) {
1210e9093204Smoritz 				reply(500, "Command too long.");
1211e9093204Smoritz 				alarm(0);
1212e9093204Smoritz 				continue;
1213df930be7Sderaadt 			}
1214df930be7Sderaadt 			(void) alarm(0);
1215df930be7Sderaadt 			if ((cp = strchr(cbuf, '\r'))) {
1216df930be7Sderaadt 				*cp++ = '\n';
1217df930be7Sderaadt 				*cp = '\0';
1218df930be7Sderaadt 			}
1219a14f8237Sericj 			if (strncasecmp(cbuf, "PASS", 4) != 0) {
1220d36a909dSderaadt 				if ((cp = strpbrk(cbuf, "\n"))) {
1221d36a909dSderaadt 					c = *cp;
1222d36a909dSderaadt 					*cp = '\0';
1223d36a909dSderaadt 					setproctitle("%s: %s", proctitle, cbuf);
1224d36a909dSderaadt 					*cp = c;
1225d36a909dSderaadt 				}
12261f83c55eSderaadt 			}
1227df930be7Sderaadt 			if ((cp = strpbrk(cbuf, " \n")))
1228df930be7Sderaadt 				cpos = cp - cbuf;
1229df930be7Sderaadt 			if (cpos == 0)
1230df930be7Sderaadt 				cpos = 4;
1231df930be7Sderaadt 			c = cbuf[cpos];
1232df930be7Sderaadt 			cbuf[cpos] = '\0';
1233df930be7Sderaadt 			upper(cbuf);
1234df930be7Sderaadt 			p = lookup(cmdtab, cbuf);
1235df930be7Sderaadt 			cbuf[cpos] = c;
1236cbe1e71dSmpech 			if (p != NULL) {
1237df930be7Sderaadt 				if (p->implemented == 0) {
1238df930be7Sderaadt 					nack(p->name);
12392378d5feSmillert 					return (LEXERR);
1240df930be7Sderaadt 				}
1241df930be7Sderaadt 				state = p->state;
1242df930be7Sderaadt 				yylval.s = p->name;
1243df930be7Sderaadt 				return (p->token);
1244df930be7Sderaadt 			}
1245df930be7Sderaadt 			break;
1246df930be7Sderaadt 
1247df930be7Sderaadt 		case SITECMD:
1248df930be7Sderaadt 			if (cbuf[cpos] == ' ') {
1249df930be7Sderaadt 				cpos++;
1250df930be7Sderaadt 				return (SP);
1251df930be7Sderaadt 			}
1252df930be7Sderaadt 			cp = &cbuf[cpos];
1253df930be7Sderaadt 			if ((cp2 = strpbrk(cp, " \n")))
1254df930be7Sderaadt 				cpos = cp2 - cbuf;
1255df930be7Sderaadt 			c = cbuf[cpos];
1256df930be7Sderaadt 			cbuf[cpos] = '\0';
1257df930be7Sderaadt 			upper(cp);
1258df930be7Sderaadt 			p = lookup(sitetab, cp);
1259df930be7Sderaadt 			cbuf[cpos] = c;
1260cbe1e71dSmpech 			if (p != NULL) {
1261df930be7Sderaadt 				if (p->implemented == 0) {
1262df930be7Sderaadt 					state = CMD;
1263df930be7Sderaadt 					nack(p->name);
12642378d5feSmillert 					return (LEXERR);
1265df930be7Sderaadt 				}
1266df930be7Sderaadt 				state = p->state;
1267df930be7Sderaadt 				yylval.s = p->name;
1268df930be7Sderaadt 				return (p->token);
1269df930be7Sderaadt 			}
1270df930be7Sderaadt 			state = CMD;
1271df930be7Sderaadt 			break;
1272df930be7Sderaadt 
1273df930be7Sderaadt 		case OSTR:
1274df930be7Sderaadt 			if (cbuf[cpos] == '\n') {
1275df930be7Sderaadt 				state = CMD;
1276df930be7Sderaadt 				return (CRLF);
1277df930be7Sderaadt 			}
1278df930be7Sderaadt 			/* FALLTHROUGH */
1279df930be7Sderaadt 
1280df930be7Sderaadt 		case STR1:
1281df930be7Sderaadt 		case ZSTR1:
1282df930be7Sderaadt 		dostr1:
1283df930be7Sderaadt 			if (cbuf[cpos] == ' ') {
1284df930be7Sderaadt 				cpos++;
1285d9661006Sderaadt 				state = state == OSTR ? STR2 : state+1;
1286df930be7Sderaadt 				return (SP);
1287df930be7Sderaadt 			}
1288df930be7Sderaadt 			break;
1289df930be7Sderaadt 
1290df930be7Sderaadt 		case ZSTR2:
1291df930be7Sderaadt 			if (cbuf[cpos] == '\n') {
1292df930be7Sderaadt 				state = CMD;
1293df930be7Sderaadt 				return (CRLF);
1294df930be7Sderaadt 			}
1295df930be7Sderaadt 			/* FALLTHROUGH */
1296df930be7Sderaadt 
1297df930be7Sderaadt 		case STR2:
1298df930be7Sderaadt 			cp = &cbuf[cpos];
1299df930be7Sderaadt 			n = strlen(cp);
1300df930be7Sderaadt 			cpos += n - 1;
1301df930be7Sderaadt 			/*
1302df930be7Sderaadt 			 * Make sure the string is nonempty and \n terminated.
1303df930be7Sderaadt 			 */
1304df930be7Sderaadt 			if (n > 1 && cbuf[cpos] == '\n') {
1305df930be7Sderaadt 				cbuf[cpos] = '\0';
13060cae0519Sdownsj 				yylval.s = strdup(cp);
13070cae0519Sdownsj 				if (yylval.s == NULL)
13080cae0519Sdownsj 					fatal("Ran out of memory.");
1309df930be7Sderaadt 				cbuf[cpos] = '\n';
1310df930be7Sderaadt 				state = ARGS;
1311df930be7Sderaadt 				return (STRING);
1312df930be7Sderaadt 			}
1313df930be7Sderaadt 			break;
1314df930be7Sderaadt 
1315df930be7Sderaadt 		case NSTR:
1316df930be7Sderaadt 			if (cbuf[cpos] == ' ') {
1317df930be7Sderaadt 				cpos++;
1318df930be7Sderaadt 				return (SP);
1319df930be7Sderaadt 			}
13204207a9b6Sderaadt 			if (isdigit((unsigned char)cbuf[cpos])) {
1321df930be7Sderaadt 				cp = &cbuf[cpos];
13224207a9b6Sderaadt 				while (isdigit((unsigned char)cbuf[++cpos]))
1323df930be7Sderaadt 					;
1324df930be7Sderaadt 				c = cbuf[cpos];
1325df930be7Sderaadt 				cbuf[cpos] = '\0';
1326df930be7Sderaadt 				yylval.i = atoi(cp);
1327df930be7Sderaadt 				cbuf[cpos] = c;
1328df930be7Sderaadt 				state = STR1;
1329df930be7Sderaadt 				return (NUMBER);
1330df930be7Sderaadt 			}
1331df930be7Sderaadt 			state = STR1;
1332df930be7Sderaadt 			goto dostr1;
1333df930be7Sderaadt 
1334df930be7Sderaadt 		case ARGS:
13354207a9b6Sderaadt 			if (isdigit((unsigned char)cbuf[cpos])) {
133609a2692aSmillert 				long long llval;
133709a2692aSmillert 
1338df930be7Sderaadt 				cp = &cbuf[cpos];
133909a2692aSmillert 				errno = 0;
134009a2692aSmillert 				llval = strtoll(cp, &cp2, 10);
134109a2692aSmillert 				if (llval < 0 ||
134209a2692aSmillert 				    (errno == ERANGE && llval == LLONG_MAX))
134309a2692aSmillert 					break;
134409a2692aSmillert 
134509a2692aSmillert 				cpos = (int)(cp2 - cbuf);
134609a2692aSmillert 				if (llval > INT_MAX) {
134709a2692aSmillert 					yylval.o = llval;
134809a2692aSmillert 					return (BIGNUM);
134909a2692aSmillert 				} else {
135009a2692aSmillert 					yylval.i = (int)llval;
1351df930be7Sderaadt 					return (NUMBER);
1352df930be7Sderaadt 				}
135309a2692aSmillert 			}
135495cc8c4aSderaadt 			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 &&
13554207a9b6Sderaadt 			    !isalnum((unsigned char)cbuf[cpos + 3])) {
1356b1750805Sitojun 				cpos += 3;
1357b1750805Sitojun 				return ALL;
1358b1750805Sitojun 			}
1359df930be7Sderaadt 			switch (cbuf[cpos++]) {
1360df930be7Sderaadt 
1361df930be7Sderaadt 			case '\n':
1362df930be7Sderaadt 				state = CMD;
1363df930be7Sderaadt 				return (CRLF);
1364df930be7Sderaadt 
1365df930be7Sderaadt 			case ' ':
1366df930be7Sderaadt 				return (SP);
1367df930be7Sderaadt 
1368df930be7Sderaadt 			case ',':
1369df930be7Sderaadt 				return (COMMA);
1370df930be7Sderaadt 
1371df930be7Sderaadt 			case 'A':
1372df930be7Sderaadt 			case 'a':
1373df930be7Sderaadt 				return (A);
1374df930be7Sderaadt 
1375df930be7Sderaadt 			case 'B':
1376df930be7Sderaadt 			case 'b':
1377df930be7Sderaadt 				return (B);
1378df930be7Sderaadt 
1379df930be7Sderaadt 			case 'C':
1380df930be7Sderaadt 			case 'c':
1381df930be7Sderaadt 				return (C);
1382df930be7Sderaadt 
1383df930be7Sderaadt 			case 'E':
1384df930be7Sderaadt 			case 'e':
1385df930be7Sderaadt 				return (E);
1386df930be7Sderaadt 
1387df930be7Sderaadt 			case 'F':
1388df930be7Sderaadt 			case 'f':
1389df930be7Sderaadt 				return (F);
1390df930be7Sderaadt 
1391df930be7Sderaadt 			case 'I':
1392df930be7Sderaadt 			case 'i':
1393df930be7Sderaadt 				return (I);
1394df930be7Sderaadt 
1395df930be7Sderaadt 			case 'L':
1396df930be7Sderaadt 			case 'l':
1397df930be7Sderaadt 				return (L);
1398df930be7Sderaadt 
1399df930be7Sderaadt 			case 'N':
1400df930be7Sderaadt 			case 'n':
1401df930be7Sderaadt 				return (N);
1402df930be7Sderaadt 
1403df930be7Sderaadt 			case 'P':
1404df930be7Sderaadt 			case 'p':
1405df930be7Sderaadt 				return (P);
1406df930be7Sderaadt 
1407df930be7Sderaadt 			case 'R':
1408df930be7Sderaadt 			case 'r':
1409df930be7Sderaadt 				return (R);
1410df930be7Sderaadt 
1411df930be7Sderaadt 			case 'S':
1412df930be7Sderaadt 			case 's':
1413df930be7Sderaadt 				return (S);
1414df930be7Sderaadt 
1415df930be7Sderaadt 			case 'T':
1416df930be7Sderaadt 			case 't':
1417df930be7Sderaadt 				return (T);
1418df930be7Sderaadt 
1419df930be7Sderaadt 			}
1420df930be7Sderaadt 			break;
1421df930be7Sderaadt 
1422df930be7Sderaadt 		default:
1423df930be7Sderaadt 			fatal("Unknown state in scanner.");
1424df930be7Sderaadt 		}
1425df930be7Sderaadt 		state = CMD;
14262378d5feSmillert 		return (LEXERR);
1427df930be7Sderaadt 	}
1428df930be7Sderaadt }
1429df930be7Sderaadt 
1430df930be7Sderaadt void
upper(char * s)143118a2fcc3Sjan upper(char *s)
1432df930be7Sderaadt {
143327b9e962Smpech 	char *p;
143427b9e962Smpech 
1435c7141df2Sjan 	for (p = s; *p; p++)
14364207a9b6Sderaadt 		*p = (char)toupper((unsigned char)*p);
1437df930be7Sderaadt }
1438df930be7Sderaadt 
1439df930be7Sderaadt static void
help(struct tab * ctab,char * s)144018a2fcc3Sjan help(struct tab *ctab, char *s)
1441df930be7Sderaadt {
1442df930be7Sderaadt 	struct tab *c;
1443df930be7Sderaadt 	int width, NCMDS;
1444df930be7Sderaadt 	char *type;
1445df930be7Sderaadt 
1446df930be7Sderaadt 	if (ctab == sitetab)
1447df930be7Sderaadt 		type = "SITE ";
1448df930be7Sderaadt 	else
1449df930be7Sderaadt 		type = "";
1450df930be7Sderaadt 	width = 0, NCMDS = 0;
1451df930be7Sderaadt 	for (c = ctab; c->name != NULL; c++) {
1452df930be7Sderaadt 		int len = strlen(c->name);
1453df930be7Sderaadt 
1454df930be7Sderaadt 		if (len > width)
1455df930be7Sderaadt 			width = len;
1456df930be7Sderaadt 		NCMDS++;
1457df930be7Sderaadt 	}
1458df930be7Sderaadt 	width = (width + 8) &~ 7;
14596ba2067dSmpech 	if (s == NULL) {
1460df930be7Sderaadt 		int i, j, w;
1461df930be7Sderaadt 		int columns, lines;
1462df930be7Sderaadt 
1463df930be7Sderaadt 		lreply(214, "The following %scommands are recognized %s.",
1464df930be7Sderaadt 		    type, "(* =>'s unimplemented)");
1465df930be7Sderaadt 		columns = 76 / width;
1466df930be7Sderaadt 		if (columns == 0)
1467df930be7Sderaadt 			columns = 1;
1468df930be7Sderaadt 		lines = (NCMDS + columns - 1) / columns;
1469df930be7Sderaadt 		for (i = 0; i < lines; i++) {
1470df930be7Sderaadt 			printf("   ");
1471df930be7Sderaadt 			for (j = 0; j < columns; j++) {
1472df930be7Sderaadt 				c = ctab + j * lines + i;
1473df930be7Sderaadt 				printf("%s%c", c->name,
1474df930be7Sderaadt 					c->implemented ? ' ' : '*');
1475df930be7Sderaadt 				if (c + lines >= &ctab[NCMDS])
1476df930be7Sderaadt 					break;
1477df930be7Sderaadt 				w = strlen(c->name) + 1;
1478df930be7Sderaadt 				while (w < width) {
1479df930be7Sderaadt 					putchar(' ');
1480df930be7Sderaadt 					w++;
1481df930be7Sderaadt 				}
1482df930be7Sderaadt 			}
1483df930be7Sderaadt 			printf("\r\n");
1484df930be7Sderaadt 		}
1485df930be7Sderaadt 		(void) fflush(stdout);
1486df930be7Sderaadt 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1487df930be7Sderaadt 		return;
1488df930be7Sderaadt 	}
1489df930be7Sderaadt 	upper(s);
1490df930be7Sderaadt 	c = lookup(ctab, s);
1491cbe1e71dSmpech 	if (c == NULL) {
1492df930be7Sderaadt 		reply(502, "Unknown command %s.", s);
1493df930be7Sderaadt 		return;
1494df930be7Sderaadt 	}
1495df930be7Sderaadt 	if (c->implemented)
1496df930be7Sderaadt 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1497df930be7Sderaadt 	else
1498df930be7Sderaadt 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1499df930be7Sderaadt 		    c->name, c->help);
1500df930be7Sderaadt }
1501df930be7Sderaadt 
1502df930be7Sderaadt static void
sizecmd(const char * filename)150318a2fcc3Sjan sizecmd(const char *filename)
1504df930be7Sderaadt {
1505df930be7Sderaadt 	switch (type) {
1506df930be7Sderaadt 	case TYPE_L:
1507df930be7Sderaadt 	case TYPE_I: {
1508df930be7Sderaadt 		struct stat stbuf;
1509df69c215Sderaadt 		if (stat(filename, &stbuf) == -1 || !S_ISREG(stbuf.st_mode))
1510df930be7Sderaadt 			reply(550, "%s: not a plain file.", filename);
1511df930be7Sderaadt 		else
151209a2692aSmillert 			reply(213, "%lld", (long long)stbuf.st_size);
1513df930be7Sderaadt 		break; }
1514df930be7Sderaadt 	case TYPE_A: {
1515df930be7Sderaadt 		FILE *fin;
1516df930be7Sderaadt 		int c;
1517df930be7Sderaadt 		off_t count;
1518df930be7Sderaadt 		struct stat stbuf;
1519df930be7Sderaadt 		fin = fopen(filename, "r");
1520df930be7Sderaadt 		if (fin == NULL) {
1521df930be7Sderaadt 			perror_reply(550, filename);
1522df930be7Sderaadt 			return;
1523df930be7Sderaadt 		}
1524df69c215Sderaadt 		if (fstat(fileno(fin), &stbuf) == -1 || !S_ISREG(stbuf.st_mode)) {
1525df930be7Sderaadt 			reply(550, "%s: not a plain file.", filename);
1526df930be7Sderaadt 			(void) fclose(fin);
1527df930be7Sderaadt 			return;
1528df930be7Sderaadt 		}
152913b440dcSitojun 		if (stbuf.st_size > 10240) {
153013b440dcSitojun 			reply(550, "%s: file too large for SIZE.", filename);
153113b440dcSitojun 			(void) fclose(fin);
153213b440dcSitojun 			return;
153313b440dcSitojun 		}
1534df930be7Sderaadt 
1535df930be7Sderaadt 		count = 0;
1536df930be7Sderaadt 		while((c = getc(fin)) != EOF) {
1537df930be7Sderaadt 			if (c == '\n')	/* will get expanded to \r\n */
1538df930be7Sderaadt 				count++;
1539df930be7Sderaadt 			count++;
1540df930be7Sderaadt 		}
1541df930be7Sderaadt 		(void) fclose(fin);
1542df930be7Sderaadt 
154309a2692aSmillert 		reply(213, "%lld", (long long)count);
1544df930be7Sderaadt 		break; }
1545df930be7Sderaadt 	default:
1546df930be7Sderaadt 		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
1547df930be7Sderaadt 	}
1548df930be7Sderaadt }
1549